wxPython:暂停主脚本并等待按下按钮

wxPython: pause main script and wait for button press

我正在使用 wxPython 开发 GUI。此 GUI 应动态向用户询问同一 window 中的几组输入,在按下按钮 "ok" 后用新的一组输入更新 window。

为此,我有一个 for 循环,它调用一个函数来提示 window 上的输入控件。我尝试使用 threading.Event class,让脚本等待按钮被按下,但是 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()

不太清楚你打算在这里做什么。您的脚本没有崩溃,它只是无限期地等待第 40 行的事件。您没有启动任何可以设置事件的额外线程,因此其他脚本将继续。

还要注意 wxPython 不是线程安全的 - 所以如果你从另一个线程开始添加小部件,你会(真的)在那里崩溃。

实际框架的设置在您的 __ init __ 函数完成之前不会开始。在这个调用中,你等待一个按钮按下,但这是不可能发生的,因为 window 还没有被创建。所以你的脚本等待。

我本来可以针对您的问题发布实际的解决方案,但如上所述,我看不到您打算在此处执行的操作。

编辑:

如评论中所述,我会使用对话框向用户询问项目。

创建对话框子类:

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()  

迈克尔