WxPython - краткий курс обучения

177

Transcript of WxPython - краткий курс обучения

WxPython - краткий курс обучения

Martin Mares

3 июля 2019

Аннотация

Это учебник по wxPython. В этом уроке вы изучите основы программирования GUI в wxPython. Учебноепособие подходит для начинающих и средних программистов.

Учебное пособие охватывает wxPython Phoenix версии 4.0.1. Источники примеров доступны в репозиторииwxPython-examples.

Это перевод на русский язык исходного документа, расположенного по адресу http://zetcode.com/wxpython.

WxPython wxPython - это кроссплатформенный инструментарий для создания приложений с графическиминтерфейсом. С помощью wxPython разработчики могут создавать приложения в Windows, Mac OSи в различных системах Unix. wxPython - это оболочка для wxWidgets, зрелой кроссплатформеннойбиблиотеки C ++.

1 Введение в wxPython

Эта глава представляет собой введение в инструментарий wxPython.

wxPython - это кроссплатформенный инструмен-тарий для создания приложений с графическим ин-терфейсом. Основной автор wxPython - Робин Данн.С помощью wxPython разработчики могут создаватьприложения на Windows, Mac и в различных системахUnix. wxPython - это оболочка для wxWidgets, зрелойкроссплатформенной библиотеки C ++.

1.1 Python

Python является успешным языком сценариев.Первоначально он был разработан Гвидо ван Россу-мом. Впервые он был выпущен в 1991 году. Python

был вдохновлен языками программирования ABC и Haskell. Python - это высокий уровень, универсальный,мультиплатформенный, интерпретируемый язык. Некоторые предпочитают называть это динамическим язы-ком. Это легко учиться. Python - это минималистичный язык. Одна из его наиболее заметных особенностейзаключается в том, что он не использует точки с запятой и скобки. Вместо этого Python использует отступ.Сегодня Python поддерживается большой группой добровольцев по всему миру.

Для создания графических пользовательских интерфейсов программисты Python могут выбрать один изтрех достойных вариантов: PyGTK, wxPython и PyQt.

1.2 Модули wxPython

wxPython - это кроссплатформенный инструментарий для создания приложений с графическим интерфей-сом. Основной автор wxPython - Робин Данн. С помощью wxPython разработчики могут создавать приложенияна Windows, Mac и в различных системах Unix. wxPython - это оболочка для wxWidgets, зрелой кроссплатфор-менной библиотеки C ++. wxPython состоит из пяти основных модулей.

1

Рис. 1: Модули Python.

Модуль Control предоставляет общие виджеты, появляющиеся в графических приложениях. Например,кнопка, панель инструментов или блокнот. Под ОС Windows виджеты называются элементами управления.Модуль Core состоит из элементарных классов, которые используются в разработке. Эти классы включают классObject, который является родителем всех классов, Sizer, которые используются для разметки виджетов, Events,базовые классы геометрии, такие как Point и Rectangle. Интерфейс графического устройства (GDI) - это наборклассов, используемых для рисования на виджетах. Этот модуль содержит классы для работы со шрифтами,цветами, кистями, ручками или изображениями. Модуль Misc содержит различные другие классы и функциимодуля. Эти классы используются для регистрации, настройки приложения, системных настроек, работы сдисплеем или джойстиком. Модуль Windows состоит из различных окон, которые формируют приложение,например Panel, Dialog, Frame или Scrolled Window.

1.3 API wxPython

wxPython API - это набор методов и объектов. Виджеты являются основными строительными блокамиприложения с графическим интерфейсом. Под Windows виджеты называются элементами управления. Мы мо-жем условно разделить программистов на две группы: они кодируют либо приложения, либо библиотеки. Внашем случае wxPython - это библиотека, которая используется прикладными программистами для кодирова-ния приложений. Технически, wxPython - это оболочка над C ++ GUI API, называемая wxWidgets. Так что этоне нативный API; то есть это не написано непосредственно в Python.

В wxPython у нас много виджетов. Их можно разделить на несколько логических групп.

1.3.1 Базовые виджеты

Эти виджеты предоставляют базовую функциональность для производных виджетов. Их называют пред-ками. Они обычно не используются напрямую.

Рис. 2: Базовые виджеты

2

1.3.2 Виджеты верхнего уровня

Эти виджеты существуют независимо друг от друга.

Рис. 3: Виджеты верхнего уровня

1.3.3 Контейнеры

Контейнеры содержат другие виджеты.

Рис. 4: Контейнеры

1.3.4 Динамические виджеты

Эти виджеты могут редактироваться пользователями.

Рис. 5: Динамические виджеты

1.3.5 Статические виджеты

Эти виджеты отображают информацию. Они не могут быть отредактированы пользователем.

1.3.6 Другие виджеты

Эти виджеты реализуют строку состояния, панель инструментов и строку меню в приложении.

3

Рис. 6: Статические виджеты

Рис. 7: Другие виджеты

1.3.7 Наследование

Существует определенная связь между виджетами в wxPython. Эта связь определяется наследованием. На-следование является важной частью объектно-ориентированного программирования. Виджеты образуют иерар-хию. Виджеты могут наследовать функциональность от других виджетов. Существующие классы называютсябазовыми классами, родителями или предками. Наследующие виджеты мы называем производными виджетами,дочерними виджетами или потомками.

Рис. 8: Диаграмма наследования

Скажем, мы используем виджет button в нашем приложении. Виджет button наследуется от четырех разныхбазовых классов. Ближайшим классом является класс wx.Control. Виджет кнопки - это своего рода небольшоеокно. Все виджеты, которые появляются на экране, являются окнами. Поэтому они наследуются от классаwx.Window. Есть объекты, которые невидимы. Примерами являются размеры, контекст устройства или объектлокали. Есть также классы, которые видны, но они не являются окнами. Например, объект color, объект caretили объект cursor. Не все виджеты являются элементами управления. Например, wx.Dialog не является своегорода контролем. Элементы управления - это виджеты, которые размещаются в других виджетах, называемыхконтейнерами. Вот почему у нас есть отдельный базовый класс wx.Control.

4

Каждое окно может реагировать на события. Так же как и виджет button. Нажав на кнопку, мы запус-каем событие wx.EVT_COMMAND_BUTTON_CLICKED. Виджет button наследует wx.EvtHandler через классwx.Window. Каждый виджет, который реагирует на события, должен наследоваться от класса wx.EvtHandler.Наконец, все объекты наследуются от класса wx.Object.

Это и было введением в wxPython.

2 Первые шаги

В этой части руководства по wxPython мы создадим несколько простых примеров.

2.1 Простой пример

Начнем с очень простого примера. Наш первый скрипт просто покажет небольшое окно.

Он не будет делать слишком много. А мы будем построчно анализировать сценарий.

Листинг 1: simple.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 # simple . py56 import wx78 app = wx .Ap_p( )910 frame = wx . Frame(None , t i t l e=’ Simple ␣ app l i c a t i o n ’ )11 frame . Show ( )1213 app . MainLoop ( )

Это наш первый пример на wxPython.

Первая строка - это she-bang, затем путь к интерпретатору Python. Вторая строка - это волшебный коммен-тарий, который определяет кодировку исходного кода. Четвертая строка - это комментарий, в котором указаноимя сценария.

import wx

Эта строка импортирует основные модули wxPython. А именно ядро, элементы управления, gdi, misc иwindows. Технически, wx - это пространство имен. Все функции и объекты из основных модулей будут начи-наться с префикса wx.. Следующая строка кода создаст объект приложения.

app = wx.App()

Каждая программа wxPython должна иметь один объект application.

frame = wx.Frame(None, title=’Simple application’)

frame.Show()

Здесь мы создаем объект wx.Frame. Виджет wx.Frame является важным контейнерным виджетом. Мы по-дробно разберем этот виджет позже. Виджет wx.Frame является родительским виджетом для других виджетов.У него нет родителя. Если мы указываем None для параметра parrent, мы указываем, что у нашего виджетанет родителей. Это верхний виджет в иерархии виджетов. После того как мы создадим виджет wx.Frame, мыдолжны вызвать метод Show(), чтобы отобразить его на экране.

5

app.MainLoop()

Последняя строка вводит программу в основной цикл. Основной цикл - это бесконечный цикл. Он ловит иотправляет все события, которые появляются в течение срока действия нашего приложения.

Это был очень упрощенный пример. Несмотря на эту простоту, мы можем многое сделать с этим окном. Мыможем изменить размер окна, максимизировать его, минимизировать. Эта функциональность требует много ко-дирования. Все это скрыто и предоставляется по умолчанию инструментарием wxPython. Нет причин изобретатьвелосипед.

Рис. 9: Простейший пример

2.2 wx.Frame

Виджет wx.Frame - один из самых важных виджетов в wxPython. Это контейнерный виджет. Это означает,что он может содержать другие виджеты. На самом деле он может содержать любое окно, которое не являетсяфреймом или диалогом. wx.Frame состоит из строки заголовка, границ и центральной области контейнера.Строка заголовка и границы являются необязательными. Они могут быть удалены различными флагами.

wx.Frame имеет следующий конструктор:

Листинг 2: Конструктор wx.Frame

1 wx . Frame(wx .Window parent ,2 int id=−1,3 s t r i n g t i t l e=’ ’ ,4 wx . Point pos=wx . De fau l tPos i t i on ,5 wx . S i z e s i z e=wx . De fau l tS i ze ,6 s t y l e=wx .DEFAULT_FRAME_STYLE,7 s t r i n g name=" frame" )

Конструктор имеет семь параметров. Первый параметр не имеет значения по умолчанию. Остальные шестьпараметров есть. Эти четыре параметра являются необязательными. Первые три являются обязательными.

wx.DEFAULT_FRAME_STYLE - это набор флагов по умолчанию: wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX| wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN. Ком-бинируя различные стили, мы можем изменить стиль виджета wx.Frame.

Листинг 3: Без иконки минимизации окна

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 # no_minimize . py56 import wx7

6

8 app = wx .App( )9 frame = wx . Frame(None , s t y l e=wx .MAXIMIZE_BOX | wx .RESIZE_BORDER10 | wx .SYSTEM_MENU | wx .CAPTION | wx .CLOSE_BOX)11 frame . Show(True )1213 app . MainLoop ( )

Мы хотели, чтобы окно отображалось без иконки минимизации Поэтому мы не указали этот флаг в пара-метре style.

2.3 Размер и положение

Мы можем указать размер нашего приложения двумя способами. У нас есть параметр размера в конструк-торе нашего виджета, или мы можем вызвать метод SetSize().

Листинг 4: set_size.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 # se t_s i z e . py56 import wx789 class Example (wx . Frame) :1011 def __init__( s e l f , parent , t i t l e ) :12 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e ,13 s i z e =(350 , 250) )141516 def main ( ) :1718 app = wx .App( )19 ex = Example (None , t i t l e=’ S i z i ng ’ )20 ex . Show ( )21 app . MainLoop ( )222324 i f __name__ == ’__main__ ’ :25 main ( )

В этом примере приложение будет иметь размер окна 350x250 пикселей.

def __init__(self, parent, title):

super(Example, self).__init__(parent, title=title,

size=(350, 250))

В конструкторе мы устанавливаем ширину виджета wx.Frame равной 350 пикселей. Высота виджета до 250пикселей.

Точно так же мы можем расположить наше приложение на экране. По умолчанию окно находится в левомверхнем углу экрана. Но оно может располагаться по разному на разных платформах ОС или даже в разныхоконных менеджерах. Некоторые менеджеры окон сами размещают окна приложений. Некоторые из них вы-полняют некоторую оптимизацию, чтобы окна не перекрывались. Программист может позиционировать окнопрограммно. Мы уже видели параметр pos в конструкторе нашего виджета wx.Frame. Предоставляя значения,отличные от значений по умолчанию, мы можем сами контролировать положение.

Есть несколько способов сделать это.

7

Таблица 1: Методы позиционирования и установки размера

Метод ОписаниеMove(wx.Point point) переместить окно в заданную позициюMoveXY(int x, int y) переместить окно в заданную позициюSetPosition(wx.Point point) установить положение окнаSetDimensions(x, y, width, height, sizeFlags) установить положение и размер окна

Листинг 5: moving.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 # moving . py56 import wx789 class Example (wx . Frame) :1011 def __init__( s e l f , parent , t i t l e ) :12 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e ,13 s i z e =(300 , 200) )1415 s e l f .Move ( (800 , 250) )161718 def main ( ) :1920 app = wx .App( )21 ex = Example (None , t i t l e=’Moving ’ )22 ex . Show ( )23 app . MainLoop ( )242526 i f __name__ == ’__main__ ’ :27 main ( )

Есть одна конкретная ситуация. Возможно, мы хотим, чтобы наше окно было развернуто на весь экран.В этом случае окно расположено в (0, 0) и занимает весь экран. wxPython внутренне вычисляет координатыэкрана. Чтобы максимизировать наш wx.Frame, мы вызываем метод Maximize ().

2.4 Центрирование на экране

Если мы хотим центрировать наше приложение на экране, у wxPython есть удобный метод. Метод Center() просто центрирует окно на экране. Не нужно рассчитывать ширину и высоту экрана. Просто вызовите метод.

Листинг 6: centering.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 # cen t e r ing . py56 import wx789 class Example (wx . Frame) :1011 def __init__( s e l f , parent , t i t l e ) :

8

12 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e ,13 s i z e =(300 , 200) )1415 s e l f . Centre ( )161718 def main ( ) :1920 app = wx .App( )21 ex = Example (None , t i t l e=’ Center ing ’ )22 ex . Show ( )23 app . MainLoop ( )242526 i f __name__ == ’__main__ ’ :27 main ( )

В этом примере мы центрируем небольшое приложения окно на экране.

self.Centre()

Метод Centre() центрирует окно на экране.

В этой главе мы создали несколько простых примеров кода в wxPython.

3 Меню и панели инструментов

Общей частью приложения с графическим интерфейсом является строка меню. Menubar состоит из объек-тов, называемых меню. Меню верхнего уровня имеют свои ярлыки на строке меню. В меню есть пункты меню.Пункты меню - это команды, которые выполняют определенное действие внутри приложения. Меню также мо-гут иметь подменю, которые имеют свои собственные пункты меню. Следующие три класса используются длясоздания меню в wxPython: wx.MenuBar, wx.Menu и wx.MenuItem.

3.1 Простое меню

В нашем первом примере мы создадим строку меню с одним файловым меню. В меню будет только одинпункт меню. При выборе пункта приложение закрывается.

Листинг 7: simple_menu.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This example shows a s imple menu .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx151617 class Example (wx . Frame) :

9

1819 def __init__( s e l f , * args , **kwargs ) :20 super (Example , s e l f ) . __init__(* args , **kwargs )2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 menubar = wx .MenuBar ( )27 f i l eMenu = wx .Menu( )28 f i l e I t em = fi leMenu . Append(wx . ID_EXIT, ’ Quit ’ , ’ Quit␣ app l i c a t i o n ’ )29 menubar . Append( f i leMenu , ’&F i l e ’ )30 s e l f . SetMenuBar (menubar )3132 s e l f . Bind (wx .EVT_MENU, s e l f . OnQuit , f i l e I t em )3334 s e l f . S e tS i z e ( (300 , 200) )35 s e l f . S e tT i t l e ( ’ Simple ␣menu ’ )36 s e l f . Centre ( )3738 def OnQuit ( s e l f , e ) :39 s e l f . Close ( )404142 def main ( ) :4344 app = wx .App( )45 ex = Example (None )46 ex . Show ( )47 app . MainLoop ( )484950 i f __name__ == ’__main__ ’ :51 main ( )

Это небольшой пример с минимальной функциональностью меню.

menubar = wx.MenuBar()

Сначала мы создаем объект menubar.

fileMenu = wx.Menu()

Далее мы создаем объект Menu.

fileItem = fileMenu.Append(wx.ID_EXIT, ’Quit’, ’Quit application’)

Добавляем пункт меню в объект меню. Первый параметр - это идентификатор пункта меню. Стандартныйидентификатор автоматически добавит значок и ярлык, Ctrl+Q в нашем случае. Второй параметр - это назва-ние пункта меню. Последний параметр определяет короткую строку справки, которая отображается в строкесостояния при выборе пункта меню. Здесь мы не создали явно wx.MenuItem. Он был создан методом Append()за кулисами. Метод возвращает созданный пункт меню. Эта ссылка будет использоваться позже, чтобы связатьсобытие, действие и пункт меню.

self.Bind(wx.EVT_MENU, self.OnQuit, fileItem)

Мы связываем wx.EVT_MENU элемента меню с пользовательским методом OnQuit(). Этот метод закроетприложение.

10

menubar.Append(fileMenu, ’&File’)

self.SetMenuBar(menubar)

После этого мы добавляем меню в MenuBar. Символ & создает клавишу ускорения. Символ, которыйследует за символом &, подчеркнут. Таким образом, меню доступно через сочетание клавиш Alt+F. В конце мывызываем метод SetMenuBar(). Этот метод принадлежит виджету wx.Frame. Он устанавливает menubar.

Рис. 10: Пример проостейшего меню

3.2 Иконки и ярлыки

Следующий пример по сути такой же, как и предыдущий. На этот раз мы вручную создаем wx.MenuItem.

Листинг 8: icons_shortcuts.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we manually c r ea t e8 a menu item .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx1617 APP_EXIT = 1181920 class Example (wx . Frame) :2122 def __init__( s e l f , * args , **kwargs ) :23 super (Example , s e l f ) . __init__(* args , **kwargs )2425 s e l f . In i tUI ( )2627 def In i tUI ( s e l f ) :2829 menubar = wx .MenuBar ( )

11

30 f i l eMenu = wx .Menu( )31 qmi = wx .MenuItem( fi leMenu , APP_EXIT, ’&Quit\ tCt r l+Q’ )32 qmi . SetBitmap (wx . Bitmap ( ’ e x i t . png ’ ) )33 f i l eMenu . Append(qmi )3435 s e l f . Bind (wx .EVT_MENU, s e l f . OnQuit , id=APP_EXIT)3637 menubar . Append( f i leMenu , ’&F i l e ’ )38 s e l f . SetMenuBar (menubar )3940 s e l f . S e tS i z e ( (350 , 250) )41 s e l f . S e tT i t l e ( ’ I cons ␣and␣ sho r t cu t s ’ )42 s e l f . Centre ( )4344 def OnQuit ( s e l f , e ) :45 s e l f . Close ( )464748 def main ( ) :4950 app = wx .App( )51 ex = Example (None )52 ex . Show ( )53 app . MainLoop ( )545556 i f __name__ == ’__main__ ’ :57 main ( )

В этом примере мы создаем пункт меню выхода. Мы выбираем пользовательский значок и ярлык дляпункта меню.

qmi = wx.MenuItem(fileMenu, APP_EXIT, ’&Quit\tCtrl+Q’)

qmi.SetBitmap(wx.Bitmap(’exit.png’))

fileMenu.Append(qmi)

Мы создаем объект wx.MenuItem. Символ & указывает клавишу ускорения. Символ, следующий за ампер-сандом, подчеркнут. Фактический ярлык определяется комбинацией символов. Мы указали символы Ctrl+QПоэтому, если мы нажимаем Ctrl+Q, то мы закроем приложение. Мы помещаем символ табуляции междусимволом & и ярлыком. Таким образом, нам удается втиснуть некоторое пространство между ними. Чтобыпредоставить значок для пункта меню, мы вызываем метод SetBitmap(). Элемент меню, созданный вручную,добавляется в меню путем вызова метода AppendItem().

self.Bind(wx.EVT_MENU, self.OnQuit, id=APP_EXIT)

Когда мы выбираем созданный пункт меню, вызывается метод OnQuit().

3.3 Подменю и разделители

Каждое меню также может иметь подменю. Таким образом, мы можем поместить похожие команды в груп-пы. Например, мы можем поместить команды, которые скрывают/показывают различные панели инструментов,такие как личная панель, адресная строка, строка состояния или панель навигации, в подменю, называемыепанелями инструментов. В меню мы можем разделять команды разделителем. Это простая линия. Обычнойпрактикой является отделение таких команд, как «Создать», «Открыть», «Сохранить» от таких команд, как«Печать», «Предварительный просмотр», с помощью одного разделителя. В нашем примере мы увидим, как мыможем создавать подменю и разделители меню.

Листинг 9: submenu.py

12

Рис. 11: Иконка и клавиша быстрого доступа

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e a submenu and a menu8 s epara tor .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kwargs ) :21 super (Example , s e l f ) . __init__(* args , **kwargs )2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 menubar = wx .MenuBar ( )2829 f i l eMenu = wx .Menu( )30 f i l eMenu . Append(wx .ID_NEW, ’&New ’ )31 f i l eMenu . Append(wx .ID_OPEN, ’&Open ’ )32 f i l eMenu . Append(wx .ID_SAVE, ’&Save ’ )33 f i l eMenu . AppendSeparator ( )3435 imp = wx .Menu( )36 imp . Append(wx .ID_ANY, ’ Import␣ newsfeed ␣ l i s t . . . ’ )37 imp . Append(wx .ID_ANY, ’ Import␣bookmarks . . . ’ )38 imp . Append(wx .ID_ANY, ’ Import␣mail . . . ’ )39

13

40 f i l eMenu .AppendMenu(wx .ID_ANY, ’ I&mport ’ , imp)4142 qmi = wx .MenuItem( fi leMenu , wx . ID_EXIT, ’&Quit\ tCt r l+W’ )43 f i l eMenu . AppendItem (qmi )4445 s e l f . Bind (wx .EVT_MENU, s e l f . OnQuit , qmi )4647 menubar . Append( f i leMenu , ’&F i l e ’ )48 s e l f . SetMenuBar (menubar )4950 s e l f . S e tS i z e ( (350 , 250) )51 s e l f . S e tT i t l e ( ’Submenu ’ )52 s e l f . Centre ( )5354 def OnQuit ( s e l f , e ) :55 s e l f . Close ( )565758 def main ( ) :5960 app = wx .App( )61 ex = Example (None )62 ex . Show ( )63 app . MainLoop ( )646566 i f __name__ == ’__main__ ’ :67 main ( )

В приведенном выше примере мы создаем стандартные пункты меню «Создать», «Открыть» и «Сохра-нить». Они отделены от подменю горизонтальным разделителем. Подменю имеет три дополнительных пунктаменю.

fileMenu.Append(wx.ID_NEW, ’&New’)

fileMenu.Append(wx.ID_OPEN, ’&Open’)

fileMenu.Append(wx.ID_SAVE, ’&Save’)

Здесь у нас есть три общих пункта меню: New, Open и Save.

fileMenu.AppendSeparator()

Разделитель меню добавляется с помощью метода AppendSeparator().

imp = wx.Menu()

imp.Append(wx.ID_ANY, ’Import newsfeed list...’)

imp.Append(wx.ID_ANY, ’Import bookmarks...’)

imp.Append(wx.ID_ANY, ’Import mail...’)

fileMenu.AppendMenu(wx.ID_ANY, ’I&mport’, imp)

Подменю также является wx.Menu. Три пункта меню добавляются в меню. Подменю добавляется в менюфайлов методом AppendMenu().

3.4 Check пункт меню

Есть три вида пунктов меню.

14

Рис. 12: Пример подменю

� нормальный пункт

� check пункт

� radio пункт

В следующем примере мы продемонстрируем check пункт меню. Пункт меню check визуально представленгалочкой в меню.

Листинг 10: checkmenu_item.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This example c r ea t e s a checked8 menu item .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kwargs ) :21 super (Example , s e l f ) . __init__(* args , **kwargs )2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 menubar = wx .MenuBar ( )28 viewMenu = wx .Menu( )2930 s e l f . sh s t = viewMenu . Append(wx .ID_ANY, ’Show␣ s ta tu sbar ’ ,31 ’Show␣ Statusbar ’ , kind=wx .ITEM_CHECK)32 s e l f . s h t l = viewMenu . Append(wx .ID_ANY, ’Show␣ too lba r ’ ,33 ’Show␣Toolbar ’ , kind=wx .ITEM_CHECK)34

15

35 viewMenu . Check ( s e l f . sh s t . GetId ( ) , True )36 viewMenu . Check ( s e l f . s h t l . GetId ( ) , True )3738 s e l f . Bind (wx .EVT_MENU, s e l f . ToggleStatusBar , s e l f . sh s t )39 s e l f . Bind (wx .EVT_MENU, s e l f . ToggleToolBar , s e l f . s h t l )4041 menubar . Append(viewMenu , ’&View ’ )42 s e l f . SetMenuBar (menubar )4344 s e l f . t oo lba r = s e l f . CreateToolBar ( )45 s e l f . t oo lba r . AddTool (1 , ’ ’ , wx . Bitmap ( ’ t e x i t . png ’ ) )46 s e l f . t oo lba r . Rea l i z e ( )4748 s e l f . s t a tu sbar = s e l f . CreateStatusBar ( )49 s e l f . s t a tu sbar . SetStatusText ( ’Ready ’ )5051 s e l f . S e tS i z e ( (450 , 350) )52 s e l f . S e tT i t l e ( ’Check␣menu␣ item ’ )53 s e l f . Centre ( )545556 def ToggleStatusBar ( s e l f , e ) :5758 i f s e l f . sh s t . IsChecked ( ) :59 s e l f . s t a tu sbar . Show ( )60 else :61 s e l f . s t a tu sbar . Hide ( )6263 def ToggleToolBar ( s e l f , e ) :6465 i f s e l f . s h t l . IsChecked ( ) :66 s e l f . t oo lba r . Show ( )67 else :68 s e l f . t oo lba r . Hide ( )697071 def main ( ) :7273 app = wx .App( )74 ex = Example (None )75 ex . Show ( )76 app . MainLoop ( )777879 i f __name__ == ’__main__ ’ :80 main ( )

У нас есть меню view, в котором у нас есть два пункта check-меню. Эти два пункта меню будут отображатьи скрывать строку состояния и панель инструментов.

self.shst = viewMenu.Append(wx.ID_ANY, ’Show statusbar’,

’Show Statusbar’, kind=wx.ITEM_CHECK)

self.shtl = viewMenu.Append(wx.ID_ANY, ’Show toolbar’,

’Show Toolbar’, kind=wx.ITEM_CHECK)

Если мы хотим добавить пункт check-меню, мы устанавливаем параметр вида wx.ITEM_CHECK. Параметрпо умолчанию - wx.ITEM_NORMAL. Метод Append() возвращает wx.MenuItem.

viewMenu.Check(self.shst.GetId(), True)

viewMenu.Check(self.shtl.GetId(), True)

16

Когда приложение запускается, видны и строка состояния, и панель инструментов. Поэтому мы проверяемоба пункта меню методом Check().

def ToggleStatusBar(self, e):

if self.shst.IsChecked():

self.statusbar.Show()

else:

self.statusbar.Hide()

Мы показываем или скрываем строку состояния в соответствии с состоянием check-пункта меню. Мы вы-ясняем состояние check-пункта меню с помощью метода IsChecked(). То же самое с панелью инструментов.

Рис. 13: check пункты подменю

3.5 Контекстное меню

Контекстное меню - это список команд, который появляется в некотором контексте. Например, в веб-браузере Firefox, когда мы щелкаем правой кнопкой мыши на веб-странице, мы получаем контекстное меню.Здесь мы можем перезагрузить страницу, вернуться назад или просмотреть страницу источника. Если щелкнутьправой кнопкой мыши на панели инструментов, мы получим другое контекстное меню для управления панелямиинструментов. Контекстные меню иногда называют всплывающими меню.

Листинг 11: context_menu.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e a con t ex t menu .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx1516 class MyPopupMenu(wx .Menu) :1718 def __init__( s e l f , parent ) :

17

19 super (MyPopupMenu , s e l f ) . __init__ ( )2021 s e l f . parent = parent2223 mmi = wx .MenuItem( s e l f , wx . NewId ( ) , ’ Minimize ’ )24 s e l f . Append(mmi)25 s e l f . Bind (wx .EVT_MENU, s e l f . OnMinimize , mmi)2627 cmi = wx .MenuItem( s e l f , wx . NewId ( ) , ’ Close ’ )28 s e l f . Append( cmi )29 s e l f . Bind (wx .EVT_MENU, s e l f . OnClose , cmi )303132 def OnMinimize ( s e l f , e ) :33 s e l f . parent . I c on i z e ( )3435 def OnClose ( s e l f , e ) :36 s e l f . parent . Close ( )373839 class Example (wx . Frame) :4041 def __init__( s e l f , * args , **kwargs ) :42 super (Example , s e l f ) . __init__(* args , **kwargs )4344 s e l f . In i tUI ( )4546 def In i tUI ( s e l f ) :4748 s e l f . Bind (wx .EVT_RIGHT_DOWN, s e l f . OnRightDown)4950 s e l f . S e tS i z e ( (350 , 250) )51 s e l f . S e tT i t l e ( ’ Context␣menu ’ )52 s e l f . Centre ( )5354 def OnRightDown( s e l f , e ) :55 s e l f . PopupMenu(MyPopupMenu( s e l f ) , e . GetPos i t ion ( ) )565758 def main ( ) :5960 app = wx .App( )61 ex = Example (None )62 ex . Show ( )63 app . MainLoop ( )646566 i f __name__ == ’__main__ ’ :67 main ( )

В примере мы создаем контекстное меню для главного окна. У него есть два пункта. Один свернет прило-жение, другой завершит его.

class MyPopupMenu(wx.Menu):

def __init__(self, parent):

super(MyPopupMenu, self).__init__()

Мы создаем отдельный класс wx.Menu.

18

mmi = wx.MenuItem(self, wx.NewId(), ’Minimize’)

self.Append(mmi)

self.Bind(wx.EVT_MENU, self.OnMinimize, mmi)

Элемент меню создается и добавляется в контекстное меню. Обработчик событий связывается с этим пунк-том меню.

self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)

Если щелкнуть правой кнопкой мыши фрейм, мы вызываем метод OnRightDown(). Для этого мы исполь-зуем механизм связывания событий wx.EVT_RIGHT_DOWN.

def OnRightDown(self, e):

self.PopupMenu(MyPopupMenu(self), e.GetPosition())

В методе OnRightDown() мы вызываем метод PopupMenu(). Этот метод показывает контекстное меню. Пер-вый параметр - это меню, которое будет показано. Второй параметр - это позиция, где появляется контекстноеменю. Контекстные меню появляются в точке курсора мыши. Чтобы получить фактическую позицию мыши,мы вызываем метод GetPosition() предоставленного объекта события.

Рис. 14: Контекстно дменю

3.6 Панели инструментов (Toolbars)

Меню группирует все команды, которые мы можем использовать в приложении. Панели инструментовобеспечивают быстрый доступ к наиболее часто используемым командам.

Чтобы создать панель инструментов, мы вызываем метод CreateToolBar() виджета фрейма.

Листинг 12: toolbar.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This example c r ea t e s a s imple t o o l b a r .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx15

19

1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kwargs ) :20 super (Example , s e l f ) . __init__(* args , **kwargs )2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 too lba r = s e l f . CreateToolBar ( )27 q too l = too lba r . AddTool (wx .ID_ANY, ’ Quit ’ , wx . Bitmap ( ’ t e x i t . png ’ ) )28 too lba r . Rea l i z e ( )2930 s e l f . Bind (wx .EVT_TOOL, s e l f . OnQuit , q too l )3132 s e l f . S e tS i z e ( (350 , 250) )33 s e l f . S e tT i t l e ( ’ Simple ␣ too lba r ’ )34 s e l f . Centre ( )3536 def OnQuit ( s e l f , e ) :37 s e l f . Close ( )383940 def main ( ) :4142 app = wx .App( )43 ex = Example (None )44 ex . Show ( )45 app . MainLoop ( )464748 i f __name__ == ’__main__ ’ :49 main ( )

В нашем примере у нас есть панель инструментов с одним инструментом. Инструмент закроет приложение,когда мы нажмем на него.

toolbar = self.CreateToolBar()

Мы создаем панель инструментов. По умолчанию панель инструментов горизонтальна, не имеет границ иотображает значки.

qtool = toolbar.AddTool(wx.ID_ANY, ’Quit’, wx.Bitmap(’texit.png’))

Чтобы создать инструмент панели инструментов, мы вызываем метод AddTool(). Второй параметр - этометка инструмента, третий - изображение инструмента. Обратите внимание, что метка не видна, потому чтостиль по умолчанию показывает только значки.

toolbar.Realize()

После того, как мы поместили наши элементы на панель инструментов, мы вызываем метод Realize(). Вызовэтого метода не является обязательным в Linux. В Windows это обязательно.

self.Bind(wx.EVT_TOOL, self.OnQuit, qtool)

Для обработки событий панели инструментов мы используем привязывание события wx.EVT_TOOL.

Если мы хотим создать более одной панели инструментов, мы должны сделать это по-другому.

20

Рис. 15: Простейшая панель инструментов

Листинг 13: toolbars.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 ’ ’ ’5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e two ho r i z on t a l8 t o o l b a r s .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 ’ ’ ’1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kwargs ) :21 super (Example , s e l f ) . __init__(* args , **kwargs )2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 vbox = wx . BoxSizer (wx .VERTICAL)2829 too lba r1 = wx . ToolBar ( s e l f )30 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ tnew . png ’ ) )31 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ topen . png ’ ) )32 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ t save . png ’ ) )33 too lba r1 . Rea l i z e ( )3435 too lba r2 = wx . ToolBar ( s e l f )36 q too l = too lba r2 . AddTool (wx . ID_EXIT, ’ ’ , wx . Bitmap ( ’ t e x i t . png ’ ) )37 too lba r2 . Rea l i z e ( )3839 vbox .Add( too lbar1 , 0 , wx .EXPAND)40 vbox .Add( too lbar2 , 0 , wx .EXPAND)4142 s e l f . Bind (wx .EVT_TOOL, s e l f . OnQuit , q too l )4344 s e l f . S e tS i z e r ( vbox )4546 s e l f . S e tS i z e ( (350 , 250) )

21

47 s e l f . S e tT i t l e ( ’ Toolbars ’ )48 s e l f . Centre ( )4950 def OnQuit ( s e l f , e ) :51 s e l f . Close ( )525354 def main ( ) :5556 app = wx .App( )57 ex = Example (None )58 ex . Show ( )59 app . MainLoop ( )606162 i f __name__ == ’__main__ ’ :63 main ( )

В приведенном выше примере мы создаем две горизонтальные панели инструментов.

toolbar1 = wx.ToolBar(self)

...

toolbar2 = wx.ToolBar(self)

Мы создали два объекта панели инструментов. И поместили их в вертикальный бокс.

Рис. 16: Два toolbar

3.7 Включить & выключить

В следующем примере мы показываем, как мы можем включать и отключать кнопки панели инструментов.Мы также добавляем разделительную линию.

Листинг 14: undo_redo.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e two ho r i z on t a l8 t o o l b a r s .

22

910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kwargs ) :20 super (Example , s e l f ) . __init__(* args , **kwargs )2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . count = 52728 s e l f . t oo lba r = s e l f . CreateToolBar ( )29 tundo = s e l f . t oo lba r . AddTool (wx .ID_UNDO, ’ ’ , wx . Bitmap ( ’ tundo . png ’ ) )30 tredo = s e l f . t oo lba r . AddTool (wx .ID_REDO, ’ ’ , wx . Bitmap ( ’ t redo . png ’ ) )31 s e l f . t oo lba r . EnableTool (wx .ID_REDO, Fal se )32 s e l f . t oo lba r . AddSeparator ( )33 t e x i t = s e l f . t oo lba r . AddTool (wx . ID_EXIT, ’ ’ , wx . Bitmap ( ’ t e x i t . png ’ ) )34 s e l f . t oo lba r . Rea l i z e ( )3536 s e l f . Bind (wx .EVT_TOOL, s e l f . OnQuit , t e x i t )37 s e l f . Bind (wx .EVT_TOOL, s e l f .OnUndo , tundo )38 s e l f . Bind (wx .EVT_TOOL, s e l f . OnRedo , t redo )3940 s e l f . S e tS i z e ( (350 , 250) )41 s e l f . S e tT i t l e ( ’Undo␣ redo ’ )42 s e l f . Centre ( )4344 def OnUndo( s e l f , e ) :45 i f s e l f . count > 1 and s e l f . count <= 5 :46 s e l f . count = s e l f . count − 14748 i f s e l f . count == 1 :49 s e l f . t oo lba r . EnableTool (wx .ID_UNDO, Fal se )5051 i f s e l f . count == 4 :52 s e l f . t oo lba r . EnableTool (wx .ID_REDO, True )5354 def OnRedo( s e l f , e ) :55 i f s e l f . count < 5 and s e l f . count >= 1 :56 s e l f . count = s e l f . count + 15758 i f s e l f . count == 5 :59 s e l f . t oo lba r . EnableTool (wx .ID_REDO, Fal se )6061 i f s e l f . count == 2 :62 s e l f . t oo lba r . EnableTool (wx .ID_UNDO, True )636465 def OnQuit ( s e l f , e ) :66 s e l f . Close ( )676869 def main ( ) :70

23

71 app = wx .App( )72 ex = Example (None )73 ex . Show ( )74 app . MainLoop ( )757677 i f __name__ == ’__main__ ’ :78 main ( )

В нашем примере у нас есть три кнопки панели инструментов. Одна кнопка для выхода из приложения. Дведругие кнопки - кнопки отмены и повтора. Они имитируют функции отмены / повтора в приложении. (Для ре-ального примера, см. Советы и рекомендации) У нас есть 4 варианта. Кнопки отмены и возврата соответственноотключены.

self.toolbar.EnableTool(wx.ID_REDO, False)

self.toolbar.AddSeparator()

В начале кнопка повтора отключена. Мы делаем это, вызывая метод EnableTool(). Мы можем создатьнесколько логических групп на панели инструментов. Мы можем разделить различные группы кнопок неболь-шой вертикальной линией. Для этого мы вызываем метод AddSeparator().

def OnUndo(self, e):

if self.count > 1 and self.count <= 5:

self.count = self.count - 1

if self.count == 1:

self.toolbar.EnableTool(wx.ID_UNDO, False)

if self.count == 4:

self.toolbar.EnableTool(wx.ID_REDO, True)

Мы моделируем функции отмены и повтора. У нас есть четыре состояния. Если разрешить любую кнопку,то кнопка отмены отключена. После отмены первого изменения мы активируем кнопку возврата. Та же логикаприменяется для метода OnRedo().

Рис. 17: Undo / Redo

В этой части руководства по wxPython мы работали с меню и панелями инструментов.

24

4 Менеджеры размещения в wxPython

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

4.1 Абсолютное позиционирование

Программист определяет положение и размер каждого виджета в пикселях. Абсолютное позиционированиеимеет несколько недостатков:

� Размер и положение виджета не изменятся, если мы изменим размер окна.

� Приложения выглядят по-разному на разных платформах.

� Изменение шрифтов в приложении может испортить макет.

� Если мы решим изменить наш макет, мы должны полностью переделать этот макет, что утомительно изанимает много времени.

Могут быть ситуации, когда мы можем использовать абсолютное позиционирование. Например, небольшиетестовые примеры. Но в основном в реальных программах программисты используют sizer -ы.

В нашем примере у нас есть простой фрэйм текстового редактора. Если мы изменим размер окна, размервывода wx.TextCtrl не изменится, как мы ожидаем.

Листинг 15: absolute.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we l ay out w idge t s us ing8 a b s o l u t e p o s i t i o n i n g .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , parent , t i t l e ) :21 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e ,22 s i z e =(350 , 300) )2324 s e l f . In i tUI ( )25 s e l f . Centre ( )2627 def In i tUI ( s e l f ) :2829 s e l f . panel = wx . Panel ( s e l f )3031 s e l f . panel . SetBackgroundColour ( " gray" )3233 s e l f . LoadImages ( )

25

3435 s e l f . mincol . S e tPos i t i on ( (20 , 20) )36 s e l f . bardejov . Se tPos i t i on ( (40 , 160) )37 s e l f . rotunda . Se tPos i t i on ( (170 , 50) )383940 def LoadImages ( s e l f ) :4142 s e l f . mincol = wx . StaticBitmap ( s e l f . panel , wx .ID_ANY,43 wx . Bitmap ( "mincol . jpg " , wx .BITMAP_TYPE_ANY) )4445 s e l f . bardejov = wx . StaticBitmap ( s e l f . panel , wx .ID_ANY,46 wx . Bitmap ( " bardejov . jpg " , wx .BITMAP_TYPE_ANY) )4748 s e l f . rotunda = wx . StaticBitmap ( s e l f . panel , wx .ID_ANY,49 wx . Bitmap ( " rotunda . jpg " , wx .BITMAP_TYPE_ANY) )505152 def main ( ) :5354 app = wx .App( )55 ex = Example (None , t i t l e=’ Absolute ␣ p o s i t i o n i n g ’ )56 ex . Show ( )57 app . MainLoop ( )585960 i f __name__ == ’__main__ ’ :61 main ( )

В приведенном выше примере мы размещаем три изображения, используя абсолютные координаты.

self.mincol.SetPosition((20, 20))

С помощью метода SetPosition () мы помещаем изображение в координаты x = 20, y = 20.

Рис. 18: Абсолютное позиционирование

26

4.2 Использование sizer-ов

Сайзеры решают все те проблемы, которые мы упомянули, используя абсолютное позиционирование. wxPythonимеет следующие sizer-ы:

� wx.BoxSizer

� wx.StaticBoxSizer

� wx.GridSizer

� wx.FlexGridSizer

� wx.GridBagSizer

4.2.1 wx.BoxSizer

wx.BoxSizer позволяет нам поместить несколько виджетов в строку или столбец. Мы можем поместить Sizerв другой существующий Sizer. Таким образом, мы можем создавать очень сложные макеты.

box = wx.BoxSizer(integer orient)

box.Add(wx.Window window, integer proportion=0, integer flag = 0, integer border = 0)

Ориентация может быть wx.VERTICAL или wx.HORIZONTAL. Добавление виджетов в wx.BoxSizer выпол-няется с помощью метода Add(). Чтобы понять это, нам нужно посмотреть на его параметры.

Параметр пропорции определяет соотношение изменения виджетов в определенной ориентации. Предполо-жим, у нас есть три кнопки с пропорциями 0, 1 и 2. Они добавляются в горизонтальный wx.BoxSizer. Кнопка спропорцией 0 не изменится вообще. Кнопка с пропорцией 2 изменится вдвое больше, чем кнопка с пропорцией1 в горизонтальном измерении.

С помощью параметра flag вы можете дополнительно настроить поведение виджетов в wx.BoxSizer. Мыможем контролировать границу между виджетами. Мы добавляем пространство между виджетами в пикселях.Чтобы применить границу, нам нужно определить стороны, где будет использоваться граница. Мы можемобъединить их с помощью оператора |; например, wx.LEFT | wx.BOTTOM. Мы можем выбирать между этимифлагами:

� wx.LEFT

� wx.RIGHT

� wx.BOTTOM

� wx.TOP

� wx.ALL

Sizer устанавливается на виджет панели с помощью метода setSizer().

Листинг 16: border.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we p lace a pane l i n s i d e8 another pane l .910 author : Jan Bodnar

27

11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , parent , t i t l e ) :21 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e )2223 s e l f . In i tUI ( )24 s e l f . Centre ( )2526 def In i tUI ( s e l f ) :2728 panel = wx . Panel ( s e l f )2930 panel . SetBackgroundColour ( ’#4f5049 ’ )31 vbox = wx . BoxSizer (wx .VERTICAL)3233 midPan = wx . Panel ( panel )34 midPan . SetBackgroundColour ( ’#ededed ’ )3536 vbox .Add(midPan , wx .ID_ANY, wx .EXPAND | wx .ALL, 20)37 panel . S e tS i z e r ( vbox )383940 def main ( ) :4142 app = wx .App( )43 ex = Example (None , t i t l e=’ Border ’ )44 ex . Show ( )45 app . MainLoop ( )464748 i f __name__ == ’__main__ ’ :49 main ( )

В приведенном выше примере мы оставляем некоторое пространство вокруг панели.

vbox.Add(midPan, wx.ID_ANY, wx.EXPAND | wx.ALL, 20)

В файле border.py мы поместили рамку размером 20 пикселей вокруг панели midPan.wx.ALL применяетразмер границы ко всем четырем сторонам.

Если мы используем флаг wx.EXPAND, наш виджет будет использовать все пространство, выделенное длянего. Наконец, мы также можем определить выравнивание наших виджетов. Мы делаем это со следующимифлагами:

� wx.ALIGN_LEFT

� wx.ALIGN_RIGHT

� wx.ALIGN_TOP

� wx.ALIGN_BOTTOM

� wx.ALIGN_CENTER_VERTICAL

� wx.ALIGN_CENTER_HORIZONTAL

28

� wx.ALIGN_CENTER

Рис. 19: Рамка вокруг панели

4.2.2 Пример GoToClass

В следующем примере мы представляем несколько важных идей.

Листинг 17: goto_class.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−345 """6 ZetCode wxPython t u t o r i a l78 In t h i s example we c r ea t e a Go To c l a s s9 l a you t wi th wx . BoxSizer .1011 author : Jan Bodnar12 web s i t e : www. ze t code . com13 l a s t modi f ied : Apr i l 201814 """1516 import wx1718 class Example (wx . Frame) :1920 def __init__( s e l f , parent , t i t l e ) :21 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e )2223 s e l f . In i tUI ( )24 s e l f . Centre ( )2526 def In i tUI ( s e l f ) :2728 panel = wx . Panel ( s e l f )2930 font = wx . SystemSett ings . GetFont (wx .SYS_SYSTEM_FONT)3132 font . Se tPo intS i z e (9 )3334 vbox = wx . BoxSizer (wx .VERTICAL)35

29

36 hbox1 = wx . BoxSizer (wx .HORIZONTAL)37 s t1 = wx . Stat i cText ( panel , l a b e l=’ Class ␣Name ’ )38 s t1 . SetFont ( f ont )39 hbox1 .Add( st1 , f l a g=wx .RIGHT, border=8)40 tc = wx . TextCtrl ( panel )41 hbox1 .Add( tc , propor t ion=1)42 vbox .Add( hbox1 , f l a g=wx .EXPAND|wx .LEFT|wx .RIGHT|wx .TOP, border=10)4344 vbox .Add((−1 , 10) )4546 hbox2 = wx . BoxSizer (wx .HORIZONTAL)47 s t2 = wx . Stat i cText ( panel , l a b e l=’Matching␣ C la s s e s ’ )48 s t2 . SetFont ( f ont )49 hbox2 .Add( s t2 )50 vbox .Add( hbox2 , f l a g=wx .LEFT | wx .TOP, border=10)5152 vbox .Add((−1 , 10) )5354 hbox3 = wx . BoxSizer (wx .HORIZONTAL)55 tc2 = wx . TextCtrl ( panel , s t y l e=wx .TE_MULTILINE)56 hbox3 .Add( tc2 , propor t ion=1, f l a g=wx .EXPAND)57 vbox .Add( hbox3 , propor t ion=1, f l a g=wx .LEFT|wx .RIGHT|wx .EXPAND,58 border=10)5960 vbox .Add((−1 , 25) )6162 hbox4 = wx . BoxSizer (wx .HORIZONTAL)63 cb1 = wx . CheckBox ( panel , l a b e l=’ Case␣ S en s i t i v e ’ )64 cb1 . SetFont ( f ont )65 hbox4 .Add( cb1 )66 cb2 = wx . CheckBox ( panel , l a b e l=’ Nested␣ C la s s e s ’ )67 cb2 . SetFont ( f ont )68 hbox4 .Add( cb2 , f l a g=wx .LEFT, border=10)69 cb3 = wx . CheckBox ( panel , l a b e l=’Non−Pro j e c t ␣ c l a s s e s ’ )70 cb3 . SetFont ( f ont )71 hbox4 .Add( cb3 , f l a g=wx .LEFT, border=10)72 vbox .Add( hbox4 , f l a g=wx .LEFT, border=10)7374 vbox .Add((−1 , 25) )7576 hbox5 = wx . BoxSizer (wx .HORIZONTAL)77 btn1 = wx . Button ( panel , l a b e l=’Ok ’ , s i z e =(70 , 30) )78 hbox5 .Add( btn1 )79 btn2 = wx . Button ( panel , l a b e l=’ Close ’ , s i z e =(70 , 30) )80 hbox5 .Add( btn2 , f l a g=wx .LEFT|wx .BOTTOM, border=5)81 vbox .Add( hbox5 , f l a g=wx .ALIGN_RIGHT|wx .RIGHT, border=10)8283 panel . S e tS i z e r ( vbox )848586 def main ( ) :8788 app = wx .App( )89 ex = Example (None , t i t l e=’Go␣To␣Class ’ )90 ex . Show ( )91 app . MainLoop ( )929394 i f __name__ == ’__main__ ’ :95 main ( )

30

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

font = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)

font.SetPointSize(9)

Мы изменили размер шрифта до 9 пикселей.

vbox.Add(hbox3, proportion=1, flag=wx.LEFT|wx.RIGHT|wx.EXPAND,

border=10)

vbox.Add((-1, 25))

Мы уже знаем, что мы можем контролировать расстояние между виджетами, комбинируя параметр flag спараметром border. Но есть одно реальное ограничение. В методе Add() мы можем указать только одну границудля всех заданных сторон. В нашем примере мы даем 10 пикселей вправо и влево. Но мы не можем дать25 пикселей снизу. Что мы можем сделать, это дать 10 пикселей внизу или 0 пикселей, если мы опускаемwx.BOTTOM. Поэтому, если нам нужны разные значения, мы можем добавить дополнительное пространство.С помощью метода Add() мы можем также вставлять виджеты и пространство.

vbox.Add(hbox5, flag=wx.ALIGN_RIGHT|wx.RIGHT, border=10)

Мы размещаем две кнопки на правой стороне окна. Для этого важны три вещи: пропорция, флаг вырав-нивания и флаг wx.EXPAND. Пропорция должна быть нулевой. Кнопки не должны менять свой размер приизменении размера нашего окна. Мы не должны указывать флаг wx.EXPAND. Кнопки скрывают только об-ласть, выделенную для них. И, наконец, мы должны указать флаг wx.ALIGN_RIGHT. Горизонтальный сайзерработает от левой стороны окна к правой стороне. Поэтому, если мы укажем флаг wx.ALIGN_RIGHT, кнопкибудут расположены справа.

Рис. 20: Окно GoToClass

4.2.3 wx.GridSizer

Wx.GridSizer размещает виджеты в двухмерной таблице. Каждая ячейка в таблице имеет одинаковыйразмер.

wx.GridSizer(int rows=1, int cols=0, int vgap=0, int hgap=0)

31

В конструкторе мы указываем количество строк и столбцов в таблице, а также вертикальное и горизон-тальное пространство между нашими ячейками.

В нашем примере мы создаем скелет калькулятора.

Листинг 18: calculator.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−345 """6 ZetCode wxPython t u t o r i a l78 In t h i s example we c r ea t e a l ayou t9 o f a c a l c u l a t o r wi th wx . Gr idS i zer .1011 author : Jan Bodnar12 web s i t e : www. ze t code . com13 l a s t modi f ied : Apr i l 201814 """1516 import wx171819 class Example (wx . Frame) :2021 def __init__( s e l f , parent , t i t l e ) :22 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e )2324 s e l f . In i tUI ( )25 s e l f . Centre ( )262728 def In i tUI ( s e l f ) :2930 menubar = wx .MenuBar ( )31 f i l eMenu = wx .Menu( )32 menubar . Append( f i leMenu , ’&F i l e ’ )33 s e l f . SetMenuBar (menubar )3435 vbox = wx . BoxSizer (wx .VERTICAL)36 s e l f . d i sp l ay = wx . TextCtrl ( s e l f , s t y l e=wx .TE_RIGHT)37 vbox .Add( s e l f . d i sp lay , f l a g=wx .EXPAND|wx .TOP|wx .BOTTOM, border=4)38 gs = wx . Gr idS i ze r (5 , 4 , 5 , 5)3940 gs .AddMany( [ (wx . Button ( s e l f , l a b e l=’ Cls ’ ) , 0 , wx .EXPAND) ,41 (wx . Button ( s e l f , l a b e l=’Bck ’ ) , 0 , wx .EXPAND) ,42 (wx . Stat i cText ( s e l f ) , wx .EXPAND) ,43 (wx . Button ( s e l f , l a b e l=’ Close ’ ) , 0 , wx .EXPAND) ,44 (wx . Button ( s e l f , l a b e l=’ 7 ’ ) , 0 , wx .EXPAND) ,45 (wx . Button ( s e l f , l a b e l=’ 8 ’ ) , 0 , wx .EXPAND) ,46 (wx . Button ( s e l f , l a b e l=’ 9 ’ ) , 0 , wx .EXPAND) ,47 (wx . Button ( s e l f , l a b e l=’ / ’ ) , 0 , wx .EXPAND) ,48 (wx . Button ( s e l f , l a b e l=’ 4 ’ ) , 0 , wx .EXPAND) ,49 (wx . Button ( s e l f , l a b e l=’ 5 ’ ) , 0 , wx .EXPAND) ,50 (wx . Button ( s e l f , l a b e l=’ 6 ’ ) , 0 , wx .EXPAND) ,51 (wx . Button ( s e l f , l a b e l=’ * ’ ) , 0 , wx .EXPAND) ,52 (wx . Button ( s e l f , l a b e l=’ 1 ’ ) , 0 , wx .EXPAND) ,53 (wx . Button ( s e l f , l a b e l=’ 2 ’ ) , 0 , wx .EXPAND) ,54 (wx . Button ( s e l f , l a b e l=’ 3 ’ ) , 0 , wx .EXPAND) ,55 (wx . Button ( s e l f , l a b e l=’− ’ ) , 0 , wx .EXPAND) ,56 (wx . Button ( s e l f , l a b e l=’ 0 ’ ) , 0 , wx .EXPAND) ,

32

57 (wx . Button ( s e l f , l a b e l=’ . ’ ) , 0 , wx .EXPAND) ,58 (wx . Button ( s e l f , l a b e l=’=’ ) , 0 , wx .EXPAND) ,59 (wx . Button ( s e l f , l a b e l=’+’ ) , 0 , wx .EXPAND) ] )6061 vbox .Add( gs , propor t ion=1, f l a g=wx .EXPAND)62 s e l f . S e tS i z e r ( vbox )636465 def main ( ) :6667 app = wx .App( )68 ex = Example (None , t i t l e=’ Ca l cu la to r ’ )69 ex . Show ( )70 app . MainLoop ( )717273 i f __name__ == ’__main__ ’ :74 main ( )

Обратите внимание, как нам удалось поместить пробел между кнопками Bck и Close. Мы просто помещаемтуда пустой wx.StaticText.

В нашем примере мы использовали метод AddMany(). Это удобный метод для добавления несколькихвиджетов одновременно.

gs.AddMany( [(wx.Button(self, label=’Cls’), 0, wx.EXPAND),

...

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

Рис. 21: Окно калькулятора

4.2.4 wx.FlexGridSizer

Этот сайзер похож на wx.GridSizer. Он также размещает свои виджеты в двухмерной таблице. Но он добав-ляет гибкости. Ячейки wx.GridSizer имеют одинаковый размер. Все ячейки в wx.FlexGridSizer имеют одинако-вую высоту в одном ряду. Все ячейки имеют одинаковую ширину в одном столбце. Но не все строки и столбцыобязательно имеют одинаковую высоту или ширину.

wx.FlexGridSizer(int rows=1, int cols=0, int vgap=0, int hgap=0)

33

rows и cols определяют количество строк и столбцов в классификаторе. vgap и hgap добавляют пространствомежду виджетами в обоих направлениях.

Много раз разработчикам приходится разрабатывать диалоги для ввода и модификации данных. Я нахожуwx.FlexGridSizer подходящим для такой задачи. Разработчик может легко настроить диалоговое окно с помощьюэтого сайзера. Этого также можно достичь с помощью wx.GridSizer, но это будет выглядеть не очень хорошоиз-за ограничения, что каждая ячейка должна иметь одинаковый размер.

Листинг 19: review.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e rev iew8 l a you t wi th wx . F l exGr idS i zer .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , parent , t i t l e ) :20 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e )2122 s e l f . In i tUI ( )23 s e l f . Centre ( )24 s e l f . Show ( )2526 def In i tUI ( s e l f ) :2728 panel = wx . Panel ( s e l f )2930 hbox = wx . BoxSizer (wx .HORIZONTAL)3132 f g s = wx . F lexGr idS ize r (3 , 2 , 9 , 25)3334 t i t l e = wx . Stat i cText ( panel , l a b e l=" T i t l e " )35 author = wx . Stat i cText ( panel , l a b e l="Author" )36 review = wx . Stat i cText ( panel , l a b e l="Review" )3738 tc1 = wx . TextCtrl ( panel )39 tc2 = wx . TextCtrl ( panel )40 tc3 = wx . TextCtrl ( panel , s t y l e=wx .TE_MULTILINE)4142 f g s .AddMany ( [ ( t i t l e ) , ( tc1 , 1 , wx .EXPAND) , ( author ) ,43 ( tc2 , 1 , wx .EXPAND) , ( review , 1 , wx .EXPAND) , ( tc3 , 1 , wx .EXPAND) ] )4445 f g s . AddGrowableRow (2 , 1)46 f g s . AddGrowableCol (1 , 1)4748 hbox .Add( fgs , propor t ion=1, f l a g=wx .ALL|wx .EXPAND, border=15)49 panel . S e tS i z e r ( hbox )505152 def main ( ) :53

34

54 app = wx .App( )55 ex = Example (None , t i t l e=’Review ’ )56 ex . Show ( )57 app . MainLoop ( )585960 i f __name__ == ’__main__ ’ :61 main ( )

В приведенном выше примере кода мы создаем окно обзора с помощью FlexGridSizer.

hbox = wx.BoxSizer(wx.HORIZONTAL)

...

hbox.Add(fgs, proportion=1, flag=wx.ALL|wx.EXPAND, border=15)

Мы создадим горизонтальный зайзер, чтобы добавить некоторое пространство (15 пикселей) вокруг таб-лицы виджетов.

fgs.AddMany([(title), (tc1, 1, wx.EXPAND), (author),

(tc2, 1, wx.EXPAND), (review, 1, wx.EXPAND), (tc3, 1, wx.EXPAND)])

Мы добавляем виджеты в сайзер с помощью метода AddMany(). Этот метод есть и в wx.FlexGridSizer и вwx.GridSizer.

fgs.AddGrowableRow(2, 1)

fgs.AddGrowableCol(1, 1)

Мы делаем третий ряд и второй столбец расширяемыми. Таким образом, мы позволяем текстовым элемен-там управления расти при изменении размера окна. Первые два текстовых элемента управления будут растив горизонтальном направлении, третий - в обоих направлениях. Чтобы это работало, мы не должны забыватьсделать виджеты расширяемыми с помощью wx.EXPAND.

Рис. 22: Окно review

4.2.5 wx.GridBagSizer

wx.GridBagSizer является наиболее гибким сайзером в wxPython, который можно использовать. Этот видсайзера характерен не только для wxPython. Мы можем найти нго и в других инструментах.

35

Этот сайзер позволяет явно позиционировать элементы. Элементы также могут опционально заниматьболее одной строки или столбца. У wx.GridBagSizer есть простой конструктор.

wx.GridBagSizer(integer vgap, integer hgap)

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

Add(self, item, tuple pos, tuple span=wx.DefaultSpan, integer flag=0,

integer border=0, userData=None)

item - это виджет, который вы вставляете в сетку. pos указывает позицию в виртуальной сетке. Верхняялевая ячейка имеет pos (0, 0). span является необязательным охватом виджета; например span(3, 2) охватываетвиджет в 3 строки и 2 столбца. flag и border обсуждались ранее в wx.BoxSizer. Элементы в сетке могут изменитьсвой размер или оставить размер по умолчанию при изменении размера окна. Если мы хотим, чтобы вашивиджеты росли и сокращались, можно использовать следующие два метода:

AddGrowableRow(integer row)

AddGrowableCol(integer col)

4.2.6 Пример окна переименования

В нашем первом примере мы создаем окно Rename. Он будет иметь один виджет wx.StaticText, одинwx.TextCtrl и две кнопки wx.Button.

Листинг 20: rename.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e a rename l ayou t8 with wx . GridBagSizer .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , parent , t i t l e ) :21 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e )2223 s e l f . In i tUI ( )24 s e l f . Centre ( )2526 def In i tUI ( s e l f ) :2728 panel = wx . Panel ( s e l f )29 s i z e r = wx . GridBagSizer (4 , 4)3031 text = wx . Stat i cText ( panel , l a b e l="Rename␣To" )

36

32 s i z e r .Add( text , pos=(0 , 0) , f l a g=wx .TOP|wx .LEFT|wx .BOTTOM, border=5)3334 tc = wx . TextCtrl ( panel )35 s i z e r .Add( tc , pos=(1 , 0) , span=(1 , 5) ,36 f l a g=wx .EXPAND|wx .LEFT|wx .RIGHT, border=5)3738 buttonOk = wx . Button ( panel , l a b e l="Ok" , s i z e =(90 , 28) )39 buttonClose = wx . Button ( panel , l a b e l="Close " , s i z e =(90 , 28) )40 s i z e r .Add( buttonOk , pos=(3 , 3) )41 s i z e r .Add( buttonClose , pos=(3 , 4) , f l a g=wx .RIGHT|wx .BOTTOM, border=10)4243 s i z e r . AddGrowableCol (1 )44 s i z e r . AddGrowableRow (2)45 panel . S e tS i z e r ( s i z e r )464748 def main ( ) :4950 app = wx .App( )51 ex = Example (None , t i t l e=’Rename ’ )52 ex . Show ( )53 app . MainLoop ( )545556 i f __name__ == ’__main__ ’ :57 main ( )

Мы должны представлять окно как на одну большую таблицу.

text = wx.StaticText(panel, label="Rename To")

sizer.Add(text, pos=(0, 0), flag=wx.TOP|wx.LEFT|wx.BOTTOM, border=10)

Текст «Переименовать в» идет в левый верхний угол. Таким образом, мы указываем (0, 0) позицию. И мыдобавляем пространство внизу, слева и снизу.

tc = wx.TextCtrl(panel)

sizer.Add(tc, pos=(1, 0), span=(1, 5),

flag=wx.EXPAND|wx.LEFT|wx.RIGHT, border=5)

Wx.TextCtrl начинается с начала второй строки (1, 0). Помните, что мы считаем с нуля. Расширяется на 1строку и 5 столбцов (1, 5). И мы помещаем 5 пикселей пространства слева и справа от виджета.

sizer.Add(buttonOk, pos=(3, 3))

sizer.Add(buttonClose, pos=(3, 4), flag=wx.RIGHT|wx.BOTTOM, border=10)

Мы поместили две кнопки в четвертый ряд. Третий ряд остается пустым, так что у нас есть некоторое про-странство между wx.TextCtrl и кнопками. Мы помещаем кнопку ОК в четвертый столбец и кнопку Закрыть впятый. Обратите внимание, что как только мы добавляем некоторое пространство к одному виджету, оно добав-ляется ко всей строке. Вот почему мы не указали нижнее пространство для кнопки ОК. Внимательный читательможет заметить, что мы не указали никакого пространства между двумя кнопками; то есть мы не ставили ни-каких пробелов ни справа от кнопки «ОК», ни справа от кнопки «Закрыть». В конструкторе wx.GridBagSizerмы помещаем некоторое пространство между всеми виджетами. Так что уже есть место.

sizer.AddGrowableCol(1)

sizer.AddGrowableRow(2)

Последнее, что мы должны сделать, - это попытаться изменить размер нашего окна диалога. Мыс де-лали второй столбец и третий ряд расширяемыми. Теперь мы можем расширить или уменьшить наше окно.Попробуйте закомментировать эти две строки и посмотрите, что произойдет.

37

Рис. 23: Окно переименования

4.2.7 Пример: Новый класс

В следующем примере мы создаем окно, которое можно найти в JDeveloper. Это окно для создания новогокласса в Java.

Листинг 21: new_class.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e a new c l a s s l a you t8 with wx . GridBagSizer .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , parent , t i t l e ) :20 super (Example , s e l f ) . __init__( parent , t i t l e=t i t l e )2122 s e l f . In i tUI ( )23 s e l f . Centre ( )2425 def In i tUI ( s e l f ) :2627 panel = wx . Panel ( s e l f )2829 s i z e r = wx . GridBagSizer (5 , 5)3031 text1 = wx . Stat i cText ( panel , l a b e l="Java␣Class " )32 s i z e r .Add( text1 , pos=(0 , 0) , f l a g=wx .TOP|wx .LEFT|wx .BOTTOM,33 border=15)3435 i con = wx . StaticBitmap ( panel , bitmap=wx . Bitmap ( ’ exec . png ’ ) )36 s i z e r .Add( icon , pos=(0 , 4) , f l a g=wx .TOP|wx .RIGHT|wx .ALIGN_RIGHT,37 border=5)3839 l i n e = wx . S ta t i cL in e ( panel )40 s i z e r .Add( l i n e , pos=(1 , 0) , span=(1 , 5) ,41 f l a g=wx .EXPAND|wx .BOTTOM, border=10)42

38

43 text2 = wx . Stat i cText ( panel , l a b e l="Name" )44 s i z e r .Add( text2 , pos=(2 , 0) , f l a g=wx .LEFT, border=10)4546 tc1 = wx . TextCtrl ( panel )47 s i z e r .Add( tc1 , pos=(2 , 1) , span=(1 , 3) , f l a g=wx .TOP|wx .EXPAND)4849 text3 = wx . Stat i cText ( panel , l a b e l="Package" )50 s i z e r .Add( text3 , pos=(3 , 0) , f l a g=wx .LEFT|wx .TOP, border=10)5152 tc2 = wx . TextCtrl ( panel )53 s i z e r .Add( tc2 , pos=(3 , 1) , span=(1 , 3) , f l a g=wx .TOP|wx .EXPAND,54 border=5)5556 button1 = wx . Button ( panel , l a b e l="Browse . . . " )57 s i z e r .Add( button1 , pos=(3 , 4) , f l a g=wx .TOP|wx .RIGHT, border=5)5859 text4 = wx . Stat i cText ( panel , l a b e l="Extends" )60 s i z e r .Add( text4 , pos=(4 , 0) , f l a g=wx .TOP|wx .LEFT, border=10)6162 combo = wx .ComboBox( panel )63 s i z e r .Add(combo , pos=(4 , 1) , span=(1 , 3) ,64 f l a g=wx .TOP|wx .EXPAND, border=5)6566 button2 = wx . Button ( panel , l a b e l="Browse . . . " )67 s i z e r .Add( button2 , pos=(4 , 4) , f l a g=wx .TOP|wx .RIGHT, border=5)6869 sb = wx . Stat icBox ( panel , l a b e l="Optional ␣ At t r ibute s " )7071 box s i z e r = wx . S ta t i cBoxS i z e r ( sb , wx .VERTICAL)72 box s i z e r .Add(wx . CheckBox ( panel , l a b e l="Publ ic " ) ,73 f l a g=wx .LEFT|wx .TOP, border=5)74 box s i z e r .Add(wx . CheckBox ( panel , l a b e l="Generate ␣Defau l t ␣Constructor " ) ,75 f l a g=wx .LEFT, border=5)76 box s i z e r .Add(wx . CheckBox ( panel , l a b e l="Generate ␣Main␣Method" ) ,77 f l a g=wx .LEFT|wx .BOTTOM, border=5)78 s i z e r .Add( boxs i ze r , pos=(5 , 0) , span=(1 , 5) ,79 f l a g=wx .EXPAND|wx .TOP|wx .LEFT|wx .RIGHT , border=10)8081 button3 = wx . Button ( panel , l a b e l=’ Help ’ )82 s i z e r .Add( button3 , pos=(7 , 0) , f l a g=wx .LEFT, border=10)8384 button4 = wx . Button ( panel , l a b e l="Ok" )85 s i z e r .Add( button4 , pos=(7 , 3) )8687 button5 = wx . Button ( panel , l a b e l="Cancel " )88 s i z e r .Add( button5 , pos=(7 , 4) , span=(1 , 1) ,89 f l a g=wx .BOTTOM|wx .RIGHT, border=10)9091 s i z e r . AddGrowableCol (2 )9293 panel . S e tS i z e r ( s i z e r )94 s i z e r . F i t ( s e l f )959697 def main ( ) :9899 app = wx .App( )100 ex = Example (None , t i t l e="Create ␣Java␣Class " )101 ex . Show ( )102 app . MainLoop ( )103104

39

105 i f __name__ == ’__main__ ’ :106 main ( )

Это более сложный макет. Мы используем как wx.GridBagSizer, так и wx.StaticBoxsizer.

line = wx.StaticLine(panel)

sizer.Add(line, pos=(1, 0), span=(1, 5),

flag=wx.EXPAND|wx.BOTTOM, border=10)

Эта строка используется для разделения групп виджетов в макете.

icon = wx.StaticBitmap(panel, bitmap=wx.Bitmap(’exec.png’))

sizer.Add(icon, pos=(0, 4), flag=wx.TOP|wx.RIGHT|wx.ALIGN_RIGHT,

border=5)

Мы помещаем wx.StaticBitmap в первый ряд сетки. Мы размещаем его на правой стороне ряда.

sb = wx.StaticBox(panel, label="Optional Attributes")

boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)

wxStaticBoxSizer похож на обычный wx.BoxSizer, но добавляет статическую рамку вокруг сайзера. Мыпомещаем check-боксы в бокс статического сайзера.

Рис. 24: Окно создания нового класса

Эта часть руководства по wxPython была посвящена управлению макетом.

5 События в wxPython

События являются неотъемлемой частью каждого приложения с графическим интерфейсом. Все приложе-ния с графическим интерфейсом основаны на событиях. Приложение реагирует на различные типы событий,которые генерируются в течение его жизни. События генерируются в основном пользователем приложения. Ноони могут быть получены и другими способами; например из Интернет-соединения, от оконного менеджера илитаймера. Поэтому, когда мы вызываем метод MainLoop(), наше приложение ожидает возникновения событий.Метод MainLoop() завершается при выходе из приложения.

40

5.1 Определения

Событие - это часть информации уровня приложения из базовой структуры, обычно из средств разработкиGUI.

Цикл событий - это программная конструкция, которая ожидает и отправляет события или сообщения впрограмме. Цикл событий многократно ищет события для обработки.

Диспетчер - это процесс, который отображает события в обработчики событий.

Обработчики событий - это методы, которые реагируют на события.

Объект события - это объект, связанный с событием. Обычно это окно.

Тип события - это уникальное событие, которое было сгенерировано.

Биндер событий - это объект, который связывает тип события с обработчиком события.

5.2 Пример на wxPython для события wx.EVT_MOVE

В следующем примере мы реагируем на событие wx.MoveEvent. Событие генерируется, когда мы переме-щаем окно на новую позицию. Биндер для этого события - wx.EVT_MOVE.

Листинг 22: simple_event.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This i s a wx . MoveEvent event demostrat ion .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx1516 class Example (wx . Frame) :1718 def __init__( s e l f , * args , **kw) :19 super (Example , s e l f ) . __init__(* args , **kw)2021 s e l f . In i tUI ( )222324 def In i tUI ( s e l f ) :2526 wx . Stat i cText ( s e l f , l a b e l=’ x : ’ , pos =(10 ,10) )27 wx . Stat i cText ( s e l f , l a b e l=’ y : ’ , pos =(10 ,30) )2829 s e l f . s t1 = wx . Stat i cText ( s e l f , l a b e l=’ ’ , pos=(30 , 10) )30 s e l f . s t2 = wx . Stat i cText ( s e l f , l a b e l=’ ’ , pos=(30 , 30) )3132 s e l f . Bind (wx .EVT_MOVE, s e l f .OnMove)3334 s e l f . S e tS i z e ( (350 , 250) )35 s e l f . S e tT i t l e ( ’Move␣ event ’ )36 s e l f . Centre ( )3738 def OnMove( s e l f , e ) :

41

3940 x , y = e . GetPos i t ion ( )41 s e l f . s t1 . SetLabel ( str ( x ) )42 s e l f . s t2 . SetLabel ( str ( y ) )434445 def main ( ) :4647 app = wx .App( )48 ex = Example (None )49 ex . Show ( )50 app . MainLoop ( )515253 i f __name__ == ’__main__ ’ :54 main ( )

В примере отображается текущая позиция окна.

self.Bind(wx.EVT_MOVE, self.OnMove)

Здесь мы связываем событие wx.EVT_MOVE с методом OnMove().

def OnMove(self, e):

x, y = e.GetPosition()

self.st1.SetLabel(str(x))

self.st2.SetLabel(str(y))

text

Параметр event в методе OnMove() является объектом, специфичным для определенного типа события. Внашем случае это экземпляр класса wx.MoveEvent. Этот объект содержит информацию о событии. Например,объект события или положение окна. В нашем случае объектом события является виджет wx.Frame. Мы можемузнать текущую позицию, вызвав метод GetPosition() события.

Рис. 25: Событие Move

5.3 Привязка события wxPython

Три шага для работы с событиями в wxPython:

42

1. Определите имя связываемого события: wx.EVT_SIZE, wx.EVT_CLOSE и т.д.

2. Создайте обработчик событий. Этот метод вызывается, когда генерируется событие.

3. Привяжите событие к обработчику событий.

В wxPython мы говорим "связать метод с событием". По английски "bind". Вы связываете событие, вызываяметод Bind(). Метод имеет следующие параметры:

Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)

event является одним из объектов EVT_*. Указывает тип события. handler - это объект, который нужновызвать. Другими словами, это метод, который программист связывает с событием. Параметр source исполь-зуется, когда мы хотим отличить один и тот же тип события от разных виджетов. Параметр id используется,когда у нас есть несколько кнопок, пунктов меню и т.l. Идентификатор используется для того, что бы их раз-личать. id2 используется, когда желательно связать обработчик с диапазоном идентификаторов, например сEVT_MENU_RANGE.

Обратите внимание, что метод Bind() определен в классе EvtHandler. Это класс, от которого наследуетсяwx.Window. wx.Window - это базовый класс для большинства виджетов в wxPython. Существует также обратныйпроцесс. Если мы хотим отсоединить метод от события, мы вызываем метод Unbind(). Он имеет те же параметры,что и вышеупомянутый Bind( ).

5.4 Вето на событие

Иногда нам нужно прекратить обработку события. Для этого мы вызываем метод Veto().

Листинг 23: event_veto.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 import wx56 """7 ZetCode wxPython t u t o r i a l89 In t h i s example we ve to an event .1011 author : Jan Bodnar12 web s i t e : www. ze t code . com13 l a s t modi f ied : Apr i l 201814 """1516 class Example (wx . Frame) :1718 def __init__( s e l f , * args , **kw) :19 super (Example , s e l f ) . __init__(* args , **kw)2021 s e l f . In i tUI ( )2223 def In i tUI ( s e l f ) :2425 s e l f . Bind (wx .EVT_CLOSE, s e l f . OnCloseWindow)2627 s e l f . S e tT i t l e ( ’ Event␣ veto ’ )28 s e l f . Centre ( )2930 def OnCloseWindow( s e l f , e ) :31

43

32 d i a l = wx . MessageDialog (None , ’Are␣you␣ sure ␣ to ␣ qu i t ? ’ , ’ Question ’ ,33 wx .YES_NO | wx .NO_DEFAULT | wx .ICON_QUESTION)3435 r e t = d i a l . ShowModal ( )3637 i f r e t == wx .ID_YES:38 s e l f . Destroy ( )39 else :40 e . Veto ( )414243 def main ( ) :4445 app = wx .App( )46 ex = Example (None )47 ex . Show ( )48 app . MainLoop ( )495051 i f __name__ == ’__main__ ’ :52 main ( )

В нашем примере мы обрабатываем wx.CloseEvent. Это событие вызывается, когда мы нажимаем кнопкуX на заголовке окна программы, нажимаем Alt+F4 или выбираем "Закрыть"из системного меню. Во многихприложениях мы хотим предотвратить случайное закрытие окна, если мы внесли некоторые изменения. Дляэтого мы должны связать механизм связывания событий wx.EVT_CLOSE.

dial = wx.MessageDialog(None, ’Are you sure to quit?’, ’Question’,

wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)

ret = dial.ShowModal()

Во время обработки события закрытия мы показываем message диалог.

if ret == wx.ID_YES:

self.Destroy()

else:

event.Veto()

В зависимости от возвращаемого значения из диалога, мы уничтожаем окно или накладываем вето насобытие. Обратите внимание, что для закрытия окна мы должны вызвать метод Destroy(). Вызов метода Close()приведет к бесконечному циклу.

5.5 Распространение события wxPython

Существует два типа событий: основные события и командные события. Они отличаются по распростра-нению. Распространение событий - это перемещение событий от дочерних виджетов к родительским и дальней-шим родительским виджетам. Основные события не распространяются. Командные события распространяются.Например, wx.CloseEvent является основным событием. Это событие не имеет смысла распространяться на ро-дительские виджеты.

По умолчанию событие, перехваченное в обработчике событий, перестает распространяться. Чтобы про-должить распространение, мы вызываем метод Skip().

Листинг 24: event_propagation.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−

44

34 """5 ZetCode wxPython t u t o r i a l67 This example demonstrates event propagat ion .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx1516 class MyPanel (wx . Panel ) :1718 def __init__( s e l f , * args , **kw) :19 super (MyPanel , s e l f ) . __init__(* args , **kw)2021 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnButtonClicked )2223 def OnButtonClicked ( s e l f , e ) :2425 print ( ’ event ␣ reached ␣ panel ␣ c l a s s ’ )26 e . Skip ( )272829 class MyButton(wx . Button ) :3031 def __init__( s e l f , * args , **kw) :32 super (MyButton , s e l f ) . __init__(* args , **kw)3334 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnButtonClicked )3536 def OnButtonClicked ( s e l f , e ) :3738 print ( ’ event ␣ reached ␣button␣ c l a s s ’ )39 e . Skip ( )404142 class Example (wx . Frame) :4344 def __init__( s e l f , * args , **kw) :45 super (Example , s e l f ) . __init__(* args , **kw)4647 s e l f . In i tUI ( )484950 def In i tUI ( s e l f ) :5152 mpnl = MyPanel ( s e l f )5354 MyButton(mpnl , l a b e l=’Ok ’ , pos=(15 , 15) )5556 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnButtonClicked )5758 s e l f . S e tT i t l e ( ’ Propagate ␣ event ’ )59 s e l f . Centre ( )6061 def OnButtonClicked ( s e l f , e ) :6263 print ( ’ event ␣ reached ␣ frame␣ c l a s s ’ )64 e . Skip ( )

45

656667 def main ( ) :6869 app = wx .App( )70 ex = Example (None )71 ex . Show ( )72 app . MainLoop ( )737475 i f __name__ == ’__main__ ’ :76 main ( )

В нашем примере есть кнопка на панели. Панель размещена в рамке виджета. Мы определяем обработчикдля всех виджетов.

def OnButtonClicked(self, e):

print(’event reached button class’)

e.Skip()

Мы обрабатываем событие нажатия кнопки в нашем пользовательском классе кнопок. Метод Skip() рас-пространяет событие дальше до класса панели.

Попробуйте опустить некоторые методы Skip() и посмотрите, что произойдет.

5.6 Идентификаторы окна

Идентификаторы окна - это целые числа, которые однозначно определяют экземпляр окна в системе собы-тий. Существует три способа создания идентификаторов окон.

1. Позволить системе автоматически создать идентификатор

2. Использовать стандартные идентификаторы

3. Создать свой собственный идентификатор

wx.Button(parent, -1)

wx.Button(parent, wx.ID_ANY)

Если мы предоставим -1 или wx.ID_ANY для параметра id, мы позволим wxPython автоматически создатьдля нас идентификатор. Автоматически созданные идентификаторы всегда отрицательны, тогда как указанныепользователем идентификаторы всегда должны быть положительными. Мы обычно используем эту опцию,когда нам не нужно менять состояние виджета. Например, статический текст, который никогда не будет измененв течение жизни приложения. Мы всегда можем получить идентификатор, если захотим. Есть метод GetId(),который определяет идентификатор.

Листинг 25: default_ids.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we use automatic i d s8 with wx .ID_ANY.9

46

10 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 pnl = wx . Panel ( s e l f )28 exitButton = wx . Button ( pnl , wx .ID_ANY, ’ Exit ’ , (10 , 10) )2930 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnExit , id=exitButton . GetId ( ) )3132 s e l f . S e tT i t l e ( "Automatic␣ i d s " )33 s e l f . Centre ( )3435 def OnExit ( s e l f , event ) :3637 s e l f . Close ( )383940 def main ( ) :4142 app = wx .App( )43 ex = Example (None )44 ex . Show ( )45 app . MainLoop ( )464748 i f __name__ == ’__main__ ’ :49 main ( )

В этом примере нас не волнует фактическое значение идентификатора.

self.Bind(wx.EVT_BUTTON, self.OnExit, id=exitButton.GetId())

Мы получаем автоматически сгенерированный идентификатор, вызывая метод GetId ().

Рекомендуется использовать стандартные идентификаторы. Идентификаторы могут предоставлять неко-торую стандартную графику или поведение на некоторых платформах.

5.7 Стандартные идентификаторы wxPython

wxPython содержит некоторые стандартные идентификаторы, такие как wx.ID_SAVE или wx.ID_NEW.

Листинг 26: standard_ids.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """

47

5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e bu t tons wi th s tandard i d s .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx1516 class Example (wx . Frame) :1718 def __init__( s e l f , * args , **kw) :19 super (Example , s e l f ) . __init__(* args , **kw)2021 s e l f . In i tUI ( )2223 def In i tUI ( s e l f ) :2425 pnl = wx . Panel ( s e l f )26 g r id = wx . Gr idS i ze r (3 , 2 , 1)2728 g r id .AddMany ( [ ( wx . Button ( pnl , wx .ID_CANCEL) , 0 , wx .TOP | wx .LEFT, 9) ,29 (wx . Button ( pnl , wx .ID_DELETE) , 0 , wx .TOP, 9) ,30 (wx . Button ( pnl , wx .ID_SAVE) , 0 , wx .LEFT, 9) ,31 (wx . Button ( pnl , wx . ID_EXIT) ) ,32 (wx . Button ( pnl , wx .ID_STOP) , 0 , wx .LEFT, 9) ,33 (wx . Button ( pnl , wx .ID_NEW) ) ] )3435 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnQuitApp , id=wx . ID_EXIT)3637 pnl . S e tS i z e r ( g r id )3839 s e l f . S e tT i t l e ( "Standard␣ i d s " )40 s e l f . Centre ( )4142 def OnQuitApp( s e l f , event ) :4344 s e l f . Close ( )454647 def main ( ) :4849 app = wx .App( )50 ex = Example (None )51 ex . Show ( )52 app . MainLoop ( )535455 i f __name__ == ’__main__ ’ :56 main ( )

В нашем примере мы используем стандартные идентификаторы на кнопках. В Linux кнопки имеют значки.

grid.AddMany([(wx.Button(pnl, wx.ID_CANCEL), 0, wx.TOP | wx.LEFT, 9),

(wx.Button(pnl, wx.ID_DELETE), 0, wx.TOP, 9),

(wx.Button(pnl, wx.ID_SAVE), 0, wx.LEFT, 9),

(wx.Button(pnl, wx.ID_EXIT)),

(wx.Button(pnl, wx.ID_STOP), 0, wx.LEFT, 9),

(wx.Button(pnl, wx.ID_NEW))])

48

Мы добавляем шесть кнопок на сетку Sizer. Стандартные идентификаторы - это wx.ID_CANCEL, wx.ID_DELETE,wx.ID_SAVE, wx.ID_EXIT, wx.ID_STOP и wx.ID_NEW.

self.Bind(wx.EVT_BUTTON, self.OnQuitApp, id=wx.ID_EXIT)

Мы связываем событие нажатия кнопки с обработчиком события OnQuitApp (). Параметр id используетсядля различения кнопок. Мы однозначно определяем источник события.

Рис. 26: Стандартные идентификаторы

5.8 Пользовательские идентификаторы событий

Последний вариант - использовать собственные идентификаторы. Мы определяем наши собственные гло-бальные идентификаторы.

Листинг 27: custom_ids.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we use custom event i d s .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx1516 ID_MENU_NEW = wx . NewId ( )17 ID_MENU_OPEN = wx . NewId ( )18 ID_MENU_SAVE = wx . NewId ( )192021 class Example (wx . Frame) :2223 def __init__( s e l f , * args , **kw) :24 super (Example , s e l f ) . __init__(* args , **kw)2526 s e l f . In i tUI ( )2728 def In i tUI ( s e l f ) :29

49

30 s e l f . CreateMenuBar ( )31 s e l f . CreateStatusBar ( )3233 s e l f . S e tS i z e ( (350 , 250) )34 s e l f . S e tT i t l e ( ’Custom␣ id s ’ )35 s e l f . Centre ( )3637 def CreateMenuBar ( s e l f ) :3839 mb = wx .MenuBar ( )4041 fMenu = wx .Menu( )42 fMenu . Append(ID_MENU_NEW, ’New ’ )43 fMenu . Append(ID_MENU_OPEN, ’Open ’ )44 fMenu . Append(ID_MENU_SAVE, ’ Save ’ )4546 mb. Append( fMenu , ’&F i l e ’ )47 s e l f . SetMenuBar (mb)4849 s e l f . Bind (wx .EVT_MENU, s e l f . DisplayMessage , id=ID_MENU_NEW)50 s e l f . Bind (wx .EVT_MENU, s e l f . DisplayMessage , id=ID_MENU_OPEN)51 s e l f . Bind (wx .EVT_MENU, s e l f . DisplayMessage , id=ID_MENU_SAVE)5253 def DisplayMessage ( s e l f , e ) :5455 sb = s e l f . GetStatusBar ( )5657 e id = e . GetId ( )5859 i f e id == ID_MENU_NEW:60 msg = ’New␣menu␣ item␣ s e l e c t e d ’61 e l i f e id == ID_MENU_OPEN:62 msg = ’Open␣menu␣ item␣ s e l e c t e d ’63 e l i f e id == ID_MENU_SAVE:64 msg = ’ Save␣menu␣ item␣ s e l e c t e d ’6566 sb . SetStatusText (msg)676869 def main ( ) :7071 app = wx .App( )72 ex = Example (None )73 ex . Show ( )74 app . MainLoop ( )757677 i f __name__ == ’__main__ ’ :78 main ( )

В примере кода мы создаем меню с тремя пунктами меню. Идентификаторы для этого пункта меню созда-ются глобально.

ID_MENU_NEW = wx.NewId()

ID_MENU_OPEN = wx.NewId()

ID_MENU_SAVE = wx.NewId()

Метод wx.NewId() создает новый уникальный идентификатор.

self.Bind(wx.EVT_MENU, self.DisplayMessage, id=ID_MENU_NEW)

50

self.Bind(wx.EVT_MENU, self.DisplayMessage, id=ID_MENU_OPEN)

self.Bind(wx.EVT_MENU, self.DisplayMessage, id=ID_MENU_SAVE)

Все три пункта меню идентифицируются по их уникальному идентификатору.

eid = e.GetId()

if eid == ID_MENU_NEW:

msg = ’New menu item selected’

elif eid == ID_MENU_OPEN:

msg = ’Open menu item selected’

elif eid == ID_MENU_SAVE:

msg = ’Save menu item selected’

Из объекта события мы получаем идентификатор. В зависимости от значения идентификатора мы подго-тавливаем сообщение, которое отображается в строке состояния приложения.

5.9 wx.PaintEvent

Событие рисования генерируется, когда окно перерисовывается. Это происходит, когда мы изменяем раз-меры окна или когда мы максимизируем его. Событие рисования также может быть сгенерировано программно.Например, когда мы вызываем метод SetLabel(), чтобы изменить виджет wx.StaticText. Обратите внимание, чтокогда мы сворачиваем окно, событие рисования не генерируется.

Листинг 28: paint_event.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we count pa in t even t s .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx151617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . count = 027 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2829 s e l f . S e tT i t l e ( ’ Paint ␣ events ’ )30 s e l f . S e tS i z e ( (350 , 250) )31 s e l f . Centre ( )3233 def OnPaint ( s e l f , e ) :34

51

35 s e l f . count += 136 dc = wx . PaintDC( s e l f )37 text = "Number␣ o f ␣ pa int ␣ events : ␣{0}" . format ( s e l f . count )38 dc . DrawText ( text , 20 , 20)394041 def main ( ) :4243 app = wx .App( )44 ex = Example (None )45 ex . Show ( )46 app . MainLoop ( )474849 i f __name__ == ’__main__ ’ :50 main ( )

В нашем примере мы подсчитываем количество событий рисования и рисуем текущее количество сгенери-рованных событий в окне.

self.Bind(wx.EVT_PAINT, self.OnPaint)

Мы связываем событие EVT_PAINT с методом OnPaint().

def OnPaint(self, e):

self.count += 1

dc = wx.PaintDC(self)

text = "Number of paint events: {0}".format(self.count)

dc.DrawText(text, 20, 20)

Внутри события OnPaint () мы увеличиваем счетчик рисования количества событий рисования в окне спомощью метода DrawText().

5.10 wx.FocusEvent

Фокус указывает на текущий выбранный виджет в приложении. Текст, введенный с клавиатуры или встав-ленный из буфера обмена, отправляется на виджет, который имеет фокус. Есть два типа событий, касающихсяфокуса. Событие wx.EVT_SET_FOCUS, которое генерируется, когда виджет получает фокус.Wx.EVT_KILL_FOCUSгенерируется, когда виджет теряет фокус. Фокус изменяется при нажатии или клавишей на клавиатуре, обычноTab или Shift + Tab.

Листинг 29: focus_event.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we work wi th wx . FocusEvent .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx

52

1516 class MyWindow(wx . Panel ) :1718 def __init__( s e l f , parent ) :19 super (MyWindow, s e l f ) . __init__( parent )2021 s e l f . c o l o r = ’#b3b3b3 ’2223 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )24 s e l f . Bind (wx .EVT_SIZE, s e l f . OnSize )25 s e l f . Bind (wx .EVT_SET_FOCUS, s e l f . OnSetFocus )26 s e l f . Bind (wx .EVT_KILL_FOCUS, s e l f . OnKillFocus )2728 def OnPaint ( s e l f , e ) :2930 dc = wx . PaintDC( s e l f )3132 dc . SetPen (wx . Pen( s e l f . c o l o r ) )33 x , y = s e l f . GetSize ( )34 dc . DrawRectangle (0 , 0 , x , y )3536 def OnSize ( s e l f , e ) :3738 s e l f . Refresh ( )3940 def OnSetFocus ( s e l f , e ) :4142 s e l f . c o l o r = ’#f f 0000 ’43 s e l f . Refresh ( )4445 def OnKillFocus ( s e l f , e ) :4647 s e l f . c o l o r = ’#b3b3b3 ’48 s e l f . Refresh ( )495051 class Example (wx . Frame) :5253 def __init__( s e l f , * args , **kw) :54 super (Example , s e l f ) . __init__(* args , **kw)5556 s e l f . In i tUI ( )575859 def In i tUI ( s e l f ) :6061 g r id = wx . Gr idS i ze r (2 , 2 , 10 , 10)62 g r id .AddMany ( [ (MyWindow( s e l f ) , 0 , wx .EXPAND|wx .TOP|wx .LEFT, 9) ,63 (MyWindow( s e l f ) , 0 , wx .EXPAND|wx .TOP|wx .RIGHT, 9) ,64 (MyWindow( s e l f ) , 0 , wx .EXPAND|wx .BOTTOM|wx .LEFT, 9) ,65 (MyWindow( s e l f ) , 0 , wx .EXPAND|wx .BOTTOM|wx .RIGHT, 9) ] )666768 s e l f . S e tS i z e r ( g r id )6970 s e l f . S e tS i z e ( (350 , 250) )71 s e l f . S e tT i t l e ( ’ Focus␣ event ’ )72 s e l f . Centre ( )737475 def OnMove( s e l f , e ) :76

53

77 print ( e . GetEventObject ( ) )78 x , y = e . GetPos i t ion ( )79 s e l f . s t1 . SetLabel ( str ( x ) )80 s e l f . s t2 . SetLabel ( str ( y ) )818283 def main ( ) :8485 app = wx .App( )86 ex = Example (None )87 ex . Show ( )88 app . MainLoop ( )899091 i f __name__ == ’__main__ ’ :92 main ( )

В нашем примере у нас есть четыре панели. Панель с фокусом подсвечивается.

self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)

self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)

Мы привязываем два события фокуса к обработчикам событий.

def OnPaint(self, e):

dc = wx.PaintDC(self)

dc.SetPen(wx.Pen(self.color))

x, y = self.GetSize()

dc.DrawRectangle(0, 0, x, y)

В методе OnPaint() мы рисуем на окнах. Цвет контура зависит от того, имеет ли окно фокус или нет.Контур окна, находящегося в фокусе, рисуется красным цветом.

def OnSetFocus(self, e):

self.color = ’#ff0000’

self.Refresh()

В методе OnSetFocus() мы устанавливаем для переменной self.color красный цвет. Мы обновляем окнофрейма, которое генерирует событие рисования для всех его дочерних виджетов. Окна перерисовываются, а уокна с фокусом новый цвет для контура.

def OnKillFocus(self, e):

self.color = ’#b3b3b3’

self.Refresh()

Метод OnKillFocus() вызывается, когда окно теряет фокус. Мы меняем значение цвета и обновляем.

5.11 wx.KeyEvent

Когда мы нажимаем клавишу на нашей клавиатуре, генерируется wx.KeyEvent. Это событие отправляетсявиджету, который в данный момент находится в фокусе. Есть три разных обработчика ключей:

54

Рис. 27: События фокуса

� wx.EVT_KEY_DOWN

� wx.EVT_KEY_UP

� wx.EVT_CHAR

Обычный запрос - закрыть приложение при нажатии клавиши Esc.

Листинг 30: key_event.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we work wi th wx . KeyEvent .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx1516 class Example (wx . Frame) :1718 def __init__( s e l f , * args , **kw) :19 super (Example , s e l f ) . __init__(* args , **kw)2021 s e l f . In i tUI ( )2223 def In i tUI ( s e l f ) :2425 pnl = wx . Panel ( s e l f )26 pnl . Bind (wx .EVT_KEY_DOWN, s e l f .OnKeyDown)27 pnl . SetFocus ( )2829 s e l f . S e tS i z e ( (350 , 250) )30 s e l f . S e tT i t l e ( ’Key␣ event ’ )31 s e l f . Centre ( )3233 def OnKeyDown( s e l f , e ) :34

55

35 key = e . GetKeyCode ( )3637 i f key == wx .WXK_ESCAPE:3839 r e t = wx . MessageBox ( ’Are␣you␣ sure ␣ to ␣ qu i t ? ’ , ’ Question ’ ,40 wx .YES_NO | wx .NO_DEFAULT, s e l f )4142 i f r e t == wx .YES:43 s e l f . Close ( )444546 def main ( ) :4748 app = wx .App( )49 ex = Example (None )50 ex . Show ( )51 app . MainLoop ( )525354 i f __name__ == ’__main__ ’ :55 main ( )

В этом примере мы обрабатываем нажатие клавиши Esc. Отображается окно сообщения для подтверждениязавершения приложения.

pnl.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)

Мы привязываем обработчик события к событию wx.EVT_KEY_DOWN.

key = e.GetKeyCode()

Здесь мы получаем код нажатой клавиши.

if key == wx.WXK_ESCAPE:

Мы проверяем код клавши. Клавиша Esc имеет код wx.WXK_ESCAPE.

В этой главе мы говорили о событиях в wxPython.

6 Диалоги wxPython

Диалоговые окна или диалоги являются неотъемлемой частью большинства современных приложений сграфическим интерфейсом. Диалог определяется как разговор между двумя или более людьми. В компьютерномприложении диалог - это окно, которое используется для «общения» с приложением. Диалог используется дляввода данных, изменения данных, изменения настроек приложения и т.д. Диалоги являются важным средствомсвязи между пользователем и компьютерной программой.

6.1 Простое окно сообщения

Окно сообщения предоставляет краткую информацию пользователю. Хорошим примером является прило-жение для записи компакт-дисков. Когда компакт-диск закончится, появится окно с сообщением.

Листинг 31: message_box.py

1 #!/ usr / b in /env python3

56

2 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This example shows a s imple8 message box .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kwargs ) :21 super (Example , s e l f ) . __init__(* args , **kwargs )2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 wx . Ca l lLate r (3000 , s e l f . ShowMessage )2829 s e l f . S e tS i z e ( (300 , 200) )30 s e l f . S e tT i t l e ( ’Message␣box ’ )31 s e l f . Centre ( )3233 def ShowMessage ( s e l f ) :34 wx . MessageBox ( ’Download␣ completed ’ , ’ I n f o ’ ,35 wx .OK | wx .ICON_INFORMATION)363738 def main ( ) :3940 app = wx .App( )41 ex = Example (None )42 ex . Show ( )43 app . MainLoop ( )444546 i f __name__ == ’__main__ ’ :47 main ( )

В этом примере окно сообщения показывается через три секунды после запуска программы.

wx.CallLater(3000, self.ShowMessage)

wx.CallLater вызывает метод через три секунды. Первый параметр - это значение времени, после котороговызывается данный метод. Параметр в миллисекундах. Второй параметр - это метод, который нужно вызвать.

def ShowMessage(self):

wx.MessageBox(’Download completed’, ’Info’,

wx.OK | wx.ICON_INFORMATION)

wx.MessageBox показывает небольшое диалоговое окно. Мы задаём три параметра: текстовое сообщение,

57

заголовок сообщения и флаги. Флаги используются для отображения различных кнопок и значков. В нашемслучае мы показываем кнопку ОК и значок информации.

Рис. 28: Окно сообщения

6.2 Предопределенные диалоги

У wxPython есть несколько предопределенных диалогов. Это диалоговые окна для общих задач програм-мирования, таких как отображение текста, получение ввода, загрузка и сохранение файлов.

6.3 Диалоги сообщений

Диалоги сообщений используются для показа сообщений пользователю. Они более гибкие, чем простыеокна сообщений, которые мы видели в предыдущем примере. Они настраиваемы. Мы можем изменить значкии кнопки, которые будут отображаться в диалоге.

Таблица 2: Флаги класса wx.MessageDialog

Флаг Значениеwx.OK Показать OK кнопкуwx.CANCEL Показать Cancel кнопкуwx.YES_NO Показать Yes, No кнопкиwx.YES_DEFAULT Задать Yes кнопку по умолчаниюwx.NO_DEFAULT Задать No кнопку по умолчаниюwx.ICON_EXCLAMATION Показать иконку предупрежденияwx.ICON_ERROR Показать иконку ошибкиwx.ICON_HAND То же самое, как wx.ICON_ERRORwx.ICON_INFORMATION Показать иконку информацииwx.ICON_QUESTION Показать иконку вопроса

Это флаги, которые можно использовать с классом wx.MessageDialog.

Листинг 32: message_dialogs.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This example shows four t ype s o f8 message d i a l o g s .910 author : Jan Bodnar

58

11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kwargs ) :21 super (Example , s e l f ) . __init__(* args , **kwargs )2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 panel = wx . Panel ( s e l f )2829 hbox = wx . BoxSizer ( )30 s i z e r = wx . Gr idS i ze r (2 , 2 , 2 , 2)3132 btn1 = wx . Button ( panel , l a b e l=’ In f o ’ )33 btn2 = wx . Button ( panel , l a b e l=’ Error ’ )34 btn3 = wx . Button ( panel , l a b e l=’ Question ’ )35 btn4 = wx . Button ( panel , l a b e l=’ Ale r t ’ )3637 s i z e r .AddMany ( [ btn1 , btn2 , btn3 , btn4 ] )3839 hbox .Add( s i z e r , 0 , wx .ALL, 15)40 panel . S e tS i z e r ( hbox )4142 btn1 . Bind (wx .EVT_BUTTON, s e l f . ShowMessage1 )43 btn2 . Bind (wx .EVT_BUTTON, s e l f . ShowMessage2 )44 btn3 . Bind (wx .EVT_BUTTON, s e l f . ShowMessage3 )45 btn4 . Bind (wx .EVT_BUTTON, s e l f . ShowMessage4 )4647 s e l f . S e tS i z e ( (300 , 200) )48 s e l f . S e tT i t l e ( ’ Messages ’ )49 s e l f . Centre ( )5051 def ShowMessage1 ( s e l f , event ) :52 d i a l = wx . MessageDialog (None , ’Download␣ completed ’ , ’ I n f o ’ , wx .OK)53 d i a l . ShowModal ( )5455 def ShowMessage2 ( s e l f , event ) :56 d i a l = wx . MessageDialog (None , ’ Error ␣ load ing ␣ f i l e ’ , ’ Error ’ ,57 wx .OK | wx .ICON_ERROR)58 d i a l . ShowModal ( )5960 def ShowMessage3 ( s e l f , event ) :61 d i a l = wx . MessageDialog (None , ’Are␣you␣ sure ␣ to ␣ qu i t ? ’ , ’ Question ’ ,62 wx .YES_NO | wx .NO_DEFAULT | wx .ICON_QUESTION)63 d i a l . ShowModal ( )6465 def ShowMessage4 ( s e l f , event ) :66 d i a l = wx . MessageDialog (None , ’ Unallowed␣ opera t ion ’ , ’ Exclamation ’ ,67 wx .OK | wx .ICON_EXCLAMATION)68 d i a l . ShowModal ( )697071 def main ( ) :72

59

73 app = wx .App( )74 ex = Example (None )75 ex . Show ( )76 app . MainLoop ( )777879 i f __name__ == ’__main__ ’ :80 main ( )

В нашем примере мы создали четыре кнопки и поместили их в сайзер типа grid. Эти кнопки покажутчетыре разных диалоговых окна. Мы создаем их, задавая различные флаги стиля.

def ShowMessage2(self, event):

dial = wx.MessageDialog(None, ’Error loading file’, ’Error’,

wx.OK | wx.ICON_ERROR)

dial.ShowModal()

Создание диалогового окна сообщения просто. Мы устанавливаем диалог как окно верхнего уровня, задаваяNone в качестве родителя. Две строки задают текст сообщения и заголовок диалога. Мы показываем кнопкуОК и значок ошибки, указывая флаги wx.OK и wx.ICON_ERROR. Чтобы показать диалоговое окно на экране,мы вызываем метод ShowModal().

6.4 Диалоговое окно ”О программе” (About)

Почти каждое приложение имеет это диалоговое окно. Обычно он находится в меню «Справка». Цель это-го диалога - предоставить пользователю основную информацию о названии и версии приложения. В прошломэти диалоги были довольно короткими. В наши дни большинство из них предоставляют дополнительную ин-формацию об авторах. Они дают список программистов или авторов документации. Они также предоставляютинформацию о лицензии приложения. На них может отображаться логотип компании или логотип приложения.

Чтобы создать диалоговое окно about, мы должны создать два объекта.

Wx.adv.AboutDialogInfo и wx.adv.AboutBox.

wxPython может отображать два вида блоков About. Это зависит от того, какую платформу мы используеми какие методы мы вызываем. Это может быть собственный диалог или универсальный диалог wxPython.Диалоговое окно Windows about не может отображать пользовательские значки, текст лицензии или URL.Если мы пропустим эти три поля, wxPython покажет собственный диалог. В противном случае он прибегнет куниверсальному. Рекомендуется указывать информацию о лицензии в отдельном пункте меню, если мы хотимоставаться как можно более нативными для ОС. GTK+ может показать все эти поля.

Листинг 33: about_dialog.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e an8 about d i a l o g box .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx16 import wx . adv17

60

1819 class Example (wx . Frame) :2021 def __init__( s e l f , * args , **kwargs ) :22 super (Example , s e l f ) . __init__(* args , **kwargs )2324 s e l f . In i tUI ( )2526 def In i tUI ( s e l f ) :2728 menubar = wx .MenuBar ( )29 help = wx .Menu( )30 help . Append(wx .ID_ANY, ’&About ’ )31 help . Bind (wx .EVT_MENU, s e l f . OnAboutBox)3233 menubar . Append(help , ’&Help ’ )34 s e l f . SetMenuBar (menubar )3536 s e l f . S e tS i z e ( (350 , 250) )37 s e l f . S e tT i t l e ( ’About␣ d i a l o g ␣box ’ )38 s e l f . Centre ( )3940 def OnAboutBox( s e l f , e ) :4142 d e s c r i p t i o n = """ F i l e Hunter i s an advanced f i l e manager f o r43 the Unix opera t ing system . Features inc l ude power fu l b u i l t−in ed i t o r ,44 advanced search c a p a b i l i t i e s , power fu l ba tch renaming , f i l e comparison ,45 e x t en s i v e arch i v e hand l ing and more .46 """4748 l i c e n c e = """ F i l e Hunter i s f r e e so f tware ; you can r e d i s t r i b u t e49 i t and/or modify i t under the terms o f the GNU General Pub l i c License as50 pub l i s h ed by the Free Sof tware Foundation ; e i t h e r ve r s i on 2 o f the License ,51 or ( at your opt ion ) any l a t e r ve r s i on .5253 F i l e Hunter i s d i s t r i b u t e d in the hope t ha t i t w i l l be u s e fu l ,54 but WITHOUT ANY WARRANTY; wi thout even the imp l i ed warranty o f55 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.56 See the GNU General Pub l i c License f o r more d e t a i l s . You shou ld have57 r e c e i v ed a copy o f the GNU General Pub l i c License a long wi th F i l e Hunter ;58 i f not , wr i t e to the Free Sof tware Foundation , Inc . , 59 Temple Place ,59 Su i t e 330 , Boston , MA 02111−1307 USA"""606162 i n f o = wx . adv . AboutDialogInfo ( )6364 i n f o . Set Icon (wx . Icon ( ’ hunter . png ’ , wx .BITMAP_TYPE_PNG) )65 i n f o . SetName( ’ F i l e ␣Hunter ’ )66 i n f o . SetVers ion ( ’ 1 . 0 ’ )67 i n f o . Se tDesc r ip t i on ( d e s c r i p t i o n )68 i n f o . SetCopyright ( ’ (C) ␣2007␣−␣2019␣Jan␣Bodnar ’ )69 i n f o . SetWebSite ( ’ http ://www. zetcode . com ’ )70 i n f o . SetL icence ( l i c e n c e )71 i n f o . AddDeveloper ( ’ Jan␣Bodnar ’ )72 i n f o . AddDocWriter ( ’ Jan␣Bodnar ’ )73 i n f o . AddArtist ( ’The␣Tango␣crew ’ )74 i n f o . AddTranslator ( ’ Jan␣Bodnar ’ )7576 wx . adv . AboutBox ( i n f o )777879 def main ( ) :

61

8081 app = wx .App( )82 ex = Example (None )83 ex . Show ( )84 app . MainLoop ( )858687 i f __name__ == ’__main__ ’ :88 main ( )

В примере есть пункт меню about. После выбора элемента отображается окно «О программе».

description = """File Hunter is an advanced file manager for

the Unix operating system. Features include powerful built-in editor,

advanced search capabilities, powerful batch renaming, file comparison,

extensive archive handling and more.

"""

Не самая лучшая идея помещать слишком много текста в код приложения. Мы не хотели делать примерслишком сложным, поэтому мы поместили весь текст в код. Но в реальных программах текст должен бытьпомещен отдельно в файл. Это помогает нам в обслуживании нашего приложения. Например, если мы хотимперевести наше приложение на другие языки.

info = wx.adv.AboutDialogInfo()

Первое, что нужно сделать, это создать объект wx.AboutDialogInfo. Конструктор пуст. Он не требует ника-ких параметров.

info.SetIcon(wx.Icon(’hunter.png’, wx.BITMAP_TYPE_PNG))

info.SetName(’File Hunter’)

info.SetVersion(’1.0’)

info.SetDescription(description)

info.SetCopyright(’(C) 2007 - 2014 Jan Bodnar’)

info.SetWebSite(’http://www.zetcode.com’)

info.SetLicence(licence)

info.AddDeveloper(’Jan Bodnar’)

info.AddDocWriter(’Jan Bodnar’)

info.AddArtist(’The Tango crew’)

info.AddTranslator(’Jan Bodnar’)

Следующее, что нужно сделать, это вызвать все необходимые методы для созданного объекта wx.AboutDialogInfo.

wx.adv.AboutBox(info)

В конце мы создаем виджет wx.adv.AboutBox. Единственный параметр, который он принимает - это объектwx.adv.AboutDialogInfo.

6.5 Пользовательский диалог

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

Листинг 34: custom_dialog.py

1 #!/ usr / b in /env python3

62

Рис. 29: Окно диалога ”О программе”

2 # −*− coding : u t f−8 −*−34 ’ ’ ’5 ZetCode wxPython t u t o r i a l67 In t h i s code example , we c rea t e a8 custom d i a l o g .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 ’ ’ ’1415 import wx1617 class ChangeDepthDialog (wx . Dia log ) :1819 def __init__( s e l f , * args , **kw) :20 super ( ChangeDepthDialog , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )23 s e l f . S e tS i z e ( (250 , 200) )24 s e l f . S e tT i t l e ( "Change␣Color ␣Depth" )252627 def In i tUI ( s e l f ) :2829 pnl = wx . Panel ( s e l f )30 vbox = wx . BoxSizer (wx .VERTICAL)3132 sb = wx . Stat icBox ( pnl , l a b e l=’ Colors ’ )33 sbs = wx . S ta t i cBoxS i z e r ( sb , o r i e n t=wx .VERTICAL)34 sbs .Add(wx . RadioButton ( pnl , l a b e l=’ 256␣Colors ’ ,35 s t y l e=wx .RB_GROUP) )36 sbs .Add(wx . RadioButton ( pnl , l a b e l=’ 16␣Colors ’ ) )37 sbs .Add(wx . RadioButton ( pnl , l a b e l=’ 2␣Colors ’ ) )3839 hbox1 = wx . BoxSizer (wx .HORIZONTAL)40 hbox1 .Add(wx . RadioButton ( pnl , l a b e l=’Custom ’ ) )41 hbox1 .Add(wx . TextCtrl ( pnl ) , f l a g=wx .LEFT, border=5)42 sbs .Add( hbox1 )

63

4344 pnl . S e tS i z e r ( sbs )4546 hbox2 = wx . BoxSizer (wx .HORIZONTAL)47 okButton = wx . Button ( s e l f , l a b e l=’Ok ’ )48 c loseButton = wx . Button ( s e l f , l a b e l=’ Close ’ )49 hbox2 .Add( okButton )50 hbox2 .Add( c loseButton , f l a g=wx .LEFT, border=5)5152 vbox .Add( pnl , propor t ion=1,53 f l a g=wx .ALL|wx .EXPAND, border=5)54 vbox .Add( hbox2 , f l a g=wx .ALIGN_CENTER|wx .TOP|wx .BOTTOM, border=10)5556 s e l f . S e tS i z e r ( vbox )5758 okButton . Bind (wx .EVT_BUTTON, s e l f . OnClose )59 c loseButton . Bind (wx .EVT_BUTTON, s e l f . OnClose )606162 def OnClose ( s e l f , e ) :6364 s e l f . Destroy ( )656667 class Example (wx . Frame) :6869 def __init__( s e l f , * args , **kw) :70 super (Example , s e l f ) . __init__(* args , **kw)7172 s e l f . In i tUI ( )737475 def In i tUI ( s e l f ) :7677 tb = s e l f . CreateToolBar ( )78 tb . AddTool ( t o o l I d=wx .ID_ANY, l a b e l=’ ’ , bitmap=wx . Bitmap ( ’ c o l o r . png ’ ) )7980 tb . Rea l i z e ( )8182 tb . Bind (wx .EVT_TOOL, s e l f . OnChangeDepth )8384 s e l f . S e tS i z e ( (350 , 250) )85 s e l f . S e tT i t l e ( ’Custom␣ d i a l o g ’ )86 s e l f . Centre ( )8788 def OnChangeDepth ( s e l f , e ) :8990 cdDialog = ChangeDepthDialog (None ,91 t i t l e=’Change␣Color ␣Depth ’ )92 cdDialog . ShowModal ( )93 cdDialog . Destroy ( )949596 def main ( ) :9798 app = wx .App( )99 ex = Example (None )100 ex . Show ( )101 app . MainLoop ( )102103104 i f __name__ == ’__main__ ’ :

64

105 main ( )

В приведенном выше примере мы создали собственный диалог.

class ChangeDepthDialog(wx.Dialog):

def __init__(self, *args, **kw):

super(ChangeDepthDialog, self).__init__(*args, **kw)

В нашем примере кода мы создаем пользовательское диалоговое окно ChangeDepthDialog. Мы наследуемот виджета wx.Dialog.

def OnChangeDepth(self, e):

cdDialog = ChangeDepthDialog(None,

title=’Change Color Depth’)

cdDialog.ShowModal()

cdDialog.Destroy()

Мы создаем экземпляр класса ChangeDepthDialog. Затем мы вызываем метод ShowModal(). Позже мыдолжны уничтожить наш диалог с помощью Destroy(). Обратите внимание на визуальную разницу междудиалогом и окном верхнего уровня. Диалог на следующем рисунке был активирован. Мы не можем работать сокном верхнего уровня, пока диалог не разрушен. Существует четкое различие в заголовке окон.

Рис. 30: Окно диалога пользователя

В этой главе мы рассмотрели диалоги.

7 Виджеты

В этом разделе мы познакомим вас с основными виджетами в wxPython. У каждого виджета будет неболь-шой пример кода. Виджеты являются основными строительными блоками приложения. wxPythont имеет ши-рокий спектр различных виджетов, включая кнопки, флажки, ползунки и списки.

7.1 wx.Button

wx.Button - простой виджет. Содержит текстовую строку. Используется для запуска действия.

Листинг 35: button_wid.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """

65

5 ZetCode wxPython t u t o r i a l67 In t h i s code example , we c rea t e a8 but ton widge t .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 pnl = wx . Panel ( s e l f )28 c loseButton = wx . Button ( pnl , l a b e l=’ Close ’ , pos=(20 , 20) )2930 c loseButton . Bind (wx .EVT_BUTTON, s e l f . OnClose )3132 s e l f . S e tS i z e ( (350 , 250) )33 s e l f . S e tT i t l e ( ’wx . Button ’ )34 s e l f . Centre ( )3536 def OnClose ( s e l f , e ) :3738 s e l f . Close (True )394041 def main ( ) :4243 app = wx .App( )44 ex = Example (None )45 ex . Show ( )46 app . MainLoop ( )474849 i f __name__ == ’__main__ ’ :50 main ( )

В примере кода мы создаем кнопку Close, которая закрывает приложение при нажатии.

cbtn = wx.Button(pnl, label=’Close’, pos=(20, 20))

Виджет wx.Button создан. В конструкторе виджета мы задаём метку для кнопки и положение на панели.

cbtn.Bind(wx.EVT_BUTTON, self.OnClose)

Событие wx.EVT_BUTTON возникает, когда мы нажимаем на кнопку. Мы задаём обработчик событиядля этого события.

def OnClose(self, e):

66

self.Close(True)

В методе OnClose() мы завершаем приложение вызовом метода Close().

Рис. 31: wx.Button

7.2 wx.ToggleButton

wx.ToggleButton - это кнопка, которая имеет два состояния: нажата и не нажата. Вы можете переключать-ся между этими двумя состояниями, нажимая на него. Есть ситуации, когда эта функциональность хорошоподходит.

Листинг 36: toggle_buttons.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s code example , we c rea t e t h r ee8 t o g g l e bu t ton widge t s .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : Apr i l 201813 """1415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 pnl = wx . Panel ( s e l f )2829 s e l f . c o l = wx . Colour (0 , 0 , 0)30

67

31 rtb = wx . ToggleButton ( pnl , l a b e l=’ red ’ , pos=(20 , 25) )32 gtb = wx . ToggleButton ( pnl , l a b e l=’ green ’ , pos=(20 , 60) )33 btb = wx . ToggleButton ( pnl , l a b e l=’ blue ’ , pos=(20 , 100) )3435 s e l f . cpnl = wx . Panel ( pnl , pos=(150 , 20) , s i z e =(110 , 110) )36 s e l f . cpnl . SetBackgroundColour ( s e l f . c o l )3738 rtb . Bind (wx .EVT_TOGGLEBUTTON, s e l f . ToggleRed )39 gtb . Bind (wx .EVT_TOGGLEBUTTON, s e l f . ToggleGreen )40 btb . Bind (wx .EVT_TOGGLEBUTTON, s e l f . ToggleBlue )4142 s e l f . S e tS i z e ( (350 , 250) )43 s e l f . S e tT i t l e ( ’ Toggle ␣ buttons ’ )44 s e l f . Centre ( )4546 def ToggleRed ( s e l f , e ) :4748 obj = e . GetEventObject ( )49 i sP r e s s ed = obj . GetValue ( )5051 green = s e l f . c o l . Green ( )52 blue = s e l f . c o l . Blue ( )5354 i f i sP r e s s ed :55 s e l f . c o l . Set (255 , green , b lue )56 else :57 s e l f . c o l . Set (0 , green , b lue )5859 s e l f . cpnl . SetBackgroundColour ( s e l f . c o l )60 s e l f . cpnl . Refresh ( )6162 def ToggleGreen ( s e l f , e ) :6364 obj = e . GetEventObject ( )65 i sP r e s s ed = obj . GetValue ( )6667 red = s e l f . c o l . Red ( )68 blue = s e l f . c o l . Blue ( )6970 i f i sP r e s s ed :71 s e l f . c o l . Set ( red , 255 , b lue )72 else :73 s e l f . c o l . Set ( red , 0 , b lue )7475 s e l f . cpnl . SetBackgroundColour ( s e l f . c o l )76 s e l f . cpnl . Refresh ( )7778 def ToggleBlue ( s e l f , e ) :7980 obj = e . GetEventObject ( )81 i sP r e s s ed = obj . GetValue ( )8283 red = s e l f . c o l . Red ( )84 green = s e l f . c o l . Green ( )8586 i f i sP r e s s ed :87 s e l f . c o l . Set ( red , green , 255)88 else :89 s e l f . c o l . Set ( red , green , 0)9091 s e l f . cpnl . SetBackgroundColour ( s e l f . c o l )92 s e l f . cpnl . Refresh ( )

68

939495 def main ( ) :9697 app = wx .App( )98 ex = Example (None )99 ex . Show ( )100 app . MainLoop ( )101102103 i f __name__ == ’__main__ ’ :104 main ( )

У нас есть красные, зеленые и синие кнопки переключения и панель. Мы меняем цвет панели, нажимая накнопки переключения.

rtb = wx.ToggleButton(pnl, label=’red’, pos=(20, 25))

Виджет wx.ToggleButton создан.

self.cpnl = wx.Panel(pnl, pos=(150, 20), size=(110, 110))

self.cpnl.SetBackgroundColour(self.col)

Это панель, цвет которой мы будем изменять с помощью кнопок переключения.

rtb.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleRed)

Обработчик события ToggleRed() вызывается, когда мы нажимаем кнопку переключения rtb.

def ToggleRed(self, e):

obj = e.GetEventObject()

isPressed = obj.GetValue()

green = self.col.Green()

blue = self.col.Blue()

if isPressed:

self.col.Set(255, green, blue)

else:

self.col.Set(0, green, blue)

self.cpnl.SetBackgroundColour(self.col)

В методе ToggleRed() мы реагируем на то, что была нажата кнопка rtb. Разбираемся с цветными частямии обновляем цвет цветовой панели.

7.3 wx.StaticText

Виджет wx.StaticText отображает одну или несколько строк текста только для чтения

Листинг 37: static_text.py

1 #!/ usr / b in /python2 # −*− coding : u t f−8 −*−3

69

Рис. 32: wx.ToggleButton

4 """5 ZetCode wxPython t u t o r i a l67 In t h i s code example , we c rea t e a s t a t i c t e x t .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx151617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 txt1 = ’ ’ ’Со мною вот что происходит :ко27 мне мой старый друг не ходит ,а28 ходят в мелкой суетеразнообразные29 не те . ’ ’ ’3031 txt2 = ’ ’ ’И он не с теми ходит гдето−и32 тоже понимает это ,и33 наш раздор необъясним ,и34 оба мучимся мы с ним . ’ ’ ’3536 pnl = wx . Panel ( s e l f )37 vbox = wx . BoxSizer (wx .VERTICAL)3839 font = wx . Font (13 , wx .DEFAULT, wx .NORMAL, wx .DEFAULT)4041 s t1 = wx . Stat i cText ( pnl , l a b e l=txt1 , s t y l e=wx .ALIGN_LEFT)42 s t2 = wx . Stat i cText ( pnl , l a b e l=txt2 , s t y l e=wx .ALIGN_LEFT)4344 s t1 . SetFont ( f ont )45 s t2 . SetFont ( f ont )4647 vbox .Add( st1 , f l a g=wx .ALL, border=15)

70

48 vbox .Add( st2 , f l a g=wx .ALL, border=15)4950 pnl . S e tS i z e r ( vbox )5152 s e l f . S e tT i t l e ( ’ B i t t e r swee t ’ )53 s e l f . Centre ( )545556 def main ( ) :5758 app = wx .App( )59 ex = Example (None )60 ex . Show ( )61 app . MainLoop ( )626364 i f __name__ == ’__main__ ’ :65 main ( )

В этом примере мы показываем две строчки песни «Со мною вот что происходит» с помощью виджетаwx.StaticText.

font = wx.Font(13, wx.DEFAULT, wx.NORMAL, wx.DEFAULT)

Мы создаем объект шрифта для текста.

txt1 = ’’’Со мною вот что происходит:

ко мне мой старый друг не ходит,

а ходят в мелкой суете

разнообразные не те.’’’

Это строка, которая будет отображаться в виджете wx.StaticText.

st1 = wx.StaticText(pnl, label=txt1, style=wx.ALIGN_LEFT)

Мы создаем виджет wx.StaticText. Текст будет выровнен по левому краю.

st1.SetFont(font)

st2.SetFont(font)

Мы устанавливаем шрифт для статических текстовых виджетов с помощью SetFont().

7.4 wx.StaticLine

Этот виджет отображает простую линию в окне. Она может быть горизонтальной или вертикальной.

Листинг 38: static_line.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s code example , we c rea t e a s t a t i c l i n e .8

71

Рис. 33: wx.StaticText

9 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """131415 import wx161718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 pnl = wx . Panel ( s e l f )2829 font = wx . Font (10 , wx .DEFAULT, wx .NORMAL, wx .BOLD)30 heading = wx . Stat i cText ( s e l f , l a b e l=’The␣Centra l ␣Europe ’ ,31 pos=(25 , 15) , s i z e =(200 , −1) )32 heading . SetFont ( f ont )3334 wx . S ta t i cL in e ( s e l f , pos=(25 , 50) , s i z e =(300 ,1) )3536 wx . Stat i cText ( s e l f , l a b e l=’ S lovak ia ’ , pos=(25 , 80) )37 wx . Stat i cText ( s e l f , l a b e l=’Hungary ’ , pos=(25 , 100) )38 wx . Stat i cText ( s e l f , l a b e l=’ Poland ’ , pos=(25 , 120) )39 wx . Stat i cText ( s e l f , l a b e l=’Czech␣Republ ic ’ , pos=(25 , 140) )40 wx . Stat i cText ( s e l f , l a b e l=’Germany ’ , pos=(25 , 160) )41 wx . Stat i cText ( s e l f , l a b e l=’ S loven ia ’ , pos=(25 , 180) )42 wx . Stat i cText ( s e l f , l a b e l=’ Austr ia ’ , pos=(25 , 200) )43 wx . Stat i cText ( s e l f , l a b e l=’ Switzer land ’ , pos=(25 , 220) )4445 wx . Stat i cText ( s e l f , l a b e l=’ 5␣445␣000 ’ , pos=(250 , 80) )46 wx . Stat i cText ( s e l f , l a b e l=’ 10␣014␣000 ’ , pos=(250 , 100) )47 wx . Stat i cText ( s e l f , l a b e l=’ 38␣186␣000 ’ , pos=(250 , 120) )48 wx . Stat i cText ( s e l f , l a b e l=’ 10␣562␣000 ’ , pos=(250 , 140) )49 wx . Stat i cText ( s e l f , l a b e l=’ 81␣799␣000 ’ , pos=(250 , 160) )50 wx . Stat i cText ( s e l f , l a b e l=’ 2␣050␣000 ’ , pos=(250 , 180) )51 wx . Stat i cText ( s e l f , l a b e l=’ 8␣414␣000 ’ , pos=(250 , 200) )52 wx . Stat i cText ( s e l f , l a b e l=’ 7␣866␣000 ’ , pos=(250 , 220) )

72

5354 wx . S ta t i cL in e ( s e l f , pos=(25 , 260) , s i z e =(300 ,1) )5556 tsum = wx . Stat i cText ( s e l f , l a b e l=’ 164␣336␣000 ’ , pos=(240 , 280) )57 sum_font = tsum . GetFont ( )58 sum_font . SetWeight (wx .BOLD)59 tsum . SetFont ( sum_font )6061 btn = wx . Button ( s e l f , l a b e l=’ Close ’ , pos=(140 , 310) )6263 btn . Bind (wx .EVT_BUTTON, s e l f . OnClose )6465 s e l f . S e tS i z e ( (360 , 380) )66 s e l f . S e tT i t l e ( ’wx . S ta t i cL in e ’ )67 s e l f . Centre ( )6869 def OnClose ( s e l f , e ) :7071 s e l f . Close (True )727374 def main ( ) :7576 app = wx .App( )77 ex = Example (None )78 ex . Show ( )79 app . MainLoop ( )808182 i f __name__ == ’__main__ ’ :83 main ( )

Скрипт отображает страны Центральной Европы и их население. Wx.StatLine делает окно более привле-кательным.

wx.StaticLine(self, pos=(25, 50), size=(300,1))

Это конструктор wx.StaticLine

7.5 wx.StaticBox

Это своего рода виджет декоратора. Он используется для логической группировки различных виджетов.Обратите внимание, что этот виджет должен быть создан до создания виджетов, которые он содержит, и чтоэти виджеты должны быть братьями, а не потомками wx.StaticBox.

Листинг 39: static_box.py

1 #!/ usr / b in /python2 # −*− coding : u t f−8 −*−34 import wx567 class Example (wx . Frame) :89 def __init__( s e l f , * args , **kw) :10 super (Example , s e l f ) . __init__(* args , **kw)1112 s e l f . In i tUI ( )

73

Рис. 34: wx.StaticLine

1314 def In i tUI ( s e l f ) :1516 pnl = wx . Panel ( s e l f )1718 wx . Stat icBox ( pnl , l a b e l=’ Persona l ␣ In f o ’ , pos=(5 , 5) , s i z e =(240 , 170) )19 wx . CheckBox ( pnl , l a b e l=’Male ’ , pos=(15 , 30) )20 wx . CheckBox ( pnl , l a b e l=’Married ’ , pos=(15 , 55) )21 wx . Stat i cText ( pnl , l a b e l=’Age ’ , pos=(15 , 95) )22 wx . SpinCtr l ( pnl , va lue=’ 1 ’ , pos=(55 , 90) , s i z e =(60 , −1) , min=1, max=120)2324 btn = wx . Button ( pnl , l a b e l=’Ok ’ , pos=(90 , 185) , s i z e =(60 , −1) )2526 btn . Bind (wx .EVT_BUTTON, s e l f . OnClose )2728 s e l f . S e tS i z e ( (270 , 250) )29 s e l f . S e tT i t l e ( ’ S t a t i c ␣box ’ )30 s e l f . Centre ( )31 s e l f . Show(True )3233 def OnClose ( s e l f , e ) :3435 s e l f . Close (True )363738 def main ( ) :3940 ex = wx .App( )41 Example (None )42 ex . MainLoop ( )434445 i f __name__ == ’__main__ ’ :46 main ( )

74

Мы создали wx.StaticBox, который украшает остальные четыре виджета.

Рис. 35: wx.StaticBox

7.6 wx.ComboBox

wx.ComboBox представляет собой комбинацию однострочного текстового поля, кнопки с изображениемстрелки вниз и списка. Когда вы нажимаете кнопку, появляется список. Пользователь может выбрать толькоодну опцию из предоставленного списка строк.

Листинг 40: combo_box.py

1 #!/ usr / b in /python2 # −*− coding : u t f−8 −*−34 import wx567 class Example (wx . Frame) :89 def __init__( s e l f , * args , **kw) :10 super (Example , s e l f ) . __init__(* args , **kw)1112 s e l f . In i tUI ( )1314 def In i tUI ( s e l f ) :1516 pnl = wx . Panel ( s e l f )1718 d i s t r o s = [ ’Ubuntu ’ , ’ Arch ’ , ’ Fedora ’ , ’ Debian ’ , ’Mint ’ ]19 cb = wx .ComboBox( pnl , pos=(50 , 30) , cho i c e s=d i s t r o s ,20 s t y l e=wx .CB_READONLY)2122 s e l f . s t = wx . Stat i cText ( pnl , l a b e l=’ ’ , pos=(50 , 140) )23 cb . Bind (wx .EVT_COMBOBOX, s e l f . OnSelect )2425 s e l f . S e tS i z e ( (250 , 230) )26 s e l f . S e tT i t l e ( ’wx .ComboBox ’ )27 s e l f . Centre ( )28 s e l f . Show(True )29

75

30 def OnSelect ( s e l f , e ) :3132 i = e . GetStr ing ( )33 s e l f . s t . SetLabel ( i )3435 def main ( ) :3637 ex = wx .App( )38 Example (None )39 ex . MainLoop ( )4041 i f __name__ == ’__main__ ’ :42 main ( )

Выбранный параметр из поля со списком показывается на метке ниже.

distros = [’Ubuntu’, ’Arch’, ’Fedora’, ’Debian’, ’Mint’]

Поле со списком будет содержать этот список строк.

cb = wx.ComboBox(pnl, pos=(50, 30), choices=distros, style=wx.CB_READONLY)

Виджет wx.ComboBox создан. Параметр choices принимает список строк для отображения в поле со спис-ком. Стиль wx.CB_READONLY делает строки списка доступными только для чтения.

cb.Bind(wx.EVT_COMBOBOX, self.OnSelect)

Когда мы выбираем опцию в поле со списком, запускается событие wx.EVT_COMBOBOX. Мы подключаемобработчик события OnSelect() к этому событию.

def OnSelect(self, e):

i = e.GetString()

self.st.SetLabel(i)

Мы получаем выбранный элемент из поля со списком и показываем его на метке.

Рис. 36: wx.ComboBox

76

7.7 wx.CheckBox

Wx.CheckBox - это виджет, который имеет два состояния: включено и выключено. Это бокс с меткой. Меткаможет быть установлена справа или слева от поля. Если wx.CheckBox включен, то это представляется галочкойв боксе.

Листинг 41: checkbox.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e a checkbox widge t .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx151617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 pnl = wx . Panel ( s e l f )2728 vbox = wx . BoxSizer (wx .HORIZONTAL)2930 cb = wx . CheckBox ( pnl , l a b e l=’Show␣ t i t l e ’ )31 cb . SetValue (True )32 cb . Bind (wx .EVT_CHECKBOX, s e l f . ShowOrHideTitle )3334 vbox .Add( cb , f l a g=wx .TOP|wx .LEFT, border=30)3536 pnl . S e tS i z e r ( vbox )3738 s e l f . S e tT i t l e ( ’wx . CheckBox ’ )39 s e l f . Centre ( )4041 def ShowOrHideTitle ( s e l f , e ) :4243 sender = e . GetEventObject ( )44 isChecked = sender . GetValue ( )4546 i f i sChecked :47 s e l f . S e tT i t l e ( ’wx . CheckBox ’ )48 else :49 s e l f . S e tT i t l e ( ’ ’ )505152 def main ( ) :5354 app = wx .App( )55 ex = Example (None )

77

56 ex . Show ( )57 app . MainLoop ( )585960 i f __name__ == ’__main__ ’ :61 main ( )

В приведенном выше примере мы показываем или скрываем заголовок окна с помощью виджета wx.CheckBox.

cb = wx.CheckBox(pnl, label=’Show title’)

Это вызов конструктор виджета wx.CheckBox.

cb.SetValue(True)

Заголовок окна фрейма отображается по умолчанию, поэтому мы устанавливаем виджет wx.CheckBox спомощью метода SetValue().

cb.Bind(wx.EVT_CHECKBOX, self.ShowOrHideTitle)

Событие wx.EVT_CHECKBOX срабатывает, когда мы нажимаем на виджет wx.CheckBox. Для этого собы-тия вызывается обработчик события ShowOrHideTitle().

def ShowOrHideTitle(self, e):

sender = e.GetEventObject()

isChecked = sender.GetValue()

if isChecked:

self.SetTitle(’wx.CheckBox’)

else:

self.SetTitle(’’)

В методе ShowOrHideTitle() мы показываем или скрываем заголовок в зависимости от состояния виджетаwx.CheckBox.

Рис. 37: wx.CheckBox

7.8 wx.StatusBar

Виджет wx.StatusBar используется для отображения информации о состоянии приложения. Он может бытьразделён на несколько частей, чтобы показать различные виды информации. Мы можем вставить другие ви-джеты в wx.StatusBar. Его можно использовать в качестве альтернативы диалогам, поскольку диалогами частозлоупотребляют и они не нравятся большинству пользователей. Мы можем создать wx.StatusBar двумя спосо-бами. Можем вручную создать наш собственный wx.StatusBar и вызвать метод SetStatusBar(), или мы можемпросто вызвать метод CreateStatusBar(). Этот метод создает для нас wx.StatusBar по умолчанию.

78

Листинг 42: status_bar.py

1 #!/ usr / b in /python2 # −*− coding : u t f−8 −*−34 import wx567 class Example (wx . Frame) :89 def __init__( s e l f , * args , **kw) :10 super (Example , s e l f ) . __init__(* args , **kw)1112 s e l f . In i tUI ( )1314 def In i tUI ( s e l f ) :1516 pnl = wx . Panel ( s e l f )1718 button = wx . Button ( pnl , l a b e l=’ Button ’ , pos=(20 , 20) )19 text = wx . CheckBox ( pnl , l a b e l=’CheckBox ’ , pos=(20 , 90) )20 combo = wx .ComboBox( pnl , pos=(120 , 22) , cho i c e s =[ ’ Python ’ , ’Ruby ’ ] )21 s l i d e r = wx . S l i d e r ( pnl , 5 , 6 , 1 , 10 , (120 , 90) , (110 , −1) )2223 pnl . Bind (wx .EVT_ENTER_WINDOW, s e l f . OnWidgetEnter )24 button . Bind (wx .EVT_ENTER_WINDOW, s e l f . OnWidgetEnter )25 text . Bind (wx .EVT_ENTER_WINDOW, s e l f . OnWidgetEnter )26 combo . Bind (wx .EVT_ENTER_WINDOW, s e l f . OnWidgetEnter )27 s l i d e r . Bind (wx .EVT_ENTER_WINDOW, s e l f . OnWidgetEnter )2829 s e l f . sb = s e l f . CreateStatusBar ( )3031 s e l f . S e tS i z e ( (250 , 230) )32 s e l f . S e tT i t l e ( ’wx . Statusbar ’ )33 s e l f . Centre ( )34 s e l f . Show(True )3536 def OnWidgetEnter ( s e l f , e ) :3738 name = e . GetEventObject ( ) . GetClassName ( )39 s e l f . sb . SetStatusText (name + ’ ␣widget ’ )40 e . Skip ( )4142 def main ( ) :4344 ex = wx .App( )45 Example (None )46 ex . MainLoop ( )4748 i f __name__ == ’__main__ ’ :49 main ( )

В нашем примере у нас есть виджет wx.Frame и пять других виджетов. Если навести указатель мыши навиджет, его имя отобразится в wx.StatusBar.

pnl.Bind(wx.EVT_ENTER_WINDOW, self.OnWidgetEnter)

Событие EVT_ENTER_WINDOW генерируется, если мы входим в область виджета.

self.sb = self.CreateStatusBar()

79

Строка состояния создается с помощью метода CreateStatusBar().

def OnWidgetEnter(self, e):

name = e.GetEventObject().GetClassName()

self.sb.SetStatusText(name + ’ widget’)

e.Skip()

Внутри метода OnWidgetEnter() мы выясняем имя виджета, на который попал указателем мыши. Затеммы устанавливаем текст в строке статуса с помощью метода SetStatusText().

Рис. 38: wx.StatusBar

7.9 wx.RadioButton

wx.RadioButton - это виджет, который позволяет пользователю выбрать один элемент из группы парамет-ров. Группа переключателей определяется тем, что первый переключатель в группе содержат стиль wx.RB_GROUP.Все другие переключатели, определенные после первого переключателя с этим флагом стиля, будут добав-лены в функциональную группу этого первого переключателя. Объявление другой радиокнопки с флагомwx.RB_GROUP начнёт новую группу радиокнопок.

Листинг 43: radio_button.py

1 #!/ usr / b in /python2 # −*− coding : u t f−8 −*−34 import wx567 class Example (wx . Frame) :89 def __init__( s e l f , * args , **kw) :10 super (Example , s e l f ) . __init__(* args , **kw)1112 s e l f . In i tUI ( )1314 def In i tUI ( s e l f ) :1516 pnl = wx . Panel ( s e l f )1718 s e l f . rb1 = wx . RadioButton ( pnl , l a b e l=’ Value␣A ’ , pos=(10 , 10) ,19 s t y l e=wx .RB_GROUP)20 s e l f . rb2 = wx . RadioButton ( pnl , l a b e l=’ Value␣B ’ , pos=(10 , 30) )

80

21 s e l f . rb3 = wx . RadioButton ( pnl , l a b e l=’ Value␣C ’ , pos=(10 , 50) )2223 s e l f . rb1 . Bind (wx .EVT_RADIOBUTTON, s e l f . SetVal )24 s e l f . rb2 . Bind (wx .EVT_RADIOBUTTON, s e l f . SetVal )25 s e l f . rb3 . Bind (wx .EVT_RADIOBUTTON, s e l f . SetVal )2627 s e l f . sb = s e l f . CreateStatusBar (3 )2829 s e l f . sb . SetStatusText ( "True" , 0)30 s e l f . sb . SetStatusText ( " Fa l se " , 1)31 s e l f . sb . SetStatusText ( " Fa l se " , 2)3233 s e l f . S e tS i z e ( (210 , 210) )34 s e l f . S e tT i t l e ( ’wx . RadioButton ’ )35 s e l f . Centre ( )36 s e l f . Show(True )3738 def SetVal ( s e l f , e ) :3940 s t a t e 1 = str ( s e l f . rb1 . GetValue ( ) )41 s t a t e 2 = str ( s e l f . rb2 . GetValue ( ) )42 s t a t e 3 = str ( s e l f . rb3 . GetValue ( ) )4344 s e l f . sb . SetStatusText ( s tate1 , 0)45 s e l f . sb . SetStatusText ( s tate2 , 1)46 s e l f . sb . SetStatusText ( s tate3 , 2)4748 def main ( ) :4950 ex = wx .App( )51 Example (None )52 ex . MainLoop ( )5354 i f __name__ == ’__main__ ’ :55 main ( )

У нас есть группа из трех радиокнопок. Состояние каждого из переключателей отображается в строкесостояния.

self.rb1 = wx.RadioButton(pnl, label=’Value A’, pos=(10, 10),

style=wx.RB_GROUP)

self.rb2 = wx.RadioButton(pnl, label=’Value B’, pos=(10, 30))

self.rb3 = wx.RadioButton(pnl, label=’Value C’, pos=(10, 50))

Мы создаем три радио кнопки. Первая радио-кнопка имеет установленный стиль wx.RB_GROUP. Начи-нается новая радиогруппа.

self.rb1.Bind(wx.EVT_RADIOBUTTON, self.SetVal)

Мы связываем событие wx.EVT_RADIOBUTTON с обработчиком событий SetVal().

self.sb = self.CreateStatusBar(3)

self.sb.SetStatusText("True", 0)

self.sb.SetStatusText("False", 1)

self.sb.SetStatusText("False", 2)

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

81

def SetVal(self, e):

state1 = str(self.rb1.GetValue())

state2 = str(self.rb2.GetValue())

state3 = str(self.rb3.GetValue())

self.sb.SetStatusText(state1, 0)

self.sb.SetStatusText(state2, 1)

self.sb.SetStatusText(state3, 2)

Внутри метода SetVal() мы узнаем состояния переключателей. Затем мы обновляем поля строки состояниядо текущих значений переключателей.

Рис. 39: wx.RadioButton

7.10 wx.Gauge

wx.Gauge - это виджет, который используется, когда мы обрабатываем продолжительные задачи. Имеетиндикатор, показывающий текущее состояние задачи.

Листинг 44: gauge_wid.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e gauge widge t .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx1516 TASK_RANGE = 501718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)

82

2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 s e l f . t imer = wx . Timer ( s e l f , 1)28 s e l f . count = 02930 s e l f . Bind (wx .EVT_TIMER, s e l f . OnTimer , s e l f . t imer )3132 pnl = wx . Panel ( s e l f )33 vbox = wx . BoxSizer (wx .VERTICAL)34 hbox1 = wx . BoxSizer (wx .HORIZONTAL)35 hbox2 = wx . BoxSizer (wx .HORIZONTAL)36 hbox3 = wx . BoxSizer (wx .HORIZONTAL)3738 s e l f . gauge = wx . Gauge ( pnl , range=TASK_RANGE, s i z e =(250 , −1) )39 s e l f . btn1 = wx . Button ( pnl , wx .ID_OK)40 s e l f . btn2 = wx . Button ( pnl , wx .ID_STOP)41 s e l f . t ex t = wx . Stat i cText ( pnl , l a b e l=’Task␣ to ␣be␣done ’ )4243 s e l f . Bind (wx .EVT_BUTTON, s e l f .OnOk, s e l f . btn1 )44 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnStop , s e l f . btn2 )4546 hbox1 .Add( s e l f . gauge , propor t ion=1, f l a g=wx .ALIGN_CENTRE)47 hbox2 .Add( s e l f . btn1 , propor t ion=1, f l a g=wx .RIGHT, border=10)48 hbox2 .Add( s e l f . btn2 , propor t ion=1)49 hbox3 .Add( s e l f . text , propor t ion=1)5051 vbox .Add( ( 0 , 30) )5253 vbox .Add( hbox1 , f l a g=wx .ALIGN_CENTRE)5455 vbox .Add( ( 0 , 20) )5657 vbox .Add( hbox2 , propor t ion=1, f l a g=wx .ALIGN_CENTRE)58 vbox .Add( hbox3 , propor t ion=1, f l a g=wx .ALIGN_CENTRE)5960 pnl . S e tS i z e r ( vbox )6162 s e l f . S e tT i t l e ( ’wx . Gauge ’ )63 s e l f . Centre ( )6465 def OnOk( s e l f , e ) :6667 i f s e l f . count >= TASK_RANGE:68 return

6970 s e l f . t imer . S ta r t (100)71 s e l f . t ex t . SetLabel ( ’ Task␣ in ␣Progres s ’ )7273 def OnStop ( s e l f , e ) :7475 i f s e l f . count == 0 or s e l f . count >= TASK_RANGE or not s e l f . t imer . IsRunning ( ) :76 return

7778 s e l f . t imer . Stop ( )79 s e l f . t ex t . SetLabel ( ’ Task␣ Inte r rupted ’ )8081 def OnTimer ( s e l f , e ) :8283 s e l f . count = s e l f . count + 1

83

84 s e l f . gauge . SetValue ( s e l f . count )8586 i f s e l f . count == TASK_RANGE:8788 s e l f . t imer . Stop ( )89 s e l f . t ex t . SetLabel ( ’Task␣Completed ’ )909192 def main ( ) :9394 app = wx .App( )95 ex = Example (None )96 ex . Show ( )97 app . MainLoop ( )9899100 i f __name__ == ’__main__ ’ :101 main ( )

У нас есть индикатор и две кнопки. Одна кнопка запускает индикатор, а другая кнопка его останавливает.

self.timer = wx.Timer(self, 1)

self.count = 0

Мы используем wx.Timer для выполнения кода через определенные промежутки времени. Мы будем обнов-лять датчик в эти моменты. Переменная count используется для определения части уже выполненной задачи.

self.gauge = wx.Gauge(pnl, range=TASK_RANGE, size=(250, -1))

Вызываем конструктор виджета wx.Gauge. Параметр range устанавливает максимальное целочисленноезначение виджета.

def OnOk(self, e):

if self.count >= TASK_RANGE:

return

self.timer.Start(100)

self.text.SetLabel(’Task in Progress’)

Когда мы нажимаем кнопку ОК, вызывается метод OnOk(). Сначала мы проверяем, находится ли пере-менная count в пределах диапазона задачи. Если нет, мы вернемся из метода. Если задача еще не выполнена,мы запускаем таймер и обновляем статический текст.

def OnStop(self, e):

if self.count == 0 or self.count >= TASK_RANGE or not self.timer.IsRunning():

return

self.timer.Stop()

self.text.SetLabel(’Task Interrupted’)

Метод OnStop() вызывается, когда мы нажимаем кнопку «Стоп». Мы проверяем условия остановки зада-ния. Если мы обнаружили их, мы останавливаем таймер и обновляем статический текст.

def OnTimer(self, e):

84

self.count = self.count + 1

self.gauge.SetValue(self.count)

if self.count == TASK_RANGE:

self.timer.Stop()

self.text.SetLabel(’Task Completed’)

Метод OnTimer() вызывается периодически после запуска таймера. В методе мы обновляем переменнуюcout и виджет датчика. Если переменная count равна TASK_RANGE, мы останавливаем таймер и обновляемстатический текст.

Рис. 40: wx.RadioButton

7.11 wx.Slider

wx.Slider - это виджет с рукояткой. Эту ручку можно тянуть взад и вперед. Мы можем приспособить егопод конкретную задачу.

Листинг 45: slider_wid.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e s l i d e r con t r o l .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx151617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 pnl = wx . Panel ( s e l f )27

85

28 s i z e r = wx . GridBagSizer (5 , 5)2930 s l d = wx . S l i d e r ( pnl , va lue=200 , minValue=150 , maxValue=500 ,31 s t y l e=wx .SL_HORIZONTAL)3233 s l d . Bind (wx .EVT_SCROLL, s e l f . OnS l i d e rS c r o l l )34 s i z e r .Add( s ld , pos=(0 , 0) , f l a g=wx .ALL|wx .EXPAND, border=25)3536 s e l f . txt = wx . Stat i cText ( pnl , l a b e l=’ 200 ’ )37 s i z e r .Add( s e l f . txt , pos=(0 , 1) , f l a g=wx .TOP|wx .RIGHT, border=25)3839 s i z e r . AddGrowableCol (0 )40 pnl . S e tS i z e r ( s i z e r )4142 s e l f . S e tT i t l e ( ’wx . S l i d e r ’ )43 s e l f . Centre ( )4445 def OnS l i d e rS c r o l l ( s e l f , e ) :4647 obj = e . GetEventObject ( )48 va l = obj . GetValue ( )4950 s e l f . txt . SetLabel ( str ( va l ) )515253 def main ( ) :5455 app = wx .App( )56 ex = Example (None )57 ex . Show ( )58 app . MainLoop ( )596061 i f __name__ == ’__main__ ’ :62 main ( )

Значение, выбранное в ползунке, отображается в статическом тексте.

sld = wx.Slider(pnl, value=200, minValue=150, maxValue=500,

style=wx.SL_HORIZONTAL)

Wx.Slider создан. Мы задаём начальное положение ползунка с параметром value, а минимальное и мак-симальное положение ползунка - параметрами minValue и maxValue. Wx.SL_HORIZONTAL делает слайдергоризонтальным.

sld.Bind(wx.EVT_SCROLL, self.OnSliderScroll)

При обнаружении события wx.EVT_SCROLL вызывается метод OnSliderScroll().

self.txt = wx.StaticText(pnl, label=’200’)

Текущее выбранное значение слайдера отображается в статическом тексте, который мы расположили рядомсо слайдером.

def OnSliderScroll(self, e):

obj = e.GetEventObject()

86

val = obj.GetValue()

self.txt.SetLabel(str(val))

В методе OnSliderScroll() мы получаем отправленное событие. Из него получаем текущее значение ползункаи устанавливаем его в качестве значения виджета статического текста.

Рис. 41: wx.Slider

7.12 wx.SpinCtrl

Виджет wx.SpinCtrl позволяет нам увеличивать и уменьшать значение.

Листинг 46: spin_ctrl.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example we c r ea t e sp in con t r o l .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : Apr i l 201812 """1314 import wx151617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 pnl = wx . Panel ( s e l f )2728 s i z e r = wx . GridBagSizer (5 , 5)2930 s t1 = wx . Stat i cText ( pnl , l a b e l=’ Convert␣Fahrenheit ␣ temperature ␣ to ␣ Ce l s i u s ’ )31 s i z e r .Add( st1 , pos=(0 , 0) , span=(1 , 2) , f l a g=wx .ALL, border=15)3233 s t2 = wx . Stat i cText ( pnl , l a b e l=’ Fahrenheit : ’ )34 s i z e r .Add( st2 , pos=(1 , 0) , f l a g=wx .ALL | wx .ALIGN_CENTER, border=15)

87

3536 s e l f . s c = wx . SpinCtr l ( pnl , va lue=’ 0 ’ )37 s e l f . s c . SetRange (−459 , 1000)3839 s i z e r .Add( s e l f . sc , pos=(1 , 1) , f l a g=wx .ALIGN_CENTER)4041 s t3 = wx . Stat i cText ( pnl , l a b e l=’ Ce l s i u s : ’ )42 s i z e r .Add( st3 , pos=(2 , 0) , f l a g=wx .ALL|wx .ALIGN_RIGHT, border=15)4344 s e l f . c e l s i u s = wx . Stat i cText ( pnl , l a b e l=’ ’ )45 s i z e r .Add( s e l f . c e l s i u s , pos=(2 , 1) , f l a g=wx .ALL, border=15)4647 computeButton = wx . Button ( pnl , l a b e l=’Compute ’ )48 computeButton . SetFocus ( )49 s i z e r .Add( computeButton , pos=(3 , 0) , f l a g=wx .ALIGN_RIGHT|wx .TOP, border=30)5051 c loseButton = wx . Button ( pnl , l a b e l=’ Close ’ )52 s i z e r .Add( c loseButton , pos=(3 , 1) , f l a g=wx .ALIGN_LEFT|wx .TOP, border=30)5354 computeButton . Bind (wx .EVT_BUTTON, s e l f . OnCompute)55 c loseButton . Bind (wx .EVT_BUTTON, s e l f . OnClose )5657 pnl . S e tS i z e r ( s i z e r )5859 s e l f . S e tT i t l e ( ’wx . Sp inCtr l ’ )60 s e l f . Centre ( )6162 def OnClose ( s e l f , e ) :6364 s e l f . Close (True )6566 def OnCompute( s e l f , e ) :6768 fahr = s e l f . s c . GetValue ( )69 c e l s = round ( ( f ahr − 32) * 5 / 9 . 0 , 2)70 s e l f . c e l s i u s . SetLabel ( str ( c e l s ) )717273 def main ( ) :7475 app = wx .App( )76 ex = Example (None )77 ex . Show ( )78 app . MainLoop ( )798081 i f __name__ == ’__main__ ’ :82 main ( )

Скрипт преобразует температуру по Фаренгейту в градусы Цельсия. Мы используем виджет wx.SpinCtrl,чтобы ввести значение температуры по Фаренгейту.

self.sc = wx.SpinCtrl(pnl, value=’0’)

self.sc.SetRange(-459, 1000)

Мы создаем виджет wx.SpinCtrl с начальным значением 0. SetRange() устанавливает диапазон значенийдля виджета.

def OnCompute(self, e):

88

fahr = self.sc.GetValue()

cels = round((fahr - 32) * 5 / 9.0, 2)

self.celsius.SetLabel(str(cels))

Когда мы нажимаем кнопку вычисления, вызывается метод OnCompute(). В теле метода мы получаем те-кущее значение из виджета wx.SpinCtrl. Мы вычисляем температуру по Цельсию и устанавливаем вычисленнуютемпературу значением виджета статического текста.

Рис. 42: wx.SpinCtrl

Эта часть руководства по wxPython была посвящена основным виджетам wxPython.

8 Расширенные виджеты в wxPython

В этой главе мы поговорим о следующих расширенных виджетах: wx.ListBox, wx.html.HtmlWindow, wx.ListCtrl.

У wxPython есть несколько хорошо известных расширенных виджетов. Например, виджет дерева, окноHTML, виджет сетки, виджет выпадающего списка, виджет списка или редактор с расширенными возможно-стями стилей.

8.1 wx.ListBox

wx.ListBox используется для отображения и работы со списком элементов.Wx.ListBox может быть создан вдвух разных состояниях: в состоянии единичного выбора или в состоянии множественного выбора. Единственноесостояние выбора является состоянием по умолчанию.

В wx.ListBox есть два значимых события. Первым является событие wx.EVT_COMMAND_LISTBOX_SELECTED.Это событие генерируется, когда мы выбираем элемент в wx.ListBox. Вторым событием является событиеwx.EVT_COMMAND_LISTBOX_DOUBLE_CLICKED. Он генерируется, когда мы дважды щелкаем элемент вwx.ListBox. Элементы нумеруются с нуля. Полосы прокрутки отображаются автоматически при необходимости.

Листинг 47: spin_ctrl.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e a wx . ListBox widge t .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : May 201812 """1314 import wx

89

1516 class Example (wx . Frame) :1718 def __init__( s e l f , * args , **kw) :19 super (Example , s e l f ) . __init__(* args , **kw)2021 s e l f . In i tUI ( )2223 def In i tUI ( s e l f ) :2425 panel = wx . Panel ( s e l f )26 hbox = wx . BoxSizer (wx .HORIZONTAL)2728 s e l f . l i s t b o x = wx . ListBox ( panel )29 hbox .Add( s e l f . l i s t box , wx .ID_ANY, wx .EXPAND | wx .ALL, 20)3031 btnPanel = wx . Panel ( panel )32 vbox = wx . BoxSizer (wx .VERTICAL)33 newBtn = wx . Button ( btnPanel , wx .ID_ANY, ’New ’ , s i z e =(90 , 30) )34 renBtn = wx . Button ( btnPanel , wx .ID_ANY, ’Rename ’ , s i z e =(90 , 30) )35 delBtn = wx . Button ( btnPanel , wx .ID_ANY, ’ De lete ’ , s i z e =(90 , 30) )36 c l rBtn = wx . Button ( btnPanel , wx .ID_ANY, ’ Clear ’ , s i z e =(90 , 30) )3738 s e l f . Bind (wx .EVT_BUTTON, s e l f . NewItem , id=newBtn . GetId ( ) )39 s e l f . Bind (wx .EVT_BUTTON, s e l f .OnRename , id=renBtn . GetId ( ) )40 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnDelete , id=delBtn . GetId ( ) )41 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnClear , id=clrBtn . GetId ( ) )42 s e l f . Bind (wx .EVT_LISTBOX_DCLICK, s e l f .OnRename)4344 vbox .Add((−1 , 20) )45 vbox .Add(newBtn)46 vbox .Add( renBtn , 0 , wx .TOP, 5)47 vbox .Add( delBtn , 0 , wx .TOP, 5)48 vbox .Add( clrBtn , 0 , wx .TOP, 5)4950 btnPanel . S e tS i z e r ( vbox )51 hbox .Add( btnPanel , 0 . 6 , wx .EXPAND | wx .RIGHT, 20)52 panel . S e tS i z e r ( hbox )5354 s e l f . S e tT i t l e ( ’wx . ListBox ’ )55 s e l f . Centre ( )5657 def NewItem( s e l f , event ) :5859 text = wx . GetTextFromUser ( ’ Enter ␣a␣new␣ item ’ , ’ I n s e r t ␣ d i a l o g ’ )60 i f t ex t != ’ ’ :61 s e l f . l i s t b o x . Append( text )6263 def OnRename( s e l f , event ) :6465 s e l = s e l f . l i s t b o x . GetSe l e c t i on ( )66 text = s e l f . l i s t b o x . GetStr ing ( s e l )67 renamed = wx . GetTextFromUser ( ’Rename␣ item ’ , ’Rename␣ d i a l o g ’ , t ex t )6869 i f renamed != ’ ’ :70 s e l f . l i s t b o x . De lete ( s e l )71 item_id = s e l f . l i s t b o x . I n s e r t ( renamed , s e l )72 s e l f . l i s t b o x . S e t S e l e c t i o n ( item_id )7374 def OnDelete ( s e l f , event ) :7576 s e l = s e l f . l i s t b o x . GetSe l e c t i on ( )

90

77 i f s e l != −1:78 s e l f . l i s t b o x . De lete ( s e l )7980 def OnClear ( s e l f , event ) :81 s e l f . l i s t b o x . Clear ( )828384 def main ( ) :8586 app = wx .App( )87 ex = Example (None )88 ex . Show ( )89 app . MainLoop ( )909192 i f __name__ == ’__main__ ’ :93 main ( )

В примере показано, как добавлять, изменять и удалять элементы из wx.ListBox.

self.listbox = wx.ListBox(panel)

hbox.Add(self.listbox, wx.ID_ANY, wx.EXPAND | wx.ALL, 20)

Мы создаем пустой wx.ListBox. Мы помещаем границу шириной 20px вокруг списка.

self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnRename)

Мы связываем события типа wx.EVT_COMMAND_LISTBOX_DOUBLE_CLICKED с помощью методаOnRename(), используя механизм связывания событий wx.EVT_LISTBOX_DCLICK. Таким образом, мы пока-зываем диалог редактирования, если дважды щелкнуть конкретный элемент в списке.

def NewItem(self, event):

text = wx.GetTextFromUser(’Enter a new item’, ’Insert dialog’)

if text != ’’:

self.listbox.Append(text)

Мы вызываем метод NewItem(), нажимая кнопку New. Этот метод показывает wx.TextEntryDialog, исполь-зуя метод-обертку wx.GetTextFromUser(). Текст, который мы вводим, возвращается в текстовую переменную.Если текст не пустой, мы добавляем его в список с помощью метода Append().

if renamed != ’’:

self.listbox.Delete(sel)

item_id = self.listbox.Insert(renamed, sel)

self.listbox.SetSelection(item_id)

Мы переименовываем элемент, удаляя его и вставляя новый элемент в ту же позицию. Мы также возвра-щаем выбор к измененному элементу.

def OnDelete(self, event):

sel = self.listbox.GetSelection()

if sel != -1:

self.listbox.Delete(sel)

Чтобы удалить элемент, мы находим индекс выбранного элемента, вызывая метод GetSelection(). Затем мыудаляем элемент с помощью метода Delete(). Параметром метода Delete() является выбранный индекс.

91

def OnClear(self, event):

self.listbox.Clear()

Проще всего очистить весь список. Мы просто вызываем метод Clear().

Рис. 43: wx.ListBox

8.2 wx.html.HtmlWindow

Виджет wx.html.HtmlWindow отображает HTML-страницы. Это не полноценный браузер. Мы можем сде-лать некоторые интересные вещи с помощью виджета wx.html.HtmlWindow.

Например, в следующей программе мы создаем окно, которое отображает основную статистику.

Листинг 48: page.html

1 <!DOCTYPE html>2 <html>3 <body bgcolor="#8e8e95 ">4 <table cellspacing="5" border="0" width="250">5 <tr width="200" align=" l e f t ">6 <td bgcolor="#e7e7e7 ">&nbsp;&nbsp ;Maximum</td>7 <td bgcolor="#aaaaaa">&nbsp;&nbsp ;<b>9000</b></td>8 </ tr>9 <tr align=" l e f t ">10 <td bgcolor="#e7e7e7 ">&nbsp;&nbsp ;Mean</td>11 <td bgcolor="#aaaaaa">&nbsp;&nbsp ;<b>6076</b></td>12 </ tr>13 <tr align=" l e f t ">14 <td bgcolor="#e7e7e7 ">&nbsp;&nbsp ;Minimum</td>15 <td bgcolor="#aaaaaa">&nbsp;&nbsp ;<b>3800</b></td>16 </ tr>17 <tr align=" l e f t ">18 <td bgcolor="#e7e7e7 ">&nbsp;&nbsp ; Median</td>19 <td bgcolor="#aaaaaa">&nbsp;&nbsp ;<b>6000</b></td>20 </ tr>21 <tr align=" l e f t ">22 <td bgcolor="#e7e7e7 ">&nbsp;&nbsp ; Standard Deviat ion</td>23 <td bgcolor="#aaaaaa">&nbsp;&nbsp ;<b>6076</b></td>24 </ tr>25 </ table>26 </body>27 </html>

Это HTML-страница для отображения.

Листинг 49: htmlwin.py

1 #!/ usr / b in /env python3

92

2 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e a wx . html . HtmlWindow widge t .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : May 201812 """1314 import wx15 import wx . html1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 panel = wx . Panel ( s e l f )2728 vbox = wx . BoxSizer (wx .VERTICAL)29 hbox = wx . BoxSizer (wx .HORIZONTAL)3031 htmlwin = wx . html . HtmlWindow( panel , wx .ID_ANY, s t y l e=wx .NO_BORDER)32 htmlwin . SetStandardFonts ( )33 htmlwin . LoadPage ( "page . html" )3435 vbox .Add((−1 , 10) , 0)36 vbox .Add( htmlwin , 1 , wx .EXPAND | wx .ALL, 9)3738 bitmap = wx . StaticBitmap ( panel , wx .ID_ANY, wx . Bitmap ( ’ newt . png ’ ) )39 hbox .Add( bitmap , 0 , wx .LEFT | wx .BOTTOM | wx .TOP, 10)40 btnOk = wx . Button ( panel , wx .ID_ANY, ’Ok ’ )4142 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnClose , id=btnOk . GetId ( ) )4344 hbox .Add( (100 , −1) , 1 , wx .EXPAND | wx .ALIGN_RIGHT)45 hbox .Add(btnOk , f l a g=wx .TOP | wx .BOTTOM | wx .RIGHT, border=10)46 vbox .Add(hbox , 0 , wx .EXPAND)4748 panel . S e tS i z e r ( vbox )4950 s e l f . S e tT i t l e ( ’ Bas ic ␣ s t a t i s t i c s ’ )51 s e l f . Centre ( )5253 def OnClose ( s e l f , event ) :54 s e l f . Close ( )555657 def main ( ) :5859 app = wx .App( )60 ex = Example (None )61 ex . Show ( )62 app . MainLoop ( )63

93

6465 i f __name__ == ’__main__ ’ :66 main ( )

Пример отображает файл HTML в виджете wx.html.HtmlWindow.

htmlwin = wx.html.HtmlWindow(panel, wx.ID_ANY, style=wx.NO_BORDER)

htmlwin.SetStandardFonts()

htmlwin.LoadPage("page.html")

Виджет Wx.html.HtmlWindow создан. Файл HTML загружается с помощью метода LoadPage().

Рис. 44: wx.html.HtmlWindow - пример окна.

8.3 Окно Help

Мы можем использовать wx.html.HtmlWindow для предоставления помощи в нашем приложении. Мы мо-жем создать отдельное окно или окно, которое будет частью приложения. Следующий скрипт создаст окносправки, используя последнюю идею.

Листинг 50: helpwindow.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e a he l p window window8 with wx . html . HtmlWindow .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : May 201813 """1415 import wx16 import wx . html as html1718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)22

94

23 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 too lba r = s e l f . CreateToolBar ( )28 too lba r . AddTool (1 , ’ Exit ’ , wx . Bitmap ( ’ e x i t . png ’ ) )29 too lba r . AddTool (2 , ’ Help ’ , wx . Bitmap ( ’ he lp . png ’ ) )30 too lba r . Rea l i z e ( )3132 s e l f . s p l i t t e r = wx . SplitterWindow ( s e l f )33 s e l f . pane lLe f t = wx . Panel ( s e l f . s p l i t t e r , wx .ID_ANY, s t y l e=wx .BORDER_SUNKEN)3435 s e l f . panelRight = wx . Panel ( s e l f . s p l i t t e r )36 vbox2 = wx . BoxSizer (wx .VERTICAL)37 header = wx . Panel ( s e l f . panelRight , wx .ID_ANY)3839 header . SetBackgroundColour ( ’#6f6a59 ’ )40 header . SetForegroundColour ( ’ white ’ )4142 hbox = wx . BoxSizer (wx .HORIZONTAL)4344 s t = wx . Stat i cText ( header , wx .ID_ANY, ’ Help ’ )45 font = s t . GetFont ( )46 font . SetFamily (wx .FONTFAMILY_ROMAN)47 font . Se tPo intS i z e (11)48 s t . SetFont ( f ont )4950 hbox .Add( st , 1 , wx .TOP | wx .BOTTOM | wx .LEFT, 8)5152 c loseBtn = wx . BitmapButton ( header , wx .ID_ANY, wx . Bitmap ( ’ c l o s ebut ton . png ’ ,53 wx .BITMAP_TYPE_PNG) , s t y l e=wx .NO_BORDER)54 c loseBtn . SetBackgroundColour ( ’#6f6a59 ’ )5556 hbox .Add( closeBtn , 0 , wx .TOP|wx .BOTTOM, 8)57 header . S e tS i z e r ( hbox )5859 vbox2 .Add( header , 0 , wx .EXPAND)6061 helpWin = html . HtmlWindow( s e l f . panelRight , s t y l e=wx .NO_BORDER)62 helpWin . LoadPage ( ’ he lp . html ’ )6364 vbox2 .Add( helpWin , 1 , wx .EXPAND)6566 s e l f . panelRight . S e tS i z e r ( vbox2 )67 s e l f . pane lLe f t . SetFocus ( )6869 s e l f . s p l i t t e r . S p l i tV e r t i c a l l y ( s e l f . pane lLe f t , s e l f . panelRight )70 s e l f . s p l i t t e r . Unsp l i t ( )7172 s e l f . Bind (wx .EVT_BUTTON, s e l f . CloseHelp , id=closeBtn . GetId ( ) )73 s e l f . Bind (wx .EVT_TOOL, s e l f . OnClose , id=1)74 s e l f . Bind (wx .EVT_TOOL, s e l f . OnHelp , id=2)7576 s e l f . pane lLe f t . Bind (wx .EVT_KEY_DOWN, s e l f . OnKeyPressed )77 s e l f . pane lLe f t . SetFocus ( )7879 s e l f . CreateStatusBar ( )8081 s e l f . S e tT i t l e ( ’ Help ’ )82 s e l f . Centre ( )8384 def OnClose ( s e l f , e ) :

95

85 s e l f . Close ( )8687 def OnHelp ( s e l f , e ) :8889 s e l f . s p l i t t e r . S p l i tV e r t i c a l l y ( s e l f . pane lLe f t , s e l f . panelRight )90 s e l f . pane lLe f t . SetFocus ( )9192 def CloseHelp ( s e l f , e ) :9394 s e l f . s p l i t t e r . Unsp l i t ( )95 s e l f . pane lLe f t . SetFocus ( )9697 def OnKeyPressed ( s e l f , e ) :9899 keycode = e . GetKeyCode ( )100 print ( keycode )101102 i f keycode == wx .WXK_F1:103104 s e l f . s p l i t t e r . S p l i t V e r t i c a l l y ( s e l f . pane lLe f t , s e l f . panelRight )105 s e l f . pane lLe f t . SetFocus ( )106107108 def main ( ) :109110 app = wx .App( )111 ex = Example (None )112 ex . Show ( )113 app . MainLoop ( )114115116 i f __name__ == ’__main__ ’ :117 main ( )

Окно справки скрыто в начале. Мы можем показать его, нажав на кнопку «Справка» на панели инструмен-тов или нажав клавишу F1. Окно справки появляется в правой части приложения. Чтобы скрыть окно справки,мы нажимаем кнопку «Закрыть».

self.splitter.SplitVertically(self.panelLeft, self.panelRight)

self.splitter.Unsplit()

Мы создаем левые и правые панели и разбиваем виджет по вертикали. После этого мы вызываем методUnsplit(). По умолчанию метод скрывает правую или нижнюю панели.

Разобьем правую панель на две части. Заголовок и тело панели. Заголовок является настроенной па-нелью wx.Panel. Заголовок состоит из статического текста и кнопки растрового изображения. Мы помещаемwx.html.Window в тело панели.

closeBtn = wx.BitmapButton(header, wx.ID_ANY, wx.Bitmap(’closebutton.png’,

wx.BITMAP_TYPE_PNG), style=wx.NO_BORDER)

closeBtn.SetBackgroundColour(’#6f6a59’)

Стиль кнопки растрового изображения задан как wx.NO_BORDER. Цвет фона устанавливается по цветупанели заголовка. Это сделано для того, чтобы кнопка отображалась как часть заголовка.

helpWin = html.HtmlWindow(self.panelRight, style=wx.NO_BORDER)

helpWin.LoadPage(’help.html’)

Мы создаем виджет wx.html.HtmlWindow на правой панели. У нас есть HTML-код в отдельном файле. Наэтот раз мы вызываем метод LoadPage() для получения HTML-кода.

96

self.panelLeft.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)

self.panelLeft.SetFocus()

Мы установили фокус на левой панели. Можно запустить окно справки с помощью клавиши F1. Чтобыуправлять окном с клавиатуры, оно должно иметь фокус. Если бы мы не установили фокус, нам сначалапришлось бы нажать на панель, и только затем мы могли бы открыть окно справки нажатием клавиши F1.

def OnHelp(self, e):

self.splitter.SplitVertically(self.panelLeft, self.panelRight)

self.panelLeft.SetFocus()

Чтобы показать окно справки, мы вызываем метод OnHelp(). Он разделяет две панели по вертикали. Мыне должны забывать снова устанавливать фокус, потому что первоначальный фокус теряется при разделении.

Ниже приведен HTML-файл, который мы загружаем в наше приложение.

Листинг 51: page.html

1 <!DOCTYPE html>2 <html>34 <body bgcolor="#ababab">5 <h4>Table o f Contents</h4>67 <ul>8 < l i><a href="#bas i c ">Basic s t a t i s t i c s</a></ l i>9 < l i><a href="#advanced">Advanced s t a t i s t i c s</a></ l i>10 < l i><a href="#in t r o ">Introduc ing Newt</a></ l i>11 < l i><a href="#char t s ">Working with char t s</a></ l i>12 < l i><a href="#pred">Pred i c t i ng va lue s</a></ l i>13 < l i><a href="#neura l ">Neural networks</a></ l i>14 < l i><a href="#g l o s ">Glossary</a></ l i>15 </ul>1617 <p>18 <a name=" bas i c ">19 <h6>Basic S t a t i s t i c s</h6>20 Overview o f e lementary concepts in s t a t i s t i c s .21 Var iab l e s . Co r r e l a t i on . Measurement s c a l e s . S t a t i s t i c a l s i g n i f i c a n c e .22 D i s t r i bu t i o n s . Normality assumption .23 </a>24 </p>2526 <p>27 <a name="advanced">28 <h6>Advanced S t a t i s t i c s</h6>29 Overview o f advanced concepts in s t a t i s t i c s . Anova . Linear r e g r e s s i o n .30 Est imation and hypothes i s t e s t i n g .31 Error terms .32 </a>33 </p>3435 <p>36 <a name=" in t r o ">37 <h6>Introduc ing Newt</h6>38 Introduc ing the ba s i c f u n c t i o n a l i t y o f the Newt app l i c a t i o n . Creat ing she e t s .39 Charts . Menus and Toolbars . Importing data . Saving data in var i ous formats .40 Exporting data . Shortcuts . L i s t o f methods .41 </a>42 </p>

97

4344 <p>45 <a name=" char t s ">46 <h6>Charts</h6>47 Working with char t s . 2D char t s . 3D char t s . Bar , l i n e , box , pie , range char t s .48 S c a t t e r p l o t s . Histograms .49 </a>50 </p>5152 <p>53 <a name="pred">54 <h6>Pred i c t i ng va lue s</h6>55 Time s e r i e s and f o r e c a s t i n g . Trend Ana lys i s . S ea s ona l i t y . Moving averages .56 Univar ia te methods . Mu l t i va r i a t e methods . Holt−Winters smoothing .57 Exponent ia l smoothing . ARIMA. Four i e r a n a l y s i s .58 </a>59 </p>6061 <p>62 <a name="neura l ">63 <h6>Neural networks</h6>64 Overview o f neura l networks . Bio logy behind neura l networks .65 Bas ic a r t i f i c i a l Model . Train ing . Preproce s s ing . Pos tp roce s s ing .66 Types o f neura l networks .67 </a>68 </p>6970 <p>71 <a name=" g l o s ">72 <h6>Glossary</h6>73 Terms and d e f i n i t i o n s in s t a t i s t i c s .74 </a>75 </p>7677 </body>78 </html>

HTML-файл содержит оглавление справки приложения.

Рис. 45: Help - пример окна.

98

8.4 Wx.ListCtrl

Wx.ListCtrl - это графическое представление списка элементов. В wx.ListBox может быть только одинстолбец. wx.ListCtrl может иметь более одного столбца. wx.ListCtrl - очень распространенный и полезный ви-джет. Например, файловый менеджер использует wx.ListCtrl для отображения каталогов и файлов в файловойсистеме. Приложение для записи компакт-дисков отображает файлы для записи внутри wx.ListCtrl.

Wx.ListCtrl может использоваться в трех разных форматах. В виде списка, отчета или значка. Эти форматыуправляются стилями окна wx.ListCtrl. Наиболее часто употребляемые стили: wx.LC_REPORT, wx.LC_LIST иwx.LC_ICON.

8.5 Стили Wx.ListCtrl

� wx.LC_LIST

� wx.LC_REPORT

� wx.LC_VIRTUAL

� wx.LC_ICON

� wx.LC_SMALL_ICON

� wx.LC_ALIGN_LEFT

� wx.LC_EDIT_LABELS

� wx.LC_NO_HEADER

� wx.LC_SORT_ASCENDING

� wx.LC_SORT_DESCENDING

� wx.LC_HRULES

� wx.LC_VRULES

8.5.1 Простой пример

В первом примере представлены некоторые основные функции wx.ListCtrl.

Листинг 52: actresses.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e a s imple8 wx . L i s tC t r l w idge t .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : May 201813 """1415 import wx1617 data = [ ( ’ J e s s i c a ␣Alba ’ , ’Pomona ’ , ’ 1981 ’ ) , ( ’ S igourney ␣Weaver ’ , ’New␣York ’ , ’ 1949 ’ ) ,18 ( ’ Angel ina ␣ J o l i e ’ , ’ l o s ␣ ange l e s ’ , ’ 1975 ’ ) , ( ’ Nata l i e ␣Portman ’ , ’ Jerusalem ’ , ’ 1981 ’ )

,19 ( ’ Rachel ␣Weiss ’ , ’ London ’ , ’ 1971 ’ ) , ( ’ S c a r l e t t ␣Johansson ’ , ’New␣York ’ , ’ 1984 ’ ) ]20

99

2122 class Example (wx . Frame) :2324 def __init__( s e l f , * args , **kw) :25 super (Example , s e l f ) . __init__(* args , **kw)2627 s e l f . In i tUI ( )2829 def In i tUI ( s e l f ) :3031 hbox = wx . BoxSizer (wx .HORIZONTAL)32 panel = wx . Panel ( s e l f )3334 s e l f . l i s t = wx . L i s tC t r l ( panel , wx .ID_ANY, s t y l e=wx .LC_REPORT)35 s e l f . l i s t . InsertColumn (0 , ’name ’ , width=140)36 s e l f . l i s t . InsertColumn (1 , ’ p l ace ’ , width=130)37 s e l f . l i s t . InsertColumn (2 , ’ year ’ , wx .LIST_FORMAT_RIGHT, 90)3839 idx = 04041 for i in data :4243 index = s e l f . l i s t . In s e r t I t em ( idx , i [ 0 ] )44 s e l f . l i s t . SetItem ( index , 1 , i [ 1 ] )45 s e l f . l i s t . SetItem ( index , 2 , i [ 2 ] )46 idx += 14748 hbox .Add( s e l f . l i s t , 1 , wx .EXPAND)49 panel . S e tS i z e r ( hbox )5051 s e l f . S e tT i t l e ( ’ Ac t r e s s e s ’ )52 s e l f . Centre ( )535455 def main ( ) :5657 app = wx .App( )58 ex = Example (None )59 ex . Show ( )60 app . MainLoop ( )616263 i f __name__ == ’__main__ ’ :64 main ( )

Пример кода отображает данные об актрисах в wx.ListCtrl.

self.list = wx.ListCtrl(panel, wx.ID_ANY, style=wx.LC_REPORT)

Мы создаем wx.ListCtrl со стилем wx.LC_REPORT.

self.list.InsertColumn(0, ’name’, width=140)

self.list.InsertColumn(1, ’place’, width=130)

self.list.InsertColumn(2, ’year’, wx.LIST_FORMAT_RIGHT, 90)

Мы вставляем три столбца. Мы можем указать ширину столбца и формат столбца. Формат по умолчанию- wx.LIST_FORMAT_LEFT.

idx = 0

100

for i in data:

index = self.list.InsertItem(idx, i[0])

self.list.SetItem(index, 1, i[1])

self.list.SetItem(index, 2, i[2])

idx += 1

Мы вставляем данные в wx.ListCtrl, используя два метода. Каждая строка начинается с метода InsertItem().Первый параметр метода указывает номер строки. Метод возвращает индекс строки. Метод SetItem() добавляетданные в последовательные столбцы текущей строки.

8.5.2 Mixins

Mixins - это классы, которые дополнительно расширяют функциональность wx.ListCtrl. Они находятся вмодуле wx.lib.mixins.listctrl. Чтобы их использовать, мы должны наследовать от этих классов.

Есть шесть Mixins:

� wx.ColumnSorterMixin

� wx.ListCtrlAutoWidthMixin

� wx.ListCtrlSelectionManagerMix

� wx.TextEditMixin

� wx.CheckListCtrlMixin

� wx.ListRowHighlighter

wx.ColumnSorterMixin - это миксин, который позволяет сортировать столбцы в представлении отчета. Классwx.ListCtrlAutoWidthMixin автоматически изменяет размер последнего столбца до конца wx.ListCtrl. По умолча-нию последний столбец не занимает всё оставшееся место. Смотрите предыдущий пример. wx.ListCtrlSelectionManagerMixопределяет независимую от платформы политику выбора. wx.TextEditMixin позволяет редактировать текст.wx.CheckListCtrlMixin добавляет флажок для каждой строки. Таким образом, мы можем контролировать стро-ки. Мы можем установить каждую строку для проверки или снятия флажка. wx.ListRowHighlighter обрабаты-вает автоматическое выделение фона для альтернативных строк в a wx.ListCtrl.

8.5.3 wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin

Следующий код показывает, как мы можем использовать wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin:

Листинг 53: autowidth.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we use wx . l i b . mixins . l i s t c t r l . ListCtr lAutoWidthMixin8 with a wx . ListBox .910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : May 201813 """1415 import wx

101

16 import wx . l i b . mixins . l i s t c t r l1718 data = [ ( ’ J e s s i c a ␣Alba ’ , ’Pomona ’ , ’ 1981 ’ ) , ( ’ S igourney ␣Weaver ’ , ’New␣York ’ , ’ 1949 ’ ) ,19 ( ’ Angel ina ␣ J o l i e ’ , ’ Los␣Angeles ’ , ’ 1975 ’ ) , ( ’ Nata l i e ␣Portman ’ , ’ Jerusalem ’ , ’ 1981 ’ )

,20 ( ’ Rachel ␣Weiss ’ , ’ London ’ , ’ 1971 ’ ) , ( ’ S c a r l e t t ␣Johansson ’ , ’New␣York ’ , ’ 1984 ’ ) ]212223 class AutoWidthListCtrl (wx . L i s tCt r l , wx . l i b . mixins . l i s t c t r l . ListCtrlAutoWidthMixin ) :2425 def __init__( s e l f , parent , * args , **kw) :26 wx . L i s tC t r l . __init__( s e l f , parent , wx .ID_ANY, s t y l e=wx .LC_REPORT)27 wx . l i b . mixins . l i s t c t r l . ListCtrlAutoWidthMixin . __init__( s e l f )282930 class Example (wx . Frame) :3132 def __init__( s e l f , * args , **kw) :33 super (Example , s e l f ) . __init__(* args , **kw)3435 s e l f . In i tUI ( )3637 def In i tUI ( s e l f ) :3839 hbox = wx . BoxSizer (wx .HORIZONTAL)4041 panel = wx . Panel ( s e l f )4243 s e l f . l i s t = AutoWidthListCtrl ( panel )44 s e l f . l i s t . InsertColumn (0 , ’name ’ , width=140)45 s e l f . l i s t . InsertColumn (1 , ’ p l ace ’ , width=130)46 s e l f . l i s t . InsertColumn (2 , ’ year ’ , wx .LIST_FORMAT_RIGHT, 90)4748 idx = 04950 for i in data :5152 index = s e l f . l i s t . In s e r t I t em ( idx , i [ 0 ] )53 s e l f . l i s t . SetItem ( index , 1 , i [ 1 ] )54 s e l f . l i s t . SetItem ( index , 2 , i [ 2 ] )55 idx += 15657 hbox .Add( s e l f . l i s t , 1 , wx .EXPAND)58 panel . S e tS i z e r ( hbox )5960 s e l f . S e tT i t l e ( ’ Ac t r e s s e s ’ )61 s e l f . Centre ( )626364 def main ( ) :6566 app = wx .App( )67 ex = Example (None )68 ex . Show ( )69 app . MainLoop ( )707172 i f __name__ == ’__main__ ’ :73 main ( )

Мы немного изменили предыдущий пример.

102

import wx.lib.mixins.listctrl

Здесь мы импортируем модуль mixin.

class AutoWidthListCtrl(wx.ListCtrl, wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin):

def __init__(self, parent, *args, **kw):

wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT)

wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.__init__(self)

Мы создаем новый классAutoWidthListCtrl. Этот класс наследуется от wx.ListCtrl и wx.lib.mixins.listctrl.ListCtrlAutoWidthMixin.Это называется множественным наследованием. Последний столбец автоматически изменит размер, чтобы за-нять оставшуюся ширину wx.ListCtrl.

8.5.4 wx.lib.mixins.listctrl.ColumnSorterMixin

В следующем примере создаются сортируемые столбцы. Если щелкнуть заголовок столбца, соответствую-щие строки в столбце будут отсортированы.

Листинг 54: sorted.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e s o r t a b l e columns wi th8 wx . l i b . mixins . l i s t c t r l . ColumnSorterMixin910 author : Jan Bodnar11 web s i t e : www. ze t code . com12 l a s t modi f ied : May 201813 """1415 import wx16 import wx . l i b . mixins . l i s t c t r l1718 a c t r e s s e s = {19 1 : ( ’ J e s s i c a ␣Alba ’ , ’Pomona ’ , ’ 1981 ’ ) ,20 2 : ( ’ S igourney ␣Weaver ’ , ’New␣York ’ , ’ 1949 ’ ) ,21 3 : ( ’ Angel ina ␣ J o l i e ’ , ’ Los␣Angeles ’ , ’ 1975 ’ ) ,22 4 : ( ’ Nata l i e ␣Portman ’ , ’ Jerusalem ’ , ’ 1981 ’ ) ,23 5 : ( ’ Rachel ␣Weiss ’ , ’ London ’ , ’ 1971 ’ ) ,24 6 : ( ’ S c a r l e t t ␣Johansson ’ , ’New␣York ’ , ’ 1984 ’ )25 }262728 class So r t edL i s tCt r l (wx . L i s tCt r l , wx . l i b . mixins . l i s t c t r l . ColumnSorterMixin ) :2930 def __init__( s e l f , parent ) :3132 wx . L i s tC t r l . __init__( s e l f , parent , wx .ID_ANY, s t y l e=wx .LC_REPORT)33 wx . l i b . mixins . l i s t c t r l . ColumnSorterMixin . __init__( s e l f , len ( a c t r e s s e s ) )34 s e l f . itemDataMap = a c t r e s s e s3536 def GetLi s tCtr l ( s e l f ) :37 return s e l f3839

103

40 class Example (wx . Frame) :4142 def __init__( s e l f , * args , **kw) :43 super (Example , s e l f ) . __init__(* args , **kw)4445 s e l f . In i tUI ( )4647 def In i tUI ( s e l f ) :4849 hbox = wx . BoxSizer (wx .HORIZONTAL)50 panel = wx . Panel ( s e l f )5152 s e l f . l i s t = Sor t edL i s tCt r l ( panel )53 s e l f . l i s t . InsertColumn (0 , ’name ’ , width=140)54 s e l f . l i s t . InsertColumn (1 , ’ p l ace ’ , width=130)55 s e l f . l i s t . InsertColumn (2 , ’ year ’ , wx .LIST_FORMAT_RIGHT, 90)5657 items = a c t r e s s e s . i tems ( )5859 idx = 06061 for key , data in i tems :6263 index = s e l f . l i s t . In s e r t I t em ( idx , data [ 0 ] )64 s e l f . l i s t . SetItem ( index , 1 , data [ 1 ] )65 s e l f . l i s t . SetItem ( index , 2 , data [ 2 ] )66 s e l f . l i s t . SetItemData ( index , key )67 idx += 16869 hbox .Add( s e l f . l i s t , 1 , wx .EXPAND)70 panel . S e tS i z e r ( hbox )7172 s e l f . S e tT i t l e ( ’ Ac t r e s s e s ’ )73 s e l f . Centre ( )747576 def main ( ) :7778 app = wx .App( )79 ex = Example (None )80 ex . Show ( )81 app . MainLoop ( )828384 i f __name__ == ’__main__ ’ :85 main ( )

Мы снова будем использовать пример с актрисами.

wx.lib.mixins.listctrl.ColumnSorterMixin.__init__(self, len(actresses))

Wx.lib.mixins.listctrl.ColumnSorterMixin принимает один аргумент: количество столбцов для сортировки.

self.itemDataMap = actresses

Мы должны мапировать наши данные на элемент управления списком с атрибутом itemDataMap. Данныедолжны быть типа словарь.

def GetListCtrl(self):

return self

104

Мы должны создать метод GetListCtrl(). Этот метод возвращает виджет wx.ListCtrl, который будет отсор-тирован.

self.list.SetItemData(index, key)

Мы должны ассоциировать каждую строку со специальным индексом. Это делается с помощью методаSetItemData.

8.5.5 wx.lib.mixins.listctrl.CheckListCtrl

В элемент управления списком может быть помещен флажок. В wxPython мы можем использовать дляэтого wx.lib.mixins.listctrl.CheckListCtrl.

Листинг 55: repository.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we c rea t e a check l i s t c on t r o l w idge t .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : May 201812 """1314 import wx15 from wx . l i b . mixins . l i s t c t r l import CheckListCtrlMixin , ListCtrlAutoWidthMixin1617 packages = [ ( ’ abiword ’ , ’ 5 . 8M’ , ’ base ’ ) , ( ’ ad i e ’ , ’ 145k ’ , ’ base ’ ) ,18 ( ’ a i r s n o r t ’ , ’ 71k ’ , ’ base ’ ) , ( ’ ara ’ , ’ 717k ’ , ’ base ’ ) , ( ’ arc ’ , ’ 139k ’ , ’ base ’ ) ,19 ( ’ asc ’ , ’ 5 . 8M’ , ’ base ’ ) , ( ’ a s c i i ’ , ’ 74k ’ , ’ base ’ ) , ( ’ ash ’ , ’ 74k ’ , ’ base ’ ) ]2021 class CheckListCtr l (wx . L i s tCt r l , CheckListCtrlMixin , ListCtrlAutoWidthMixin ) :2223 def __init__( s e l f , parent ) :24 wx . L i s tC t r l . __init__( s e l f , parent , wx .ID_ANY, s t y l e=wx .LC_REPORT |25 wx .SUNKEN_BORDER)26 CheckListCtr lMixin . __init__( s e l f )27 ListCtrlAutoWidthMixin . __init__( s e l f )282930 class Example (wx . Frame) :3132 def __init__( s e l f , * args , **kw) :33 super (Example , s e l f ) . __init__(* args , **kw)3435 panel = wx . Panel ( s e l f )3637 vbox = wx . BoxSizer (wx .VERTICAL)38 hbox = wx . BoxSizer (wx .HORIZONTAL)3940 l e f tPan e l = wx . Panel ( panel )41 r i ghtPane l = wx . Panel ( panel )4243 s e l f . l og = wx . TextCtrl ( r ightPane l , s t y l e=wx .TE_MULTILINE|wx .TE_READONLY)44 s e l f . l i s t = CheckListCtr l ( r i ghtPane l )45 s e l f . l i s t . InsertColumn (0 , ’ Package ’ , width=140)46 s e l f . l i s t . InsertColumn (1 , ’ S i z e ’ )

105

47 s e l f . l i s t . InsertColumn (2 , ’ Repos i tory ’ )4849 idx = 05051 for i in packages :5253 index = s e l f . l i s t . In s e r t I t em ( idx , i [ 0 ] )54 s e l f . l i s t . SetItem ( index , 1 , i [ 1 ] )55 s e l f . l i s t . SetItem ( index , 2 , i [ 2 ] )56 idx += 15758 vbox2 = wx . BoxSizer (wx .VERTICAL)5960 se lBtn = wx . Button ( l e f tPane l , l a b e l=’ S e l e c t ␣Al l ’ )61 desBtn = wx . Button ( l e f tPane l , l a b e l=’ Dese l e c t ␣Al l ’ )62 appBtn = wx . Button ( l e f tPane l , l a b e l=’Apply ’ )6364 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnSelectAl l , id=se lBtn . GetId ( ) )65 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnDeselectAll , id=desBtn . GetId ( ) )66 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnApply , id=appBtn . GetId ( ) )6768 vbox2 .Add( selBtn , 0 , wx .TOP|wx .BOTTOM, 5)69 vbox2 .Add( desBtn , 0 , wx .BOTTOM, 5)70 vbox2 .Add( appBtn )7172 l e f tPan e l . S e tS i z e r ( vbox2 )7374 vbox .Add( s e l f . l i s t , 4 , wx .EXPAND | wx .TOP, 3)75 vbox .Add((−1 , 10) )76 vbox .Add( s e l f . log , 1 , wx .EXPAND)77 vbox .Add((−1 , 10) )7879 r i ghtPane l . S e tS i z e r ( vbox )8081 hbox .Add( l e f tPane l , 0 , wx .EXPAND | wx .RIGHT, 5)82 hbox .Add( r ightPane l , 1 , wx .EXPAND)83 hbox .Add( ( 3 , −1) )8485 panel . S e tS i z e r ( hbox )8687 s e l f . S e tT i t l e ( ’ Repos i tory ’ )88 s e l f . Centre ( )8990 def OnSelectAl l ( s e l f , event ) :9192 num = s e l f . l i s t . GetItemCount ( )93 for i in range (num) :94 s e l f . l i s t . CheckItem ( i )9596 def OnDese lectAl l ( s e l f , event ) :9798 num = s e l f . l i s t . GetItemCount ( )99 for i in range (num) :100 s e l f . l i s t . CheckItem ( i , Fa l se )101102 def OnApply ( s e l f , event ) :103104 num = s e l f . l i s t . GetItemCount ( )105106 for i in range (num) :107108 i f i == 0 : s e l f . l og . Clear ( )

106

109110 i f s e l f . l i s t . IsChecked ( i ) :111 s e l f . l og . AppendText ( s e l f . l i s t . GetItemText ( i ) + ’ \n ’ )112113114 def main ( ) :115116 app = wx .App( )117 ex = Example (None )118 ex . Show ( )119 app . MainLoop ( )120121122 i f __name__ == ’__main__ ’ :123 main ( )

В этом примере создается пользовательский интерфейс хранилища с помощью wx.lib.mixins.listctrl.CheckListCtrl.

class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):

class CheckListCtrl(wx.ListCtrl, CheckListCtrlMixin, ListCtrlAutoWidthMixin):

def __init__(self, parent):

wx.ListCtrl.__init__(self, parent, wx.ID_ANY, style=wx.LC_REPORT |

wx.SUNKEN_BORDER)

CheckListCtrlMixin.__init__(self)

ListCtrlAutoWidthMixin.__init__(self)

Мы наследуем от трех разных классов.

def OnSelectAll(self, event):

num = self.list.GetItemCount()

for i in range(num):

self.list.CheckItem(i)

Метод OnSelectAll() выбирает все флажки. GetItemCount() определяет количество элементов, а методCheckItem() отмечает текущий флажок.

Рис. 46: Repository.

В этой части руководства по wxPython мы рассмотрели несколько расширенных виджетов, включая wx.ListBox,wx.html.HtmlWindow и wx.ListCtrl.

107

9 Drag & drop в wxPython

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

Операция перетаскивания позволяет вам делать сложные вещи интуитивно.

В операциях перетаскивания мы перетаскиваем некоторые данные из источника данных в цель данных.Итак, мы должны иметь:

� Некоторые данные

� Источник данных

� Цель данных

В wxPython у нас есть две предопределенные цели данных: wx.TextDropTarget и wx.FileDropTarget.

9.1 wx.TextDropTarget

wx.TextDropTarget - это предопределенная цель drop для работы с текстовыми данными.

Листинг 56: dragdrop_text.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 In t h i s example , we drag and drop t e x t data .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : May 201812 """1314 from path l i b import Path15 import os16 import wx1718 class MyTextDropTarget (wx . TextDropTarget ) :1920 def __init__( s e l f , object ) :2122 wx . TextDropTarget . __init__( s e l f )23 s e l f . object = object

2425 def OnDropText ( s e l f , x , y , data ) :2627 s e l f . object . In s e r t I t em (0 , data )28 return True293031 class Example (wx . Frame) :3233 def __init__( s e l f , * args , **kw) :34 super (Example , s e l f ) . __init__(* args , **kw)

108

3536 s e l f . In i tUI ( )3738 def In i tUI ( s e l f ) :3940 s p l i t t e r 1 = wx . SplitterWindow ( s e l f , s t y l e=wx .SP_3D)41 s p l i t t e r 2 = wx . SplitterWindow ( s p l i t t e r 1 , s t y l e=wx .SP_3D)4243 home_dir = str ( Path . home ( ) )4445 s e l f . dirWid = wx . Gener i cDirCtr l ( s p l i t t e r 1 , dir=home_dir ,46 s t y l e=wx .DIRCTRL_DIR_ONLY)4748 s e l f . l c 1 = wx . L i s tC t r l ( s p l i t t e r 2 , s t y l e=wx .LC_LIST)49 s e l f . l c 2 = wx . L i s tC t r l ( s p l i t t e r 2 , s t y l e=wx .LC_LIST)5051 dt = MyTextDropTarget ( s e l f . l c 2 )52 s e l f . l c 2 . SetDropTarget ( dt )5354 s e l f . Bind (wx .EVT_LIST_BEGIN_DRAG, s e l f . OnDragInit , id=s e l f . l c 1 . GetId ( ) )5556 t r e e = s e l f . dirWid . GetTreeCtrl ( )5758 s p l i t t e r 2 . S p l i tHo r i z o n t a l l y ( s e l f . l c1 , s e l f . l c2 , 150)59 s p l i t t e r 1 . S p l i t V e r t i c a l l y ( s e l f . dirWid , s p l i t t e r 2 , 200)6061 s e l f . Bind (wx .EVT_TREE_SEL_CHANGED, s e l f . OnSelect , id=tr e e . GetId ( ) )6263 s e l f . OnSelect (0 )6465 s e l f . S e tT i t l e ( ’Drag␣and␣drop␣ text ’ )66 s e l f . Centre ( )6768 def OnSelect ( s e l f , event ) :6970 l i s t = os . l i s t d i r ( s e l f . dirWid . GetPath ( ) )7172 s e l f . l c 1 . C l earAl l ( )73 s e l f . l c 2 . C l earAl l ( )7475 for i in range ( len ( l i s t ) ) :7677 i f l i s t [ i ] [ 0 ] != ’ . ’ :78 s e l f . l c 1 . In se r t I t em (0 , l i s t [ i ] )7980 def OnDragInit ( s e l f , event ) :8182 text = s e l f . l c 1 . GetItemText ( event . GetIndex ( ) )83 tdo = wx . TextDataObject ( t ex t )84 tds = wx . DropSource ( s e l f . l c 1 )8586 tds . SetData ( tdo )87 tds . DoDragDrop (True )888990 def main ( ) :9192 app = wx .App( )93 ex = Example (None )94 ex . Show ( )95 app . MainLoop ( )96

109

9798 i f __name__ == ’__main__ ’ :99 main ( )

В этом примере мы показываем файловую систему в wx.GenericDirCtrl. Содержимое выбранного каталогаотображается в правом верхнем списке управления. Имена файлов можно перетащить в правый нижний элементуправления списком.

def OnDropText(self, x, y, data):

self.object.InsertItem(0, data)

return True

Когда мы помещаем текстовые данные на цель, они вставляются в элемент управления списком с помощьюметода InsertItem().

dt = MyTextDropTarget(self.lc2)

self.lc2.SetDropTarget(dt)

Цель операции drop создана. Мы устанавливаем её на второй элемент управления списком с помощьюметода SetDropTarget().

self.Bind(wx.EVT_LIST_BEGIN_DRAG, self.OnDragInit, id=self.lc1.GetId())

Когда начинается операция перетаскивания, вызывается метод OnDragInit().

def OnDragInit(self, event):

text = self.lc1.GetItemText(event.GetIndex())

tdo = wx.TextDataObject(text)

tds = wx.DropSource(self.lc1)

...

В методе OnDragInit() мы создаем объект wx.TextDataObject, который содержит наши текстовые данные.Источник Drag&Drop создается из первого элемента управления списком.

tds.SetData(tdo)

tds.DoDragDrop(True)

Мы устанавливаем данные для источника Drag&Drop с помощью SetData() и инициируем операцию пере-таскивания с помощью DoDragDrop().

9.2 wx.FileDropTarget

wx.FileDropTarget - это целевой объект, который принимает файлы, которые перетаскиваются из файловогоменеджера.

Листинг 57: dragdrop_file.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l

110

67 In t h i s example , we drag and drop f i l e s .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : May 201812 """1314 import wx1516 class FileDrop (wx . Fi leDropTarget ) :1718 def __init__( s e l f , window) :1920 wx . Fi leDropTarget . __init__( s e l f )21 s e l f . window = window2223 def OnDropFiles ( s e l f , x , y , f i l enames ) :2425 for name in f i l enames :2627 try :28 f i l e = open(name , ’ r ’ )29 text = f i l e . read ( )30 s e l f . window . WriteText ( t ex t )3132 except IOError as e r r o r :3334 msg = "Error ␣ opening ␣ f i l e \n␣{}" . format ( str ( e r r o r ) )35 dlg = wx . MessageDialog (None , msg)36 dlg . ShowModal ( )3738 return False3940 except UnicodeDecodeError as e r r o r :4142 msg = "Cannot␣open␣non␣ a s c i i ␣ f i l e s \n␣{}" . format ( str ( e r r o r ) )43 dlg = wx . MessageDialog (None , msg)44 dlg . ShowModal ( )4546 return False4748 f ina l ly :4950 f i l e . c l o s e ( )5152 return True5354 class Example (wx . Frame) :5556 def __init__( s e l f , * args , **kw) :57 super (Example , s e l f ) . __init__(* args , **kw)5859 s e l f . In i tUI ( )6061 def In i tUI ( s e l f ) :6263 s e l f . t ex t = wx . TextCtrl ( s e l f , s t y l e = wx .TE_MULTILINE)64 dt = FileDrop ( s e l f . t ex t )6566 s e l f . t ex t . SetDropTarget ( dt )67

111

68 s e l f . S e tT i t l e ( ’ F i l e ␣drag␣and␣drop ’ )69 s e l f . Centre ( )707172 def main ( ) :7374 app = wx .App( )75 ex = Example (None )76 ex . Show ( )77 app . MainLoop ( )787980 i f __name__ == ’__main__ ’ :81 main ( )

В примере создается простой wx.TextCtrl. Мы можем перетащить текстовые файлы в элемент управленияиз файлового менеджера.

def OnDropFiles(self, x, y, filenames):

for name in filenames:

...

Мы можем перетащить несколько файлов одновременно.

try:

file = open(name, ’r’)

text = file.read()

self.window.WriteText(text)

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

except IOError as error:

msg = "Error opening file\n {}".format(str(error))

dlg = wx.MessageDialog(None, msg)

dlg.ShowModal()

return False

В случае ошибки ввода/вывода мы показываем диалоговое окно сообщения и завершаем операцию.

self.text = wx.TextCtrl(self, style = wx.TE_MULTILINE)

dt = FileDrop(self.text)

self.text.SetDropTarget(dt)

Wx.TextCtrl является целью перетаскивания.

В этой главе мы работали с операциями перетаскивания в wxPython.

10 Графика в wxPython

GDI (Graphics Device Interface) - это интерфейс для работы с графикой. Он используется для взаимодей-ствия с графическими устройствами, такими как монитор, принтер или файл. GDI позволяет программистам

112

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

С точки зрения программиста, GDI - это группа классов и методов для работы с графикой. GDI состоитиз 2D векторной графики, шрифтов и изображений.

Рис. 47: Структура GDI

Чтобы начать рисовать графику, мы должны создать объект контекста устройства (DC). В wxPythonконтекст устройства называется wx.DC. Документация определяет wx.DC как контекст устройства, на которомможно рисовать графику и текст. Он представляет большое количество разных устройств в общем виде. Один итот же фрагмент кода может записывать изображение на различные виды устройств. Будь то экран или принтер.Wx.DC не предназначен для непосредственного использования. Вместо этого программист должен выбратьодин из производных классов. Каждый производный класс предназначен для использования в определенныхусловиях.

10.1 Классы, наследуемые от wx.DC

� wxBufferedDC

� wxBufferedPaintDC

� wxPostScriptDC

� wxMemoryDC

� wxPrinterDC

� wxScreenDC

� wxClientDC

� wxPaintDC

� wxWindowDC

Wx.ScreenDC используется для рисования в любом месте экрана. Wx.WindowDC используется, если мыхотим рисовать на всем окне (только для Windows). Оно включает в себя украшения окна. Wx.ClientDC ис-пользуется для рисования в клиентской области окна. Клиентская область - это область окна без декораций(заголовок и рамка). Wx.PaintDC также используется для рисования в клиентской области. Но есть одно отли-чие между wx.PaintDC и wx.ClientDC. Wx.PaintDC следует использовать только из wx.PaintEvent. Wx.ClientDCне должен использоваться из wx.PaintEvent. Wx.MemoryDC используется для рисования графики на растровомизображении.Wx.PostScriptDC используется для записи в файлы PostScript на любой платформе.Wx.PrinterDCиспользуется для доступа к принтеру (только для Windows).

10.2 Рисование простой линии

Наш первый пример нарисует простую линию в клиентской области окна.

113

DrawLine(self, x1, y1, x2, y2)

Этот метод рисует линию от первой точки ко второй; не включая вторую точку.

Листинг 58: draw_line.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws a l i n e on the8 frame window a f t e r a wh i l e .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 wx . Ca l lLate r (2000 , s e l f . DrawLine )2728 s e l f . S e tT i t l e ( "Line " )29 s e l f . Centre ( )3031 def DrawLine ( s e l f ) :3233 dc = wx . ClientDC ( s e l f )34 dc . DrawLine (50 , 60 , 190 , 60)3536 def main ( ) :3738 app = wx .App( )39 ex = Example (None )40 ex . Show ( )41 app . MainLoop ( )424344 i f __name__ == ’__main__ ’ :45 main ( )

Через две секунды после запуска программы, мы рисуем линию на окне кадра.

wx.FutureCall(2000, self.DrawLine)

Мы вызываем метод DrawLine() после того, как окно было создано. Мы делаем это потому, что когда окносоздаётся, оно рисуется с нуля. Все наши рисунки будут потеряны. Мы можем начать рисовать после того, какокно уже было создано. По этой причине мы вызываем метод wx.FutureCall().

def DrawLine(self):

114

dc = wx.ClientDC(self)

dc.DrawLine(50, 60, 190, 60)

Мы создаем контекст устройства wx.ClientDC. Единственный параметр - это окно, в котором мы хотимрисовать. В нашем случае это self, которое является ссылкой на наш виджет wx.Frame. Мы вызываем методDrawLine() контекста устройства. Этот вызов фактически рисует линию в нашем окне.

Очень важно понимать следующее поведение. Если мы изменим размер окна, линия исчезнет. Почемуэто происходит? Каждое окно перерисовывается, если оно изменено. Оно также перерисовывается, если егомаксимизировали. Окно также перерисовывается, если мы накрываем его другим окном и затем открываем.Окно возвращается к его состоянию по умолчанию, и наша линия теряется. Мы должны рисовать линию каждыйраз, когда размер окна изменяется. Решением является wx.PaintEvent. Это событие срабатывает каждый раз,когда окно перерисовывается. Мы нарисуем нашу линию внутри метода, который будет привязан к событиюрисования.

В следующем примере показано, как это делается.

Листинг 59: draw_line2.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws a l i n e in8 a pa in t event .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2728 s e l f . S e tT i t l e ( "Line " )29 s e l f . Centre ( )3031 def OnPaint ( s e l f , e ) :3233 dc = wx . PaintDC( s e l f )34 dc . DrawLine (50 , 60 , 190 , 60)353637 def main ( ) :3839 app = wx .App( )40 ex = Example (None )41 ex . Show ( )42 app . MainLoop ( )43

115

4445 i f __name__ == ’__main__ ’ :46 main ( )

Мы рисуем ту же линию. На этот раз в ответ на событие перерисовывания.

self.Bind(wx.EVT_PAINT, self.OnPaint)

Здесь мы связываем метод OnPaint с событием wx.PaintEvent. Это означает, что каждый раз, когда нашеокно перерисовывается, мы вызываем метод OnPaint(). Теперь линия не исчезнет, если мы изменим размерынашего окна (закройте его, разверните его).

dc = wx.PaintDC(self)

Обратите внимание, что на этот раз мы использовали контекст устройства wx.PaintDC.

Рис. 48: Рисование линии

10.3 Компьютерная графика

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

Оба типа компьютерной графики имеют свои преимущества и недостатки. Преимущества векторной гра-фики перед растровой:

� Меньший размер

� Возможность масштабирования до бесконечности

� Перемещение, масштабирование, заполнение или вращение не ухудшают качество изображения

10.3.1 Типы примитивов

Ниже приведен частичный список графических примитивов.

� points

� lines

116

� polylines

� polygons

� сircles

� ellipses

� splines

10.3.2 Атрибуты контекста устройства

Контекст устройства содержит несколько атрибутов, таких как кисть, перо или шрифт. wx.Brush - инстру-мент для рисования при заполнении областей. Используется для рисования фона фигур. У него есть цвет истиль. wx.Pen используется для рисования контуров фигур. У него есть цвет, ширина и стиль. wx.Font - этообъект, который определяет внешний вид текста.

10.3.3 Основные элементы

Далее мы вводим несколько элементарных объектов: цвета, кисти, ручки, объединения, шапки и градиенты.

Цвета Цвет - это объект, представляющий комбинацию значений интенсивности красного, зеленого и синего(RGB). Допустимые значения RGB находятся в диапазоне от 0 до 255. Существует три способа установки цветов.Мы можем создать объект wx.Colour, использовать предопределенное имя цвета или использовать строку шест-надцатеричного значения. wx.Colour(0,0,255), «СИНИЙ», «# 0000FF». Эти три обозначения дают одинаковыйцвет.

Идеальный инструмент для работы с цветами можно найти на сайте colorjack.com. Или мы можем исполь-зовать такой инструмент, как Gimp.

У нас также есть список предопределенных имен цветов, которые мы можем использовать в наших про-граммах.

Таблица 3: таблица стандартных цветов

AQUAMARINE BLACK BLUE BLUE VIOLET BROWNCADET BLUE CORAL CORNFLOWER

BLUECYAN DARK GREY

DARK GREEN DARK OLIVEGREEN

DARK ORCHID DARK SLATEBLUE

DARK SLATEGREY

DARKTURQUOISE

DIM GREY FIREBRICK FOREST GREEN GOLD

GOLDENROD GREY GREEN GREEN YELLOW INDIAN REDKHAKI LIGHT BLUE LIGHT GREY LIGHT STEEL

BLUELIME GREEN

MAGENTA MAROON MEDIUMAQUAMARINE

MEDIUM BLUE MEDIUMFOREST GREEN

MEDIUMGOLDENROD

MEDIUMORCHID

MEDIUM SEAGREEN

MEDIUM SLATEBLUE

MEDIUM SPRINGGREEN

MEDIUMTURQUOISE

MEDIUM VIOLETRED

MIDNIGHT BLUE NAVY ORANGE

ORANGE RED ORCHID PALE GREEN PINK PLUMPURPLE RED SALMON SEA GREEN SIENNASKY BLUE SLATE BLUE SPRING GREEN STEEL BLUE TANTHISTLE TURQUOISE VIOLET VIOLET RED WHEATWHITE YELLOW YELLOW GREEN

В следующем примере используются несколько значений цвета.

117

Листинг 60: colours.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws nine co loured r e c t an g l e s8 on the window .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2728 s e l f . S e tT i t l e ( "Colours " )29 s e l f . Centre ( )303132 def OnPaint ( s e l f , e ) :3334 dc = wx . PaintDC( s e l f )35 dc . SetPen (wx . Pen( ’#d4d4d4 ’ ) )3637 dc . SetBrush (wx . Brush ( ’#c56c00 ’ ) )38 dc . DrawRectangle (10 , 15 , 90 , 60)3940 dc . SetBrush (wx . Brush ( ’#1ac500 ’ ) )41 dc . DrawRectangle (130 , 15 , 90 , 60)4243 dc . SetBrush (wx . Brush ( ’#539e47 ’ ) )44 dc . DrawRectangle (250 , 15 , 90 , 60)4546 dc . SetBrush (wx . Brush ( ’#004 f c5 ’ ) )47 dc . DrawRectangle (10 , 105 , 90 , 60)4849 dc . SetBrush (wx . Brush ( ’#c50024 ’ ) )50 dc . DrawRectangle (130 , 105 , 90 , 60)5152 dc . SetBrush (wx . Brush ( ’#9e4757 ’ ) )53 dc . DrawRectangle (250 , 105 , 90 , 60)5455 dc . SetBrush (wx . Brush ( ’#5f3b00 ’ ) )56 dc . DrawRectangle (10 , 195 , 90 , 60)5758 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ ) )59 dc . DrawRectangle (130 , 195 , 90 , 60)60

118

61 dc . SetBrush (wx . Brush ( ’#785 f36 ’ ) )62 dc . DrawRectangle (250 , 195 , 90 , 60)636465 def main ( ) :6667 app = wx .App( )68 ex = Example (None )69 ex . Show ( )70 app . MainLoop ( )717273 i f __name__ == ’__main__ ’ :74 main ( )

Мы рисуем девять прямоугольников и заливаем их разными цветами.

dc.SetBrush(wx.Brush(’#c56c00’))

dc.DrawRectangle(10, 15, 90, 60)

Мы указываем цвет кисти в шестнадцатеричном формате. Кисть - это фоновая заливка фигуры. Затем мырисуем прямоугольник с помощью метода DrawRectangle().

Рис. 49: Цвета

wx.Pen Перо - элементарный графический объект. Он используется для рисования линий, кривых и контуровпрямоугольников, эллипсов, многоугольников или других фигур.

wx.Pen(wx.Colour colour, width=1, style=wx.SOLID)

Конструктор wx.Pen имеет три параметра: цвет, ширина и стиль. Ниже приведен список возможных стилейпера:

� wx.SOLID

� wx.DOT

� wx.LONG_DASH

� wx.SHORT_DASH

119

� wx.DOT_DASH

� wx.TRANSPARENT

Листинг 61: pens.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws s i x r e c t an g l e s wi th d i f f e r e n t pens .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx1516 class Example (wx . Frame) :1718 def __init__( s e l f , * args , **kw) :19 super (Example , s e l f ) . __init__(* args , **kw)2021 s e l f . In i tUI ( )2223 def In i tUI ( s e l f ) :2425 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2627 s e l f . S e tT i t l e ( "Pens" )28 s e l f . Centre ( )2930 def OnPaint ( s e l f , event ) :31 dc = wx . PaintDC( s e l f )3233 dc . SetPen (wx . Pen( ’#4c4c4c ’ , 1 , wx . SOLID) )34 dc . DrawRectangle (10 , 15 , 90 , 60)3536 dc . SetPen (wx . Pen( ’#4c4c4c ’ , 1 , wx .DOT) )37 dc . DrawRectangle (130 , 15 , 90 , 60)3839 dc . SetPen (wx . Pen( ’#4c4c4c ’ , 1 , wx .LONG_DASH) )40 dc . DrawRectangle (250 , 15 , 90 , 60)4142 dc . SetPen (wx . Pen( ’#4c4c4c ’ , 1 , wx .SHORT_DASH) )43 dc . DrawRectangle (10 , 105 , 90 , 60)4445 dc . SetPen (wx . Pen( ’#4c4c4c ’ , 1 , wx .DOT_DASH) )46 dc . DrawRectangle (130 , 105 , 90 , 60)4748 dc . SetPen (wx . Pen( ’#4c4c4c ’ , 1 , wx .TRANSPARENT) )49 dc . DrawRectangle (250 , 105 , 90 , 60)505152 def main ( ) :5354 app = wx .App( )55 ex = Example (None )56 ex . Show ( )57 app . MainLoop ( )

120

585960 i f __name__ == ’__main__ ’ :61 main ( )

Если мы не указываем пользовательскую кисть, используется кисть по умолчанию. Кистью по умолчаниюявляется wx.WHITE_BRUSH. Периметр прямоугольников нарисован пером. Последний не имеет границы. Онопрозрачна, то есть не видна.

Рис. 50: Цвета

Соединения и завершения Объект пера имеет два дополнительных параметра: соединение и завершение.Соединение определяет, как будут нарисованы соединения между линиями. Стиль соединения имеет следующиезначения:

� wx.JOIN_MITER

� wx.JOIN_BEVEL

� wx.JOIN_ROUND

При использовании wx.JOIN_MITER внешние края линий расширяются. Они встречаются под углом, иэта область заполняется. При wx.JOIN_BEVEL заполняется треугольная выемка между двумя линиями. Приwx.JOIN_ROUND заполняется дуга окружности между двумя линиями. Значением по умолчанию являетсяwx.JOIN_ROUND.

Завершение определяет, как будут нарисованы пером концы линий. Варианты:

� wx.CAP_ROUND

� wx.CAP_PROJECTING

� wx.CAP_BUTT

Wx.CAP_ROUND рисует закругленные концы. wx.CAP_PROJECTING и wx.CAP_BUTT заканчиваютлинии рисуя квадрат. Разница между ними заключается в том, что wx.CAP_PROJECTING будет выходитьза пределы конечной точки на половину ширины линии. Wx.CAP_ROUND также расширяется за пределыконечной точки.

Листинг 62: joins_caps.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l

121

67 This program draws uses d i f f e r e n t j o i n s8 and caps in drawing .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2728 s e l f . S e tT i t l e ( " Jo ins ␣and␣ caps " )29 s e l f . Centre ( )3031 def OnPaint ( s e l f , e ) :3233 dc = wx . PaintDC( s e l f )3435 pen = wx . Pen( ’#4c4c4c ’ , 10 , wx . SOLID)3637 pen . SetJo in (wx .JOIN_MITER)38 dc . SetPen ( pen )39 dc . DrawRectangle (15 , 15 , 80 , 50)4041 pen . SetJo in (wx .JOIN_BEVEL)42 dc . SetPen ( pen )43 dc . DrawRectangle (125 , 15 , 80 , 50)4445 pen . SetJo in (wx .JOIN_ROUND)46 dc . SetPen ( pen )47 dc . DrawRectangle (235 , 15 , 80 , 50)4849 pen . SetCap (wx .CAP_BUTT)50 dc . SetPen ( pen )51 dc . DrawLine (30 , 150 , 150 , 150)5253 pen . SetCap (wx .CAP_PROJECTING)54 dc . SetPen ( pen )55 dc . DrawLine (30 , 190 , 150 , 190)5657 pen . SetCap (wx .CAP_ROUND)58 dc . SetPen ( pen )59 dc . DrawLine (30 , 230 , 150 , 230)6061 pen2 = wx . Pen( ’#4c4c4c ’ , 1 , wx . SOLID)62 dc . SetPen ( pen2 )63 dc . DrawLine (30 , 130 , 30 , 250)64 dc . DrawLine (150 , 130 , 150 , 250)65 dc . DrawLine (155 , 130 , 155 , 250)6667

122

68 def main ( ) :6970 app = wx .App( )71 ex = Example (None )72 ex . Show ( )73 app . MainLoop ( )747576 i f __name__ == ’__main__ ’ :77 main ( )

pen = wx.Pen(’#4c4c4c’, 10, wx.SOLID)

Чтобы увидеть различные стили соединения и ограничения, нам нужно установить ширину пера больше 1.

dc.DrawLine(150, 130, 150, 250)

dc.DrawLine(155, 130, 155, 250)

Обратите внимание на две окружающие вертикальные линии. Расстояние между ними составляет 5 пиксе-лей. Это ровно половина текущей ширины пера.

Рис. 51: Соединения и завершения

Градиенты В компьютерной графике градиент - это плавное смешение оттенков от светлого до темного илиот одного цвета к другому. В программах 2D-рисования и программах рисования градиенты используются длясоздания красочных фонов и спецэффектов, а также для имитации света и теней.

GradientFillLinear(self, rect, initialColour, destColour, nDirection=RIGHT)

Этот метод заполняет область, указанную прямоугольником, линейным градиентом, начиная с initialColourи постепенно переходя в destColour. Параметр nDirection указывает направление изменения цвета; значение поумолчанию - wx.EAST.

Листинг 63: joins_caps.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws four r e c t an g l e s f i l l e d

123

8 with g r ad i en t s .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2728 s e l f . S e tT i t l e ( "Gradients " )29 s e l f . Centre ( )3031 def OnPaint ( s e l f , event ) :3233 dc = wx . PaintDC( s e l f )3435 dc . Grad i en tF i l lL in ea r ( (20 , 20 , 180 , 40) , ’#f f e c 0 0 ’ , ’#000000 ’ , wx .NORTH)36 dc . Grad i en tF i l lL in ea r ( (20 , 80 , 180 , 40) , ’#f f e c 0 0 ’ , ’#000000 ’ , wx .SOUTH)37 dc . Grad i en tF i l lL in ea r ( (20 , 140 , 180 , 40) , ’#f f e c 0 0 ’ , ’#000000 ’ , wx .EAST)38 dc . Grad i en tF i l lL in ea r ( (20 , 200 , 180 , 40) , ’#f f e c 0 0 ’ , ’#000000 ’ , wx .WEST)394041 def main ( ) :4243 app = wx .App( )44 ex = Example (None )45 ex . Show ( )46 app . MainLoop ( )474849 i f __name__ == ’__main__ ’ :50 main ( )

В этом примере четыре прямоугольника заполнены градиентами.

wx.Brush Кисть - это элементарный графический объект. Он используется для рисования фона графическихфигур, таких как прямоугольники, эллипсы или многоугольники.

wxPython имеет следующие встроенные типы кистей:

� wx.SOLID

� wx.STIPPLE

� wx.BDIAGONAL_HATCH

� wx.CROSSDIAG_HATCH

� wx.FDIAGONAL_HATCH

� wx.CROSS_HATCH

124

Рис. 52: Градиенты

� wx.HORIZONTAL_HATCH

� wx.VERTICAL_HATCH

� wx.TRANSPARENT

Листинг 64: brushes.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws e i g h t r e c t an g l e s f i l l e d8 with d i f f e r e n t brushes .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )27

125

28 s e l f . S e tT i t l e ( "Brushes " )29 s e l f . Centre ( )3031 def OnPaint ( s e l f , e ) :3233 dc = wx . PaintDC( s e l f )3435 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx .CROSS_HATCH) )36 dc . DrawRectangle (10 , 15 , 90 , 60)3738 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx . SOLID) )39 dc . DrawRectangle (130 , 15 , 90 , 60)4041 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx .BDIAGONAL_HATCH) )42 dc . DrawRectangle (250 , 15 , 90 , 60)4344 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx .CROSSDIAG_HATCH) )45 dc . DrawRectangle (10 , 105 , 90 , 60)4647 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx .FDIAGONAL_HATCH) )48 dc . DrawRectangle (130 , 105 , 90 , 60)4950 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx .HORIZONTAL_HATCH) )51 dc . DrawRectangle (250 , 105 , 90 , 60)5253 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx .VERTICAL_HATCH) )54 dc . DrawRectangle (10 , 195 , 90 , 60)5556 dc . SetBrush (wx . Brush ( ’#4c4c4c ’ , wx .TRANSPARENT) )57 dc . DrawRectangle (130 , 195 , 90 , 60)585960 def main ( ) :6162 app = wx .App( )63 ex = Example (None )64 ex . Show ( )65 app . MainLoop ( )666768 i f __name__ == ’__main__ ’ :69 main ( )

Восемь различных встроенных типов кисти были использованы в примере.

Пользовательские шаблоны Мы не ограничены в использовании предопределенных шаблонов. Мы можемлегко создавать наши собственные шаблоны.

Листинг 65: custom_patterns.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws th r ee r e c t an g l e s wi th custom8 brush pa t t e rn s .910 author : Jan Bodnar11 web s i t e : z e t code . com

126

Рис. 53: Кисти

12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2728 s e l f . S e tT i t l e ( "Custom␣ pat t e rns " )29 s e l f . Centre ( )3031 def OnPaint ( s e l f , e ) :3233 dc = wx . PaintDC( s e l f )3435 dc . SetPen (wx . Pen( ’#C7C3C3 ’ ) )3637 brush1 = wx . Brush (wx . Bitmap ( ’ pattern1 . png ’ ) )38 dc . SetBrush ( brush1 )39 dc . DrawRectangle (10 , 15 , 90 , 60)4041 brush2 = wx . Brush (wx . Bitmap ( ’ pattern2 . png ’ ) )42 dc . SetBrush ( brush2 )43 dc . DrawRectangle (130 , 15 , 90 , 60)4445 brush3 = wx . Brush (wx . Bitmap ( ’ pattern3 . png ’ ) )46 dc . SetBrush ( brush3 )47 dc . DrawRectangle (250 , 15 , 90 , 60)484950 def main ( ) :5152 app = wx .App( )

127

53 ex = Example (None )54 ex . Show ( )55 app . MainLoop ( )565758 i f __name__ == ’__main__ ’ :59 main ( )

Мы создали несколько небольших растровых изображений. Эти изображения были созданы в Gimp.

brush1 = wx.Brush(wx.Bitmap(’pattern1.png’))

dc.SetBrush(brush1)

dc.DrawRectangle(10, 15, 90, 60)

Кисть создается из растрового изображения и устанавливается в контексте устройства. Она используетсядля заполнения внутренней части прямоугольника.

Рис. 54: Пользовательские шаблоны

Точки Самый простой геометрический объект - это точка. Это простая точка в окне.

DrawPoint(self, x, y)

Этот метод рисует точку в координатах x, y.

Листинг 66: points.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws one thousand po in t s8 randomly on the window .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx16 import random1718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )24

128

25 def In i tUI ( s e l f ) :2627 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2829 s e l f . S e tT i t l e ( "Points " )30 s e l f . Centre ( )3132 def OnPaint ( s e l f , e ) :3334 dc = wx . PaintDC( s e l f )3536 dc . SetPen (wx . Pen( ’RED’ ) )3738 for i in range (1000) :3940 w, h = s e l f . GetSize ( )41 x = random . rand int (1 , w−1)42 y = random . rand int (1 , h−1)43 dc . DrawPoint (x , y )444546 def main ( ) :4748 app = wx .App( )49 ex = Example (None )50 ex . Show ( )51 app . MainLoop ( )525354 i f __name__ == ’__main__ ’ :55 main ( )

Может быть трудно увидеть одну точку, поэтому мы создаём 1000 точек.

dc.SetPen(wx.Pen(’RED’))

Здесь мы устанавливаем красный цвет пера.

w, h = self.GetSize()

x = random.randint(1, w-1)

Точки распределяются случайным образом по клиентской области окна. Они также распределяются ди-намически. Если мы изменим размер окна, точки будут нарисованы случайным образом по новому размеруклиента. Метод randint(a, b) возвращает случайное целое число в диапазоне [a, b], включая оба конца.

Формы Формы более сложные геометрические объекты. Мы рисуем различные геометрические фигуры вследующем примере.

Листинг 67: shapes.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws var ious shapes on8 the window .9

129

Рис. 55: Рисование точек

10 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2728 s e l f . S e tT i t l e ( "Shapes" )29 s e l f . Centre ( )303132 def OnPaint ( s e l f , e ) :3334 dc = wx . PaintDC( s e l f )35 dc . SetBrush (wx . Brush ( ’#777 ’ ) )36 dc . SetPen (wx . Pen( "#777" ) )3738 dc . DrawEll ipse (20 , 20 , 90 , 60)39 dc . DrawRoundedRectangle (130 , 20 , 90 , 60 , 10)40 dc . DrawArc (240 , 40 , 340 , 40 , 290 , 20)4142 dc . DrawRectangle (20 , 120 , 80 , 50)43 dc . DrawPolygon ( ( ( 130 , 140) , (180 , 170) , (180 , 140) , (220 , 110) , (140 , 100) ) )44 dc . DrawSpline ( ( ( 240 , 170) , (280 , 170) , (285 , 110) , (325 , 110) ) )4546 dc . DrawLines ( ( ( 2 0 , 260) , (100 , 260) , (20 , 210) , (100 , 210) ) )47 dc . DrawCircle (170 , 230 , 35)48 dc . DrawRectangle (250 , 200 , 60 , 60)495051 def main ( ) :5253 app = wx .App( )54 ex = Example (None )55 ex . Show ( )

130

56 app . MainLoop ( )575859 i f __name__ == ’__main__ ’ :60 main ( )

В нашем примере мы нарисовали эллипс, скругленный прямоугольник, дугу, прямоугольник, многоуголь-ник, сплайны, линии, круг и квадрат. Круг - это особый вид эллипса, а квадрат - это особый вид прямоугольника.

Рис. 56: Формы

Регионы Контекст устройства может быть разделен на несколько частей, называемых регионами. Регионможет иметь любую форму, такую как прямоугольник или круг. С помощью операций Union, Intersect, Substractи Xor мы можем создавать сложные области. Регионы используются для обводки, заполнения и обрезки.

Мы можем создать регионы тремя способами. Самый простой способ - создать прямоугольную область.Более сложные регионы могут быть созданы из списка точек, или из растрового изображения.

Прежде чем мы углубимся в регионы, мы сначала сделаем небольшой пример. Мы делим тему на несколькочастей, чтобы ее было легче понять. Хорошая идея - пересмотреть свою школьную математику. Здесь мы можемнайти хорошую статью.

Листинг 68: lines.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program draws var ious shapes on8 the window .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx16 from math import hypot , s in , cos , p i1718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :

131

21 super (Example , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2829 s e l f . S e tT i t l e ( ’ L ines ’ )30 s e l f . Centre ( )3132 def OnPaint ( s e l f , e ) :3334 dc = wx . PaintDC( s e l f )35 size_x , s ize_y = s e l f . GetCl i entS i ze ( )36 dc . SetDeviceOr ig in ( s ize_x /2 , s ize_y /2)3738 rad iu s = hypot ( s ize_x /2 , s ize_y /2)39 ang le = 04041 while ( ang le < 2* pi ) :42 x = rad iu s * cos ( ang le )43 y = rad iu s * s i n ( ang le )44 dc . DrawLine ( ( 0 , 0) , (x , y ) )45 ang le = ang le + 2* pi /360464748 def main ( ) :4950 app = wx .App( )51 ex = Example (None )52 ex . Show ( )53 app . MainLoop ( )545556 i f __name__ == ’__main__ ’ :57 main ( )

В этом примере мы рисуем 360 линий от середины клиентской области. Расстояние между двумя линиямисоставляет 1 градус. Мы создаем интересную фигуру.

import wx

from math import hypot, sin, cos, pi

Нам нужны три математические функции и одна константа из математического модуля.

dc.SetDeviceOrigin(size_x/2, size_y/2)

Метод SetDeviceOrigin() создает новое начало системы координат. Мы помещаем его в середину клиентскойобласти. Перемещая систему координат, мы делаем наш процесс рисования менее сложным.

radius = hypot(size_x/2, size_y/2)

Здесь мы получаем гипотенузу. Это самая длинная линия, которую мы можем нарисовать от серединыклиентской области. Это линия, которая должна быть нарисована от начала координат до угла окна. Такимобразом, большинство линий будут нарисованы не полностью. Перекрывающиеся части не видны. см. Гипоте-нуза.

132

x = radius*cos(angle)

y = radius*sin(angle)

Это параметрические функции. Они используются для поиска точек [x, y] на кривой. Все 360 линий нари-сованы от начала системы координат до точек на окружности.

Рис. 57: Лнии

Обрезка Обрезка ограничивает рисунок определенной областью. Обрезка часто используется для созданияэффектов и повышения производительности приложения. Мы ограничиваем рисование определенным региономс помощью метода SetClippingRegionAsRegion().

В следующем примере мы изменим и улучшим нашу предыдущую программу.

Листинг 69: star.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program demonstrates a c l i p p i n g opera t ion8 when drawing a s t a r o b j e c t .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx16 from math import hypot , s in , cos , p i1718 class Example (wx . Frame) :1920 def __init__( s e l f , * args , **kw) :21 super (Example , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2829 s e l f . S e tT i t l e ( " Star " )30 s e l f . Centre ( )31

133

32 def OnPaint ( s e l f , e ) :3334 dc = wx . PaintDC( s e l f )3536 dc . SetPen (wx . Pen( ’#424242 ’ ) )37 size_x , s ize_y = s e l f . GetCl i entS i ze ( )38 dc . SetDeviceOr ig in ( s ize_x /2 , s ize_y /2)3940 po in t s = ( ( ( 0 , 85) , (75 , 75) , (100 , 10) , (125 , 75) , (200 , 85) ,41 (150 , 125) , (160 , 190) , (100 , 150) , (40 , 190) , (50 , 125) ) )4243 reg i on = wx . Region ( po in t s )44 dc . SetDeviceCl ippingRegion ( r eg i on )4546 rad iu s = hypot ( s ize_x /2 , s ize_y /2)47 ang le = 04849 while ( ang le < 2* pi ) :5051 x = rad iu s * cos ( ang le )52 y = rad iu s * s i n ( ang le )53 dc . DrawLine ( ( 0 , 0) , (x , y ) )54 ang le = ang le + 2* pi /3605556 dc . DestroyCl ippingRegion ( )575859 def main ( ) :6061 app = wx .App( )62 ex = Example (None )63 ex . Show ( )64 app . MainLoop ( )656667 i f __name__ == ’__main__ ’ :68 main ( )

Мы снова рисуем все 360 линий. Но на этот раз, только часть клиентского поля отрисовывается. Область,которой мы ограничиваем наш рисунок, является звёздообразным объектом.

region = wx.Region(points)

dc.SetDeviceClippingRegion(region)

Мы создаем регион из списка точек. Метод SetDeviceClippingRegion() ограничивает рисование указаннымрегионом. В нашем случае это звёздообразный объект.

dc.DestroyClippingRegion()

Мы должны уничтожить регион отсечения.

Операции над регионами Регионы могут быть объединены для создания более сложных форм. Мы можемиспользовать четыре операции над множествами: union, intersect, substract, xor.

В следующем примере показаны все четыре операции в действии.

Листинг 70: region_operations.py

1 #!/ usr / b in /env python3

134

Рис. 58: Обрезка по звезде

2 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program performs s e t opera t i ons on reg ions .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx1516 class Example (wx . Frame) :1718 def __init__( s e l f , * args , **kw) :19 super (Example , s e l f ) . __init__(* args , **kw)2021 s e l f . In i tUI ( )2223 def In i tUI ( s e l f ) :2425 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )2627 s e l f . S e tT i t l e ( "Regions " )28 s e l f . Centre ( )2930 def OnPaint ( s e l f , e ) :3132 dc = wx . PaintDC( s e l f )33 dc . SetPen (wx . Pen( ’#d4d4d4 ’ ) )3435 dc . DrawRectangle (20 , 20 , 50 , 50)36 dc . DrawRectangle (30 , 40 , 50 , 50)3738 dc . SetBrush (wx . Brush ( ’# f f f f f f ’ ) )39 dc . DrawRectangle (100 , 20 , 50 , 50)40 dc . DrawRectangle (110 , 40 , 50 , 50)4142 reg ion1 = wx . Region (100 , 20 , 50 , 50)43 reg ion2 = wx . Region (110 , 40 , 50 , 50)44 reg ion1 . I n t e r s e c t ( r eg ion2 )4546 r e c t = reg ion1 . GetBox ( )

135

47 dc . SetDeviceCl ippingRegion ( reg ion1 )48 dc . SetBrush (wx . Brush ( ’#f f 0 000 ’ ) )49 dc . DrawRectangle ( r e c t )50 dc . DestroyCl ippingRegion ( )5152 dc . SetBrush (wx . Brush ( ’# f f f f f f ’ ) )53 dc . DrawRectangle (180 , 20 , 50 , 50)54 dc . DrawRectangle (190 , 40 , 50 , 50)5556 reg ion1 = wx . Region (180 , 20 , 50 , 50)57 reg ion2 = wx . Region (190 , 40 , 50 , 50)58 reg ion1 . Union ( reg ion2 )59 dc . SetDeviceCl ippingRegion ( reg ion1 )6061 r e c t = reg ion1 . GetBox ( )62 dc . SetBrush (wx . Brush ( ’#fa8e00 ’ ) )63 dc . DrawRectangle ( r e c t )64 dc . DestroyCl ippingRegion ( )6566 dc . SetBrush (wx . Brush ( ’# f f f f f f ’ ) )67 dc . DrawRectangle (20 , 120 , 50 , 50)68 dc . DrawRectangle (30 , 140 , 50 , 50)69 reg ion1 = wx . Region (20 , 120 , 50 , 50)70 reg ion2 = wx . Region (30 , 140 , 50 , 50)71 reg ion1 . Xor ( r eg ion2 )7273 r e c t = reg ion1 . GetBox ( )74 dc . SetDeviceCl ippingRegion ( reg ion1 )75 dc . SetBrush (wx . Brush ( ’#619e1b ’ ) )76 dc . DrawRectangle ( r e c t )77 dc . DestroyCl ippingRegion ( )7879 dc . SetBrush (wx . Brush ( ’# f f f f f f ’ ) )80 dc . DrawRectangle (100 , 120 , 50 , 50)81 dc . DrawRectangle (110 , 140 , 50 , 50)82 reg ion1 = wx . Region (100 , 120 , 50 , 50)83 reg ion2 = wx . Region (110 , 140 , 50 , 50)84 reg ion1 . Subtract ( r eg ion2 )8586 r e c t = reg ion1 . GetBox ( )87 dc . SetDeviceCl ippingRegion ( reg ion1 )88 dc . SetBrush (wx . Brush ( ’#715b33 ’ ) )89 dc . DrawRectangle ( r e c t )90 dc . DestroyCl ippingRegion ( )9192 dc . SetBrush (wx . Brush ( ’# f f f f f f ’ ) )93 dc . DrawRectangle (180 , 120 , 50 , 50)94 dc . DrawRectangle (190 , 140 , 50 , 50)95 reg ion1 = wx . Region (180 , 120 , 50 , 50)96 reg ion2 = wx . Region (190 , 140 , 50 , 50)97 reg ion2 . Subtract ( r eg ion1 )9899 r e c t = reg ion2 . GetBox ( )100 dc . SetDeviceCl ippingRegion ( reg ion2 )101 dc . SetBrush (wx . Brush ( ’#0d0060 ’ ) )102 dc . DrawRectangle ( r e c t )103 dc . DestroyCl ippingRegion ( )104105106 def main ( ) :107108 app = wx .App( )

136

109 ex = Example (None )110 ex . Show ( )111 app . MainLoop ( )112113114 i f __name__ == ’__main__ ’ :115 main ( )

В этом примере мы представляем шесть операций с регионами.

region1 = wx.Region(100, 20, 50, 50) region2 = wx.Region(110, 40, 50, 50) region1.Intersect(region2)

Этот код выполняет операцию пересечения двух областей.

Рис. 59: Набор операций над регионами

10.4 Режимы картирования

Режим отображения определяет единицу измерения, используемую для преобразования единиц простран-ства страницы в единицы пространства устройства, а также определяет ориентацию осей x и y устройства.

Speak in English, measure in metric

Английский язык стал глобальным языком общения. Так метрическая система стала глобальной системойизмерения. Согласно этой статье в википедии, есть только три исключения. США, Либерия и Мьянма. Напри-мер, американцы используют градусы Фаренгейта для измерения температуры, галлоны для заправки своихавтомобилей или фунты для взвешивания грузов.

Хотя мы в Европе используем метрическую систему, есть все еще исключения. США доминируют в сфереинформационных технологий, и мы импортируем их стандарты. Поэтому мы также говорим, что у нас есть17-дюймовый монитор. Графика может быть помещена в файл, отображена на экране монитора или другогоустройства (камеры, видеокамеры, мобильные телефоны) или распечатана на принтере. Размер бумаги можнозадать в миллиметрах, точках или дюймах, разрешение экрана в пикселях, качество текста определяется коли-чеством точек на дюйм. У нас также есть точки, биты или сэмплы. Это одна из причин, по которой у нас естьлогические единицы и единицы устройства.

10.4.1 Логические единицы и единицы устройства

Если мы рисуем текстовые или геометрические примитивы в клиентской области, мы размещаем их, ис-пользуя логические единицы.

Если мы хотим нарисовать некоторый текст, мы предоставляем параметр текста и позиции x, y. Где х, узадаём в логических единицах. Затем устройство рисует текст в единицах устройства. Логические единицы и

137

единицы устройства могут быть одинаковыми или могут отличаться. Устройства измерения, которые использу-ются людьми, выдают логические единицы (миллиметры), единицы измерения устройства являются роднымидля определенного устройства. Например, единицей устройства для экрана является пиксель. Для HEWLETTPACKARD LaserJet 1022 имеет разрешение 1200 DPI (точек на дюйм) в нативных единицах.

До сих пор мы говорили о различных единицах измерения. Режим отображения устройства - это способпреобразования логических единиц в единицы устройства. wxPython имеет следующие режимы отображения:

Таблица 4: Логические единицы

Режим мапирования Логические единицыwx.MM_TEXT 1 пиксельwx.MM_METRIC 1 миллиметрwx.MM_LOMETRIC 1/10 миллиметраwx.MM_POINTS 1 точка, 1/72 дюймаwx.MM_TWIPS 1/20 точки или 1/1440 дюйма

Режим отображения по умолчанию - wx.MM_TEXT. В этом режиме логическая единица совпадает с еди-ницей устройства. Когда люди размещают объект на экране или проектируют веб-страницу, они обычно думаютв пикселях. Веб-дизайнеры создают три страницы столбцов, и эти столбцы задаются в пикселях. Наименьшийобщий знаменатель для страницы часто составляет 800 пикселей и т.д. Это мышление является естественным,поскольку мы знаем, что наши мониторы, например, 1024x768 пикселей Мы не собираемся делать преобразова-ния, скорее мы привыкли думать в пикселях. Если мы хотим нарисовать структуру в миллиметрах, мы можемиспользовать два режима отображения метрик. Рисование непосредственно в миллиметрах слишком грубое дляэкрана, поэтому у нас есть режим отображения wx.MM_LOMETRIC.

Чтобы установить другой режим отображения, мы используем метод SetMapMode().

10.4.2 Пример линейки

Линейка измеряет экранные объекты в пикселях.

Листинг 71: star.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a r u l e r .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx151617 RW = 701 # ru l e r width18 RM = 10 # ru l e r margin19 RH = 80 # ru l e r h e i g h t202122 class Example (wx . Frame) :2324 def __init__( s e l f , parent ) :25 wx . Frame . __init__( s e l f , parent , s i z e =(RW + 2*RM, RH) ,26 s t y l e=wx .FRAME_NO_TASKBAR | wx .NO_BORDER | wx .STAY_ON_TOP)27 s e l f . f ont = wx . Font (7 , wx .FONTFAMILY_DEFAULT, wx .FONTSTYLE_NORMAL,28 wx .FONTWEIGHT_BOLD, False , ’ Cour ier ␣10␣Pitch ’ )

138

2930 s e l f . In i tUI ( )3132 def In i tUI ( s e l f ) :3334 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )35 s e l f . Bind (wx .EVT_LEFT_DOWN, s e l f . OnLeftDown)36 s e l f . Bind (wx .EVT_LEFT_UP, s e l f . OnLeftUp )37 s e l f . Bind (wx .EVT_RIGHT_DOWN, s e l f . OnRightDown)38 s e l f . Bind (wx .EVT_MOTION, s e l f . OnMouseMove)3940 s e l f . Centre ( )41 s e l f . Show(True )4243 def OnPaint ( s e l f , e ) :4445 dc = wx . PaintDC( s e l f )4647 brush = wx . Brush (wx . Bitmap ( ’ g r an i t e . png ’ ) )48 dc . SetBrush ( brush )49 dc . DrawRectangle (0 , 0 , RW+2*RM, RH)50 dc . SetFont ( s e l f . f ont )5152 dc . SetPen (wx . Pen( ’#F8FF25 ’ ) )53 dc . SetTextForeground ( ’#F8FF25 ’ )5455 for i in range (RW) :5657 i f not ( i % 100) :5859 dc . DrawLine ( i+RM, 0 , i+RM, 10)60 w, h = dc . GetTextExtent ( str ( i ) )61 dc . DrawText ( str ( i ) , i+RM−w/2 , 11)6263 e l i f not ( i % 20) :6465 dc . DrawLine ( i+RM, 0 , i+RM, 8)6667 e l i f not ( i % 2) :6869 dc . DrawLine ( i+RM, 0 , i+RM, 4)7071 def OnLeftDown( s e l f , e ) :7273 x , y = s e l f . Cl ientToScreen ( e . GetPos i t ion ( ) )74 ox , oy = s e l f . GetPos i t ion ( )7576 dx = x − ox77 dy = y − oy7879 s e l f . d e l t a = ( ( dx , dy ) )8081 def OnMouseMove( s e l f , e ) :8283 i f e . Dragging ( ) and e . LeftIsDown ( ) :8485 s e l f . SetCursor (wx . Cursor (wx .CURSOR_HAND) )8687 x , y = s e l f . Cl ientToScreen ( e . GetPos i t ion ( ) )88 fp = (x − s e l f . d e l t a [ 0 ] , y − s e l f . d e l t a [ 1 ] )89 s e l f .Move( fp )90

139

91 def OnLeftUp ( s e l f , e ) :9293 s e l f . SetCursor (wx . Cursor (wx .CURSOR_ARROW) )9495 def OnRightDown( s e l f , e ) :9697 s e l f . Close ( )9899100 def main ( ) :101102 app = wx .App( )103 ex = Example (None )104 ex . Show ( )105 app . MainLoop ( )106107108 i f __name__ == ’__main__ ’ :109 main ( )

В этом примере мы создаем линейку. Эта линейка измеряет экранные объекты в пикселях. Мы оставилирежим отображения по умолчанию, который является wx.MM_TEXT. Как мы уже упоминали, этот режимимеет одинаковые логические и аппаратные единицы. В нашем случае это пиксели.

def __init__(self, parent):

wx.Frame.__init__(self, parent, size=(RW + 2*RM, RH),

style=wx.FRAME_NO_TASKBAR | wx.NO_BORDER | wx.STAY_ON_TOP)

Мы создали окно без границ. Линейка имеет ширину 721 пикселей: RW + 2 * RM = 701 + 20 = 721.Линейка показывает 700 чисел; 0 ... 700 - это 701 пиксель. Линейка имеет поле с обеих сторон, 2 * 10 составляет20 пикселей. Вместе это составляет 721 пикселей.

brush = wx.Brush(wx.Bitmap(’granite.png’))

dc.SetBrush(brush)

dc.DrawRectangle(0, 0, RW+2*RM, RH)

Здесь мы рисуем собственный шаблон на окне. Мы использовали предопределенный шаблон, доступный вGimp. Он называется гранит.

w, h = dc.GetTextExtent(str(i))

dc.DrawText(str(i), i+RM-w/2, 11)

Эти строки обеспечивают правильное выравнивание текста. Метод GetTextExtent () возвращает ширину ивысоту текста.

У нас нет границы вокруг нашего окна. Поэтому мы должны справиться с перемещением вручную. МетодыOnLeftDown() и OnMouseMove() позволяют нам перемещать линейку.

def OnLeftDown(self, e):

x, y = self.ClientToScreen(e.GetPosition())

ox, oy = self.GetPosition()

dx = x - ox

dy = y - oy

self.delta = ((dx, dy))

140

В методе OnLeftDown() мы определяем координаты окна и курсора мыши; Дельта-значение - это расстояниеуказателя мыши от верхнего левого угла нашего окна. Нам нужно значение дельты, чтобы переместить окно.

def OnMouseMove(self, e):

if e.Dragging() and e.LeftIsDown():

self.SetCursor(wx.Cursor(wx.CURSOR_HAND))

x, y = self.ClientToScreen(e.GetPosition())

fp = (x - self.delta[0], y - self.delta[1])

self.Move(fp)

Код выполняется, когда мы одновременно перетаскиваем окно и нажимаем левую кнопку мыши. В блокекода мы меняем курсор мыши с помощью SetCursor() и перемещаем окно методом Move (). Значение дельтыиспользуется для получения расстояния.

def OnLeftUp(self, e):

self.SetCursor(wx.Cursor(wx.CURSOR_ARROW))

Когда мы отпускаем левую кнопку мыши, мы меняем курсор обратно на стрелку.

def OnRightDown(self, e):

self.Close()

Окно закрывается щелчком правой кнопкой мыши в области окна.

Рис. 60: Пример «Линейка»

В этой главе мы работали с графикой в wxPython.

11 Создание пользовательских виджетов

Инструментарий обычно предоставляет только самые распространенные виджеты, такие как кнопки, тек-стовые виджеты, полосы прокрутки, ползунки и т. д. Ни один инструментарий не может предоставить всевозможные виджеты. У wxPython есть много виджетов; но наиболее специализированные виджеты создаютсяклиентскими программистами.

Пользовательские виджеты создаются двумя способами: либо мы модифицируем или улучшаем существу-ющий виджет, либо создаем пользовательский виджет с нуля.

11.1 Виджет гиперссылки

Первый пример создаст гиперссылку. Виджет гиперссылки будет основан на существующем виджете wx.lib.stattext.GenStaticText.

Листинг 72: star.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−3

141

4 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a Hyper l ink widge t .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx15 from wx . l i b . s t a t t e x t import GenStaticText16 import webbrowser171819 class Link ( GenStaticText ) :2021 def __init__( s e l f , * args , **kw) :22 super ( Link , s e l f ) . __init__(* args , **kw)2324 s e l f . f ont1 = wx . Font (11 , wx . SWISS , wx .NORMAL, wx .BOLD, True , ’ Verdana ’ )25 s e l f . f ont2 = wx . Font (11 , wx . SWISS , wx .NORMAL, wx .BOLD, False , ’ Verdana ’ )2627 s e l f . SetFont ( s e l f . f ont2 )28 s e l f . SetForegroundColour ( ’#0000 f f ’ )2930 s e l f . Bind (wx .EVT_MOUSE_EVENTS, s e l f . OnMouseEvent )31 s e l f . Bind (wx .EVT_MOTION, s e l f . OnMouseEvent )3233 def SetUrl ( s e l f , u r l ) :3435 s e l f . u r l = u r l363738 def OnMouseEvent ( s e l f , e ) :3940 i f e . Moving ( ) :4142 s e l f . SetCursor (wx . Cursor (wx .CURSOR_HAND) )43 s e l f . SetFont ( s e l f . f ont1 )4445 e l i f e . LeftUp ( ) :4647 webbrowser . open_new( s e l f . u r l )4849 else :50 s e l f . SetCursor (wx . Nul lCursor )51 s e l f . SetFont ( s e l f . f ont2 )5253 e . Skip ( )545556 class Example (wx . Frame) :5758 def __init__( s e l f , * args , **kw) :59 super (Example , s e l f ) . __init__(* args , **kw)6061 s e l f . In i tUI ( )6263 def In i tUI ( s e l f ) :6465 panel = wx . Panel ( s e l f )

142

6667 vbox = wx . BoxSizer (wx .VERTICAL)68 hbox = wx . BoxSizer (wx .HORIZONTAL)6970 s t = GenStaticText ( panel , l a b e l=’Go␣ to ␣web␣ s i t e : ’ )71 s t . SetFont (wx . Font (11 , wx . SWISS , wx .NORMAL, wx .BOLD, False , ’ Verdana ’ ) )72 hbox .Add( st , f l a g=wx .LEFT, border=20)7374 link_wid = Link ( panel , l a b e l=’ ZetCode ’ )75 link_wid . SetUrl ( ’ http ://www. zetcode . com ’ )76 hbox .Add( link_wid , f l a g=wx .LEFT, border=20)7778 vbox .Add(hbox , f l a g=wx .TOP, border=30)79 panel . S e tS i z e r ( vbox )8081 s e l f . S e tT i t l e ( ’A␣Hyperl ink ’ )82 s e l f . Centre ( )838485 def main ( ) :8687 app = wx .App( )88 ex = Example (None )89 ex . Show ( )90 app . MainLoop ( )919293 i f __name__ == ’__main__ ’ :94 main ( )

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

from wx.lib.stattext import GenStaticText

import webbrowser

Здесь мы импортируем базовый виджет, из которого мы получаем наш виджет гиперссылки и модульвеб-браузера. Модуль веб-браузера является стандартным модулем Python. Мы будем использовать его дляоткрытия ссылок в браузере по умолчанию.

self.SetFont(self.font2)

self.SetForegroundColour(’#0000ff’)

Идея создания виджета гиперссылки проста. Мы наследуем от базового класса виджетов wx.lib.stattext.GenStaticText.Итак, у нас есть текстовый виджет. Затем мы немного его модифицируем. Мы меняем шрифт и цвет текста.

if e.Moving():

self.SetCursor(wx.Cursor(wx.CURSOR_HAND))

self.SetFont(self.font1)

Если навести указатель мыши на ссылку, мы изменим шрифт на подчеркнутый, а также указатель мышипревратится в «руку».

elif e.LeftUp():

webbrowser.open_new(self.url)

Если мы щелкнем левой кнопкой мыши по ссылке, то откроется эта ссылка в браузере по умолчанию.

143

Рис. 61: Гиперссылка

11.2 Виджет прожига CD

Это пример виджета, который мы создаем с нуля. Мы помещаем wx.Panel внизу окна и рисуем весь виджетвручную. Если вы когда-либо записывали CD или DVD, вы уже видели этот виджет.

Листинг 73: star.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a Burning widge t .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx1516 class Burning (wx . Panel ) :17 def __init__( s e l f , parent ) :18 wx . Panel . __init__( s e l f , parent , s i z e =(−1, 30) , s t y l e=wx .SUNKEN_BORDER)1920 s e l f . parent = parent21 s e l f . f ont = wx . Font (9 , wx .FONTFAMILY_DEFAULT, wx .FONTSTYLE_NORMAL,22 wx .FONTWEIGHT_NORMAL, False , ’ Cour ier ␣10␣Pitch ’ )2324 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )25 s e l f . Bind (wx .EVT_SIZE, s e l f . OnSize )262728 def OnPaint ( s e l f , e ) :2930 num = range (75 , 700 , 75)31 dc = wx . PaintDC( s e l f )32 dc . SetFont ( s e l f . f ont )

144

33 w, h = s e l f . GetSize ( )3435 s e l f . cw = s e l f . parent . GetParent ( ) . cw3637 step = int (round(w / 10 . 0 ) )3839 j = 04041 t i l l = (w / 750 .0 ) * s e l f . cw42 f u l l = (w / 750 .0 ) * 7004344 i f s e l f . cw >= 700 :4546 dc . SetPen (wx . Pen( ’#FFFFB8 ’ ) )47 dc . SetBrush (wx . Brush ( ’#FFFFB8 ’ ) )48 dc . DrawRectangle (0 , 0 , f u l l , 30)49 dc . SetPen (wx . Pen( ’#f f a f a f ’ ) )50 dc . SetBrush (wx . Brush ( ’#f f a f a f ’ ) )51 dc . DrawRectangle ( f u l l , 0 , t i l l −f u l l , 30)52 else :5354 dc . SetPen (wx . Pen( ’#FFFFB8 ’ ) )55 dc . SetBrush (wx . Brush ( ’#FFFFB8 ’ ) )56 dc . DrawRectangle (0 , 0 , t i l l , 30)575859 dc . SetPen (wx . Pen( ’#5C5142 ’ ) )6061 for i in range ( step , 10* step , s tep ) :6263 dc . DrawLine ( i , 0 , i , 6)64 width , he ight = dc . GetTextExtent ( str (num[ j ] ) )65 dc . DrawText ( str (num[ j ] ) , i−width /2 , 8)66 j = j + 16768 def OnSize ( s e l f , e ) :6970 s e l f . Refresh ( )717273 class Example (wx . Frame) :7475 def __init__( s e l f , * args , **kwargs ) :76 super (Example , s e l f ) . __init__(* args , **kwargs )7778 s e l f . In i tUI ( )7980 def In i tUI ( s e l f ) :8182 s e l f . cw = 758384 panel = wx . Panel ( s e l f )85 CenterPanel = wx . Panel ( panel )8687 s e l f . s l d = wx . S l i d e r ( CenterPanel , va lue=75, maxValue=750 , s i z e =(200 , −1) ,88 s t y l e=wx .SL_LABELS)8990 vbox = wx . BoxSizer (wx .VERTICAL)91 hbox = wx . BoxSizer (wx .HORIZONTAL)92 hbox2 = wx . BoxSizer (wx .HORIZONTAL)93 hbox3 = wx . BoxSizer (wx .HORIZONTAL)94

145

95 s e l f . wid = Burning ( panel )96 hbox .Add( s e l f . wid , 1 , wx .EXPAND)9798 hbox2 .Add( CenterPanel , 1 , wx .EXPAND)99 hbox3 .Add( s e l f . s ld , 0 , wx .LEFT|wx .TOP, 35)100101 CenterPanel . S e tS i z e r ( hbox3 )102103 vbox .Add( hbox2 , 1 , wx .EXPAND)104 vbox .Add(hbox , 0 , wx .EXPAND)105106 s e l f . Bind (wx .EVT_SCROLL, s e l f . OnScro l l )107108 panel . S e tS i z e r ( vbox )109110 s e l f . s l d . SetFocus ( )111112 s e l f . S e tT i t l e ( "Burning␣widget " )113 s e l f . Centre ( )114115 def OnScrol l ( s e l f , e ) :116117 s e l f . cw = s e l f . s l d . GetValue ( )118 s e l f . wid . Refresh ( )119120121 def main ( ) :122123 app = wx .App( )124 ex = Example (None )125 ex . Show ( )126 app . MainLoop ( )127128129 i f __name__ == ’__main__ ’ :130 main ( )

Этот виджет графически отображает общую емкость носителя и доступное нам пространство. Виджетуправляется слайдером. Минимальное значение нашего пользовательского виджета равно 0, максимальное -750. Если мы достигнем значения 700, мы начнем рисовать красным цветом. Это обычно указывает на пережог.

w, h = self.GetSize()

self.cw = self.parent.GetParent().cw

...

till = (w / 750.0) * self.cw

full = (w / 750.0) * 700

Рисуем виджет динамически. Чем больше окно, тем больше виджет прожига. И наоборот. Вот почему мыдолжны рассчитать размер wx.Panel, на который мы рисуем пользовательский виджет. Параметр till определя-ет общий размер для рисования. Это значение берётся из виджета слайдера. Это просто часть всей площади.Полный параметр определяет точку, где мы начинаем рисовать красным цветом. Обратите внимание на исполь-зование арифметики с плавающей точкой. Это сделано для достижения большей точности.

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

def OnSize(self, e):

self.Refresh()

146

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

def OnScroll(self, e):

self.cw = self.sld.GetValue()

self.wid.Refresh()

Если прокрутить ползунок, мы получим фактическое значение и сохраним его в параметре self.cw. Этозначение используется при рисовании виджета прожига. Затем мы вызываем перерисовку виджета.

Рис. 62: Виджет прожига CD

11.3 Виджет ЦП

Существуют системные приложения, которые измеряют системные ресурсы, такие как температура, памятьили загрузка ЦП. Специализированные виджеты создаются, чтобы сделать приложение более привлекательным.

Следующий виджет часто используется в системных приложениях.

Листинг 74: star.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a CPU widge t .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx151617 class CPU(wx . Panel ) :

147

1819 def __init__( s e l f , parent ) :20 wx . Panel . __init__( s e l f , parent , s i z e =(80 , 110) )2122 s e l f . parent = parent23 s e l f . SetBackgroundColour ( ’#000000 ’ )24 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )252627 def OnPaint ( s e l f , e ) :2829 dc = wx . PaintDC( s e l f )3031 dc . SetDeviceOr ig in (0 , 100)32 dc . SetAx i sOr i entat ion (True , True )3334 pos = s e l f . parent . GetParent ( ) . GetParent ( ) . s e l35 r e c t = pos / 53637 for i in range (1 , 21) :3839 i f i > r e c t :4041 dc . SetBrush (wx . Brush ( ’#075100 ’ ) )42 dc . DrawRectangle (10 , i *4 , 30 , 5)43 dc . DrawRectangle (41 , i *4 , 30 , 5)4445 else :46 dc . SetBrush (wx . Brush ( ’#36 f f 2 7 ’ ) )47 dc . DrawRectangle (10 , i *4 , 30 , 5)48 dc . DrawRectangle (41 , i *4 , 30 , 5)495051 class Example (wx . Frame) :5253 def __init__( s e l f , * args , **kwargs ) :54 super (Example , s e l f ) . __init__(* args , **kwargs )5556 s e l f . In i tUI ( )5758 def In i tUI ( s e l f ) :5960 s e l f . s e l = 06162 panel = wx . Panel ( s e l f )63 centerPane l = wx . Panel ( panel )6465 s e l f . cpu = CPU( centerPane l )6667 hbox = wx . BoxSizer (wx .HORIZONTAL)6869 s e l f . s l i d e r = wx . S l i d e r ( panel , va lue=s e l f . s e l , maxValue=100 , s i z e =(−1, 100) ,70 s t y l e=wx .VERTICAL | wx .SL_INVERSE)71 s e l f . s l i d e r . SetFocus ( )7273 hbox .Add( centerPanel , 0 , wx .LEFT | wx .TOP, 20)74 hbox .Add( s e l f . s l i d e r , 0 , wx .LEFT | wx .TOP, 30)7576 s e l f . Bind (wx .EVT_SCROLL, s e l f . OnScro l l )7778 panel . S e tS i z e r ( hbox )79

148

80 s e l f . S e tT i t l e ( "CPU" )81 s e l f . Centre ( )828384 def OnScro l l ( s e l f , e ) :8586 s e l f . s e l = e . GetInt ( )87 s e l f . cpu . Refresh ( )888990 def main ( ) :9192 app = wx .App( )93 ex = Example (None )94 ex . Show ( )95 app . MainLoop ( )969798 i f __name__ == ’__main__ ’ :99 main ( )

Мы создаем черную панель. Затем мы рисуем маленькие прямоугольники на этой панели. Цвет прямо-угольников зависит от значения ползунка. Цвет может быть темно-зеленым или ярко-зеленым.

dc.SetDeviceOrigin(0, 100)

dc.SetAxisOrientation(True, True)

Здесь мы меняем систему координат по умолчанию на декартову. Это должно сделать рисунок интуитивнопонятным.

pos = self.parent.GetParent().GetParent().sel

rect = pos / 5

Здесь мы получаем значение Sizer. У нас есть 20 прямоугольников в каждом столбце. Слайдер имеет100 значений. Параметр rect выполняет преобразование значений ползунка в прямоугольники, которые будутнарисованы ярко-зеленым цветом.

for i in range(1, 21):

if i > rect:

dc.SetBrush(wx.Brush(’#075100’))

dc.DrawRectangle(10, i*4, 30, 5)

dc.DrawRectangle(41, i*4, 30, 5)

else:

dc.SetBrush(wx.Brush(’#36ff27’))

dc.DrawRectangle(10, i*4, 30, 5)

dc.DrawRectangle(41, i*4, 30, 5)

Здесь мы рисуем 40 прямоугольников, по 20 в каждом столбце. Если номер нарисованного прямоугольникабольше, чем преобразованное значение прямоугольника, мы рисуем его темно-зеленым цветом; в остальномярко-зеленым.

В этой главе мы создали пользовательские виджеты в wxPython.

149

Рис. 63: Виджет CPU

12 Скелеты приложений в wxPython

В этом разделе мы создадим несколько скелетов приложений. Наши скрипты проработают интерфейс, ноне будут реализовывать функциональность. Цель состоит в том, чтобы показать, как в wxPython можно сделатьнесколько хорошо известных интерфейсов GUI.

12.1 Файловый менеджер

«File Hunter» - это скелет файлового менеджера. Он копирует внешний вид Krusader, файлового менеджера,доступного в системах Unix. Если дважды щелкнуть виджет сплиттера, он поделит окно «File Hunter» на двечасти одинаковой ширины. То же самое происходит, если мы изменим размер главного окна.

Листинг 75: file_hunter.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a s k e l e t on8 o f a f i l e manager UI .910 author : Jan Bodnar11 web s i t e : z e t code . com12 l a s t e d i t e d : May 201813 """1415 import wx16 import os17 import time1819 ID_BUTTON=10020 ID_EXIT=20021 ID_SPLITTER=3002223

150

24 class MyListCtrl (wx . L i s tC t r l ) :2526 def __init__( s e l f , parent ) :27 wx . L i s tC t r l . __init__( s e l f , parent , s t y l e=wx .LC_REPORT)2829 images = [ ’ images /empty . png ’ , ’ images / f o l d e r . png ’ , ’ images / source−py . png ’ ,30 ’ images / image . png ’ , ’ images /pdf . png ’ , ’ images /up16 . png ’ ]3132 s e l f . InsertColumn (0 , ’Name ’ )33 s e l f . InsertColumn (1 , ’ Ext ’ )34 s e l f . InsertColumn (2 , ’ S i z e ’ , wx .LIST_FORMAT_RIGHT)35 s e l f . InsertColumn (3 , ’ Modif ied ’ )3637 s e l f . SetColumnWidth (0 , 220)38 s e l f . SetColumnWidth (1 , 70)39 s e l f . SetColumnWidth (2 , 100)40 s e l f . SetColumnWidth (3 , 420)4142 s e l f . i l = wx . ImageList (16 , 16)4344 for i in images :4546 s e l f . i l .Add(wx . Bitmap ( i ) )4748 s e l f . SetImageList ( s e l f . i l , wx .IMAGE_LIST_SMALL)4950 j = 15152 s e l f . In s e r t I t em (0 , ’ . . ’ )53 s e l f . SetItemImage (0 , 5)5455 f i l e s = os . l i s t d i r ( ’ . ’ )5657 for i in f i l e s :5859 (name , ext ) = os . path . s p l i t e x t ( i )60 ex = ext [ 1 : ]61 s i z e = os . path . g e t s i z e ( i )62 sec = os . path . getmtime ( i )6364 s e l f . In s e r t I t em ( j , name)65 s e l f . SetItem ( j , 1 , ex )66 s e l f . SetItem ( j , 2 , str ( s i z e ) + ’ ␣B ’ )67 s e l f . SetItem ( j , 3 , time . s t r f t ime ( ’%Y−%m−%d␣%H:%M’ , time . l o c a l t ime ( sec ) ) )6869 i f os . path . i s d i r ( i ) :70 s e l f . SetItemImage ( j , 1)71 e l i f ex == ’py ’ :72 s e l f . SetItemImage ( j , 2)73 e l i f ex == ’ jpg ’ :74 s e l f . SetItemImage ( j , 3)75 e l i f ex == ’ pdf ’ :76 s e l f . SetItemImage ( j , 4)77 else :78 s e l f . SetItemImage ( j , 0)7980 i f ( j % 2) == 0 :8182 s e l f . SetItemBackgroundColour ( j , ’#e6 f 1 f 5 ’ )8384 j = j + 185

151

8687 class Example (wx . Frame) :8889 def __init__( s e l f , * args , **kw) :90 super (Example , s e l f ) . __init__(* args , **kw)9192 s e l f . In i tUI ( )9394 def In i tUI ( s e l f ) :9596 s e l f . s p l i t t e r = wx . SplitterWindow ( s e l f , ID_SPLITTER, s t y l e=wx .SP_BORDER)97 s e l f . s p l i t t e r . SetMinimumPaneSize (50)9899 p1 = MyListCtrl ( s e l f . s p l i t t e r )100 p2 = MyListCtrl ( s e l f . s p l i t t e r )101 s e l f . s p l i t t e r . S p l i tV e r t i c a l l y (p1 , p2 )102103 s e l f . Bind (wx .EVT_SIZE, s e l f . OnSize )104 s e l f . Bind (wx .EVT_SPLITTER_DCLICK, s e l f . OnDoubleClick , id=ID_SPLITTER)105106 f i l emenu= wx .Menu( )107 f i l emenu . Append(ID_EXIT, "E&x i t " , "␣Terminate␣ the ␣program" )108 editmenu = wx .Menu( )109 netmenu = wx .Menu( )110 showmenu = wx .Menu( )111 configmenu = wx .Menu( )112 helpmenu = wx .Menu( )113114 menuBar = wx .MenuBar ( )115 menuBar . Append( f i lemenu , "&F i l e " )116 menuBar . Append( editmenu , "&Edit " )117 menuBar . Append(netmenu , "&Net" )118 menuBar . Append( showmenu , "&Show" )119 menuBar . Append( configmenu , "&Config " )120 menuBar . Append( helpmenu , "&Help" )121 s e l f . SetMenuBar (menuBar )122 s e l f . Bind (wx .EVT_MENU, s e l f . OnExit , id=ID_EXIT)123124 tb = s e l f . CreateToolBar ( wx .TB_HORIZONTAL | wx .NO_BORDER |125 wx .TB_FLAT)126127 tb . AddTool (10 , ’ Previous ’ , wx . Bitmap ( ’ images / prev ious . png ’ ) , shortHelp=’

Previous ’ )128 tb . AddTool (20 , ’Up ’ , wx . Bitmap ( ’ images /up . png ’ ) , shortHelp=’Up␣one␣ d i r e c t o r y ’

)129 tb . AddTool (30 , ’Home ’ , wx . Bitmap ( ’ images /home . png ’ ) , shortHelp=’Home ’ )130 tb . AddTool (40 , ’ Refresh ’ , wx . Bitmap ( ’ images / r e f r e s h . png ’ ) , shortHelp=’ Refresh

’ )131 tb . AddSeparator ( )132 tb . AddTool (50 , ’ Edit ␣ t ext ’ , wx . Bitmap ( ’ images / t e x t e d i t . png ’ ) , shortHelp=’ Edit

␣ t ext ’ )133 tb . AddTool (60 , ’ Terminal ’ , wx . Bitmap ( ’ images / te rmina l . png ’ ) , shortHelp=’

Terminal ’ )134 tb . AddSeparator ( )135 tb . AddTool (70 , ’ Help ’ , wx . Bitmap ( ’ images / help . png ’ ) , shortHelp=’Show␣help ’ )136 tb . Rea l i z e ( )137138 s e l f . s i z e r 2 = wx . BoxSizer (wx .HORIZONTAL)139140 button1 = wx . Button ( s e l f , ID_BUTTON + 1 , "F3␣View" )141 button2 = wx . Button ( s e l f , ID_BUTTON + 2 , "F4␣Edit " )142 button3 = wx . Button ( s e l f , ID_BUTTON + 3 , "F5␣Copy" )

152

143 button4 = wx . Button ( s e l f , ID_BUTTON + 4 , "F6␣Move" )144 button5 = wx . Button ( s e l f , ID_BUTTON + 5 , "F7␣Mkdir" )145 button6 = wx . Button ( s e l f , ID_BUTTON + 6 , "F8␣Delete " )146 button7 = wx . Button ( s e l f , ID_BUTTON + 7 , "F9␣Rename" )147 button8 = wx . Button ( s e l f , ID_EXIT, "F10␣Quit" )148149 s e l f . s i z e r 2 .Add( button1 , 1 , wx .EXPAND)150 s e l f . s i z e r 2 .Add( button2 , 1 , wx .EXPAND)151 s e l f . s i z e r 2 .Add( button3 , 1 , wx .EXPAND)152 s e l f . s i z e r 2 .Add( button4 , 1 , wx .EXPAND)153 s e l f . s i z e r 2 .Add( button5 , 1 , wx .EXPAND)154 s e l f . s i z e r 2 .Add( button6 , 1 , wx .EXPAND)155 s e l f . s i z e r 2 .Add( button7 , 1 , wx .EXPAND)156 s e l f . s i z e r 2 .Add( button8 , 1 , wx .EXPAND)157158 s e l f . Bind (wx .EVT_BUTTON, s e l f . OnExit , id=ID_EXIT)159160 s e l f . s i z e r = wx . BoxSizer (wx .VERTICAL)161 s e l f . s i z e r .Add( s e l f . s p l i t t e r , 1 ,wx .EXPAND)162 s e l f . s i z e r .Add( s e l f . s i z e r 2 , 0 ,wx .EXPAND)163 s e l f . S e tS i z e r ( s e l f . s i z e r )164165 # s i z e = wx . D i sp l ayS i z e ( )166 # s e l f . S e tS i z e ( s i z e )167168 sb = s e l f . CreateStatusBar ( )169 sb . SetStatusText ( os . getcwd ( ) )170171 s e l f . S e tT i t l e ( " F i l e ␣Hunter" )172 s e l f . Center ( )173174175 def OnExit ( s e l f , e ) :176177 s e l f . Close (True )178179 def OnSize ( s e l f , e ) :180181 s i z e = s e l f . GetSize ( )182 s e l f . s p l i t t e r . Se tSashPos i t i on ( s i z e . x / 2)183184 e . Skip ( )185186 def OnDoubleClick ( s e l f , e ) :187188 s i z e = s e l f . GetSize ( )189 s e l f . s p l i t t e r . Se tSashPos i t i on ( s i z e . x / 2)190191192 def main ( ) :193194 app = wx .App( )195 ex = Example (None )196 ex . Show ( )197 app . MainLoop ( )198199200 i f __name__ == ’__main__ ’ :201 main ( )

В этом примере создается пользовательский интерфейс двухпанельного файлового менеджера.

153

class MyListCtrl(wx.ListCtrl):

def __init__(self, parent):

wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT)

Основную область приложения занимает виджет wx.ListCtrl.

self.il = wx.ImageList(16, 16)

for i in images:

self.il.Add(wx.Bitmap(i))

self.SetImageList(self.il, wx.IMAGE_LIST_SMALL)

Элемент управления списком содержит изображение списка, которое используется для указания типа фай-ла.

files = os.listdir(’.’)

for i in files:

(name, ext) = os.path.splitext(i)

ex = ext[1:]

size = os.path.getsize(i)

sec = os.path.getmtime(i)

...

Мы получаем содержимое текущего рабочего каталога и определяем расширение файла, размер и времяпоследнего изменения.

if os.path.isdir(i):

self.SetItemImage(j, 1)

elif ex == ’py’:

self.SetItemImage(j, 2)

elif ex == ’jpg’:

self.SetItemImage(j, 3)

elif ex == ’pdf’:

self.SetItemImage(j, 4)

else:

self.SetItemImage(j, 0)

Изображение для файла выбирается в зависимости от расширения файла.

self.splitter = wx.SplitterWindow(self, ID_SPLITTER, style=wx.SP_BORDER)

self.splitter.SetMinimumPaneSize(50)

p1 = MyListCtrl(self.splitter)

p2 = MyListCtrl(self.splitter)

self.splitter.SplitVertically(p1, p2)

У нас есть два элемента управления списком, разделенных по вертикали виджетом сплиттера.

menuBar = wx.MenuBar()

menuBar.Append(filemenu, "&File")

menuBar.Append(editmenu, "&Edit")

...

154

У нас есть менюбар.

tb = self.CreateToolBar( wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT)

tb.AddTool(10, ’Previous’, wx.Bitmap(’images/previous.png’), shortHelp=’Previous’)

tb.AddTool(20, ’Up’, wx.Bitmap(’images/up.png’), shortHelp=’Up one directory’)

...

У нас есть панель инструментов.

self.sizer2 = wx.BoxSizer(wx.HORIZONTAL)

button1 = wx.Button(self, ID_BUTTON + 1, "F3 View")

button2 = wx.Button(self, ID_BUTTON + 2, "F4 Edit")

button3 = wx.Button(self, ID_BUTTON + 3, "F5 Copy")

button4 = wx.Button(self, ID_BUTTON + 4, "F6 Move")

...

Восемь кнопок расположены в горизонтальном сайзере, который добавляется в нижнюю часть окна.

Рис. 64: Файловый менеджер

12.2 Электронная таблица

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

Листинг 76: file_hunter.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a SpreadSheet UI .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 from wx . l i b import shee t

155

15 import wx161718 class MySheet (wx . g r id . Grid ) :1920 def __init__( s e l f , * args , **kw) :21 super (MySheet , s e l f ) . __init__(* args , **kw)2223 s e l f . In i tUI ( )2425 def In i tUI ( s e l f ) :2627 nOfRows = 5528 nOfCols = 252930 s e l f . row = s e l f . c o l = 031 s e l f . CreateGrid (nOfRows , nOfCols )3233 s e l f . Se tCo lLabe lS i ze (20)34 s e l f . SetRowLabelSize (50)3536 s e l f . Bind (wx . g r id .EVT_GRID_SELECT_CELL, s e l f . OnGridSe lectCel l )3738 for i in range (nOfRows) :39 s e l f . SetRowSize ( i , 20)4041 for i in range ( nOfCols ) :42 s e l f . Se tCo lS i ze ( i , 75)4344 def OnGridSe lectCel l ( s e l f , e ) :4546 s e l f . row , s e l f . c o l = e .GetRow( ) , e . GetCol ( )4748 con t r o l = s e l f . GetParent ( ) . GetParent ( ) . p o s i t i o n49 value = s e l f . GetColLabelValue ( s e l f . c o l ) + s e l f . GetRowLabelValue ( s e l f . row )50 con t r o l . SetValue ( va lue )5152 e . Skip ( )535455 class Example (wx . Frame) :5657 def __init__( s e l f , * args , **kw) :58 super (Example , s e l f ) . __init__(* args , **kw)5960 s e l f . In i tUI ( )6162 def In i tUI ( s e l f ) :6364 f on t s = [ ’Times␣New␣Roman ’ , ’ Times ’ , ’ Cour ier ’ , ’ Cour ier ␣New ’ , ’ He lve t i ca ’ ,65 ’ Sans ’ , ’ verdana ’ , ’ u tka l ’ , ’ aakar ’ , ’ Ar i a l ’ ]66 f on t_s i z e s = [ ’ 10 ’ , ’ 11 ’ , ’ 12 ’ , ’ 14 ’ , ’ 16 ’ ]6768 box = wx . BoxSizer (wx .VERTICAL)69 menuBar = wx .MenuBar ( )7071 menu1 = wx .Menu( )72 menuBar . Append(menu1 , ’&F i l e ’ )73 menu2 = wx .Menu( )74 menuBar . Append(menu2 , ’&Edit ’ )75 menu3 = wx .Menu( )76 menuBar . Append(menu3 , ’&Edit ’ )

156

77 menu4 = wx .Menu( )78 menuBar . Append(menu4 , ’&In s e r t ’ )79 menu5 = wx .Menu( )80 menuBar . Append(menu5 , ’F&ormat ’ )81 menu6 = wx .Menu( )82 menuBar . Append(menu6 , ’&Tools ’ )83 menu7 = wx .Menu( )84 menuBar . Append(menu7 , ’&Data ’ )85 menu8 = wx .Menu( )86 menuBar . Append(menu8 , ’&Help ’ )8788 s e l f . SetMenuBar (menuBar )8990 too lba r1 = wx . ToolBar ( s e l f , s t y l e= wx .TB_HORIZONTAL)9192 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images /new . png ’ ) )93 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images /open . png ’ ) )94 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / save . png ’ ) )9596 too lba r1 . AddSeparator ( )9798 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / cut . png ’ ) )99 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images /copy . png ’ ) )100 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / paste . png ’ ) )101 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / d e l e t e . png ’ ) )102103 too lba r1 . AddSeparator ( )104105 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images /undo . png ’ ) )106 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / redo . png ’ ) )107108 too lba r1 . AddSeparator ( )109110 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / asc . png ’ ) )111 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / desc . png ’ ) )112113 too lba r1 . AddSeparator ( )114 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / chart . png ’ ) )115116 too lba r1 . AddSeparator ( )117 too lba r1 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / e x i t . png ’ ) )118119 too lba r1 . Rea l i z e ( )120121 too lba r2 = wx . ToolBar ( s e l f , wx .TB_HORIZONTAL | wx .TB_TEXT)122123 s e l f . p o s i t i o n = wx . TextCtrl ( too lba r2 )124125 font = wx .ComboBox( too lbar2 , va lue=’Times ’ , c ho i c e s=fonts , s i z e =(100 , −1) ,126 s t y l e=wx .CB_DROPDOWN)127128 font_height = wx .ComboBox( too lbar2 , va lue=’ 10 ’ , c ho i c e s=font_s i ze s ,129 s i z e =(50 , −1) , s t y l e=wx .CB_DROPDOWN)130131 too lba r2 . AddControl ( s e l f . p o s i t i o n )132 too lba r2 . AddControl ( f ont )133 too lba r2 . AddControl ( font_height )134135 too lba r2 . AddSeparator ( )136137 too lba r2 . AddCheckTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / text−bold . png ’ ) )138 too lba r2 . AddCheckTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / text− i t a l i c . png ’ ) )

157

139 too lba r2 . AddCheckTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / text−unde r l i n e . png ’ ) )140141 too lba r2 . AddSeparator ( )142143 too lba r2 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / a l i gn− l e f t . png ’ ) )144 too lba r2 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / a l i gn−cente r . png ’ ) )145 too lba r2 . AddTool (wx .ID_ANY, ’ ’ , wx . Bitmap ( ’ images / a l i gn−r i g h t . png ’ ) )146147 box .Add( too lbar1 , border=5)148 box .Add( ( 5 , 5 ) , 0)149 box .Add( too lba r2 )150 box .Add( ( 5 , 10 ) , 0)151152 too lba r2 . Rea l i z e ( )153 s e l f . S e tS i z e r ( box )154155 notebook = wx . Notebook ( s e l f , s t y l e=wx .RIGHT)156157 sheet1 = MySheet ( notebook )158 sheet2 = MySheet ( notebook )159 sheet3 = MySheet ( notebook )160 sheet1 . SetFocus ( )161162 notebook . AddPage ( sheet1 , ’ Sheet1 ’ )163 notebook . AddPage ( sheet2 , ’ Sheet2 ’ )164 notebook . AddPage ( sheet3 , ’ Sheet3 ’ )165166 box .Add( notebook , 1 , wx .EXPAND)167168 s e l f . CreateStatusBar ( )169170 s e l f . S e tS i z e ( (550 , 550) )171 s e l f . S e tT i t l e ( " SpreadSheet " )172 s e l f . Centre ( )173174 def main ( ) :175176 app = wx .App( )177 ex = Example (None )178 ex . Show ( )179 app . MainLoop ( )180181182 i f __name__ == ’__main__ ’ :183 main ( )

В примере кода создается пользовательский интерфейс приложения SpreadSheet. У меня есть меню, панелиинструментов и виджет центральной сетки.

class MySheet(wx.grid.Grid):

def __init__(self, *args, **kw):

super(MySheet, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

nOfRows = 55

nOfCols = 25

158

self.row = self.col = 0

self.CreateGrid(nOfRows, nOfCols)

...

Мы создаем настроенный виджет wx.grid.Grid. На каждом из наших листов будет пятьдесят пять строк идвадцать пять столбцов. Сетка ячеек создается с помощью метода CreateGrid().

control = self.GetParent().GetParent().position

Элемент управления позицией текста показывает выбранную ячейку виджета сетки. Это первый виджетвторой панели инструментов. Находясь внутри класса MySheet, нам нужно получить ссылку на текстовыйэлемент управления, который определен в классе Example. MySheet является дочерним классом notebook. Аnotebook - наследник Example. Таким образом, нам удается добраться до элемента управления позицией текста,дважды вызвав метод GetParent().

notebook = wx.Notebook(self, style=wx.RIGHT)

sheet1 = MySheet(notebook)

sheet2 = MySheet(notebook)

sheet3 = MySheet(notebook)

sheet1.SetFocus()

notebook.AddPage(sheet1, ’Sheet1’)

notebook.AddPage(sheet2, ’Sheet2’)

notebook.AddPage(sheet3, ’Sheet3’)

Три листа создаются и помещаются в виджет notebook.

12.3 Проигрыватель

Следующий пример представляет собой скелет типичного видеоплейера.

Листинг 77: player.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a Player UI .89 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx151617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :

159

Рис. 65: Электронная таблица

2526 s e l f . CreateMenuBar ( )2728 panel = wx . Panel ( s e l f )2930 pnl1 = wx . Panel ( s e l f )31 pnl1 . SetBackgroundColour (wx .BLACK)32 pnl2 = wx . Panel ( s e l f )3334 s l i d e r 1 = wx . S l i d e r ( pnl2 , va lue=18, minValue=0, maxValue=1000)35 pause = wx . BitmapButton ( pnl2 , bitmap=wx . Bitmap ( ’ images /pause . png ’ ) )36 play = wx . BitmapButton ( pnl2 , bitmap=wx . Bitmap ( ’ images / play . png ’ ) )37 forw = wx . BitmapButton ( pnl2 , bitmap=wx . Bitmap ( ’ images / forw . png ’ ) )38 back = wx . BitmapButton ( pnl2 , bitmap=wx . Bitmap ( ’ images /back . png ’ ) )39 vo l = wx . BitmapButton ( pnl2 , bitmap=wx . Bitmap ( ’ images /volume . png ’ ) )40 s l i d e r 2 = wx . S l i d e r ( pnl2 , va lue=1, minValue=0, maxValue=100 ,41 s i z e =(120 , −1) )4243 vbox = wx . BoxSizer (wx .VERTICAL)44 hbox1 = wx . BoxSizer (wx .HORIZONTAL)45 hbox2 = wx . BoxSizer (wx .HORIZONTAL)4647 hbox1 .Add( s l i d e r 1 , propor t ion=1)48 hbox2 .Add( pause )49 hbox2 .Add( play , f l a g=wx .RIGHT, border=5)50 hbox2 .Add( forw , f l a g=wx .LEFT, border=5)

160

51 hbox2 .Add( back )52 hbox2 .Add((−1 , −1) , proport ion=1)53 hbox2 .Add( vo l )54 hbox2 .Add( s l i d e r 2 , f l a g=wx .TOP|wx .LEFT, border=5)5556 vbox .Add( hbox1 , f l a g=wx .EXPAND|wx .BOTTOM, border=10)57 vbox .Add( hbox2 , propor t ion=1, f l a g=wx .EXPAND)58 pnl2 . S e tS i z e r ( vbox )5960 s i z e r = wx . BoxSizer (wx .VERTICAL)61 s i z e r .Add( pnl1 , propor t ion=1, f l a g=wx .EXPAND)62 s i z e r .Add( pnl2 , f l a g=wx .EXPAND|wx .BOTTOM|wx .TOP, border=10)6364 s e l f . SetMinSize ( (350 , 300) )65 s e l f . CreateStatusBar ( )66 s e l f . S e tS i z e r ( s i z e r )6768 s e l f . S e tS i z e ( (350 , 200) )69 s e l f . S e tT i t l e ( ’ Player ’ )70 s e l f . Centre ( )7172 def CreateMenuBar ( s e l f ) :7374 menubar = wx .MenuBar ( )75 f i l em = wx .Menu( )76 play = wx .Menu( )77 view = wx .Menu( )78 t o o l s = wx .Menu( )79 f a v o r i t e s = wx .Menu( )80 help = wx .Menu( )8182 f i l em . Append(wx .ID_ANY, ’&Quit ’ , ’ Quit␣ app l i c a t i o n ’ )8384 menubar . Append( f i l em , ’&F i l e ’ )85 menubar . Append( play , ’&Play ’ )86 menubar . Append( view , ’&View ’ )87 menubar . Append( too l s , ’&Tools ’ )88 menubar . Append( f a v o r i t e s , ’F&avo r i t e s ’ )89 menubar . Append(help , ’&Help ’ )9091 s e l f . SetMenuBar (menubar )929394 def main ( ) :9596 app = wx .App( )97 ex = Example (None )98 ex . Show ( )99 app . MainLoop ( )100101102 i f __name__ == ’__main__ ’ :103 main ( )

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

pnl1 = wx.Panel(self)

pnl1.SetBackgroundColour(wx.BLACK)

Основную область приложения занимает панель с черным фоном.

161

slider1 = wx.Slider(pnl2, value=18, minValue=0, maxValue=1000)

Wx.Slider используется, чтобы показать процент воспроизведения фильма.

pause = wx.BitmapButton(pnl2, bitmap=wx.Bitmap(’images/pause.png’))

play = wx.BitmapButton(pnl2, bitmap=wx.Bitmap(’images/play.png’))

Кнопки с растровым изображением используются в качестве кнопок управления.

self.SetMinSize((350, 300))

Здесь мы устанавливаем минимальный размер плеера. Нет смысла сжимать окно ниже некоторого значе-ния.

Рис. 66: Видеопроигрыватель

12.4 Браузер

В следующем примере мы имитируем внешний вид классического пользовательского интерфейса браузера.

Листинг 78: browser.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This program cr ea t e s a browser UI .8

162

9 author : Jan Bodnar10 web s i t e : z e t code . com11 l a s t e d i t e d : May 201812 """1314 import wx15 from wx . l i b . buttons import GenBitmapTextButton1617 class Example (wx . Frame) :1819 def __init__( s e l f , * args , **kw) :20 super (Example , s e l f ) . __init__(* args , **kw)2122 s e l f . In i tUI ( )2324 def In i tUI ( s e l f ) :2526 s e l f . CreateMenuBar ( )2728 panel = wx . Panel ( s e l f )29 panel . SetBackgroundColour ( ’ white ’ )3031 vbox = wx . BoxSizer (wx .VERTICAL)32 hbox1 = wx . BoxSizer (wx .HORIZONTAL)33 hbox2 = wx . BoxSizer (wx .HORIZONTAL)3435 l i n e 1 = wx . S ta t i cL in e ( panel )36 vbox .Add( l i n e1 , 0 , wx .EXPAND)3738 too lba r1 = wx . Panel ( panel , s i z e =(−1, 30) )3940 back = wx . BitmapButton ( too lbar1 , bitmap=wx . Bitmap ( ’ images /back . png ’ ) ,41 s t y l e=wx .NO_BORDER)42 forward = wx . BitmapButton ( too lbar1 , bitmap=wx . Bitmap ( ’ images / forw . png ’ ) ,43 s t y l e=wx .NO_BORDER)44 r e f r e s h = wx . BitmapButton ( too lbar1 , bitmap=wx . Bitmap ( ’ images / r e f r e s h . png ’ ) ,45 s t y l e=wx .NO_BORDER)46 stop = wx . BitmapButton ( too lbar1 , bitmap=wx . Bitmap ( ’ images / stop . png ’ ) ,47 s t y l e=wx .NO_BORDER)48 home = wx . BitmapButton ( too lbar1 , bitmap=wx . Bitmap ( ’ images /home . png ’ ) ,49 s t y l e=wx .NO_BORDER)50 address = wx .ComboBox( too lbar1 , s i z e =(50 , −1) )51 go = wx . BitmapButton ( too lbar1 , bitmap=wx . Bitmap ( ’ images / play . png ’ ) ,52 s t y l e=wx .NO_BORDER)53 text = wx . TextCtrl ( too lbar1 , s i z e =(150 , −1) )5455 hbox1 .Add( back )56 hbox1 .Add( forward )57 hbox1 .Add( r e f r e s h )58 hbox1 .Add( stop )59 hbox1 .Add(home)60 hbox1 .Add( address , 1 , wx .TOP, 3)61 hbox1 .Add( go , 0 , wx .TOP | wx .LEFT, 3)62 hbox1 .Add( text , 0 , wx .TOP | wx .RIGHT, 3)6364 too lba r1 . S e tS i z e r ( hbox1 )65 vbox .Add( too lbar1 , 0 , wx .EXPAND)66 l i n e = wx . S ta t i cL in e ( panel )67 vbox .Add( l i n e , 0 , wx .EXPAND)6869 too lba r2 = wx . Panel ( panel , s i z e =(−1, 30) )70 bookmark1 = wx . BitmapButton ( too lbar2 , bitmap=wx . Bitmap ( ’ images / love . png ’ ) ,

163

71 s t y l e=wx .NO_BORDER)72 bookmark2 = wx . BitmapButton ( too lbar2 , bitmap=wx . Bitmap ( ’ images /book . png ’ ) ,73 s t y l e=wx .NO_BORDER)74 bookmark3 = wx . BitmapButton ( too lbar2 , bitmap=wx . Bitmap ( ’ images /sound . png ’ ) ,75 s t y l e=wx .NO_BORDER)7677 hbox2 .Add( bookmark1 , f l a g=wx .RIGHT, border=5)78 hbox2 .Add( bookmark2 , f l a g=wx .RIGHT, border=5)79 hbox2 .Add( bookmark3 )8081 too lba r2 . S e tS i z e r ( hbox2 )82 vbox .Add( too lbar2 , 0 , wx .EXPAND)8384 l i n e 2 = wx . S ta t i cL in e ( panel )85 vbox .Add( l i n e2 , 0 , wx .EXPAND)8687 panel . S e tS i z e r ( vbox )8889 s e l f . CreateStatusBar ( )9091 s e l f . S e tT i t l e ( "Browser" )92 s e l f . Centre ( )9394 def CreateMenuBar ( s e l f ) :9596 menubar = wx .MenuBar ( )97 f i l e = wx .Menu( )98 f i l e . Append(wx .ID_ANY, ’&Quit ’ , ’ ’ )99 ed i t = wx .Menu( )100 view = wx .Menu( )101 go = wx .Menu( )102 bookmarks = wx .Menu( )103 t o o l s = wx .Menu( )104 help = wx .Menu( )105106 menubar . Append( f i l e , ’&F i l e ’ )107 menubar . Append( ed i t , ’&Edit ’ )108 menubar . Append( view , ’&View ’ )109 menubar . Append( go , ’&Go ’ )110 menubar . Append( bookmarks , ’&Bookmarks ’ )111 menubar . Append( too l s , ’&Tools ’ )112 menubar . Append(help , ’&Help ’ )113114 s e l f . SetMenuBar (menubar )115116117 def main ( ) :118119 app = wx .App( )120 ex = Example (None )121 ex . Show ( )122 app . MainLoop ( )123124125 i f __name__ == ’__main__ ’ :126 main ( )

Так как мы пытаемся создать поле переменного размера со списком, мы не можем использовать wx.Toolbar.Поэтому мы создаем наши пользовательские панели инструментов на основе wx.Panel.

toolbar1 = wx.Panel(panel, size=(-1, 40))

164

В начале мы создаем простую wx.Panel.

hbox1 = wx.BoxSizer(wx.HORIZONTAL)

...

hbox1.Add(back)

hbox1.Add(forward)

hbox1.Add(refresh)

А затем создаем горизонтальный сайзер и добавляем в него все необходимые кнопки.

hbox1.Add(address, 1, wx.TOP, 4)

Затем мы добавляем поле со списком в сортировщик. Этот вид комбинированного окна обычно называетсяадресной строкой. Обратите внимание, что это единственный виджет, для которого proportion установлена в 1.Это было необходимо для изменения его размера.

line2 = wx.StaticLine(panel)

vbox.Add(line2, 0, wx.EXPAND)

Панели инструментов разделены линией.

Рис. 67: UI браузера

В этой части руководства по wxPython мы создали скелеты нескольких типов приложений.

13 Игра Тетрис в wxPython

Игра Тетрис является одной из самых популярных компьютерных игр, когда-либо созданных. Оригиналь-ная игра была разработана и запрограммирована русским программистом Алексеем Пажитновым в 1985 году.С тех пор Tetris доступен практически на любой компьютерной платформе во множестве вариаций.

Тетрис называют головоломкой с падающими блоками. В этой игре у нас есть семь различных форм, назы-ваемых тетромино: S-образная, Z-образная, T-образная, L-образная, линия, отражённая L и квадрат. Каждаяиз этих форм состоит из четырех квадратов. Формы падают с вершины игрового поля. Цель игры Tetris -перемещать и вращать фигуры, чтобы они подходили друг к другу как можно плотнее. Если нам удастся сфор-мировать заполненную строку, то строка уничтожается, и мы зарабатываем очко. Мы играем в тетрис, пока недостигнем максимума.

165

Рис. 68: Тетромино

wxPython - это инструментарий, предназначенный для создания приложений. Есть и другие библиотеки,нацеленные на создание компьютерных игр. Тем не менее, wxPython и другие наборы приложений могут ис-пользоваться для создания игр.

13.1 Разработка

У нас нет изображений для нашей игры в тетрис, мы рисуем тетромино с помощью API рисования, доступ-ного в wxPython. За каждой компьютерной игрой стоит математическая модель. Так же и в тетрисе.

Некоторые идеи, лежащие в основе игры:

� Мы используем wx.Timer для создания игрового цикла;

� Тетромино рисуются, они не являются изображениями из файловЖ

� Тетромино перемещаются за такт на квадрат (а не пиксель за пикселем)

� Математически доска представляет собой простой список чисел

Листинг 79: tetris.py

1 #!/ usr / b in /env python32 # −*− coding : u t f−8 −*−34 """5 ZetCode wxPython t u t o r i a l67 This i s Te t r i s game c lone in wxPython .89 author : Jan Bodnar10 web s i t e : www. ze t code . com11 l a s t modi f ied : May 201812 """1314 import wx15 import random1617 class Tet r i s (wx . Frame) :1819 def __init__( s e l f , parent ) :20 wx . Frame . __init__( s e l f , parent , s i z e =(180 , 380) ,21 s t y l e=wx .DEFAULT_FRAME_STYLE ^ wx .RESIZE_BORDER ^ wx .MAXIMIZE_BOX)2223 s e l f . in itFrame ( )24

166

25 def in itFrame ( s e l f ) :2627 s e l f . s t a tu sbar = s e l f . CreateStatusBar ( )28 s e l f . s t a tu sbar . SetStatusText ( ’ 0 ’ )29 s e l f . board = Board ( s e l f )30 s e l f . board . SetFocus ( )31 s e l f . board . s t a r t ( )3233 s e l f . S e tT i t l e ( " Te t r i s " )34 s e l f . Centre ( )353637 class Board (wx . Panel ) :3839 BoardWidth = 1040 BoardHeight = 2241 Speed = 30042 ID_TIMER = 14344 def __init__( s e l f , * args , **kw) :4546 super (Board , s e l f ) . __init__(* args , **kw)4748 s e l f . in i tBoard ( )4950 def in i tBoard ( s e l f ) :5152 s e l f . t imer = wx . Timer ( s e l f , Board .ID_TIMER)53 s e l f . i sWai t ingAfte rL ine = False54 s e l f . curPiece = Shape ( )55 s e l f . nextPiece = Shape ( )56 s e l f . curX = 057 s e l f . curY = 058 s e l f . numLinesRemoved = 059 s e l f . board = [ ]6061 s e l f . i s S t a r t e d = False62 s e l f . i sPaused = False6364 s e l f . Bind (wx .EVT_PAINT, s e l f . OnPaint )65 s e l f . Bind (wx .EVT_KEY_DOWN, s e l f .OnKeyDown)66 s e l f . Bind (wx .EVT_TIMER, s e l f . OnTimer , id=Board .ID_TIMER)6768 s e l f . c l earBoard ( )6970 def shapeAt ( s e l f , x , y ) :7172 return s e l f . board [ ( y * Board . BoardWidth ) + x ]7374 def setShapeAt ( s e l f , x , y , shape ) :7576 s e l f . board [ ( y * Board . BoardWidth ) + x ] = shape7778 def squareWidth ( s e l f ) :7980 return s e l f . GetCl i entS i ze ( ) . GetWidth ( ) // Board . BoardWidth8182 def squareHeight ( s e l f ) :8384 return s e l f . GetCl i entS i ze ( ) . GetHeight ( ) // Board . BoardHeight8586 def s t a r t ( s e l f ) :

167

8788 i f s e l f . i sPaused :89 return

9091 s e l f . i s S t a r t e d = True92 s e l f . i sWai t ingAfte rL ine = False93 s e l f . numLinesRemoved = 094 s e l f . c l earBoard ( )9596 s e l f . newPiece ( )97 s e l f . t imer . S ta r t (Board . Speed )9899 def pause ( s e l f ) :100101 i f not s e l f . i s S t a r t e d :102 return

103104 s e l f . i sPaused = not s e l f . i sPaused105 s ta tu sbar = s e l f . GetParent ( ) . s t a tu sbar106107 i f s e l f . i sPaused :108 s e l f . t imer . Stop ( )109 s ta tu sbar . SetStatusText ( ’ paused ’ )110 else :111 s e l f . t imer . S ta r t (Board . Speed )112 s ta tu sbar . SetStatusText ( str ( s e l f . numLinesRemoved ) )113114 s e l f . Refresh ( )115116 def c learBoard ( s e l f ) :117118 for i in range (Board . BoardHeight * Board . BoardWidth ) :119 s e l f . board . append ( Tetrominoes . NoShape )120121 def OnPaint ( s e l f , event ) :122123 dc = wx . PaintDC( s e l f )124125 s i z e = s e l f . GetCl i entS i ze ( )126 boardTop = s i z e . GetHeight ( ) − Board . BoardHeight * s e l f . squareHeight ( )127128 for i in range (Board . BoardHeight ) :129 for j in range (Board . BoardWidth ) :130131 shape = s e l f . shapeAt ( j , Board . BoardHeight − i − 1)132133 i f shape != Tetrominoes . NoShape :134 s e l f . drawSquare ( dc ,135 0 + j * s e l f . squareWidth ( ) ,136 boardTop + i * s e l f . squareHeight ( ) , shape )137138 i f s e l f . curPiece . shape ( ) != Tetrominoes . NoShape :139140 for i in range (4 ) :141142 x = s e l f . curX + s e l f . curPiece . x ( i )143 y = s e l f . curY − s e l f . curPiece . y ( i )144145 s e l f . drawSquare ( dc , 0 + x * s e l f . squareWidth ( ) ,146 boardTop + (Board . BoardHeight − y − 1) * s e l f . squareHeight ( ) ,147 s e l f . curPiece . shape ( ) )148

168

149150 def OnKeyDown( s e l f , event ) :151152 i f not s e l f . i s S t a r t e d or s e l f . curPiece . shape ( ) == Tetrominoes . NoShape :153 event . Skip ( )154 return

155156 keycode = event . GetKeyCode ( )157158 i f keycode == ord ( ’P ’ ) or keycode == ord ( ’p ’ ) :159 s e l f . pause ( )160 return

161162 i f s e l f . i sPaused :163 return

164165 e l i f keycode == wx .WXK_LEFT:166 s e l f . tryMove ( s e l f . curPiece , s e l f . curX − 1 , s e l f . curY )167168 e l i f keycode == wx .WXK_RIGHT:169 s e l f . tryMove ( s e l f . curPiece , s e l f . curX + 1 , s e l f . curY )170171 e l i f keycode == wx .WXK_DOWN:172 s e l f . tryMove ( s e l f . curPiece . rotatedRight ( ) , s e l f . curX , s e l f . curY )173174 e l i f keycode == wx .WXK_UP:175 s e l f . tryMove ( s e l f . curPiece . r o t a t edLe f t ( ) , s e l f . curX , s e l f . curY )176177 e l i f keycode == wx .WXK_SPACE:178 s e l f . dropDown ( )179180 e l i f keycode == ord ( ’D ’ ) or keycode == ord ( ’d ’ ) :181 s e l f . oneLineDown ( )182183 else :184 event . Skip ( )185186187 def OnTimer ( s e l f , event ) :188189 i f event . GetId ( ) == Board .ID_TIMER:190191 i f s e l f . i sWai t ingAfte rL ine :192 s e l f . i sWai t ingAfte rL ine = False193 s e l f . newPiece ( )194195 else :196 s e l f . oneLineDown ( )197198 else :199 event . Skip ( )200201202 def dropDown( s e l f ) :203204 newY = s e l f . curY205206 while newY > 0 :207 i f not s e l f . tryMove ( s e l f . curPiece , s e l f . curX , newY − 1) :208 break

209 newY −= 1210

169

211 s e l f . pieceDropped ( )212213 def oneLineDown ( s e l f ) :214215 i f not s e l f . tryMove ( s e l f . curPiece , s e l f . curX , s e l f . curY − 1) :216 s e l f . pieceDropped ( )217218219 def pieceDropped ( s e l f ) :220221 for i in range (4 ) :222223 x = s e l f . curX + s e l f . curPiece . x ( i )224 y = s e l f . curY − s e l f . curPiece . y ( i )225 s e l f . setShapeAt (x , y , s e l f . curPiece . shape ( ) )226227 s e l f . removeFul lLines ( )228229 i f not s e l f . i sWai t ingAfte rL ine :230 s e l f . newPiece ( )231232233 def removeFul lLines ( s e l f ) :234235 numFullLines = 0236237 s ta tu sbar = s e l f . GetParent ( ) . s t a tu sbar238239 rowsToRemove = [ ]240241 for i in range (Board . BoardHeight ) :242 n = 0243 for j in range (Board . BoardWidth ) :244 i f not s e l f . shapeAt ( j , i ) == Tetrominoes . NoShape :245 n = n + 1246247 i f n == 10 :248 rowsToRemove . append ( i )249250 rowsToRemove . r e v e r s e ( )251252 for m in rowsToRemove :253 for k in range (m, Board . BoardHeight ) :254 for l in range (Board . BoardWidth ) :255 s e l f . setShapeAt ( l , k , s e l f . shapeAt ( l , k + 1) )256257 numFullLines = numFullLines + len ( rowsToRemove )258259 i f numFullLines > 0 :260261 s e l f . numLinesRemoved = s e l f . numLinesRemoved + numFullLines262 s ta tu sbar . SetStatusText ( str ( s e l f . numLinesRemoved ) )263 s e l f . i sWai t ingAfte rL ine = True264 s e l f . curP iece . setShape ( Tetrominoes . NoShape )265 s e l f . Refresh ( )266267268 def newPiece ( s e l f ) :269270 s e l f . curPiece = s e l f . nextPiece271 s ta tu sbar = s e l f . GetParent ( ) . s t a tu sbar272 s e l f . nextPiece . setRandomShape ( )

170

273274 s e l f . curX = Board . BoardWidth // 2 + 1275 s e l f . curY = Board . BoardHeight − 1 + s e l f . curPiece .minY( )276277 i f not s e l f . tryMove ( s e l f . curPiece , s e l f . curX , s e l f . curY ) :278279 s e l f . curPiece . setShape ( Tetrominoes . NoShape )280 s e l f . t imer . Stop ( )281 s e l f . i s S t a r t e d = False282 s ta tu sbar . SetStatusText ( ’Game␣ over ’ )283284285 def tryMove ( s e l f , newPiece , newX, newY) :286287 for i in range (4 ) :288289 x = newX + newPiece . x ( i )290 y = newY − newPiece . y ( i )291292 i f x < 0 or x >= Board . BoardWidth or y < 0 or y >= Board . BoardHeight :293 return False294295 i f s e l f . shapeAt (x , y ) != Tetrominoes . NoShape :296 return False297298 s e l f . curPiece = newPiece299 s e l f . curX = newX300 s e l f . curY = newY301 s e l f . Refresh ( )302303 return True304305306 def drawSquare ( s e l f , dc , x , y , shape ) :307308 c o l o r s = [ ’#000000 ’ , ’#CC6666 ’ , ’#66CC66 ’ , ’#6666CC’ ,309 ’#CCCC66 ’ , ’#CC66CC ’ , ’#66CCCC’ , ’#DAAA00 ’ ]310311 l i g h t = [ ’#000000 ’ , ’#F89FAB ’ , ’#79FC79 ’ , ’#7979FC ’ ,312 ’#FCFC79 ’ , ’#FC79FC ’ , ’#79FCFC ’ , ’#FCC600 ’ ]313314 dark = [ ’#000000 ’ , ’#803C3B ’ , ’#3B803B ’ , ’#3B3B80 ’ ,315 ’#80803B ’ , ’#803B80 ’ , ’#3B8080 ’ , ’#806200 ’ ]316317 pen = wx . Pen( l i g h t [ shape ] )318 pen . SetCap (wx .CAP_PROJECTING)319 dc . SetPen ( pen )320321 dc . DrawLine (x , y + s e l f . squareHeight ( ) − 1 , x , y )322 dc . DrawLine (x , y , x + s e l f . squareWidth ( ) − 1 , y )323324 darkpen = wx . Pen( dark [ shape ] )325 darkpen . SetCap (wx .CAP_PROJECTING)326 dc . SetPen ( darkpen )327328 dc . DrawLine (x + 1 , y + s e l f . squareHeight ( ) − 1 ,329 x + s e l f . squareWidth ( ) − 1 , y + s e l f . squareHeight ( ) − 1)330 dc . DrawLine (x + s e l f . squareWidth ( ) − 1 ,331 y + s e l f . squareHeight ( ) − 1 , x + s e l f . squareWidth ( ) − 1 , y + 1)332333 dc . SetPen (wx .TRANSPARENT_PEN)334 dc . SetBrush (wx . Brush ( c o l o r s [ shape ] ) )

171

335 dc . DrawRectangle ( x + 1 , y + 1 , s e l f . squareWidth ( ) − 2 ,336 s e l f . squareHeight ( ) − 2)337338339 class Tetrominoes ( object ) :340341 NoShape = 0342 ZShape = 1343 SShape = 2344 LineShape = 3345 TShape = 4346 SquareShape = 5347 LShape = 6348 MirroredLShape = 7349350351 class Shape ( object ) :352353 coordsTable = (354 ( ( 0 , 0) , (0 , 0) , (0 , 0) , (0 , 0) ) ,355 ( ( 0 , −1) , (0 , 0) , (−1 , 0) , (−1 , 1) ) ,356 ( ( 0 , −1) , (0 , 0) , (1 , 0) , (1 , 1) ) ,357 ( ( 0 , −1) , (0 , 0) , (0 , 1) , (0 , 2) ) ,358 ((−1 , 0) , (0 , 0) , (1 , 0) , (0 , 1) ) ,359 ( ( 0 , 0) , (1 , 0) , (0 , 1) , (1 , 1) ) ,360 ((−1 , −1) , (0 , −1) , (0 , 0) , (0 , 1) ) ,361 ( ( 1 , −1) , (0 , −1) , (0 , 0) , (0 , 1) )362 )363364 def __init__( s e l f ) :365366 s e l f . coords = [ [ 0 , 0 ] for i in range (4 ) ]367 s e l f . p ieceShape = Tetrominoes . NoShape368369 s e l f . setShape ( Tetrominoes . NoShape )370371 def shape ( s e l f ) :372373 return s e l f . p ieceShape374375 def setShape ( s e l f , shape ) :376377 tab l e = Shape . coordsTable [ shape ]378 for i in range (4 ) :379 for j in range (2 ) :380 s e l f . coords [ i ] [ j ] = tab l e [ i ] [ j ]381382 s e l f . p ieceShape = shape383384 def setRandomShape ( s e l f ) :385386 s e l f . setShape ( random . rand int (1 , 7) )387388 def x ( s e l f , index ) :389390 return s e l f . coords [ index ] [ 0 ]391392 def y ( s e l f , index ) :393394 return s e l f . coords [ index ] [ 1 ]395396 def setX ( s e l f , index , x ) :

172

397398 s e l f . coords [ index ] [ 0 ] = x399400 def setY ( s e l f , index , y ) :401402 s e l f . coords [ index ] [ 1 ] = y403404 def minX( s e l f ) :405406 m = s e l f . coords [ 0 ] [ 0 ]407 for i in range (4 ) :408 m = min(m, s e l f . coords [ i ] [ 0 ] )409410 return m411412 def maxX( s e l f ) :413414 m = s e l f . coords [ 0 ] [ 0 ]415 for i in range (4 ) :416 m = max(m, s e l f . coords [ i ] [ 0 ] )417418 return m419420 def minY( s e l f ) :421422 m = s e l f . coords [ 0 ] [ 1 ]423 for i in range (4 ) :424 m = min(m, s e l f . coords [ i ] [ 1 ] )425426 return m427428 def maxY( s e l f ) :429430 m = s e l f . coords [ 0 ] [ 1 ]431432 for i in range (4 ) :433 m = max(m, s e l f . coords [ i ] [ 1 ] )434435 return m436437 def r o t a t edLe f t ( s e l f ) :438439 i f s e l f . p ieceShape == Tetrominoes . SquareShape :440 return s e l f441442 r e s u l t = Shape ( )443 r e s u l t . p ieceShape = s e l f . p ieceShape444445 for i in range (4 ) :446 r e s u l t . setX ( i , s e l f . y ( i ) )447 r e s u l t . setY ( i , − s e l f . x ( i ) )448449 return r e s u l t450451 def rotatedRight ( s e l f ) :452453 i f s e l f . p ieceShape == Tetrominoes . SquareShape :454 return s e l f455456 r e s u l t = Shape ( )457 r e s u l t . p ieceShape = s e l f . p ieceShape458

173

459 for i in range (4 ) :460 r e s u l t . setX ( i , − s e l f . y ( i ) )461 r e s u l t . setY ( i , s e l f . x ( i ) )462463 return r e s u l t464465466 def main ( ) :467468 app = wx .App( )469 ex = Tet r i s (None )470 ex . Show ( )471 app . MainLoop ( )472473474 i f __name__ == ’__main__ ’ :475 main ( )

Игра немного упрощена, чтобы ее было легче понять. Запускается сразу после запуска приложения. Мыможем приостановить игру, нажав клавишу p. Клавиша пробела опускает падающий кусок тетриса сразу надно. Клавиша d сбрасывает фигуру на одну строку вниз. (Его можно использовать для ускорения падения.)Игра идет с постоянной скоростью, ускорение не реализовано. Счет - это количество удаленных нами строк.

def __init__(self, *args, **kw):

super(Board, self).__init__(*args, **kw)

Примечание для пользователей Windows. Если вы не можете использовать клавиши со стрелками, добавьтеstyle = wx.WANTS_CHARS в конструктор Board.

...

self.curX = 0

self.curY = 0

self.numLinesRemoved = 0

self.board = []

...

Прежде чем начать игровой цикл, мы инициализируем некоторые важные переменные. Переменная self.boardпредставляет собой список чисел от 0 до 7. Она представляет положение падающих тетромино и тетромино оста-ющихся на игровом поле.

for i in range(Board.BoardHeight):

for j in range(Board.BoardWidth):

shape = self.shapeAt(j, Board.BoardHeight - i - 1)

if shape != Tetrominoes.NoShape:

self.drawSquare(dc,

0 + j * self.squareWidth(),

boardTop + i * self.squareHeight(), shape)

Отрисовка игры разделена на два этапа.

На первом этапе мы рисуем все фигуры или остатки тех фигур, которые были сброшены на дно доски. Всеквадраты запоминаются в переменной типа список self.board. Мы получаем к нему доступ с помощью методаshapeAt().

if self.curPiece.shape() != Tetrominoes.NoShape:

174

for i in range(4):

x = self.curX + self.curPiece.x(i)

y = self.curY - self.curPiece.y(i)

self.drawSquare(dc,

0 + x * self.squareWidth(),

boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),

self.curPiece.shape())

На втором этапе выполняется рисование падающих фигур.

elif keycode == wx.WXK_LEFT:

self.tryMove(self.curPiece, self.curX - 1, self.curY)

В методе OnKeyDown() мы проверяем наличие нажатых клавиш. Если мы нажимаем левую клавишу сострелкой, мы пытаемся переместить фигуру влево. Мы говорим «попробуй», потому что фигура может и несдвинуться.

def tryMove(self, newPiece, newX, newY):

for i in range(4):

x = newX + newPiece.x(i)

y = newY - newPiece.y(i)

if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:

return False

if self.shapeAt(x, y) != Tetrominoes.NoShape:

return False

self.curPiece = newPiece

self.curX = newX

self.curY = newY

self.Refresh()

return True

В методе tryMove() мы пытаемся переместить наши фигуры. Если фигура находится на краю доски илипримыкает к какой-то другой фмгуре, мы возвращаем False; в противном случае мы помещаем текущий пада-ющий блок в новую позицию и возвращаем True.

def OnTimer(self, event):

if event.GetId() == Board.ID_TIMER:

if self.isWaitingAfterLine:

self.isWaitingAfterLine = False

self.newPiece()

else:

self.oneLineDown()

else:

event.Skip()

В методе OnTimer() мы либо создаем новую фигуру, после того как предыдущая была опущена на дно,либо перемещаем падающую фигуру на одну строку вниз.

def removeFullLines(self):

175

numFullLines = 0

rowsToRemove = []

for i in range(Board.BoardHeight):

n = 0

for j in range(Board.BoardWidth):

if not self.shapeAt(j, i) == Tetrominoes.NoShape:

n = n + 1

if n == 10:

rowsToRemove.append(i)

rowsToRemove.reverse()

for m in rowsToRemove:

for k in range(m, Board.BoardHeight):

for l in range(Board.BoardWidth):

self.setShapeAt(l, k, self.shapeAt(l, k + 1))

...

Если фигура достигает дна, мы вызываем метод removeFullLines(). Сначала мы определяем все заполненныестроки и удалим их. Мы делаем это, перемещая все строки выше текущей полной строки, чтобы удалить однустроку внизу. Обратите внимание, что мы меняем порядок строк, которые должны быть удалены. В противномслучае это не будет работать правильно. В нашем случае мы используем наивную гравитацию. Это означает,что блоки могут плавать над пустыми промежутками, вопреки законам равновесия.

def newPiece(self):

self.curPiece = self.nextPiece

statusbar = self.GetParent().statusbar

self.nextPiece.setRandomShape()

self.curX = Board.BoardWidth / 2 + 1

self.curY = Board.BoardHeight - 1 + self.curPiece.minY()

if not self.tryMove(self.curPiece, self.curX, self.curY):

self.curPiece.setShape(Tetrominoes.NoShape)

self.timer.Stop()

self.isStarted = False

statusbar.SetStatusText(’Game over’)

Метод newPiece() случайным образом создает новый блок тетриса. Если фигура не может поместиться висходное положение, игра окончена.

Класс Shape сохраняет информацию о фигурах тетриса.

self.coords = [[0,0] for i in range(4)]

При создании фигуры мы создаем пустой список координат. В этом списке сохранятся координаты блоковфигуры тетриса. Например, кортежи (0, -1), (0, 0), (-1, 0), (-1, -1) представляют повернутую S-форму. Следующаядиаграмма иллюстрирует форму.

Когда мы рисуем текущую падающую фигуру, мы рисуем ее в позиции self.curX и self.curY. Затем мысмотрим на таблицу координат и рисуем все четыре квадрата.

Мы написали игру Tetris с помощью wxPython.

176

Рис. 69: Координаты блоков фигуры

Рис. 70: Тетрис

177