Версия 2 от 2010-05-22 12:14:28

Убрать это сообщение

Крепкий фундамент для ваших программ

Эта глава включает:

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

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

Что нужно знать об основных объектах?

Давайте начнем с описания этих двух фундаментальных объектов. Прикладной объект управляет главным циклом обработки событий, который является сердцем вашей wxPython программы. Цикл обработки событий будет подробно обсуждаться в главе 3. Пока, достаточно сказать, что запуск главного цикла - это работа прикладного объекта. Кроме того, прикладной объект имеет последнюю возможность обработать события, которые были проигнорированы другими объектами приложения. Без прикладного объекта ваше wxPython приложение выполняться не может.

Главное окно содержит элементы интерфейса, посредством которых пользователь может управлять данными. Например, в текстовом редакторе, главное окно – отображает документ и позволяет, манипулировать фрагментами текста. Точно так же, главное окно web-браузера отображая страницу, позволяет вам управлять ею как объектом данных.

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

Рисунок 2.1 Схематическое изображение структуры приложения wxPython. Показаны отношениям между прикладным объектом, главным окном и циклом обработки событий

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

Как создать и использовать прикладной объект?

Каждое приложение wxPython нуждается в одном прикладном объекте. Прикладной объект должен быть объектом класса wx.App или производного от него. Основная цель прикладного объекта состоит в том, чтобы управлять циклом обработки событий. Этот цикл отвечает на события посылая их соответствующим обработчикам событий. Прикладной объект настолько важен для управления процессами wxPython, что вы не сможете создать никакие другие графические объекты wxPython, пока ваша программа не создаст прикладной объект.

Базовый класс wx.App определяет также свойства, которые являются глобальными для всего приложения. Пока, это - все что вам нужно от прикладного объекта. Собственный прикладной класс может использоваться, если вы должны управлять другими глобальными данными или подключениями (например, подключением к базе данных). В некоторых случаях, вы могли бы также хотеть изменить главный цикл обработки событий для более специализированной обработки. Однако, цикл по умолчанию подойдет почти для всех приложений wxPython, которые вы напишете.

Создание подкласса wx.App

Создание вашего собственного подкласса wx.App настолько просто, что будет хорошей идеей создать его в самом начале разработки вашего приложения. Даже если вы не нуждаетесь ни в каких дополнительных функциональных возможностях. Таким образом, у вас будет подкласс, который позже вы сможете расширить. Чтобы создать и использовать подкласс wx.App, вы должны сделать четыре шага:

  1. Определить подкласс.
  2. Написать метод OnInit()..

  3. Создать объект этого класса в основном разделе вашей программы.
  4. Вызвать метод MainLoop(). Этот метод передает управление wxPython.

Мы видели метод OnInit() в главе 1. Его вызывает система wxPython, когда приложение стартовало, но главный цикл еще не запущен. Этот метод не принимает никаких параметров и возвращает логическое значение. Если возвращаемое значение False, то приложение завершится немедленно. В большинстве случаев этот метод будет возвращать True. Возврат False позволяет надлежащим образом обработать определенные ошибочные ситуации, например, отсутствие необходимого ресурса.

Поскольку метод OnInit() является частью структуры wxPython, любая инициализация, необходимая для вашего собстенного класса, обычно производится в нем, а не в конструкторе init. Если вы решите, что нуждаетесь, по каким-то причинам, в собстенном конструкторе init, не забудьте вызвать конструктор базового класса из своего конструктора:

   1 wx.App.__init__(self)

Как правило, вы создаете по крайней мере один объект фрейм в методе OnInit(), и вызываете метод Show() этого фрейма. Там же вы можете указать, что фрейм является главным окном приложения, вызвав метод SetTopWindow(). Главное окно используется как родительское по умолчанию для окон диалогов, которые созданы без указания родителя. Мы обсудим главное окно в разделе 2.5.

Когда можно не создавать свой подкласс wx.App

Вы не обязаны создавать ваш собственный подкласс wx.App. Такой подкласс обычно определяется для того, чтобы создать главный фрейм в методе OnInit(). Но ничего не мешает вам создать фрейм вне определения прикладного класса, например в секции main. Единственное условие – сначала должен быть создан объект wx.App. Вообще, это - хорошая идея тогда, когда в приложении есть только один фрейм, и определение прикладного класса тривиально. В этом случае, wxPython обеспечивает удобный класс wx.PySimpleApp. Этот класс обеспечивает простейший метод OnInit(), который определен следующим образом:

   1 class PySimpleApp(wx.App):
   2     
   3     def __init__(self, redirect=False, filename=None,
   4                 useBestVisual=False, clearSigInt=True):
   5         wx.App.__init__(self, redirect, filename, useBestVisual,
   6                 clearSigInt)
   7     
   8     def OnInit(self):
   9         return True

При использовании wx.PySimpleApp программа могла бы выглядеть так:

   1 if __name__ == '__main__':
   2     app = wx.PySimpleApp()
   3     frame = MyNewFrame(None)
   4     frame.Show(True)
   5     app.MainLoop()

В первой строке этого фрагмента, вы создаете прикладной объект класса wx.PySimpleApp(). Так как мы используем класс wx.PySimpleApp, мы не имеем собственного метода OnInit, поэтому во второй строке мы создаем фрейм. Фрейм не имеет никакого определенного родителя, поэтому он счтается главным окном приложения. (Естественно, класс MyNewFrame должен быть определен где-то выше.) Третья строка делает фрейм видимым, и в последней строке запускается цикл обработки событий.

Использование wx.PySimpleApp позволяет вам выполнять вашу программу wxPython, не создавая ваш собственный прикладной класс. Но вы должны использовать wx.PySimpleApp, только если приложение достаточно простое, и не нуждается ни в каких других глобальных данных.

На заметку

Соглашения по наименованиям. В то время как wxPython делает фантастическую работу по упрощению использования C++ инструментария, C++ происхождение накладывает свой отпечаток. Ярким примером наследия C++ является соглашение по наименованию методов. В Python, названия методов обычно используют стиль lower_case_ separated_by_underscores или стиль lowerCaseInterCap. Однако, в wxWidgets для методов используется стиль UpperCaseInterCap. Это может немного смущать, если вы привыкли к стилю Python. Чтобы быть последовательными, мы рекомендем вам использовать стиль wxWidgets в ваших собственных классах wxPython. (Естественно, вы должны будете его использовать при вызове методов wxWidgets).

Отметим также, что классы wxPython используют явные Get и Set методы для доступа к свойствам. Это тоже больше стиль C++, потому что большинство программ Python не определило бы специальные методы доступа для простых случаев.

Члены данных классов C++ в большинстве случаев являются частными. Для того чтобы обратиться к данным класса wxPython, вы должны использовать методы доступа. Вы не можете просто использовать название атрибута.

Время жизни прикладного объекта

Жизненный путь вашего прикладного объекта начинается при его создании и заканчивается, когда закрывается последнее окно приложения. Это не обязательно соответствует началу и окончанию сценария Python. Сценарий может выполнить некоторую работу перед созданием прикладного объекта wxPython, и может сделать последующую очистку после выхода из цикла MainLoop(). Однако, вся деятельность wxPython, должна быть выполнена в течение жизни прикладного объекта. И как мы уже упоминали, главный фрейм не может быть создан, пока не создан объект wx.App. (Это - одна из причин, почему мы рекомендуем создать главный фрейм в методе OnInit(). Это гарантирует, что прикладной объект уже существует.)

На рисунке 2.2 показано как созданный прикладной объект вызывает метод OnInit(), что позволяет создать новые оконные объекты. После OnInit(), сценарий вызывает MainLoop(), показывая, что события wxPython теперь обрабатываются. Приложение живет, обрабатывая события, пока не закрыты окна. После закрытия всех окон верхнего уровня, происходит возврат из метода MainLoop(), и прикладной объект разрушается. После этого, сценарий может закрыть любые другие подключения или освободить занятые ресурсы.

Рисунок 2.2 Главные события в жизни приложения wxPython, включая создание и разрушение прикладного объекта, а также начало и окончание сценария, который его окружает

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

Как изменить направление вывода из программы wxPython?

Все программы Python могут осуществлять вывод текста через два стандартных потока: стандартный выходной поток (sys.stdout) и поток для вывода сообщений об ошибках (sys.stderr). Обычный Python направляет стандартные выходные потоки на консоль, с которой был запущен сценарий. Однако, когда создан ваш прикладной объект, вы можете использовать wxPython для управления стандартными потоками и перенаправить вывод в окно. Такое перенаправление по умолчанию установлено для wxPython под Windows. В Unix системах, где существует окно консоли, wxPython по умолчанию не управляет стандартными потоками. Во всех системах может быть явно определено направление вывода, после создания прикладного объекта. Мы рекомендуем использовать эту особенность в своих интересах, и всегда определять направление стандартного вывода, чтобы избежать любых проблем поведения на различных платформах.

Перенаправление стандартного вывода

Если wxPython управляет стандартными потоками, то текст, посланный потокам любым из механизмов вывода (вывод оператора print или системные сообщения), будет переадресован в отдельный фрейм wxPython. Текст, посланный потокам перед созданим прикладного объекта wxPython или после его разрушения, обрабатывается как обычно. Листинг 2.1, демонстрирует жизненный цикл прикладного объекта и переназначение стандартных потоков вывода.

Листинг 2.1 Пример, показывающий переназначение выходного потока

   1 #!/usr/bin/env python
   2 
   3 import wx
   4 import sys
   5 
   6 class Frame(wx.Frame):
   7 
   8     def __init__(self, parent, id, title):
   9         print "Frame __init__"
  10         wx.Frame.__init__(self, parent, id, title)
  11 
  12 class App(wx.App):
  13 
  14     def __init__(self, redirect=True, filename=None):
  15         print "App __init__"
  16         wx.App.__init__(self, redirect, filename)
  17 
  18     def OnInit(self):
  19         # Writing to stdout
  20         print "OnInit"
  21         # Creating the frame
  22         self.frame = Frame(parent=None, id=-1, title='Startup')
  23         self.frame.Show()
  24         self.SetTopWindow(self.frame)
  25         # Writing to stderr
  26         print >> sys.stderr, "A pretend error message"
  27         return True
  28 
  29     def OnExit(self):
  30         print "OnExit"
  31 
  32 if __name__ == '__main__':
  33     # (1) Text redirection starts here
  34     app = App(redirect=True)
  35     print "before MainLoop"
  36     # (2) The main event loop is entered here
  37     app.MainLoop()
  38     print "after MainLoop"

(1) В этой строке создается прикладной объект. После этой строки, весь текст, посланный в stderr или stdout может быть переадресован в фрейм wxPython. Параметр конструктора определяет, имеет ли место переназначение.

(2) При выполнении, приложение создает пустой фрейм, и дополнительно создает фрейм с переадресованным выводом, как показано на рисунке 2.3. Заметьте, что сообщения стандартных потоков направлены в окно.

Рисунок 2.3 окно stdout/stderr, созданное программой из листинга 2.1

После выполнения программы, вы увидите на своей консоли следующий вывод:

   1 App __init__
   2 after MainLoop

Первая строка выведена перед открытием фреймов. Вторая строка выведена после закрытия всех фреймов.

Глядя на консоль и окно вывода, мы можем проследить жизненный цикл приложения.

Первый пункт (Script start) рисунка 2.2 cоответствует началу выполнения секции main. Переход к следующему пункту происходит немедленно и отмечен в листинге цифрой (1). При создании прикладного объекта вызывается метод wx.App.init(). Потом управление передается в OnInit(), который wxPython вызывает автоматически. Далее, программа вызывает wx.Frame. init(). Наконец, управление возвращается назад в секцию main, где вызывается MainLoop(), что соответствует третьему пункту на рисунке 2.2. При выходе из главного цикла wxPython вызывает wx.App.OnExit(), переходя к четвертому пункту диаграммы и далее к концу сценария.

“Минуточку,” скажете вы, “сообщение OnExit() не отображалось ни в окне ни на консоли.” На самом деле сообщение действительно отображается в окне wxPython, но происходит это непосредственно перед закрытием окна, поэтому оно и не попало на скриншот.

Быстрое исчезновение сообщения OnExit() – это симптом большой проблемы связанной с фреймом вывода. Хотя это может быть полезно во время разработки, вы, скорее всего, не захотите, чтобы фрейм с ошибками выскакивал во время выполнения программы. Кроме того, если ошибка произойдет в методе OnInit(), сообщение об ошибке будет направлено в окно, но ошибка заставит приложение завершиться, так как OnInit() возвратит значение False. В результате сообщение исчезнет быстрее, чем вы его заметите.

Изменение перенаправления по умолчанию

Чтобы изменить это поведение, wxPython позволяет вам, при создании прикладного объекта, установить два параметра. Первый параметр: redirect. Вывод будет перенаправлен в окно, если присвоить ему значение True. Если значение - False, то для вывода используется консоль. Если redirect - True, то можно задать второй параметр: имя файла вывода. Если параметр задан, то весь вывод будет перенаправлен в файл, а не в окно wxPython. Поэтому, изменив строку, в которой создается wx.App в листинге 2.1:

   1 app = App(False)

мы направляем весь вывод на консоль:

   1 App __init__
   2 OnInit
   3 Frame __init__
   4 A pretend error message
   5 before MainLoop
   6 OnExit
   7 after MainLoop

Заметьте, что сообщение OnExit() здесь отображено. Изменение строки на:

   1 app = App(True, "output")

направит вывод в файл output. Причем сообщения App init и after MainLoop будут все еще выводиться на консоль, потому что они происходят вне периода жизни объекта wx.App, управляющего потоками.

Как завершается приложение wxPython?

Когда пользователь закроет последнее окно верхнего уровня в вашем приложении, оно завершится. Окном верхнего уровня мы называем любой фрейм не имеющий родителя, а не только фрейм определенный методом SetTopWindow(), включая любые фреймы непосредственно созданные wxPython. Например, в листинге 2.1, приложение не завершится до тех пор пока и главный фрейм и фрейм переназначения вывода не будут закрыты. Несмотря на то, что только главный фрейм зарегистрирован, используя SetTopWindow(), и даже несмотря на то, что приложение явно не создает окно вывода. Чтобы вызывать завершение программы, вы должны вызвать метод Close() для всех окон верхнего уровня.

Управление нормальным завершением

В процессе завершения, wxPython заботится об удалении всех окон и освобождении ресурсов. Но вы имеете возможность выполнить собственную очистку в процессе выхода. Метод OnExit() вашего подкласса wx.App вызывается после закрытия последнего окна, но перед внутренней очисткой wxPython. Вы можете использовать этот метод, чтобы очистить любые не-wxPython ресурсы, которые вы создали сами (например, подключения к базе данных). Метод OnExit() вызывается, даже если приложение закрыто с использованием wx.Exit().

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

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

Управление аварийным завершением

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

Есть два механизма для выхода из приложения wxPython в чрезвычайной ситуации. Вы можете вызвать метод ExitMainLoop() класса wx.App. Этот метод прерывает главный цикл, заставляя передать управление из функции MainLoop() далее. Это эквивалентно закрытию всех окон верхнего уровня.

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

Иногда, ваше приложение должно завершится из-за внешнего события. Например, когда операционная система перезагружается или собирается отключить пользователя. В этом случае, ваше приложение должно отреагировать на это сохранив документы, завершив соединение с базой данных или еще как-нибудь. Если в вашем приложении есть обработчик связанный с событием wx.EVT_QUERY_END_SESSION, то он будет вызван wxPython во время завершения. (Мы покажем, как связать события с обработчиками позже в этой главе, и более подробно в главе 3.) Событие с параметром wx.CloseEvent. Событие закрытия может не позволить приложению завершится. Чтобы узнать возможно ли завершение, используйте метод CanVeto(). Приложение может запретить завершение вызвав метод Veto(). Он применяется, если вы не можете успешно сохранить или закрыть все ресурсы. Обработчик по умолчанию для события wx.EVT_QUERY_END_SESSION вызывает метод Close() для всех окон верхнего уровня. Если любой метод Close() возвращает False тогда, приложение пытается запретить свое завершение.

Как создать и использовать окно верхнего уровня?

Окно верхнего уровня – это виджет (обычно это фрейм), который не содержится ни в каком другом виджете вашего приложения. Указав на него, обычный пользователь скажет: “Это - программа”. Окно верхнего уровня обычно является главным окном вашего приложения. Оно содержит виджеты интерфейса, с которыми взаимодействует пользователь. Как мы уже говорили, выход из приложения происходит, когда закрываются все окна верхнего уровня.

Ваше приложение должно иметь по крайней мере одно окно верхнего уровня. Обычно это объект класса производного от wx.Frame, хотя это может быть и подкласс wx.Dialog. Большую часть времени, вы будете определять собственные подклассы wx.Frame для использования в вашем приложении. Однако, есть множество предопределенных подклассов wx.Dialog, которые обеспечивают стандартные диалоги для вашего приложения.

Окно верхнего уровня – это любой виджет в вашем приложении, который не связан с родительским контейнером. Ваше приложение должно иметь по крайней мере одно такое окно, Но, в принципе, в приложении может быть столько таких окон, сколько вам нужно. Однако, только одно из этих окон может быть обявлено главным используя метод SetTopWindow(). Если вы не определите главное окно при помощи SetTopWindow, то главным считается фрейм верхнего уровня, который создан первым. Таким образом, явное определение главного окна не всегда необходимо. Вы не нуждаетесь в нем если, например, у вас только одно окно в приложении. Повторный вызов SetTopWindow() заменят текущее главное окно, так как приложение может иметь только одно главное окно одновременно.

Работа с wx.Frame

То, что бычный пользователь GUI назвал бы окном, в терминологии wxPython называается фрейм. То есть фрейм – это контейнер, который пользователь может перемещать по экрану, и который включает такие элементы оформления как заголовок, строка меню и др. Класс wx.Frame - базовый класс всех фреймов в wxPython. Есть также несколько специализированных подклассов wx.Frame, которые вы можете использовать. Этот раздел даст краткий обзор класса wx.Frame — достаточный для того, чтобы начать его использовать. Более полное описание класса wx.Frame будет представлено в главе 8.

Когда вы создаете подклассы wx.Frame, метод init(), вашего класса должен вызвать конструктор базового класса wx.Frame.init(). Конструктор имеет следующие параметры.

   1 wx.Frame(parent, id=-1, title="", pos=wx.DefaultPosition,
   2     size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE,
   3     name="frame")

Большинство параметров имеют разумные значения по умолчанию. Мы будем встречать параметры, подобные этим и в других конструкторах виджетов — это типичный набор параметров в wxPython. Таблица 2.1 описывает каждый из параметров.

Таблица 2.1 Параметра конструктора wx.Frame

Параметр

Описание

parent

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

id

Идентификатор (ID) wxPython для нового окна. Вы можете задать его явно, или указать значение -1, которое заставит wxPython автоматически создать новый ID. Для получения дополнительной информации cм. раздел “Работа с ID в wxPython”.

title

Текст заголовка окна.

pos

Объект wx.Point, определяющий, в какой точке экрана расположен левый верхний угол нового окна. Как обычно в графических приложениях, (0, 0) – это левый верхний угол экрана монитора. Значение по умолчанию - (-1, -1), заставляет операционную систему выбрать место расположения окна. Для получения дополнительной информации cм. раздел “Работа с wx.Size и wx.Point”.

size

Объект wx.Size, определяющий начальные размеры окна. Значение по умолчанию - (-1, -1), заставляет систему определять начальные размеры. Для получения дополнительной информации cм. раздел “Работа с wx.Size и wx.Point”.

style

Битовая маска констант, определяющих стиль окна. Вы можете использовать оператор поразрядное или (|), чтобы объединить их. Рекомендации по использованию см. в разделе “Работа со стилями wx.Frame”.

name

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

Обратите внимание, это параметры базового конструктора wx.Frame.init(). Список параметров конструктора вашего класса может быть другим. Это позволяет гарантированно установить значения по умолчанию для вашего собственного фрейма. Например, если бы вы хотели, чтобы ваш фрейм всегда был квадратом 300 на 300 пикселей, конструктор вашего класса не имел бы параметра size и явно задавал бы размер фрейма. В листинге 2.2 показан класс фрейма, который не позволяет ни одному из атрибутов окна быть переданным как параметр.

Листинг 2.2 Подкласс wx.Frame, который устанавливает собственные значения по умолчанию

   1 class MyFrame(wx.Frame):
   2 
   3     def __init__(self):
   4         wx.Frame.__init__(self, None, -1, "My Friendly Window",
   5             (100, 100), (100, 100))

В листинге 2.2, метод init() класса MyFrame не принимает никаких параметров. Это означает, что пользователи MyFrame не могут переопределить жестко заданные аргументы, которые MyFrame.init() передает в конструктор базового класса. Хотя, решительный пользователь вашего класса может изменить эти значения, вызывая set-методы, после того, как фрейм создан.

Работа с идентификаторами в wxPython

В таблице 2.1 упоминается идентификатор ID для нового фрейма. ID – это атрибут всех виджетов в wxPython, поэтому необходимо четко представлять себе как они работают. Каждый виджет в приложении wxPython имеет свой идентификатор. Идентификаторы должны быть уникальными в пределах одного фрейма. Однако, мы рекомендуем, чтобы ваши идентификаторы были уникальными во всем приложении. Это предотвратит ошибки и беспорядок при обработке событий. Хотя, есть предопределенные стандартные ID, которые будут повторяться в вашем приложении (например, wx.ID_OK и wx. ID_CANCEL является идентификаторами кнопок OK И Cancel в диалоговом окне). Обычно повторное использование стандартных идентификаторов не вызывает проблем в вашем приложении, пока вы используете их стандартным образом. ID - обычно второй параметр в конструкторе виджета wxPython, после параметра parent. Самое важное, при использовании ID, это создать уникальную связь между событием и функцией, которая вызывается для обработки этого события. Если ID дублируются, возможен вызов не той функции для обработки события.

Есть три способа создания идентификатора виджета:

  1. Явно передать положительное целое число в конструктор.
  2. Использовать функцию wx.NewId().

  3. Передать в конструктор виджета константу wx.ID_ANY или -1.

Явный выбор ID

Вы можете явно передать целое положительное число в конструктор, и это число станет идентификатором виджета. При этом вы сами должны отслеживать не дублируются ли ID в пределах фрейма, или используйте одну из предопределенных констант. Вы можете проверить, не используется ли ваш явный ID в другом месте приложения, вызвав функцию wx.RegisterId(). При явном задании ID, вы должны использовать числа из диапазона, ограниченного константами wx.ID_LOWEST и wx.ID_HIGHEST.

Использование функции NewID()

Однако, обеспечение уникальности чисел ID может быстро стать обременительным. Вместо этого вы можете поручить wxPython создавать идентификаторы, использовав функцию wx.NewId():

   1 id = wx.NewId()
   2 frame = wx.Frame.__init__(None, id)

Использование константы

Многие конструкторы принимают в качестве параметров константы wx.ID_ANY или -1, что заставляет wxPython генерировать новый ID автоматически. Чтобы узнать значение ID используйте метод GetId():

   1 frame = wx.Frame.__init__(None, -1)
   2 id = frame.GetId()

Между этими стилями нет никакого функционального различия.

Работа с wx.Size и wx.Point

В списке параметров конструктора wx.Frame (таблица 2.1) упоминаются классы wx.Size и wx.Point. Эти два класса используются очень часто при программировании в wxPython, поэтому эти классы были немного доработаны.

Классы wx.Size и wx.Point очень похожи. Класс wx.Point представляет точку или позицию. Конструктор принимает два параметра: координаты x и y. Значения по умолчанию для обоих параметров – нуль.

   1 point = wx.Point(10, 12)

Чтобы установить обе координаты в одной строке, используйте функцию Set(x, y). Чтобы получить координаты, используйте метод Get(), который возвращает два значения как кортеж Python. В отличие от большинства wxWidgets классов, координаты точки wx.Point доступны и по отдельности как простые атрибуты Python:

   1 x = point.x
   2 y = point.y

Кроме того, для объектов wx.Point определены операции сложения, вычитания и сравнения точно так же, как для других объектов Python. Например:

   1 x = wx.Point(2, 3)
   2 y = wx.Point(5, 7)
   3 z = x + y
   4 bigger = x > y

В классе wx.Point координаты - это целые числа. Если вы нуждаетесь в координатах с плавающей запятой, можете использовать класс wx.RealPoint, который работает почти так же как wx.Point.

Класс wx.Size почти идентичен wx.Point, за исключением того, что переменные объекта называются width и height вместо x и y.

Когда объект wx.Point или wx.Size понадобится где-нибудь в вашей программе, например в конструкторе для другого объекта, вы не обязаны создавать объект явно. Вместо этого вы можете передать конструктору кортеж Python, а wxPython неявно создаст объект wx.Size или wx.Point:

   1 frame = wx.Frame(None, -1, pos=(10, 10), size=(100, 100))

Это работает не только в конструкторах, но и везде где ожидаются объекты wx.Point или wx.Size. Вы даже можете написать что-то типа этого:

   1 frame.SetPosition((2, 3))

Работа с со стилями wx.Frame

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

Что такое битовая маска?

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

Каждому атрибуту соответствует константа. Константа имеет значение равное степени двойки, как бы включая один бит в двоичном представлении. Значение битовой маски – это сумма всех признаков, которые "включены". Таким образом, каждый признак соответствует единственному биту в полной сумме, позволяя весь набор признаков сохранить в единственном целом числе. Например, если признак a=1 (двоичное 0001), b=2 (0010), c=4 (0100), и d=8 (1000), то любая комбинация этих чисел имеет уникальную сумму, которая может быть сохранена в целом числе. Сумма a и c равна 5 (двоичное 0101), в то же время сумма b, c, и d будет 14 (двоичное 1110). В wxPython, все признаки имеют символические константы.

Определить стиль для всех виджетов wxPython можно, передавая битовую маску в параметре style конструктора. Некоторые виджеты имеют метод SetStyle(), который позволяет изменить стиль уже после того, как виджет создан. Все индивидуальные элементы стиля, имеют предопределенные константы (например, wx.MINIMIZE_BOX). Чтобы комбинировать стили, вы можете использовать оператор поразрядное ИЛИ ( | ). Например, константа wx.DEFAULT_FRAME_STYLE определена как комбинация таких стилевых элементов:

   1 wx.MAXIMIZE_BOX | wx.MINIMIZE_BOX | wx.RESIZE_BORDER |
   2 wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX

Чтобы исключить биты из составного стиля, используйте оператор XOR ( ^ ). Например, чтобы создать окно, со стилем по умолчанию, размеры которого нельзя изменять, вы могли бы сделать так:

   1 wx.DEFAULT_FRAME_STYLE ^ (wx.RESIZE_BORDER | wx.MINIMIZE_BOX |
   2 wx.MAXIMIZE_BOX)

Настоятельно рекомендуем использовать стиль по умолчанию для фреймов верхнего уровня, чтобы пользователь мог легко распознать их. По крайней мере, вы должны гарантировать, что есть некий способ закрыть окно верхнего уровня. Легче всего это сделать, включив в стиль константу wx.SYSTEM_MENU. Также знайте, что, неосторожно используя операцию AND (&), вместо OR, вы можете получить фрейм без рамки, который нельзя перемещать, и у которого нельзя изменить размеры. Это, конечно, не рекомендуется.

В таблице 2.2 приведен список самых важных стилей для wx.Frame.

Таблица 2.2 Наиболее часто используемые параметры стиля для wx.Frame

Перевод: Володеев Сергей