Which EvtHandler to use for PostEvent in wxPython - python

This question addresses the very specific question of the EvtHandler required to post en event using wxPython.
Background
I'm using Python 2.7.
In the example below I have two kinds of events:
StartMeausuringEvent is triggered within a wx.Panel derived object (DisplayPanel), hence I use self.GetEventHandler(), this works, even though the binding is in the parent object.
NewResultEvent is triggered from a threading.Thread derived object (MeasurementThread), and it has no event handler, hence I have been forced to send an event handler along, and I chose the event handler of the wx.Frame derived object (MeasurementFrame), as this is also the object that "cathes" the event eventually.
Question
WHY does the first one work, since the object ? And more generally, how tight has the connection between the event handler and the object that "catches" the event has to be?
Code example
import wx
import time
import threading
import numpy as np
import wx.lib.newevent
# Create two new event types
StartMeasuringEvent, EVT_START_MEASURING = wx.lib.newevent.NewCommandEvent()
NewResultEvent, EVT_NEW_RESULT = wx.lib.newevent.NewEvent()
class MeasurementFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Lets measure!", size=(300, 300))
# Layout
self.view = DisplayPanel(self)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.view, 1, wx.ALIGN_CENTER)
self.SetSizer(sizer)
self.SetMinSize((300, 300))
self.CreateStatusBar()
# Create a new measuring device object to embody a physical measuring device
self.device = MeasuringDevice(self.GetEventHandler(), amplification=10)
# Bind events to the proper handlers
self.Bind(EVT_START_MEASURING, self.OnStartMeasurement)
self.Bind(EVT_NEW_RESULT, self.OnNewResult)
def OnStartMeasurement(self, evt):
self.view.SetStatus("Measuring")
self.device.start_measurement()
def OnNewResult(self, evt):
self.view.SetStatus("New Result!")
print evt.result_data
class DisplayPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Attributes
self._result_display = wx.StaticText(self, label="0")
self._result_display.SetFont(wx.Font(16, wx.MODERN, wx.NORMAL, wx.NORMAL))
self._status_display = wx.StaticText(self, label="Ready!")
self._status_display.SetFont(wx.Font(8, wx.MODERN, wx.NORMAL, wx.NORMAL))
# Layout
sizer = wx.BoxSizer(wx.VERTICAL)
button1 = wx.Button(self, wx.NewId(), "Increment Counter")
# button2 = wx.Button(self, wx.NewId(), "Decrease Counter")
sizer.AddMany([(button1, 0, wx.ALIGN_CENTER),
# (button2, 0, wx.ALIGN_CENTER),
((15, 15), 0),
(self._result_display, 0, wx.ALIGN_CENTER),
(self._status_display, 0, wx.ALIGN_LEFT)])
self.SetSizer(sizer)
# Event Handlers
button1.Bind(wx.EVT_BUTTON, self.OnButton)
def OnButton(self, evt):
""" Send an event ... but to where? """
wx.PostEvent(self.GetEventHandler(), StartMeasuringEvent(self.GetId()))
def SetStatus(self, status=""):
""" Set status text in the window"""
self._status_display.SetLabel(status)
class MeasuringDevice:
def __init__(self, event_handler, amplification=10):
self.amplification = amplification
self.event_handler = event_handler # The object to which all event are sent
def start_measurement(self, repetitions=1):
""" Start a thread that takes care of obtaining a measurement """
for n in range(repetitions):
worker = MeasurementThread(self.event_handler, self.amplification)
worker.start()
class MeasurementThread(threading.Thread):
def __init__(self, event_handler, amplification):
threading.Thread.__init__(self)
self.event_handler = event_handler
self.amplification = amplification
def run(self):
print("Beginning simulated measurement")
time.sleep(1) # My simulated calculation time
result = np.random.randn()*self.amplification
evt = NewResultEvent(result_data=result)
wx.PostEvent(self.event_handler, evt)
print("Simulated Measurement done!")
if __name__ == '__main__':
my_app = wx.App(False)
my_frame = MeasurementFrame(None)
my_frame.Show()
my_app.MainLoop()

The first one works because command events automatically propagate up the containment hierarchy (a.k.a the window parent/child connections) until there is a matching binding found or until it reaches a top-level parent window like a frame or dialog. See http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind and also http://wxpython.org/OSCON2006/wxPython-intro-OSCON2006.pdf starting at slide 53 for more explanation.
There is a specific path that the event processor will search when looking for a matching event binding. In a nutshell: as mentioned above, event types that derive directly or indirectly from wx.CommandEvent will continue searching up through the parents until a match is found, and for other types of events it will only look for bindings in the window that the event was sent to and will not propagate to parent windows. If a handler calls event.Skip() then when the handler returns the event processor will continue looking for another matching handler (still limited to the same window for non-command events.) There is a lot more to it than that, (see slide 52 in the PDF linked above) but if you understand this much then it will be enough for almost every event binding/handling situation you'll likely encounter.
BTW, the wx.Window class derives from wx.EvtHandler so unless you've done something like PushEventHandler then window.GetEventHandler() will return the window itself. So unless you need to push new event handler instances (most Python programs don't, it is used more often in C++) then you can save some typing by just using window instead of window.GetEventHandler().

Related

wxpython Choice key down event won't run

i am using wxpython and wx.Choice.
I Tried to bind it but it don't reach the function and don't work, whys that?
Also when i do focus at this Choice (and he already binded), it do run the function but twice.
Why's that and how i can change it?
Select=wx.Choice(parent, choices=SectorList,pos=pos,size=(100,25))
Select.Bind(wx.EVT_KEY_DOWN,self.OnInputCharPressSelect)
I am not sure what you are doing since you don't have a small runnable sample. Here is an example:
import wx
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
txt = wx.TextCtrl(self)
self.choice_widget = wx.Choice(self, choices=['a', 'b', 'c'])
self.choice_widget.Bind(wx.EVT_KEY_DOWN, self.OnInputCharPressSelect)
main_sizer = wx.BoxSizer(wx.VERTICAL)
main_sizer.Add(txt, 0, wx.ALL, 5)
main_sizer.Add(self.choice_widget, 0, wx.ALL, 5)
self.SetSizer(main_sizer)
def OnInputCharPressSelect(self, event):
print('OnInputCharPressSelect fired')
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Choices')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
When I TAB into the Choice widget from the TextCtrl widget, the bound event handler does not fire, which is correct. It also won't fire if I select an item using the mouse. To do that, you would need to bind the Choice widget to EVT_CHOICE.
To get the OnInputCharPressSelect to fire, you must have the Choice widget highlighted (i.e. selected) and then press a key on your keyboard. This will cause the handler to be fired once per key press.
I tested this code on Window 7 with wxPython 4.0.0b2 and Python 3.6.

Is there a wx.EVT_CHAR or event.GetKeyCode() equivalent for wxPython( Phoenix )?

I just started learning Python so I'm trying to avoid working too much in Python 2. Currently learning GUI elements with wxPython. The Python 3 documentation doesn't have an introductory section yet so I'm using the Python 2 'getting started' documentation and converting to Python 3 where needed.
I'm currently at this section. There is a section for wx.EVT_CHAR for event handling when a keypress is detected on the focused object. I don't see a reference to it in the comparison chart, or in the CommandEvent docs, or in the "events emmitted by this class" section of the wx.TextCtrl docs. I've been able to convert most other non-Python 3 code such as SizerFlags, but I can't find an equivalent for this.
This is what I'm working with.
import wx
class ExampleFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
baseSizer = wx.BoxSizer(wx.VERTICAL)
# Create an editable text field
self.textfield = wx.TextCtrl(self)
# Attach event handlers to text field
# Event for when the text changes
self.Bind(wx.EVT_TEXT, self.OnChange, self.textfield)
# Event for when a key is pressed, for example an arrow key should fire this event but not the EVT_TEXT event
self.Bind(wx.EVT_CHAR, self.OnKeyPress, self.textfield)
# Create a button that will clear the textfield
clearButton = wx.Button(self, wx.ID_CLEAR, "Clear")
# Attach event handler on the clearButton to call OnClear()
self.Bind(wx.EVT_BUTTON, self.OnClear, clearButton)
# Multiline text field for seeing the events fire
self.logger = wx.TextCtrl(self, -1, style= wx.TE_MULTILINE | wx.TE_READONLY )
# Add items to frame sizer
baseSizer.Add(self.textfield, wx.SizerFlags(0).Expand())
baseSizer.Add(clearButton, wx.SizerFlags(0).Expand())
baseSizer.Add(self.logger, wx.SizerFlags(1).Expand())
# Set sizer for frame
self.SetSizer(baseSizer)
# Show
self.Show()
def OnClear(self, e):
# Clear all text entered into the textfield and return focus
self.textfield.SetValue("")
self.textfield.SetFocus()
def OnChange(self, e):
# Log every time this event is fired
self.logger.AppendText("OnChange: " + e.GetString() + '\n')
def OnKeyPress(self, e):
# Log every key press in the textfield
self.logger.AppendText("OnKeyPress: " + e.GetKeyCode() + '\n')
app = wx.App(False)
ExampleFrame(None)
app.MainLoop()
The OnChange() will fire every single time the text in textfield changes. The OnKeyPress never fires. If I did get it to fire though, I don't see a GetKeyCode() equivalent in the CommandEvent methods summary.
EDIT:
Problem solved thanks to Mike Driscoll. I implemented his change which was to change this:
self.Bind(wx.EVT_CHAR, self.OnKeyPress, self.textfield)
to this:
self.textfield.Bind(wx.EVT_CHAR, self.OnKeyPress, self.textfield)
Also I had to add e.Skip() to the OnKeyPress function. Otherwise it was logging the key, but not adding text to the textfield. The other events were fine without Skip()ing to pass the event up the control tree to other listeners.
You just bound the event incorrectly in this case. You want
self.textfield.Bind(wx.EVT_CHAR, self.OnKeyPress, self.textfield)
instead of
self.Bind(wx.EVT_CHAR, self.OnKeyPress, self.textfield)
And here it is in context:
import wx
class ExampleFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
baseSizer = wx.BoxSizer(wx.VERTICAL)
# Create an editable text field
self.textfield = wx.TextCtrl(self)
# Attach event handlers to text field
# Event for when the text changes
self.Bind(wx.EVT_TEXT, self.OnChange, self.textfield)
# Event for when a key is pressed, for example an arrow key should fire this event but not the EVT_TEXT event
self.textfield.Bind(wx.EVT_CHAR, self.OnKeyPress, self.textfield)
# Create a button that will clear the textfield
clearButton = wx.Button(self, wx.ID_CLEAR, "Clear")
# Attach event handler on the clearButton to call OnClear()
self.Bind(wx.EVT_BUTTON, self.OnClear, clearButton)
# Multiline text field for seeing the events fire
self.logger = wx.TextCtrl(self, -1, style= wx.TE_MULTILINE | wx.TE_READONLY )
# Add items to frame sizer
baseSizer.Add(self.textfield, 1, flag=wx.EXPAND)
baseSizer.Add(clearButton,0)
baseSizer.Add(self.logger, 1, flag=wx.EXPAND)
# Set sizer for frame
self.SetSizer(baseSizer)
# Show
self.Show()
def OnClear(self, e):
# Clear all text entered into the textfield and return focus
self.textfield.SetValue("")
self.textfield.SetFocus()
e.Skip()
def OnChange(self, e):
# Log every time this event is fired
self.logger.AppendText("OnChange: " + e.GetString() + '\n')
e.Skip()
def OnKeyPress(self, e):
# Log every key press in the textfield
self.logger.AppendText("OnKeyPress: " + str(e.GetKeyCode()) + '\n')
e.Skip()
app = wx.App(False)
ExampleFrame(None)
app.MainLoop()
You will probably want to read the following wiki entry on the difference binding methods:
http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind
As for your other question, while I can't be certain, I would be very surprised if OnKeyPress didn't exist in Phoenix. I would give that a try even if it isn't in the documentation as I would guess it's still in there.

wxPython: Window and Event Id's

I have a Panel on which I display a StaticBitmap initialised with an id of 2. When I bind a mouse event to the image and call GetId() on the event, it returns -202. Why?
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, id=-1):
wx.Frame.__init__(self,parent,id)
self.panel = wx.Panel(self,wx.ID_ANY)
img = wx.Image("img1.png",wx.BITMAP_TYPE_ANY)
img2 = wx.StaticBitmap(self.panel,2,wx.BitmapFromImage(img))
print img2.GetId() # prints 2
img2.Bind(wx.EVT_LEFT_DOWN,self.OnDClick)
def OnDClick(self, event):
print event.GetId() # prints -202
if __name__ == "__main__":
app = wx.PySimpleApp()
frame = MyFrame(None)
frame.Show()
app.MainLoop()
You're printing the event's ID, not the bitmap's ID.
Try print event.GetEventObject().GetId()
GetEventObject returns the widget associated with the event, in this case, the StaticBitmap.
FWIW, I've never needed to assign ID's to any widgets, and you probably shouldn't need to either.
Edit: I saw some other questions you asked and this is what I would recommend, especially if GetEventObject is returning the parent instead (I'm very surprised if that's true, you should double check):
import functools
widget1.Bind(wx.EVT_LEFT_DOWN, functools.partial(self.on_left_down, widget=widget1))
widget2.Bind(wx.EVT_LEFT_DOWN, functools.partial(self.on_left_down, widget=widget2))
# or the above could be in a loop, creating lots of widgets
def on_left_down(self, event, widget):
# widget is the one that was clicked
# event is still the wx event
# handle the event here...

Return value from wxPython Frame

Could someone show me how I could return a value from a wxPython Frame? When the use clicks close, I popup a message dialog asking him a question. I would like to return the return code of this message dialog to my calling function.
Thanks
Because the wxFrame has events that process via the app.MainLoop() functionality, the only way to get at the return value of a wx.Frame() is via catching an event.
The standard practice of handling events is typically from within the class which derives from wx.Window itself (e.g., Frame, Panel, etc.). Since you want code exterior to the wx.Frame to receive information that was gathered upon processing the OnClose() event, then the best way to do that is to register an event handler for your frame.
The documentation for wx.Window::PushEventHandler is probably the best resource and even the wxpython wiki has a great article on how to do this. Within the article, they register a custom handler which is an instance of "MouseDownTracker." Rather than instantiating within the PushEventHandler call, you'd want to instantiate it prior to the call so that you can retain a handle to the EventHandler derived class. That way, you can check on your derived EventHandler class-variables after the Frame has been destroyed, or even allow that derived class to do special things for you.
Here is an adaptation of that code from the wx python wiki (admittedly a little convoluted due to the requirement of handling the results of a custom event with a "calling" function):
import sys
import wx
import wx.lib.newevent
(MyCustomEvent, EVT_CUSTOM) = wx.lib.newevent.NewEvent()
class CustomEventTracker(wx.EvtHandler):
def __init__(self, log, processingCodeFunctionHandle):
wx.EvtHandler.__init__(self)
self.processingCodeFunctionHandle = processingCodeFunctionHandle
self.log = log
EVT_CUSTOM(self, self.MyCustomEventHandler)
def MyCustomEventHandler(self, evt):
self.log.write(evt.resultOfDialog + '\n')
self.processingCodeFunctionHandle(evt.resultOfDialog)
evt.Skip()
class MyPanel2(wx.Panel):
def __init__(self, parent, log):
wx.Panel.__init__(self, parent)
self.log = log
def OnResults(self, resultData):
self.log.write("Result data gathered: %s" % resultData)
class MyFrame(wx.Frame):
def __init__(self, parent, ID=-1, title="", pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
wx.Frame.__init__(self, parent, ID, title, pos, size, style)
self.panel = panel = wx.Panel(self, -1, style=wx.TAB_TRAVERSAL | wx.CLIP_CHILDREN | wx.FULL_REPAINT_ON_RESIZE)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add((25, 25))
row = wx.BoxSizer(wx.HORIZONTAL)
row.Add((25,1))
m_close = wx.Button(self.panel, wx.ID_CLOSE, "Close")
m_close.Bind(wx.EVT_BUTTON, self.OnClose)
row.Add(m_close, 0, wx.ALL, 10)
sizer.Add(row)
self.panel.SetSizer(sizer)
def OnClose(self, evt):
dlg = wx.MessageDialog(self, "Do you really want to close this frame?", "Confirm Exit", wx.OK | wx.CANCEL | wx.ICON_QUESTION)
result = dlg.ShowModal()
dlg.Destroy()
if result == wx.ID_CANCEL:
event = MyCustomEvent(resultOfDialog="User Clicked CANCEL")
self.GetEventHandler().ProcessEvent(event)
else: # result == wx.ID_OK
event = MyCustomEvent(resultOfDialog="User Clicked OK")
self.GetEventHandler().ProcessEvent(event)
self.Destroy()
app = wx.App(False)
f2 = wx.Frame(None, title="Frame 1 (for feedback)", size=(400, 350))
p2 = MyPanel2(f2, sys.stdout)
f2.Show()
eventTrackerHandle = CustomEventTracker(sys.stdout, p2.OnResults)
f1 = MyFrame(None, title="PushEventHandler Tester (deals with on close event)", size=(400, 350))
f1.PushEventHandler(eventTrackerHandle)
f1.Show()
app.MainLoop()
You can get the result of clicking the OK, CANCEL buttons from the Dialog ShowModal method.
Given dialog is an instance of one of the wxPython Dialog classes:
result = dialog.ShowModal()
if result == wx.ID_OK:
print "OK"
else:
print "Cancel"
dialog.Destroy()
A few years late for the initial question, but when looking for the answer to this question myself, I stumbled upon a built-in method of getting a return value from a modal without messing with any custom event funniness. Figured I'd post here in case anyone else needs it.
It's simply this guy right here:
wxDialog::EndModal void EndModal(int retCode)
Ends a modal dialog, passing a value to be returned from the
*wxDialog::ShowModal invocation.*
Using the above, you can return whatever you want from the Dialog.
An example usage would be subclassing a wx.Dialog, and then placing the EndModal function in the button handlers.
class ProjectSettingsDialog(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, "Project Settings", size=(600,400))
sizer = wx.BoxSizer(wx.VERTICAL) #main sized
sizer.AddStretchSpacer(1)
msg = wx.StaticText(self, -1, label="This is a sample message")
sizer.Add(msg, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 15)
horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)
okButton = wx.Button(self, -1, 'OK')
self.Bind(wx.EVT_BUTTON, self.OnOK, okButton)
cancelBtn = wx.Button(self, -1, "Cancel")
self.Bind(wx.EVT_BUTTON, self.OnCancel, cancelBtn)
horizontal_sizer.Add(okButton, 0, wx.ALIGN_LEFT)
horizontal_sizer.AddStretchSpacer(1)
horizontal_sizer.Add(cancelBtn, 0, wx.ALIGN_RIGHT)
sizer.Add(horizontal_sizer, 0)
sizer.AddStretchSpacer(1)
self.SetSizer(sizer)
def OnOK(self, event):
self.EndModal(wx.ID_OK) #returns numeric code to caller
self.Destroy()
def OnCancel(self, event):
self.EndModal(wx.ID_CANCEL) #returns numeric code to caller
self.Destroy()
(Note: I just banged this code out quickly; didn't test the sizers)
As you can see, all you need to do is call the EndModal from a button event to return a value to whatever spawned the dialog.
I wanted to do the same thing, to have a graphical "picker" that I could run from within a console app. Here's how I did it.
# Fruit.py
import wx
class Picker (wx.App):
def __init__ (self, title, parent=None, size=(400,300)):
wx.App.__init__(self, False)
self.frame = wx.Frame(parent, title=title, size=size)
self.apple_button = wx.Button(self.frame, -1, "Apple", (0,0))
self.apple_button.Bind(wx.EVT_BUTTON, self.apple_button_click)
self.orange_button = wx.Button(self.frame, -1, "Orange", (0,100))
self.orange_button.Bind(wx.EVT_BUTTON, self.orange_button_click)
self.fruit = None
self.frame.Show(True)
def apple_button_click (self, event):
self.fruit = 'apple'
self.frame.Destroy()
def orange_button_click (self, event):
self.fruit = 'orange'
self.frame.Destroy()
def pick (self):
self.MainLoop()
return self.fruit
Then from a console app, I would run this code.
# Usage.py
import Fruit
picker = Fruit.Picker('Pick a Fruit')
fruit = picker.pick()
print 'User picked %s' % fruit
user1594322's answer works but it requires you to put all of your controls in your wx.App, instead of wx.Frame. This will make recycling the code harder.
My solution involves define a "PassBack" variable when defining your init function. (similar to "parent" variable, but it is normally used already when initiating a wx.Frame)
From my code:
class MyApp(wx.App):
def __init__ (self, parent=None, size=(500,700)):
wx.App.__init__(self, False)
self.frame = MyFrame(parent, -1, passBack=self) #Pass this app in
self.outputFromFrame = "" #The output from my frame
def getOutput(self):
self.frame.Show()
self.MainLoop()
return self.outputFromFrame
and for the frame class:
class MyFrame(wx.Frame):
def __init__(self, parent, ID, passBack, title="My Frame"):
wx.Frame.__init__(self, parent, ID, title, size=(500, 700))
self.passBack = passBack #this will be used to pass back variables/objects
and somewhere during the execution of MyFrame
self.passBack.outputFromFrame = "Hello"
so all in all, to get a string from an application
app = MyApp()
val = app.getOutput()
#Proceed to do something with val
Check this answer on comp.lang.python: Linkie
I don't think a wxFrame can return a value since it is not modal. If you don't need to use a wxFrame, then a modal dialog could work for you. If you really need a frame, I'd consider using a custom event.
It would go something like this:
(1) User clicks to close the wxFrame
(2) You override OnClose (or something like that) to pop up a dialog to ask the user a question
(3) Create and post the custom event
(4) Close the wxFrame
(5) Some other code processes your custom event
I think I just had the same problem as you. Instead of making that popup a frame, I made it a dialog instead. I made a custom dialog by inheriting a wx.dialog instead of a wx.frame. Then you can utilize the code that joaquin posted above. You check the return value of the dialog to see what was entered. This can be done by storing the value of the textctrl when the user clicks ok into a local variable. Then before it's destroyed, you get that value somehow.
The custom dialog section of this site helped me out greatly.
http://zetcode.com/wxpython/dialogs/

Opening a wx.Frame in Python via a new thread

I have a frame that exists as a start up screen for the user to make a selection before the main program starts. After the user makes a selection I need the screen to stay up as a sort of splash screen until the main program finishes loading in back.
I've done this by creating an application and starting a thread:
class App(wx.App):
'''
Creates the main frame and displays it
Returns true if successful
'''
def OnInit(self):
try:
'''
Initialization
'''
self.newFile = False
self.fileName = ""
self.splashThread = Splash.SplashThread(logging, self)
self.splashThread.start()
#...More to the class
which launches a frame:
class SplashThread(threading.Thread):
def __init__(self, logger, app):
threading.Thread.__init__(self)
self.logger = logger
self.app = app
def run(self):
frame = Frame(self.logger, self.app)
frame.Show()
The app value is needed as it contains the callback which allows the main program to continue when the user makes their selection. The problem is that the startup screen only flashes for a millisecond then goes away, not allowing the user to make a selection and blocking the rest of start up.
Any ideas? Thanks in advance!
You don't need threads for this. The drawback is that the splash window will block while loading but that is an issue only if you want to update it's contents (animate it) or if you want to be able to drag it. An issue that can be solved by periodically calling wx.SafeYield for example.
import time
import wx
class Loader(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
sizer = wx.BoxSizer(wx.VERTICAL)
self.SetSizer(sizer)
self.btn1 = wx.Button(self, label="Option 1")
self.btn2 = wx.Button(self, label="Option 2")
sizer.Add(self.btn1, flag=wx.EXPAND)
sizer.Add(self.btn2, flag=wx.EXPAND)
self.btn1.Bind(wx.EVT_BUTTON, self.OnOption1)
self.btn2.Bind(
wx.EVT_BUTTON, lambda e: wx.MessageBox("There is no option 2")
)
def OnOption1(self, event):
self.btn1.Hide()
self.btn2.Hide()
self.Sizer.Add(
wx.StaticText(self, label="Loading Option 1..."),
1, wx.ALL | wx.EXPAND, 15
)
self.Layout()
self.Update()
AppFrame(self).Show()
class AppFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent)
time.sleep(3)
parent.Hide()
# the top window (Loader) is hidden so the app needs to be told to exit
# when this window is closed
self.Bind(wx.EVT_CLOSE, lambda e: wx.GetApp().ExitMainLoop())
app = wx.PySimpleApp()
app.TopWindow = Loader()
app.TopWindow.Show()
app.MainLoop()

Categories