How to run code only when i click on a button..python/wxpython/boa constructor
-
04-06-2021 - |
質問
I'm developing a GUI using wxPython (Boa Constructor IDE). My GUI has the following:
- Rich text control
- Start button
- Stop Button
My requirement is that when I press the START button, numbers (1, 2, 3, etc.) should start printing in the text control; it should stop when I press the STOP button. Code and GUI are as shown. What changes do I need to make to meet my requirements?
Appearance:
Code:
import wx
import wx.richtext
def create(parent):
return Frame3(parent)
[wxID_FRAME3, wxID_FRAME3BUTTON1, wxID_FRAME3BUTTON2, wxID_FRAME3PANEL1,
wxID_FRAME3RICHTEXTCTRL1,
] = [wx.NewId() for _init_ctrls in range(5)]
class Frame3(wx.Frame):
def _init_ctrls(self, prnt):
# generated method, don't edit
wx.Frame.__init__(self, id=wxID_FRAME3, name='', parent=prnt,
pos=wx.Point(579, 234), size=wx.Size(414, 492),
style=wx.DEFAULT_FRAME_STYLE, title='Frame3')
self.SetClientSize(wx.Size(406, 458))
self.panel1 = wx.Panel(id=wxID_FRAME3PANEL1, name='panel1', parent=self,
pos=wx.Point(0, 0), size=wx.Size(406, 458),
style=wx.TAB_TRAVERSAL)
self.richTextCtrl1 = wx.richtext.RichTextCtrl(id=wxID_FRAME3RICHTEXTCTRL1,
parent=self.panel1, pos=wx.Point(96, 96), size=wx.Size(200, 100),
style=wx.richtext.RE_MULTILINE, value=u'')
self.richTextCtrl1.SetLabel(u'richText')
self.button2 = wx.Button(id=wxID_FRAME3BUTTON2, label=u'STOP',
name='button2', parent=self.panel1, pos=wx.Point(256, 280),
size=wx.Size(75, 23), style=0)
self.button2.Bind(wx.EVT_BUTTON, self.OnButton2Button,
id=wxID_FRAME3BUTTON2)
self.button1 = wx.Button(id=wxID_FRAME3BUTTON1, label=u'START',
name='button1', parent=self.panel1, pos=wx.Point(88, 280),
size=wx.Size(75, 23), style=0)
self.button1.Bind(wx.EVT_BUTTON, self.OnButton1Button,
id=wxID_FRAME3BUTTON1)
def __init__(self, parent):
self._init_ctrls(parent)
def OnButton1Button(self, event): #START BUTTON
event.Skip()
def OnButton2Button(self, event): #STOP BUTTON
event.Skip()
if __name__ == '__main__':
app = wx.PySimpleApp()
frame = create(None)
frame.Show()
app.MainLoop()
解決
Summary
In general, this is an accepted way of doing it in wxPython, and comes straight from the docs
Create a new "data" event.
Make your controller listen for the "data" events
Let pressing the "Start" button start a thread that increments a counter and sends a "data" event to your window with the counter value attached.
Upon reception of the event, add the value to your
RichTextCtrl
Example
Here's an example. Note in particular the use of Connect()
to listen for incoming value events.
import wx
import wx.richtext
import threading
import time
EVT_ID_VALUE = wx.NewId()
class DataEvent(wx.PyEvent):
def __init__(self, data):
wx.PyEvent.__init__(self)
self.SetEventType(EVT_ID_VALUE)
self.data=data
class Frame3(wx.Frame):
def __init__(self, parent, title):
self.counter = 0
self.worker = None
wx.Frame.__init__(
self, name='',
parent=parent,
pos=wx.Point(579, 234),
size=wx.Size(414, 492),
style=wx.DEFAULT_FRAME_STYLE, title='Frame3'
)
self.panel1 = wx.Panel(
name='panel1',
parent=self,
pos=wx.Point(0, 0),
size=wx.Size(406, 458),
style=wx.TAB_TRAVERSAL
)
self.richTextCtrl1 = wx.richtext.RichTextCtrl(
parent=self.panel1,
pos=wx.Point(96, 96),
size=wx.Size(200, 100),
style=wx.richtext.RE_MULTILINE,
value=u''
)
self.richTextCtrl1.SetLabel(u'richText')
self.richTextCtrl1.SetScrollbars(20,20,50,50)
self.button2 = wx.Button(
label=u'STOP',
name='button2',
parent=self.panel1,
pos=wx.Point(256, 280),
size=wx.Size(75, 23),
style=0
)
self.button2.Bind(
wx.EVT_BUTTON,
self.OnStop
)
self.button1 = wx.Button(
label=u'START',
name='button1',
parent=self.panel1,
pos=wx.Point(88, 280),
size=wx.Size(75, 23),
style=0
)
self.button1.Bind(
wx.EVT_BUTTON,
self.OnStart
)
self.Connect(-1, -1, EVT_ID_VALUE, self.OnValue )
def OnValue(self, event):
self.richTextCtrl1.AppendText("%d\n"%event.data)
def OnStart(self, event): #START BUTTON
self.richTextCtrl1.AppendText("START\n")
if not self.worker:
self.worker = WorkerThread(self)
self.worker.start()
def OnStop(self, event): #STOP BUTTON
self.richTextCtrl1.AppendText("STOP\n")
if self.worker:
self.worker.stop()
class WorkerThread(threading.Thread):
def __init__(self, notify_window):
threading.Thread.__init__(self)
self.counter = 0
self._notify_window = notify_window
self.abort = False
def run(self):
while not self.abort:
self.counter += 1
wx.PostEvent(self._notify_window, DataEvent(self.counter))
time.sleep(1)
def stop(self):
self.abort = True
if __name__ == '__main__':
app = wx.App()
frame = Frame3(None, "My Hello App")
frame.Show(True)
app.MainLoop()
他のヒント
For something as simple as this, I would just use a wx.Timer instead of messing with threads. like in this tutorial.
If you really want to use threads, then you'll probably want to read one of the following: