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. Как создавать удобные меню

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

Читать

Красивые летние шлепанцы

Красивые летние шлепанцы

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

Красивые летние шлепанцы

Читать

Красивый натюрморт

Красивый натюрморт

Сегодня проводим эксперименты в технике декупаж и создаем красивый натюрморт своими руками в старинном стиле.

натюрморт своими руками

Читать

Садовый очаг

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

у вечернего костра

Хотя садовый очаг у нас не так популярен, как, например, в США, все же, я думаю, мода на него придет и к нам. Рассмотрим строительство садового очага своими руками в пошаговом исполнении, где автор подробно показывает все этапы его сооружения.

Читать

Вклад Ручеек-Онлайн стал самым доступным вкладом от Джей энд Ти Банка

Вклад Ручеек-Онлайн стал самым доступным вкладом от Джей энд Ти Банка

Вклад Ручеек-Онлайн стал самым доступным
вкладом от Джей энд Ти Банка
================================================================================
Александр Сторожук on 21/05/2013 10:59:00

Вклад «Ручеек-Онлайн» стал самым доступным
из предлагаемых банком выгодных вкладов. В
отличие от других продуктов, новый вклад
может порадовать клиентов рядом приятных
особенностей:
*открытие вклада онлайн на сайте Банка
*дистанционное оформление вклада (выезд
менеджера в офис или на дом), *пополнение
банковского счета клиента безналичным
платежом, *ежемесячная выплата процентов,
*высокая процентная ставка при небольшой
сумме вклада.
Что необходимо для открытия вклада
«Ручеек-Онлайн»?
Клиенту Джей энд Ти Банка достаточно
позвонить в офис Банка и договориться о
встрече с представителем. После подписания
документов Клиент может внести денежные
средства на свой банковский счет на сумму
от 100 000 рублей безналичным путем или через
сеть офисов и банкоматов S3 Банка.
Вклад открывается сроком на 12 месяцев, а
процентная ставка составляет 12% годовых.
Проценты начисляются каждый месяц на
банковскую карту клиента, однако по
условиям вклада «Ручеек-Онлайн» не
предусматриваются частичное снятие и
пополнение средств.
Открыть вклад и воспользоваться выгодным
предложением клиенты Банка могут только до
30.06.2013.
Получить более подробную информацию о
вкладе, оформить заявку на открытие вклада
онлайн клиенты Джей энд Ти Банка могут
сделать на странице вклада «Ручеек-Онлайн»
или по телефону 8 (495) 662-45-45.
Кратко о Джей энд Ти Банке
Джей энд Ти Банк – частный коммерческий
банк, входящий в состав международной
инвестиционно-финансовой группы J&T Group.
Офисы Банка находятся в г.Москва и
г.Йошкар-Ола. Банк предоставляет
качественные финансовые продукты, высокие
уровень сервиса и услуг частным лицам
(вклады, кредиты, банковские карты,
депозитные ячейки), компаниям и
предпринимателям (кредиты бизнесу,
кредитные линии, тендерные кредиты,
овердрафт бизнесу).