2016-09-12 4 views
0

Я работаю над графическим интерфейсом, используя wxPython. Этот графический интерфейс должен динамически запрашивать у пользователя несколько наборов входов в одном окне, обновляя окно с помощью нового набора входов после нажатия кнопки «ok».wxPython: приостановить основной скрипт и ждать нажатия кнопки

Для этого у меня есть цикл for, который вызывает функцию, которая запрашивает элементы управления ввода в окне. Я попытался использовать класс threading.Event, позволяя сценарию дождаться нажатия кнопки, но сбой python.

Здесь ниже представлена ​​интересующая часть кода.

 # Iterate through parameters 
     self.cv = threading.Event() 
     for key in parameters: 
      self.input_sizer = [None]*len(parameters[key]) 
      self.cv.clear() 
      self.make_widgets(key, parameters[key]) 
      self.cv.wait() 
     # Panel final settings 
     self.panel.SetSizer(self.main_sizer) 
     self.main_sizer.SetSizeHints(self) 

    def make_widgets(self, request, parameters): 
     # Function for widget prompting on the panel 
     self.main_sizer = wx.BoxSizer(wx.VERTICAL) 
     self.controls = {"request": wx.StaticText(self.panel, label="Please type the new alias' " + request), 
         "txt": [None]*len(parameters), 
         "tc": [None]*len(parameters)} 
     self.main_sizer.Add(self.controls["request"], 1, wx.ALL, 10) 
     for i in range(len(parameters)): 
      self.input_sizer[i] = wx.BoxSizer(wx.HORIZONTAL) 
      self.main_sizer.Add(self.input_sizer[i], 1, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) 
      # Text 
      self.controls['txt'][i] = wx.StaticText(self.panel, label=parameters[i]) 
      self.input_sizer[i].Add(self.controls["txt"][i], 0, wx.ALIGN_CENTER | wx.LEFT, 10) 
      # Input 
      self.controls['tc'][i] = wx.TextCtrl(self.panel) 
      self.input_sizer[i].Add(self.controls["tc"][i], 0, wx.ALIGN_CENTER) 
     # Ok button 
     self.button_ok = wx.Button(self.panel, label="Ok") 
     self.main_sizer.Add(self.button_ok, 1, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) 
     self.button_ok.Bind(wx.EVT_BUTTON, self.carry_on) 

    def carry_on(self, event): 
     self.cv.set() 

У кого-нибудь есть идеи?

Спасибо, здесь ниже приведен полный код.

import wx 
from collections import OrderedDict 
import threading 


############################################################################ 
class AddMainName(wx.Frame): 
    # Class definition for main Name addition 
    def __init__(self, parent, title): 
     # Constructor 
     super().__init__(parent, title=title) 
     # Run the name addition 
     self.set_alias_name() 
     self.Centre() 
     self.Show() 

    def set_alias_name(self): 
     # Function for definition of alias name 
     # Panel 
     self.panel = wx.Panel(self) 
     # Lists of parameters to be typed 
     parameters = OrderedDict([("name parts", [ 
      "Prefix", "Measurement", "Direction", "Item", "Location", "Descriptor", "Frame", "RTorigin" 
      ]), 
            ("SI units", [ 
             "A", "cd", "K", "kg", "m", "mol", "Offset", "rad", "s", "ScaleFactor" 
             ]), 
            ("normal display unit", [ 
             "A", "cd", "K", "kg", "m", "mol", "Offset", "rad", "s", "ScaleFactor" 
            ]), 
            ("other parameters", [ 
             "Meaning", "Orientation Convention", "Contact Person", "Note", 
            ])]) 
     # Iterate through parameters 
     self.cv = threading.Event() 
     for key in parameters: 
      self.input_sizer = [None]*len(parameters[key]) 
      self.cv.clear() 
      self.make_widgets(key, parameters[key]) 
      self.cv.wait() 
     # Panel final settings 
     self.panel.SetSizer(self.main_sizer) 
     self.main_sizer.SetSizeHints(self) 

    def make_widgets(self, request, parameters): 
     # Function for widget prompting on the panel 
     self.main_sizer = wx.BoxSizer(wx.VERTICAL) 
     self.controls = {"request": wx.StaticText(self.panel, label="Please type the new alias' " + request), 
         "txt": [None]*len(parameters), 
         "tc": [None]*len(parameters)} 
     self.main_sizer.Add(self.controls["request"], 1, wx.ALL, 10) 
     for i in range(len(parameters)): 
      self.input_sizer[i] = wx.BoxSizer(wx.HORIZONTAL) 
      self.main_sizer.Add(self.input_sizer[i], 1, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) 
      # Text 
      self.controls['txt'][i] = wx.StaticText(self.panel, label=parameters[i]) 
      self.input_sizer[i].Add(self.controls["txt"][i], 0, wx.ALIGN_CENTER | wx.LEFT, 10) 
      # Input 
      self.controls['tc'][i] = wx.TextCtrl(self.panel) 
      self.input_sizer[i].Add(self.controls["tc"][i], 0, wx.ALIGN_CENTER) 
     # Ok button 
     self.button_ok = wx.Button(self.panel, label="Ok") 
     self.main_sizer.Add(self.button_ok, 1, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) 
     self.button_ok.Bind(wx.EVT_BUTTON, self.carry_on) 

    def carry_on(self, event): 
     self.cv.set() 


############################################################################ 
class AddCommonName(wx.Frame): 
    # Class definition for common name addition 
    def __init__(self, parent, title): 
     # Constructor 
     super().__init__(parent, title=title, 
         size=(400, 150)) 
     # Run the name addition 
     self.set_alias() 
     self.Centre() 
     self.Show() 

    def set_alias(self): 
     panel = wx.Panel(self) 
     sizer = wx.GridBagSizer(5, 5) 
     text1 = wx.StaticText(panel, label="Please type the new alias name.") 
     sizer.Add(text1, pos=(0, 0), flag=wx.TOP | wx.LEFT | wx.BOTTOM, 
          border=15) 
     panel.SetSizer(sizer) 


############################################################################ 
class AliasGUI(wx.Frame): 

    def __init__(self, parent, title): 
     # Constructor 
     super().__init__(parent, title=title) 
     # Run first dialog of the GUI 
     self.begin() 
     self.Centre() 
     self.Show() 

    def begin(self): 
     # Panel initial settings 
     panel_start = wx.Panel(self) 
     main_sizer = wx.BoxSizer(wx.VERTICAL) 
     # Alias type selection 
     text_start = wx.StaticText(panel_start, label="Please select the type of the new alias.") 
     main_sizer.Add(text_start, 1, wx.ALL, 10) 
     # Buttons 
     buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) 
     main_sizer.Add(buttons_sizer, 1, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) 
     # Main name button 
     button_main_name = wx.Button(panel_start, label="Main Name") 
     buttons_sizer.Add(button_main_name, 0, wx.ALIGN_CENTER | wx.RIGHT, 10) 
     button_main_name.Bind(wx.EVT_BUTTON, self.main_name) 
     # Common name button 
     button_common_name = wx.Button(panel_start, label="Common Name") 
     buttons_sizer.Add(button_common_name, 0, wx.ALIGN_CENTER) 
     button_common_name.Bind(wx.EVT_BUTTON, self.common_name) 
     # Panel final settings 
     panel_start.SetSizer(main_sizer) 
     main_sizer.SetSizeHints(self) 

    @staticmethod 
    def main_name(event): 
     # Function for main name addition 
     frame = AddMainName(None, title="New Main Name") 
     frame.Centre() 
     frame.Show() 

    @staticmethod 
    def common_name(event): 
     # Function for common name addition 
     frame = AddCommonName(None, title="New Common Name") 
     frame.Centre() 
     frame.Show() 


# GUI execution 
if __name__ == '__main__': 
    app = wx.App() 
    AliasGUI(None, title="Aliases Management GUI") 
    app.MainLoop() 

ответ

1

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

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

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

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

Edit:

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

Создать Dialog Подкласс:

class getDataDlg(wx.Dialog): 

    def __init__(self, *args, **kwargs): 
     self.parameters = parameters = kwargs.pop('parameters', None) 
     request = kwargs.pop('request', None) 
     assert parameters is not None 
     assert request is not None 

     wx.Dialog.__init__(self, *args, **kwargs) 

     self.data = {} 

     vsizer = wx.BoxSizer(wx.VERTICAL) 
     reqLbl = wx.StaticText(self, label="Please type new alias {}".format(request)) 
     vsizer.Add(reqLbl, 1, wx.ALL, 10) 
     self.controls = controls = {} 

     for item in parameters: 
      hsizer = wx.BoxSizer(wx.HORIZONTAL) 
      parLbl = wx.StaticText(self, label=item) 
      hsizer.Add(parLbl, 0, wx.ALIGN_CENTER | wx.LEFT, 10) 
      ctrl = controls[item] = wx.TextCtrl(self) 
      hsizer.Add(ctrl, 0, wx.ALIGN_CENTER) 
      vsizer.Add(hsizer, 1, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) 
     okBtn = wx.Button(self, id=wx.ID_OK) 
     vsizer.Add(okBtn, 1, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT | wx.BOTTOM, 10) 
     self.SetSizer(vsizer) 
     self.Fit() 
     self.Layout() 

     okBtn.Bind(wx.EVT_BUTTON, self.saveData) 

    def saveData(self, event): 
     for item in self.parameters: 
      self.data[item] = self.controls[item].GetValue() 
     event.Skip() 

Изменить функцию main_name на следующее:

def main_name(self, event): 
    parameters = OrderedDict([("name parts", ["Prefix", "Measurement", "Direction", "Item", "Location", "Descriptor", "Frame", "RTorigin"]), 
           ("SI units", ["A", "cd", "K", "kg", "m", "mol", "Offset", "rad", "s", "ScaleFactor"]), 
           ("normal display unit", ["A", "cd", "K", "kg", "m", "mol", "Offset", "rad", "s", "ScaleFactor"]), 
           ("other parameters", ["Meaning", "Orientation Convention", "Contact Person", "Note",])]) 
    # Iterate through parameters 
    self.cv = threading.Event() 
    for itemKey in parameters: 
     item = parameters[itemKey] 
     dlg = getDataDlg(self, parameters=item, request=itemKey) 
     result = dlg.ShowModal() 
     if result == wx.ID_OK: 
      print("Got Result from Dialog:") 
      print(dlg.data) 
     dlg.Destroy() 

Michael

+0

спасибо за ответ и прошу прощения за мой бедный язык. На самом деле сценарий ждет неопределенно долго и в определенный момент не отвечает, но он не падает. То, что я хочу, это разные окна, которые появляются один за другим, после нажатия кнопки «ok» (и текущее окно также должно закрываться). Я не знал, что настройка фрейма не запускается до тех пор, пока моя функция '__init__' не будет выполнена. Теперь я переместил цикл for в функцию 'main_name', чтобы создать новый кадр для каждого набора входов. Полагая также «ждать», сценарий все еще ждет бесконечно. – Francesco

+0

Лично я бы использовал Modal Dialog, чтобы спросить у пользователя информацию, а не фрейм. Остальная часть скрипта ждет автоматически до тех пор, пока диалог не будет выполнен, и вы сможете получить информацию, которую вы хотите оттуда. – Lokla

+0

Спасибо за ваше предложение. Дело в том, что я хотел бы использовать wxPython, так как набор запросов, запрошенных пользователю, весьма разнообразен. Сначала у меня должны быть окна с запросом на некоторые тексты, а затем на другие окна с списком прокрутки и другим текстом. Мне не удается найти решение для рекурсивного всплытия фреймов и их закрытия. Проблема заключается в том, что это делается внутри цикла 'for'. Таким образом, выполняется основной скрипт, который одновременно создает все кадры.В противном случае, если я буду использовать 'wait' в любой момент, я всегда ожидаю неограниченное время. – Francesco

Смежные вопросы