PyGTK: Динамическое создание виджетов

6 апреля 2011 г.

В интернете уже есть много информации про компоновку виджетов, но во всех отсутствует довольно важная деталь для новичка в программировании интерфейсов PyGTK — описание технологии динамического создания виджетов, их размещение на форме и доступ к их методам. Под динамическим здесь я подразумеваю такой программный код, который мог бы в одном или более циклов положить на форму бесконечно возможное количество кнопок, полей ввода, картинок и т.д., и связать между собой обработчиками сигналов.
Поняв эту несложную технологию, довольно просто будет написать предельно короткий код для однотипных связей между многочисленными виджетами.

Для демонстрационного примера я создал форму с визуальным представлением иконок, подписей к кнопкам и оригинальным названием классов gtk.STOCK_…

Код

Итак, листинг gtkstock.py с комментариями внутри:

#!/usr/bin/python
# -*- coding: utf-8 -*-

import pygtk
pygtk.require('2.0')
import gtk

class ShowStock:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_position(gtk.WIN_POS_CENTER)
self.window.set_title("Представление gtk.STOCK")
self.window.set_border_width(3)
self.window.resize(300,50)


self.window.connect("delete_event", self.delete_event)
self.window.connect("destroy", lambda w: gtk.main_quit())

self.button = gtk.Button("Показать список")
self.button.connect("clicked", self.show_stock)

self.list_stock = ['gtk.STOCK_ABOUT',
'gtk.STOCK_ADD',
'gtk.STOCK_APPLY',
'gtk.STOCK_BOLD',
'gtk.STOCK_CANCEL',
'gtk.STOCK_CDROM',
'gtk.STOCK_CLEAR',
'gtk.STOCK_CLOSE',
'gtk.STOCK_COLOR_PICKER',
'gtk.STOCK_CONVERT',
'gtk.STOCK_CONNECT',
'gtk.STOCK_COPY',
'gtk.STOCK_CUT',
'gtk.STOCK_DELETE',
'gtk.STOCK_DIALOG_AUTHENTICATION',
'gtk.STOCK_DIALOG_ERROR',
'gtk.STOCK_DIALOG_INFO',
'gtk.STOCK_DIALOG_QUESTION',
'gtk.STOCK_DIALOG_WARNING',
'gtk.STOCK_DIRECTORY',
'gtk.STOCK_DISCONNECT',
'gtk.STOCK_DND',
'gtk.STOCK_DND_MULTIPLE',
'gtk.STOCK_EDIT',
'gtk.STOCK_EXECUTE',
'gtk.STOCK_FILE',
'gtk.STOCK_FIND',
'gtk.STOCK_FIND_AND_REPLACE',
'gtk.STOCK_FLOPPY',
'gtk.STOCK_FULLSCREEN',
'gtk.STOCK_GOTO_BOTTOM',
'gtk.STOCK_GOTO_FIRST',
'gtk.STOCK_GOTO_LAST',
'gtk.STOCK_GOTO_TOP',
'gtk.STOCK_GO_BACK',
'gtk.STOCK_GO_DOWN',
'gtk.STOCK_GO_FORWARD',
'gtk.STOCK_GO_UP',
'gtk.STOCK_HARDDISK',
'gtk.STOCK_HELP',
'gtk.STOCK_HOME',
'gtk.STOCK_INDENT',
'gtk.STOCK_INDEX',
'gtk.STOCK_INFO',
'gtk.STOCK_ITALIC',
'gtk.STOCK_JUMP_TO',
'gtk.STOCK_JUSTIFY_CENTER',
'gtk.STOCK_JUSTIFY_FILL',
'gtk.STOCK_JUSTIFY_LEFT',
'gtk.STOCK_JUSTIFY_RIGHT',
'gtk.STOCK_LEAVE_FULLSCREEN',
'gtk.STOCK_MEDIA_FORWARD',
'gtk.STOCK_MEDIA_NEXT',
'gtk.STOCK_MEDIA_PAUSE',
#'gtk.STOCK_MEDIA_PLAYRTL',
'gtk.STOCK_MEDIA_PREVIOUS',
'gtk.STOCK_MEDIA_RECORD',
'gtk.STOCK_MEDIA_REWIND',
'gtk.STOCK_MEDIA_STOP',
'gtk.STOCK_MISSING_IMAGE',
'gtk.STOCK_NETWORK',
'gtk.STOCK_NEW',
'gtk.STOCK_NO',
'gtk.STOCK_OK',
'gtk.STOCK_OPEN',
'gtk.STOCK_PASTE',
'gtk.STOCK_PREFERENCES',
'gtk.STOCK_PRINT',
'gtk.STOCK_PRINT_PREVIEW',
'gtk.STOCK_PROPERTIES',
'gtk.STOCK_QUIT',
'gtk.STOCK_REDO',
'gtk.STOCK_REFRESH',
'gtk.STOCK_REMOVE',
'gtk.STOCK_REVERT_TO_SAVED',
'gtk.STOCK_SAVE',
'gtk.STOCK_SAVE_AS',
'gtk.STOCK_SELECT_COLOR',
'gtk.STOCK_SELECT_FONT',
'gtk.STOCK_SORT_ASCENDING',
'gtk.STOCK_SORT_DESCENDING',
'gtk.STOCK_SPELL_CHECK',
'gtk.STOCK_STOP',
'gtk.STOCK_STRIKETHROUGH',
'gtk.STOCK_UNDELETE',
'gtk.STOCK_UNDERLINE',
'gtk.STOCK_UNDO',
'gtk.STOCK_UNINDENT',
'gtk.STOCK_YES',
'gtk.STOCK_ZOOM_100',
'gtk.STOCK_ZOOM_FIT',
'gtk.STOCK_ZOOM_IN',
'gtk.STOCK_ZOOM_OUT']

# Основа - окно с автоматически появляющейся прокруткой
self.scrolledwindow = gtk.ScrolledWindow()
self.scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

# Столбец для виджетов на всю ширину окна
self.vbox = gtk.VBox()
self.scrolledwindow.add_with_viewport(self.vbox)

# Стартовая кнопка
self.vbox.pack_start(self.button)

# Таблица с программно добавляемыми виджетами
self.table = gtk.Table(1,1)
self.vbox.pack_start(self.table)

self.window.add(self.scrolledwindow)
self.window.show_all()

# Словарик размеров картинок для функции change_scale
self.dictimages = {}

def show_stock(self, widget):
"""Программное создание виджетов, сигналов, размещения"""

for x in range(0, len(self.list_stock)):

if not self.__dict__.has_key("label" + str(x)):

# Строки превращаются в ссылки на классы модуля "gtk"
STOCK = eval(self.list_stock[x]) 

# Стандартный размер картинки 
scale = 4 

# Ссылка на словарь атрибутов, для краткости написания.
dictattr = self.__dict__

# Создание атрибутов текущего экземпляра и присваивание им классов,
# как если бы вручную написали: self.button100500 = gtk.Button() 
dictattr["image" + str(x)] = gtk.Image()
dictattr["image" + str(x)].set_from_stock(STOCK, scale)

dictattr["button" + str(x)] = gtk.Button(STOCK, STOCK)

#~ # сигнал с данными (Вариант №1): каждый хранит строку,
#~ # в self.dictimages ключами тоже являются строки
#~ dictattr["button" + str(x)].connect('clicked', 
#~ self.change_scale, ("image" + str(x), STOCK, scale))

# сигнал с данными (Вариант №2, предпочтительнее): хранит ссылку
# на значение в словаре атрибутов, в self.dictimages ключи - ссылки
dictattr["button" + str(x)].connect('clicked', 
self.change_scale, (dictattr["image" + str(x)], STOCK, scale))

dictattr["label" + str(x)] = gtk.Label("==>> " + self.list_stock[x])
dictattr["label" + str(x)].set_alignment(0,0.5)

# Размещение виджетов в таблице
self.table.attach(dictattr["image" + str(x)], 0,1,x,x+1)
self.table.attach(dictattr["button" + str(x)], 1,2,x,x+1)
self.table.attach(dictattr["label" + str(x)], 2,3,x,x+1)

self.window.resize(600,600)
self.window.show_all()
self.button.hide()

def change_scale(self, widget, data):
"""Увеличение размера картинки"""

scale = 0
if not self.dictimages.has_key(data[0]):
scale = data[2] + 1
else:
scale = self.dictimages[data[0]]
if scale == 6: scale = data[2]
else: scale += 1

self.dictimages[data[0]] = scale

#~ # Для варианта №1 в show_stock()
#~ self.__dict__[data[0]].set_from_stock(data[1], scale)

# Для варианта №2 в show_stock()
data[0].set_from_stock(data[1], scale)

def delete_event(self, widget, event, data=None):
print "delete event occurred"

return False

#~ def destroy(self, widget, data=None):
#~ print "destroy signal occurred"
#~ gtk.main_quit()

def main(self):
gtk.main()

if __name__ == "__main__":
showstock = ShowStock()
showstock.main()

Итоговое окно

Итоговое окно

Дополнительные пояснения

Построением виджетов занимается функция show_stock. В ней есть закомментированный участок 1 варианта кода, который, на мой взгляд, более явный для программиста, но требующий больше выделения памяти для создаваемых сигналов. Для того, чтобы использовать его, нужно закомментировать 2 вариант, а также сделать соответствующие изменения в функции change_scale.

Закомментированная функция destroy, заменена lambda-выражением прямо при декларировании сигнала. Но оставлена на тот случай, когда при закрытии главного окна потребуется что-либо сохранить (для этого в неё нужно будет добавить свой сохраняющий код).

Теги: рубрика Программирование
  • Похожие статьи
  • Предыдущие из рубрики