Это краткая справочная информация к Tkinter. Ее следует рассматривать как дополнение, а не альтернативу основному справочному материалу. Глубина изложения варьируется от самого простого уровня до изощренных приемов. Документ содержит информацию, которую мне самому было трудно найти.
import Tkinter root = Tkinter.Tk() # опишите свой интерфейс, затем запустите приложение с помощью команды: root.mainloop()
Простые элементы управления: Toplevel, Frame, Button, Checkbutton, Entry, Label, Listbox, OptionMenu, Photoimage, Radiobutton, Scale.
Сложные элементы управления: Canvas, Text. Они могут содержать внутри себя помеченные тегами элементы, которые ведут себя подобно объектам.
Трудные для использования: Menu, Menubutton, Scrollbar.
Переменные Tk: StringVar, IntVar, DoubleVar, BooleanVar. Это своего рода контейнеры данных (контейнер - объект или приложение, содержащие другие объекты.- Прим. пер.). Они необходимы для некоторых элементов управления и взаимодействуют со многими другими, обеспечивая легкий доступ к их содержимому, а также могут инициировать обратные вызовы при изменении своих данных.
tkFont определяет класс Font для параметров шрифта и присвоения имен шрифтов. Если объект Font связать с шрифтом элемента управления, тогда изменение объекта Font будет автоматически приводить к соответствующим изменениям данного элемента управления.
FileDialog определяет FileDialog, LoadFileDialog, SaveFileDialog. Пример:
fdlg = FileDialog.LoadFileDialog(root, title="Выберите файл") fname = fdlg.go() # необязательные аргументы:
dir_or_file=os.curdir, pattern="*", default="", key=None) if fname == None: # отмена пользователем
tkColorChooser определяет функцию askcolor(initialcolor), которая возвращает выбранный пользователем цвет.
tkSimpleDialog определяет askinteger(title, prompt, initialvalue, minvalue, maxvalue), askfloat и askstring.
Просмотрите исходный код файлов с расширением .py в каталоге .../Lib/lib-tk. Там есть и другие модули, а также примеры использования.
pack(side="top/right/bottom/left", expand=0/1, anchor="n/nw/w...", fill="x/y/both")
grid(row, column, rowspan=?, columnspan=?, sticky="news", ipadx=?, ipady=?, padx=?, pady=?)
columnconfigure(row, weight=?, minsize=?, pad=?)
columnconfigure(column, weight=?, minsize=?, pad=?
События описываются с помощью строк вида: "<modifiers-type-qualifier>" ("модификаторы-тип-определитель").
Типы событий:
Предупреждение: то, какие именно события видит тот или иной элемент управления, зависит от платформы.
Определители для KeyPress и KeyRelease - это символы клавиатуры (keysyms). Буквы и цифры используются как есть, но знаки пунктуации и все остальные клавиши требуют особых, чувствительных к регистру имен, как-то: comma (запятая), period (точка), dollar (доллар), asciicircum (^), numbersign (#), exclam (восклицательный знак), Return, Escape, BackSpace, Tab, Up (вверх), Down (вниз), Left (влево), Right (вправо)... В сомнительных случаях запустите интерактивный тест, наподобие примера, приведенного ниже.
Для того чтобы при наступлении некоторого события происходило обращение к обратному вызову (callback), создайте соответствующую привязку (bind):
widget.bind(event, callback)
Ниже приводится пример, в котором при нажатии любой кнопки клавиатуры выводится соответствующий символ клавиши:
#!/usr/local/bin/Python """Отображает символ клавиши (keysym) для каждого события KeyPress (нажатия клавиатуры).""" import Tkinter root = Tkinter.Tk() root.title("Регистратор символов клавиатуры") def reportEvent(event): print 'keysym=%s, keysym_num=%s' % (event.keysym, event.keysym_num) text = Tkinter.Text(root, width=20, height=5, highlightthickness=2) text.bind('<KeyPress>', reportEvent) text.pack(expand=1, fill="both") text.focus_set() root.mainloop()
Обратный вызов (callback) события Destroy (уничтожить) запускается слишком поздно, чтобы с его помощью можно было очистить элемент управления или предотвратить уничтожение окна. Сделать что-либо из перечисленного удастся, если применить обработчик протокола WM_DELETE_WINDOW. (Но если удаление требуется при выходе из приложения, лучше воспользоваться стандартной библиотекой python "atexit"; это проще!). Этот протокол позволяет производить действия, отличные от поведения, принятого по умолчанию. Если вам действительно нужно уничтожить окно, сделайте это сами. Данный обратный вызов (callback) не получает никаких аргументов.
toplevel.protocol("WM_DELETE_WINDOW", callback)
Существуют два других протокола: WM_SAVE_YOURSELF и WM_TAKE_FOCUS. Не вполне ясно, для чего они нужны.
Элементы управления, такие как Button (кнопка), имеют параметр "command" ("команда"). В данном случае обратный вызов (callback) не получает никаких аргументов. Это самый лучший способ запустить обратный вызов (callback) нажатием кнопки Button, поскольку не существует другого события, связанного с мышью, делающего ту же самую работу. Конечно жаль, что "командные" (относящиеся к параметру "command".- Прим. пер.) обратные вызовы (callbacks) не передают функции обратного вызова (callback function) никакой информации, но ситуацию легко исправить с помощью т.н. оболочки обратного вызова (Callback Shim).
Для того чтобы при изменении переменной Tk запускался обратный вызов (callback), можно воспользоваться методом trace_variable:
traceName = tkvar.trace_variable(mode, callback)
Чтобы запустить обратный вызов (callback) с определенной задержкой, например для анимации, воспользуйтесь методом after (после):
widget.after(timems, callback, arg1, arg2...)
Вопрос в том, как организовать взаимодействие посредством сокета (особенно, как считывать информацию), не используя цикл событий и избегая неэффективных решений. Есть несколько вариантов:
Обработчик файлов обращается к обратному вызову (callback), когда в файле либо сокете есть данные для чтения или записи. Обработчики файлов просты и хорошо интегрированы в Tkinter. К сожалению, они не работают под Windows (по крайней мере с версией Python 2.3.4 и Windows XP). Но если ваша программа будет запускаться только на unix и/или MacOS X, то из-за простоты применения предпочтение следует отдать именно им.
wdg.tk.createfilehandler(file_or_socket, mask, callback)
где wdg - произвольный элемент управления Tkinter (если под рукой ничего нет, создайте для этих целей новый фрейм (frame)).
Сокеты Tcl полностью кроссплатформенны, но пользоваться ими немного труднее, поскольку приходится самостоятельно писать tcl-код. Тем не менее, это совсем не сложно, а затраченные усилия с лихвой окупаются тем, что вы получаете полностью переносимый код. Как пример использования tcl-сокетов можно загрузить мой пакет RO package и просмотреть RO.Comm.TkSocket. См. также следующее сообщение Мэтью Синсера (Matthew Cincera), которое я взял в качестве отправной точки (попутно приношу благодарность Стефану Беланду (Stephane Beland), посоветовавшему мне эту ссылку).
Twisted Framework - это свободная кроссплатформенная сетевая библиотека, работающая с несколькими разными инструментариями GUI (фактически, для своей работы она и не нуждается в каком-либо GUI-инструментарии). У нее очень хорошая репутация. До сих пор мне не приходилось пользоваться этой библиотекой, но, скорее всего, когда-нибудь я на нее перейду.
Так получается, что мне часто бывает нужно передать в функцию обратного вызова (callback function) больше данных, чем это предусмотрено. К примеру, элемент управления Button (кнопка) не передает никаких аргументов через свой "командный" (имеется ввиду параметр command) обратный вызов (callback), но может оказаться эффективнее использовать одну функцию обратного вызова (callback function) для управления несколькими кнопками; в этом случае мне необходимо знать, какая именно кнопка была нажата.
Реализовать это возможно, определив функцию обратного вызова (callback function) непосредственно перед ее пересылкой в элемент управления и включив в нее всю дополнительную информацию, которая вам нужна. К сожалению, Python, как и большинство других языков, не поддерживает на достаточно хорошем уровне смешение раннего связывания (информация, известная при определении функции) и позднего связывания (информация, известная при вызове функции). Я считаю, что самым простым и ясным будет следующее решение:
Надеюсь, что приведенный далее пример прояснит сказанное.
Оболочка обратного вызова (callback shim), которой я пользуюсь, называется RO.Alg.GenericCallback. Она входит в мой пакет RO package. Упрощенная версия оболочки, не умеющая работать с аргументами в виде ключевых слов, описана в примере, приведенном ниже по тексту. Весь код оболочки основан на python-рецепте Скотта Дэвида Дэниэлса (Scott David Daniels), который называет этот прием "каррирование функции" ("currying a function"); последний термин, возможно, более распространен, чем "оболочка обратного вызова" ("callback shim").
#!/usr/local/bin/Python """Пример, демонстрирующий применение оболочки обратного вызова (callback shim)""" import Tkinter def doButton(buttonName): """Желаемый обратный вызов (callback). Мне потребуется оболочка
обратного вызова (callback shim), поскольку через параметр command элемента
Button (кнопка) обратные вызовы (callback) никаких аргументов получить не могут."""
print buttonName, "pressed" class SimpleCallback: """Создается оболочка обратного вызова (callback shim), основанная на коде,
предложенном Скоттом Дэвидом Дэниелсом (Scott David Daniels),
который может работать и с ключевыми словами-аргументами.""" def __init__(self, callback, *firstArgs): self.__callback = callback self.__firstArgs = firstArgs def __call__(self, *args): return self.__callback (*(self.__firstArgs + args)) root = Tkinter.Tk() buttonNames = ("Button 1", "Button 2", "Button 3") for name in buttonNames: callback = SimpleCallback(doButton, name) Tkinter.Button(root, text=name, command=callback).pack() root.mainloop()
[Примечание переводчика : В стандартной библиотеке Python 2.5+ появилась функция, реализующая каррирование. См. http://docs.python.org/library/functools.html#functools.partial. Но гораздо проще передавать аргументы в вызываемую функцию (callback) с помощью lambda. См. http://www.russianlutheran.org/python/lambda/python_lambda.html (добавлено по совету А.Иваненко).]
Эти обозначения могут быть модифицированы путем добавления следующих строк:
Например, "1.0 lineend" означает ссылку на конец первой строки.
Есть несколько путей, как получить информацию о настройке элемента управления:
Во всех случаях каждый параметр настройки возвращается в виде строки. Это может стать главным источником головной боли. К примеру, булевы значения будут "0" или "1" (оба из которых логически истинны в Python). Проблема становится еще хуже, когда речь заходит об извлечении обычных объектов Tkinter, таких как переменные Tk или графические элементы управления. Следующий пример иллюстрирует эту проблему (хотя и в такой простой ситуации, когда приходится иметь дело лишь с оригинальными объектами Tk); ниже обсуждаются ее возможные решения:
import Tkinter root = Tkinter.Tk() aVar = Tkinter.StringVar() aLabel = Tkinter.Label(textvar = aVar) aLabel["textvar"] 'PY_VAR0' root.setvar(aLabel["textvar"], "foo") aLabel.getvar(aLabel["textvar"]) 'foo' str(aLabel) '.8252000' root.nametowidget(str(aLabel)) <Tkinter.Label instance at 0x7dea60> aLabel.master <Tkinter.Label instance at 0x7dea60> root.master None
Выбор подхода зависит от того, что вы хотите извлечь:
getvar(имя_переменной)
и setvar(имя_переменной, новое_значение)
.
Можно также создать переменную Tkinter, которая будет в точности дублировать оригинал, но это очень некрасивое решение. Вместо этого рекомендуется использовать getvar и setvar
(или использовать первоначальную переменную Tkinter). Если все-таки по какой-то причине дублирование необходимо, сначала создайте новую переменную Tkinter, например v = Tkinter.StringVar()
, а затем задайте ее свойство _name
: v._name = name_of_variable
.
Проблема в том, что создается новая переменная Tk, которая никогда не будет использоваться, а базируется вся эта методика на недокументированных свойствах переменных Tkinter. (У меня даже нет полной уверенности, что это безопасно, из-за возможных проблем, связанных со сборкой мусора в памяти.) nametowidget(имя_элемента)
. Также возможно извлечь и родительский элемент управления с помощью выражения wdg.master (который вместо названия элемента управления возвращает соответствующий элемент управления Tkinter). wdg
, который использует данный именованный шрифт. Но этот вариант настолько некрасив, что обычно предпочитают работать с первоначальным объектом
tkFont.Font. Сначала создайте новый объект tkFont.Font f = tkFont.Font()
, затем присвойте ему соответствующее имя f.name = wdg["font"]
. Если попробовать более прямой путь: f = tkFont.Font(name = wdg["font"])
, появится сообщение об ошибке _tkinter.TclError: "named font ... already exists" - "именованный шрифт ... уже существует" (по крайней мере с версией Python 2.3b1).любой_элемент.getvar(имя_переменной)
.Это перечень ошибок, часто встречающихся при программировании на python и Tkinter, а также способы их избежания.
after
из основного цикла (main loop), чтобы запросить threading Queue
, который записывает ваш поток). Высказывалось мнение, что использование потоками event_create
для коммуникации с главным потоком является безопасным, но проверка показала, что этот способ не безопасен.def foo(alist=[]):...
- это мина, ждущая своего часа. Избежать подобных проблем можно, взяв в качестве значения по умолчанию None, провести внутреннюю проверку для None и заменить его на [] (или {} или что-либо другое). Автор документа - Рассел Оуэн (Russell Owen). Последнее изменение 2004-10-12 (переписан раздел Обработчик файлов/сокетов, кроме того удалена неработающая ссылка на FAQ - ответы на часто задаваемые вопросы). Настоящий документ можно свободно распространять и использовать, но нельзя продавать.
Python для инженеров и исследователей
Интернет-адрес оригинального документа:
http://staff.washington.edu/rowen/TkinterSummary.html
Перевод на русский язык: Ф.С.ЗАНЬКО
Настоящий перевод можно свободно распространять и использовать, но нельзя продавать. При этом текст перевода должен оставаться в неизменном виде.
О замеченных ошибках, неточностях, опечатках просьба сообщать по электронному адресу:
russianlutheran@gmail.com