Архив рубрики: Python

wxPython in Action. Глава 9. Возможность выбора. Диалоги. (часть 4)

9.5 Использование валидаторов

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

  1. Проверяет данные в элементах перед закрытием диалогов
  2. Автоматически передаёт данные из / в диалог
  3. Проверяет данные по мере их ввода пользователем

9.5.1 Как с помощью валидатора убедиться, что я получил верные данные?

Валидатор представляет из себя подкласс класса wx.Validator, который в свою очередь является абстрактным классом в наборе wxWidget для С++, поэтому Вам необходимо определять свои собственные классы на его основе. Как мы увидим позже, Ваши классы создаются не напрямую от wx.Validator, а посредством наследования эксклюзивного класса wx.PyValidator, что даёт Вам возможность переопределять все родительские методы (среди них надо переопределить метод Clone(), возвращающий копию валидатора).
Сам валидатор должен быть прикреплён к какому-нибудь виджету, что можно сделать двумя способами. Во-первых, если виджет это позволяет, валидатор может быть передан его конструктору в качестве аргумента. В противном случае Вы можете создать виджет и прикрепить к нему валидатор с помощью метода этого виджета SetValidator(validator).
Для того, чтобы осуществлять проверку вводимых данных надо сперва переопределить метод Validate(parent) в вашем подклассе валидаторов. Аргумент parent — это родительское окно вашего виджета, соответственно это либо диалог либо панель. Вы можете использовать его для получения информации с других виджетов или просто проигнорировать. Для получения ссылки на связаный с вашим валидатором виджет используйте метод self.GetWindow(). Значение, возвращаемое методом Validate() имеет логический тип: True означает, что данные введены верно, False — что есть какая-то ошибка. Вы можете использовать wx.MessageBox() для отображения предупреждения в методе Validate(), но Вы не должны делать что-либо, что приводит к созданию новых событий.
Значение, возвращаемое методом Validate() играет роль когда пользователь закрывает диалог, нажимая на кнопку «ОК». Как часть обработки этого события wxPthon вызывает функции Validate() всех виджетов диалога, если эти функции определены. И если хоть одна из этих функций вернёт False, диалог не закроется. Листинг 9.13 показывает простой диалог с валидатором, который проверяет наличие данных во всех текстовых полях:

Листинг 9.13
import wx
about_txt = """
Валидатор, используемый в этом примере проверяет наличие текста в
текстовых полях при нажатии на кнопку "ок" и не даёт Вам закрыть диалог,
если это условие не выполняется."""

# создаём подкласс валидатора
class NotEmptyValidator(wx.PyValidator):
def __init__(self):
wx.PyValidator.__init__(self)

def Clone(self):
"""
Обратите внимание, что каждый валидатор должен реализовать метод Clone().
"""
return NotEmptyValidator()

# метод проверки
def Validate(self, win):
textCtrl = self.GetWindow()
text = textCtrl.GetValue()

if len(text) == 0:
wx.MessageBox("This field must contain some text!", "Error")
textCtrl.SetBackgroundColour("pink")
textCtrl.SetFocus()
textCtrl.Refresh()
return False
else:
textCtrl.SetBackgroundColour(
wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
textCtrl.Refresh()
return True

def TransferToWindow(self):
return True

def TransferFromWindow(self):
return True

class MyDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Validators: validating")

# Создаём поля для ввода текста
about = wx.StaticText(self, -1, about_txt)
name_l = wx.StaticText(self, -1, "Name:")
email_l = wx.StaticText(self, -1, "Email:")
phone_l = wx.StaticText(self, -1, "Phone:")

# используем валидаторы
name_t = wx.TextCtrl(self, validator=NotEmptyValidator())
email_t = wx.TextCtrl(self, validator=NotEmptyValidator())
phone_t = wx.TextCtrl(self, validator=NotEmptyValidator())

# Используем стандартные ID кнопок
okay = wx.Button(self, wx.ID_OK)
okay.SetDefault()
cancel = wx.Button(self, wx.ID_CANCEL)

# размещаем виджеты с помощью координаторов
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(about, 0, wx.ALL, 5)
sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)

fgs = wx.FlexGridSizer(3, 2, 5, 5)
fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
fgs.Add(name_t, 0, wx.EXPAND)
fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
fgs.Add(email_t, 0, wx.EXPAND)
fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
fgs.Add(phone_t, 0, wx.EXPAND)
fgs.AddGrowableCol(1)
sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)

btns = wx.StdDialogButtonSizer()
btns.AddButton(okay)
btns.AddButton(cancel)
btns.Realize()
sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)

self.SetSizer(sizer)
sizer.Fit(self)

app = wx.PySimpleApp()

dlg = MyDialog()
dlg.ShowModal()
dlg.Destroy()

app.MainLoop()
  1. Метод Validate() проверяет, что связанные с ним виджеты содержат какой-нибудь текст. Если же это не так, то цвет их фона меняется на розовый
  2. Для использования валидатора мы прикрепляем его к виджету, передавая в качестве аргумента конструктора виджета экземпляр нашего класса
На рисунке 9.13 показан наш диалог после попытки закрыть его с одним пустым полем.
рис 9.13
Код, который заставляет диалог задействовать валидаторы отсутствует в листинге, так как он является частью системы событий wxPython. В этом заключается ещё одна разница между фреймами и диалогами — диалоги автоматически запускают валидацию, а фреймы нет. Если же Вам понадобиться использовать валидацию во фреймах, то вызовите метод Validate() родительского окна. В таком случае, если у Вас установлен расширеный флаг стиля wx.WS_EX_VALIDATE_RECURSIVELY, будут вызваны методы валидации и всех дочерних виджетов. Как и в случае с диалогом, если хотя бы одна валидация будет провалена, проверка выдаст False. В следующем разделе мы поговорим о том, как с помощью валидаторов передвать данные.

9.5.2 Передача данных с помощью валидаторов

Вторая важная функция валидаторов состоит в том, что они автоматически передают данные в диалог перед его отображением и из диалога при его закрытии. На рисунке 9.14 изображён простейший диалог.
рис 9.14
Для того, чтобы реализовать эту функциональность Вам потребуется переопределить ещё два метода вашего подкласса. Метод TransferToWindow() вызывается автоматически при открытии диалога; его необходимо использовать для передачи данных в виджет, связаный с валидатором. Метод TransferFromWindow() вызывается при закрытии диалога кнопкой «ок» уже после выполненных проверок. Его необходимо использовать для передачи данных из виджета в другое место.
Тот факт, что данные откуда-то и куда-то передаются, означает, что валидатор должен знать об этих откуда-то и куда-то, как показано в листинге 9.14. В этом примере каждый валидатор инициализируется со ссылкой на глобальный словарь данных и с ключом этого словаря. Когда диалог открывается TransferToWindow() читает значение словаря для данного ключа и помещает данные в текстовое поле. При закрытии диалога метод TransferFromWindow() делает то же самое для записи значения в этот словарь. В примере будет отображено окно диалога для того, чтобы показать Вам процесс передачи данных.
Листинг 9.14
import wx
import pprint

about_txt = """
В данном примере валидатор используется для демонстрации
возможности автоматической передачи данных в и из текстового поля
при открытии и закрытии диалога."""

# объявляем валидатор
class DataXferValidator(wx.PyValidator):
def __init__(self, data, key):
wx.PyValidator.__init__(self)
self.data = data
self.key = key

def Clone(self):
"""
Будьте внимательны: каждый валидатор должен реализовать метод Clone()
"""
return DataXferValidator(self.data, self.key)

# не проверяем данные
def Validate(self, win):
return True

# вызывается при открытии диалога
def TransferToWindow(self):
textCtrl = self.GetWindow()
textCtrl.SetValue(self.data.get(self.key, ""))
return True

# вызывается при закрытии диалога
def TransferFromWindow(self):
textCtrl = self.GetWindow()
self.data[self.key] = textCtrl.GetValue()
return True

class MyDialog(wx.Dialog):
def __init__(self, data):
wx.Dialog.__init__(self, None, -1, "Validators: data transfer")

about = wx.StaticText(self, -1, about_txt)
name_l = wx.StaticText(self, -1, "Name:")
email_l = wx.StaticText(self, -1, "Email:")
phone_l = wx.StaticText(self, -1, "Phone:")

name_t = wx.TextCtrl(self,
validator=DataXferValidator(data, "name"))
email_t = wx.TextCtrl(self,
validator=DataXferValidator(data, "email"))
phone_t = wx.TextCtrl(self,
validator=DataXferValidator(data, "phone"))

okay = wx.Button(self, wx.ID_OK)
okay.SetDefault()
cancel = wx.Button(self, wx.ID_CANCEL)

sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(about, 0, wx.ALL, 5)
sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)

fgs = wx.FlexGridSizer(3, 2, 5, 5)
fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
fgs.Add(name_t, 0, wx.EXPAND)
fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
fgs.Add(email_t, 0, wx.EXPAND)
fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
fgs.Add(phone_t, 0, wx.EXPAND)
fgs.AddGrowableCol(1)
sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)

btns = wx.StdDialogButtonSizer()
btns.AddButton(okay)
btns.AddButton(cancel)
btns.Realize()
sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)

self.SetSizer(sizer)
sizer.Fit(self)

app = wx.PySimpleApp()

data = { "name" : "Jordyn Dunn" }
dlg = MyDialog(data)
dlg.ShowModal()
dlg.Destroy()

wx.MessageBox("You entered these values:nn" +
pprint.pformat(data))
app.MainLoop()
Вызов методов передачи данных валидаторов происходит в диалогах автоматически. Для того же, чтобы использовать их вне пределов диалогов вызывайте методы TransferDataFromWindow() и TransferDataToWindow() родительского окна. В таком случае, если у Вас установлен расширеный флаг стиля wx.WS_EX_VALIDATE_RECURSIVELY, будут вызваны методы передачи данных и всех дочерних виджетов.
В следующем разделе мы поговорим о наиболее востребованном использовании валидаторов — проверке данных в процессе их ввода пользователем.

9.5.3 Как проверять данные в процессе ввода?

Кроме всего прочего Вы можете использовать валидаторы для проверки данных, вводимых пользователем, «на лету», ещё до их передачи в виджет. Это очень важная возможность, так как Вы можете уберечь свою программу от попадания в неё плохих данных. На рисунке 9.15 изображён пример такого диалога с текстом, поясняющим эту идею.
рис 9.15
Данный метод проверки данных менее автоматизирован чем те, что мы рассмотрели выше. Вы должны напрямую связать событие введения символов в виджет валидатора с функцией проверки:
self.Bind(wx.EVT_CHAR, self.OnChar)
Виджет сам передаёт событие валидатору. Пример реализации такой возможности приведён в листинге 9.15
Листинг 9.15
import wx
import string

about_txt = """
Валидатор, используемый в данном примере проверяет "на лету" ввод данных
пользователем и не ждёт нажатия кнопки "ок". Первое поле не допускает
ввода цифр, во второе поле можно ввести всё, что угодно, а в третье -
всё, кроме букв.
"""

class CharValidator(wx.PyValidator):
def __init__(self, flag):
wx.PyValidator.__init__(self)
self.flag = flag
# связываем функцию с событием ввода символа
self.Bind(wx.EVT_CHAR, self.OnChar)

def Clone(self):
"""
Будьте внимательны: каждый валидатор должен реализовать метод Clone()
"""
return CharValidator(self.flag)

def Validate(self, win):
return True

def TransferToWindow(self):
return True

def TransferFromWindow(self):
return True

# обработчик ввода данных в виджет
def OnChar(self, evt):
key = chr(evt.GetKeyCode())
if self.flag == "no-alpha" and key in string.letters:
return
if self.flag == "no-digit" and key in string.digits:
return
evt.Skip()

class MyDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Validators: behavior
modification")

# Создаём поля для ввода текста
about = wx.StaticText(self, -1, about_txt)
name_l = wx.StaticText(self, -1, "Name:")
email_l = wx.StaticText(self, -1, "Email:")
phone_l = wx.StaticText(self, -1, "Phone:")

# добавляем валидатор
name_t = wx.TextCtrl(self, validator=CharValidator("no-digit"))
email_t = wx.TextCtrl(self, validator=CharValidator("any"))
phone_t = wx.TextCtrl(self, validator=CharValidator("no-alpha"))
okay = wx.Button(self, wx.ID_OK)
okay.SetDefault()
cancel = wx.Button(self, wx.ID_CANCEL)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(about, 0, wx.ALL, 5)
sizer.Add(wx.StaticLine(self), 0, wx.EXPAND|wx.ALL, 5)

fgs = wx.FlexGridSizer(3, 2, 5, 5)
fgs.Add(name_l, 0, wx.ALIGN_RIGHT)
fgs.Add(name_t, 0, wx.EXPAND)
fgs.Add(email_l, 0, wx.ALIGN_RIGHT)
fgs.Add(email_t, 0, wx.EXPAND)
fgs.Add(phone_l, 0, wx.ALIGN_RIGHT)
fgs.Add(phone_t, 0, wx.EXPAND)
fgs.AddGrowableCol(1)
sizer.Add(fgs, 0, wx.EXPAND|wx.ALL, 5)

btns = wx.StdDialogButtonSizer()
btns.AddButton(okay)
btns.AddButton(cancel)
btns.Realize()
sizer.Add(btns, 0, wx.EXPAND|wx.ALL, 5)

self.SetSizer(sizer)
sizer.Fit(self)

app = wx.PySimpleApp()

dlg = MyDialog()
dlg.ShowModal()
dlg.Destroy()

app.MainLoop()
Так как метод OnChar() находится в валидаторе, он будет вызван ещё до реакции виджета на ввод символа. Мы так же позволяем виджету обработать этот метод, вызывая метод события Skip() (иначе это повлияло бы на нормальную обработку этого события). Валидатор производит проверку допустимости ввода данных и если ввод содержит недопустимые символы метод Skip() не будет вызван и обработка события на этом завершается. Если у Вас возникнет необходимость, Вы можете связать и другие методы

wxPython in Action. Глава 10. Создаём и используем меню в wxPython.

В этой главе Вы узнаете:

  1. Как создавать меню
  2. Как работать с элементами меню
  3. Как добавлять подменю, всплывающие меню и собственные виды меню
  4. Как создавать удобные меню

Трудно представить себе приложение без знакомой полоски сверху со словами «Файл», «Редактировать» и «Справка». Меню настолько привычная часть интерфейса, что многие не уделяют достаточного внимания его созданию, что тоже плохо. Ведь настолько легко и быстро предоставить пользователю доступ к возможностям вашего приложения без меню практически не возможно!

Читать

Реализация импорта в Питоне переписана на чистый Питон

Python 3.3 продолжает радовать. Сегодня в него вошли изменения от Bratt Cannon. Теперь import реализован почти весь на питоне, в библиотеке importlib. Она и раньше присутствовала — но теперь __import__ ведёт напрямую в importlib. Читать

Быстрая сортировка(quicksort, сортировка Хоара)

Быстрая сортировка (англ. quicksort), часто называемая qsort по имени реализации в стандартной библиотеке языка Си — широко известный алгоритм сортировки, разработанный английским информатиком Чарльзом Хоаром в 1960 году. Один из быстрых известных универсальных алгоритмов сортировки массивов (в среднем O(n log n) обменов при упорядочении n элементов), хотя и имеющий ряд недостатков. Например, в худшем случае (на некоторых входных массивах) использует время Ω(n2), что, например, хуже, чем сложность в наихудшем случае алгоритма сортировки слиянием

Функция QuickSort сводит сортировку данного ей массива к разделению (partitioning) этого массива на две группы элементов и сортировке этих двух групп по отдельности.


Пример рекурсивного алгоритма с детерменированным(определённым) выбором оси:

Пусть нам нужно отсортировать участок массива A с p-го по q-й элемент включительно, будем называть этот участок подмассивом и обозначать как A[p..q].

  • ШАГ 1: Возьмем элемент A[p] за ось и «раскидаем» остальные элементы A[(p+1)..q] по разные стороны от него стороны — меньшие влево, большие — вправо, то есть переставим элементы подмассива A[p..q] так, чтобы вначале шли элементы меньше либо равные A[p] потом элементы, больше либо равные A[p]. Назовет этот шаг разделением (partition).

  • ШАГ 2: Пусть r есть новый индекс элемента A[p]. Тогда, если q — p > 2, вызовем функцию сортировки для подмассивов A[p..(r-1)] и A[(r+1)..q].

Ключевая идея алгоритма заключается в процедуре «partition», которая за линейное время от размера массива, осуществляет такую перестановку элементов, относительно некоторой «оси» — заданного значения, равного одному из значений сортируемого интервала массива, что переставленный массив состоит из трех интервалов, идущих по порядку:

  1. Элементы меньшие «оси»
  2. Элементы равные «оси»
  3. Элемент

Сервис онлайн тестирования Quizful

    Недавно в блоге Алена С++ прочитал пост о бесплатном прохождении тестов на сайте Brainbench. Халява была ограниченна по времени, так что я задался вопросом найти бесплатный аналог и на русском. Больше всего мне понравился сайт онлайн тестов Quizful.

 На котором есть достаточно большое количество тестов по различным тематикам:

  • тесты по администрированию (Unix, Linux, Windows, MacOS)
  • тесты по программированию (C++, Java, C#, 1C, PHP, Python, Ruby, Delphi)
  • тесты по базам данных (SQL, Oracle, MS SQL, MySql)
  • тесты по веб-технологиям (HTML, CSS, JavaScript, HTTP)
  • тесты по управлению проектами (экстремальное программирование, Scrum)

Прохождение тестов для меня это возможность узнать свои слабые места и подтянуть знания.

Преимущества сервиса Quizful

  • Это некоммерческий сервис направленный на помощь IT сообществу
  • После прохождения тестов можно просмотреть правильные ответы с объяснениями
  • Сайт поддерживается сообществом и пройдя пару тройку тестов, можно добавить свои вопросы или задачи.  

Автор: Dmitriy Falko
Дата публикации: 2012-03-19T10:08:00.003+04:00

Выражение raise

raise_stmt ::= «raise» [expression [«from» expression]]

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

В противном случае raise выполняет первый expression и получает объект исключения. Он должен являться либо подклассом либо экземпляром BaseException. Если первый expression является именем класса, то создается объект путём вызова класса без передачи аргументов. Читать