Мастер установки – это отличная вещь, когда она работает как следует. Также он может быть очень надоедливым. Многие мастера установки не позволяют нажать на кнопку «Далее» до тех пор, пока вы не закончите настройку или же не заполните все необходимые поля. В данной статье мы рассмотрим, как реализовать подобную блокировку в ваших приложениях.
Как вы отключаете кнопку «Далее» в мастере установки ?
Изначально эта идея появилась у человека, который хотел, чтобы пользователь заполнил обе необходимые строки перед тем, как сможет перейти к следующему диалоговому окну. Это значит, что кнопка «Далее» должна быть отключена до тех пор, пока в обеих строках не появится каких-то данных.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Мне пришла в голову идея использовать wx.Timer, чтобы раз в секунду проверять не появились ли данные в этих строках. Если появились, то таймер запускает хендлер события, который разблокирует кнопку «Далее». Давайте сначала взглянем на панель классов мастера установки. Пример, расположенный ниже, подойдёт для wxPython Classic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# wxPython Classic Edition import wx import wx.wizard class WizardPage(wx.wizard.PyWizardPage): def __init__(self, parent, title): wx.wizard.PyWizardPage.__init__(self, parent) self.next = None self.prev = None self.initializeUI(title) def initializeUI(self, title): # create grid layout manager self.sizer = wx.GridBagSizer() self.SetSizerAndFit(self.sizer) def addWidget(self, widget, pos, span): self.sizer.Add(widget, pos, span, wx.EXPAND) # getters and setters def SetPrev(self, prev): self.prev = prev def SetNext(self, next): self.next = next def GetPrev(self): return self.prev def GetNext(self): return self.next |
Это довольно стандартный подкласс PyWizardPage. Он просто инициализирует кнопки «Назад» и «Далее» ещё до того, как вы добавили страницы. Давайте продвинемся немного дальше и посмотрим, как можно применить этот класс. Добавьте этот код в тот же файл, в который вы записали предыдущий фрагмент:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
class MyWizard(wx.wizard.Wizard): """""" def __init__(self): """Constructor""" wx.wizard.Wizard.__init__(self, None, title="Disable Next") self.SetPageSize((500, 350)) mypage1 = self.create_page1() forward_btn = self.FindWindowById(wx.ID_FORWARD) forward_btn.Disable() self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onUpdate, self.timer) self.timer.Start(1) self.RunWizard(mypage1) def create_page1(self): page1 = WizardPage(self, "Page 1") d = wx.StaticText(page1, label="test") page1.addWidget(d, (2, 1), (1,5)) self.text1 = wx.TextCtrl(page1) page1.addWidget(self.text1, (3,1), (1,5)) self.text2 = wx.TextCtrl(page1) page1.addWidget(self.text2, (4,1), (1,5)) page2 = WizardPage(self, "Page 2") page2.SetName("page2") self.text3 = wx.TextCtrl(page2) self.Bind(wx.wizard.EVT_WIZARD_PAGE_CHANGED, self.onPageChanged) page3 = WizardPage(self, "Page 3") # Set links page2.SetPrev(page1) page1.SetNext(page2) page3.SetPrev(page2) page2.SetNext(page3) return page1 def onPageChanged(self, event): """""" page = event.GetPage() if page.GetName() == "page2": self.text3.SetValue(self.text2.GetValue()) def onUpdate(self, event): """ Делает доступным кнопку «Далее», если обе строки получили новые значения """ value_one = self.text1.GetValue() value_two = self.text2.GetValue() if value_one and value_two: forward_btn = self.FindWindowById(wx.ID_FORWARD) forward_btn.Enable() self.timer.Stop() def main(): """""" wizard = MyWizard() if __name__ == "__main__": app = wx.App(False) main() app.MainLoop() |
Давайте сделаем небольшой перерыв. Первым классом, который мы рассматривали, был MyWizard в котором любое действие не требует условий. MyWizard это подкласс класса Wizard в wxPython. В __init__, мы создаём страницу и появляется кнопка «Далее», так что теперь мы можем её отключить. Затем мы создаём и запускаем объект таймера, привязывая его к методу onUpdate. Наконец-то, мы запускаем мастер установки.
Когда мы создаём страницу мастера установки, мы подтверждаем класс WizardPage. Название этого класса говорит само за себя. Как бы то ни было, мы заканчиваем установкой нескольких виджетов, которые должны быть на странице мастера установки. Из оставшихся битов, интерес представляет лишь метод onUpdate. Он используется для проверки того, ввёл ли пользователь данные в обе строки.
Если он ввёл, мы находим кнопку «Далее», делаем её доступной и останавливаем таймер. Здесь есть потенциальный баг. Что произойдёт, если пользователь возьмёт и сотрёт часть данных уже ПОСЛЕ того, как он заполнил обе строки? Кнопка «Далее» не может самостоятельно отключится. Я предлагаю вам обновлённую версию метода onUpdate, которая решает данную проблему:
1 2 3 4 5 6 7 8 9 10 11 |
def onUpdate(self, event): """ Enables the Next button if both text controls have values """ value_one = self.text1.GetValue() value_two = self.text2.GetValue() if value_one and value_two: forward_btn.Enable() else: if forward_btn.IsEnabled(): forward_btn.Disable() |
В ней мы вовсе не отключаем таймер. Вместо этого, таймер постоянно проверяет значения обеих строк, и если оказывается, что одна из них не содержит данных, а кнопка «Далее» доступна, то хендлер её отключит.
Приступаем к работе с Phoenix
Теперь, когда вы понимаете, как работает код, давайте модифицируем его таким образом, чтобы он работал в последней версии wxPython, которой на данный момент является Phoenix. В Phoenix wx.wizard переместилась в wx.adv. Так что нам нужно изменить код следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# wxPython Phoenix Version import wx from wx.adv import Wizard, WizardPage class MyWizardPage(WizardPage): def __init__(self, parent, title): WizardPage.__init__(self, parent) self.next = None self.prev = None self.initializeUI(title) def initializeUI(self, title): # Создать сетку менеджера слоёв self.sizer = wx.GridBagSizer() self.SetSizer(self.sizer) def addWidget(self, widget, pos, span): self.sizer.Add(widget, pos, span, wx.EXPAND) # Геттеры и сеттеры def SetPrev(self, prev): self.prev = prev def SetNext(self, next): self.next = next def GetPrev(self): return self.prev def GetNext(self): return self.next class MyWizard(Wizard): """""" def __init__(self): """Constructor""" Wizard.__init__(self, None, title="Disable Next") self.SetPageSize((500, 350)) mypage1 = self.create_page1() forward_btn = self.FindWindowById(wx.ID_FORWARD) forward_btn.Disable() self.timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onUpdate, self.timer) self.timer.Start(1) self.RunWizard(mypage1) def create_page1(self): page1 = MyWizardPage(self, "Page 1") d = wx.StaticText(page1, label="test") page1.addWidget(d, (2, 1), (1,5)) self.text1 = wx.TextCtrl(page1) page1.addWidget(self.text1, (3,1), (1,5)) self.text2 = wx.TextCtrl(page1) page1.addWidget(self.text2, (4,1), (1,5)) page2 = MyWizardPage(self, "Page 2") page2.SetName("page2") self.text3 = wx.TextCtrl(page2) self.Bind(wx.adv.EVT_WIZARD_PAGE_CHANGED, self.onPageChanged) page3 = MyWizardPage(self, "Page 3") # Задаём ссылки page2.SetPrev(page1) page1.SetNext(page2) page3.SetPrev(page2) page2.SetNext(page3) return page1 def onPageChanged(self, event): """""" page = event.GetPage() if page.GetName() == "page2": self.text3.SetValue(self.text2.GetValue()) def onUpdate(self, event): """ Активируем кнопку "Далее" если поля содержат данные """ value_one = self.text1.GetValue() value_two = self.text2.GetValue() if value_one and value_two: forward_btn = self.FindWindowById(wx.ID_FORWARD) forward_btn.Enable() self.timer.Stop() def main(): """""" wizard = MyWizard() if __name__ == "__main__": app = wx.App(False) main() app.MainLoop() |
Вы заметите, что теперь мы импортируем классы Wizard и WizardPage напрямую. PyWizardPage больше не является частью wxPython. Мы переместились из wx.wizard.EVT_WIZARD_PAGE_CHANGED в wx.adv.EVT_WIZARD_PAGE_CHANGED. В остальном, код остался прежним.
Итоги
Отключение кнопки «Далее» в wxPython является не сложным процессом, а лишь слегка запутанным. Было бы неплохо, если бы API виджета Wizard давало немного больше доступа к стандартным виджетам, которые оно создаёт. Как бы то ни было, теперь вы знаете как работать с ними и менять их статус. Используйте эти знания по назначению!
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»