среда, 18 апреля 2012 г.

Python 4/4: GUI библиотеки

Это перевод последней статьи в серии о языке программирования Python, все оригинальные статьи можно найти здесь

Пишем графический интерфейс для нашего RSS аггрегатора


План

Я завершу рассмотрение языка программирование Python обзором графической библиотеки Tkinter. Существует большое количество других графических библиотек доступных для Python-a наиболее известная из которых wxPython. Я выбрал Tkinter потому, что он поставляется вместе с самим Python, и главной целью является использовать то, что сразу доступно "из коробки".

Ожидаемый результат

После изучение этой статьи:
    вы будете иметь:
  • базовый GUI для RSS ридера из предыдущей статьи
    вы будете знать:
  • как создавать GUI используя Tkinter
  • как использовать lambda выражение для создания функций на лету

Наше окончательное GUI будет выглядить так:


Файлы используемые в нашем проекте

  • RssReader.txt: Библиотека для чтения RSS информации, которую я написал для предыдущего проекта. Переименуйте файл в "RssReader.py" после того, как скачаете.
  • RssReaderGui.txt: Исходный код для нашего проекта. Переименуйте файл в "RssReaderGui.py" после того, как скачаете.
  • feeds.txt: Простой файл с вводимыми данными.

Код


Библиотеки

from RssReader import generateRsses
from Tkinter import *
from tkFileDialog import askopenfilename


  • Tkinter: Python-овская встроенная GUI библиотека.
  • tkFileDialog: Стандартный диалог для выбора файлов.

Глобальные переменные

currentFeeds = {}
rssDisplay = None
chooseChannel = None
currentChannel = None
currentFeeds = None

Я вижу два способа организация кода для одного простого GUI окна в Python. Первый - разместить всё в одном большом классе. Второй -написать группу функций, которые будут совместно использовать один набор глобальных переменных. В этом случае единстсвенный класс имел бы те же проблемы как и глобальные переменные с необходимостью добавлять self перед каждым вызовом метода и переменной класса.
Переменные, которым присвоено значение None в коде вверху являются плейсхолдерами для глобальных переменных, которые будут назначены позже. Их не обязательно назначать здесь, но это полезно с самого начала знать какие у вас глобальные переменные.

Создание окна верхнего уровня

def rssWindow():
    root = Tk()
    root.title("Rss Reader")
    root.geometry("750x500")
 root = channelFrame(root)
    root = buttonFrame(root) 
    return root

Хотя вы можете определить ваши функции в произвольном порядке, я написал их в более менее иерархическом порядке. Я пройдусь по ним строка за строкой:
2. На самом верхнем уровне мы создаем Tk объект. 3. Задаем заголовок для окна. 4. задаем начальный размер. 5. Фрейм который содержит выпадающее (drop down) меню и список статей. 6. Фрейм который содержит кнопки выбора и загрузки.

Upper Window c Dropdown и Listbox Layout-ом


def channelFrame(parent):
 channelFrame = Frame(parent)
 channelSelectFrame = Frame(channelFrame)
 channelSelectFrame = channelLabel(channelSelectFrame)
 channelSelectFrame = channelSelect(channelSelectFrame)
 channelSelectFrame.pack(side=LEFT)
 channelFrame = rssDisplay(channelFrame)
 channelFrame.pack(side=TOP, expand=YES, fill=BOTH)
 return parent 

Здесь мы создаем окно, содержащее текстовую информацию, поступающую от RSS feeds. Для этого я создал фреймы которые содержат или другие фреймы или элементы GUI. Как только элементы созданы и организованы во фрейм, поведение окна устанавливается с помощью .pack


def rssDisplay(parent):
    global rssDisplay
    rssDisplay = Listbox(parent)
    rssDisplay.pack(side=RIGHT, expand=YES, fill=BOTH)
    return parent
  
def channelLabel(parent):
    label = Label(parent, text="Select a channel:")
    label.pack(side=TOP)
    return parent
  
def channelSelect(parent):
    global chooseChannel, currentChannel
    currentChannel = StringVar(parent)
    channelList = ["None"]
    currentChannel.set(channelList[0])
    chooseChannel = OptionMenu(parent, currentChannel, *channelList)
    chooseChannel.pack(side=BOTTOM)
    return parent

Здесь созданы отдельные компоненты, поведение каждого из которых быдет описано ниже. В строке 21 используется ключевое слово global. Вам нужно использовать это ключевое слово только если вы планируете изменение этой переменной, но не собираетесь обращаться к ней.

Окно низшего уровня с кнопками'Set Config File' и 'Load Feeds'


def buttonFrame(parent):
    buttonFrame = Frame(parent)
    buttonFrame = setConfigFileButton(buttonFrame)
    buttonFrame = loadRssButton(buttonFrame)
    buttonFrame.pack(side=RIGHT, expand=YES, fill=X)
    return parent
 
def setConfigFileButton(parent):
    button = Button(parent, command=setConfigFile)
    button["text"] = "Set Config File"
    button.pack(side=LEFT)
    return parent
 
def loadRssButton(parent):
    button = Button(parent, command=loadRss)
    button["text"] = "Load Feeds"
    button.pack(side=RIGHT)
    return parent

Здесь я создаю лейаут верхнего уровня для каждой кнопки где описываю специфическое поведение каждой кнопки. Мы описываем поведение кнопок передачей функции, вызываемой с помощью аргумента command.

Команды вызываемые разными элементами


Lambda, функции первого класса

def loadRss():
    global chooseChannel, currentChannel
    chooseChannel["menu"].delete(0, END)
    channelList = currentFeeds.keys()
    for channelName in channelList:
     chooseChannel["menu"].add_command(label=channelName, 
         command=lambda (temp = channelName): selectChannel(temp))
    selectChannel(channelList[0])

Функция loadRss очищает значения drop down меню и затем добавляет в него каждый из текущих каналов. Она также использует lambda функцию, чтобы задать поведение drop down элемента когда канал выбирается.

Lambda функции, такие как используемая в строке 7 вверху, чрезвычайно полезны. Эточень удобно, как и показано вверху, использовать их, когда вам необходимо создать функцию, для которой некоторые из внутренних переменных неизвестны, то момента выполнения. Здесь мы используем lambda функцию, чтобы назначить значение по умолчанию, channelName функции selectChannel и определить её как chooseChannel команду.
В общем виде lambda функции используются в виде:
 lambda arg, arg: arg + arg
Для людей, кто не привык к функциональному программированию важно запомнить, что в lambda функции может быть только одна единственная строка и значение этой строки всегда возвращается без необходимости использовать ключевое слово return
.
def selectChannel(channelName):
    global chooseChannel, rssDisplay
    chooseChannel.setvar(chooseChannel.cget("textvariable"), value = channelName)
    rssDisplay.delete(0, END)
    for feed in currentFeeds[channelName]:
     rssDisplay.insert(END, feed)

Когда один из канклов выбран в drop down меню, соответсвующая функция выполняется и заполняет отображаемое окно текстом текущей статьи.
def setConfigFile():
    global currentFeeds
    currentFeedFile = askopenfilename(filetypes=[("allfiles", "*"), 
                                                 ("textfiles","*.txt")])
    currentFeeds = combineFeeds(currentFeedFile)
    loadRss()

В данном случае программа открывает файловый диалог с помощью ,kopenfilename который возвратит выбранный файл. Затем программа объединит все feeds имеющие одинаковые имена каналов и загрузит их select box, чтобы отобразить с помощью loadRSS.
def combineFeeds(fileName):
    feeds = {}
 for feed in generateRsses(fileName):
     for channelName in feed.keys():
       if feeds.has_key(channelName):
         feeds[channelName] += feed[channelName]
       else:
         feeds[channelName] = feed[channelName]
 return feeds


combineFeeds читает источники RSS из данного файла, загружает feeds используя библиотеку из предыдущей статьи и зате комбинирует все каналы, имеющие одно и то же имя.
if __name__ == "__main__":
   rssWindow().mainloop()

Чтобы создать окно и затем его использовать, наша программа вызывает rssWindow(), описанную в начале файле, затем запускает метод mainloop().


Подведение итога


Я получал удовольствие знакомясь с Python и пришел к выводу что язык программирования Python имеет:
  • пологую кривую обучения;
  • много хорошо документированных библиотек;
  • легко читаемый синтаксис;
  • гибкую семантику
На следующей неделе я буду знакомиться с Markdown, легковесным языком разметки, который позволяет создавать хорошо размеченные документы используя обычные текстовые редакторы. В декабре я собираюсь рассмотреть функциональный и объектно-ориентированный язык программирования Scala запускаемый на Java VM.

Ресурсы



Автор: Frank Berthold
Оригинальная статья: Python 4/4: GUI Libraries

Комментариев нет:

Отправить комментарий