Reputation: 289
I am working on a GUI using wxPython. This GUI should dynamically ask to the user several sets of inputs in the same window, updating the window with the new set of inputs after a button "ok" is pressed.
To do this I have a for loop which calls a function that prompts the input controls on the window. I have tried to use the threading.Event
class, letting the script wait for the button to be pressed, but python crashes.
Here below is the interested part of the code.
# 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()
Does anyone has any idea?
Thanks, here below there's the complete code.
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()
Upvotes: 0
Views: 1239
Reputation: 386
It's not really clear what you intend to do here. Your Script doesnt crash, it just waits indefinatly on the event at line 40. You did not start any additional thread which could set the event, so the other script would continue.
Also be aware that wxPython is not thread safe - so if you start adding widgets from inside another thread, you will (really) crash there.
The setup of the actual frame is not started until your __ init __ function is done. Inside this call you wait for a button press which cannot happen, as the window is yet to be created. So your script waits.
I would have posted an actual solution to your problem, but as stated above, I do not see what you intend to do here.
Edit:
As stated in the comment, I would use a dialog to ask the user for the items.
Create a Dialog Subclass:
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()
Change main_name function to the following:
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
Upvotes: 2