How to run code only when i click on a button..python/wxpython/boa constructor

1.6k views Asked by At

I'm developing a GUI using wxPython (Boa Constructor IDE). My GUI has the following:

  1. Rich text control
  2. Start button
  3. 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:

enter image description here

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

There are 2 answers

1
brice On BEST ANSWER

Summary

In general, this is an accepted way of doing it in wxPython, and comes straight from the docs

  1. Create a new "data" event.

  2. Make your controller listen for the "data" events

  3. 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.

  4. 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()
0
Mike Driscoll On

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: