Probably not pubsub problem, button access?

I am looking for some assistance(of course, or I would not be posting here). I am writing a Stop Watch program and am having trouble accessing buttons from other functions. Attached is a mini version of the program with the first problem. I need the start function to enable the stop button when the start button is clicked. I will also need the stop button to be able to tell the start function to stop the timer and more. I have settled on pubsub, but if anyone has a better idea I am glad to hear it.

Oh ya, my versions are python 2.6.2, wxpython 2.8.10.1 and pyppubsub 3.3.0

The latest version (attached) is getting me close but not quite. I see the message is getting through but the expected action is not happening until the loop is done. So, maybe this is not a pubsub problem. It is probably more basic than that.

Thanks for any help or comments. It is greatly appreciated.
boB
wxswPost.py (2.1 KB)

One problem is that wxPython isn’t getting the chance to update the state of the Stop button before the code that updates the time runs (including sleeping for 1 second for each iteration of the while loop).

You can force the update of the Stop button using:

            self.btnStop.Enable()
            self.btnStop.Refresh()
            self.btnStop.Update()

However, there may be other similar issues elsewhere where the inline processing of the elapsed time would prevent the hci from updating, such as displaying the value on the label.

For this sort of application I would use a wx.Timer and bind wx.EVT_TIMER to a handler that updates the display of the time value. The Start button would call self.timer.Start(TIMER_INTERVAL) and the stop button would call self.timer.Stop(). TIMER_INTERVAL would be set to the number of milliseconds between each refresh of the display. Using a timer like this allows the wxPython event loop to refresh the display between the timer events.

While pubsub is excellent for passing messages between different windows in the same application, it’s not really necessary for handling events inside a single window. Does your real application need separate windows?

An alternative way to make your example work, would be to call wx.Yield() in the while loop:

            self.lblcountup.SetLabel(str(tonow))
            wx.Yield()
            time.sleep(1)

That does work (at least using Python 3.10.6 + wxPython 4.2.0 gtk3 (phoenix) wxWidgets 3.2.0 + Linux Mint 21), but I would still prefer to use the wx.Timer approach myself.

Morning Richard and thank you for your help. I like the looks of both so I guess I will do some playing with both and probably expand my mini example to check out some other things I will need. I can put some updates here if anyone would like to know how it is going. Thanks a lot!

You need to bear in mind that wxPython itself doesn’t do any concurrent processing. Having a one second sleep in an event handler is not a good idea because the wxPython main event loop will be blocked while the sleep is happening. Even if you do call wx.Yield() before the sleep, the sleep call will make the hci unresponsive to other events, for example resizing the frame will be very jerky, and there will be a potential delay in responding to clicking on the Stop button.

By all means post updates here. If you have more problems I will try to help where I can. A while ago I wrote a simple countdown timer app which uses wx.Timer to update the time remaining, which is displayed in a wx.lib.gizmos.LEDNumberCtrl

Screenshot at 2022-09-30 10-27-04

Here is a quick hack of your code to use a wx.Timer:

"""
Modified wxswPost.py to use a wx.Timer.

"""
import wx
import time

class wxStopwatch(wx.Frame):

    def __init__(self, parent, title):

        super(wxStopwatch, self).__init__(parent, title=title, size=(500, 400))

        self.InitUI()
        self.Centre()

    def InitUI(self):
        #this section defines the form
        panel = wx.Panel(self)
        sizer = wx.GridBagSizer(4, 4)
        #left of form
        txtcountup = wx.StaticText(panel, label="Count Up Timer")
        sizer.Add(txtcountup, pos=(4,2), flag=wx.TOP|wx.LEFT|wx.BOTTOM, border=5)
        self.lblcountup = wx.StaticText(panel, wx.ALIGN_CENTER)
        sizer.Add(self.lblcountup, pos=(5,2), flag=wx.LEFT|wx.RIGHT, border=5)
        self.btnStartup = wx.Button(panel, label="Start", size=(70,25))
        self.btnStartup.Bind(wx.EVT_BUTTON, self.DoStartup)
        sizer.Add(self.btnStartup, pos=(6,2))
        self.btnStop= wx.Button(panel, label="Stop", size=(70,25))
        self.btnStop.Bind(wx.EVT_BUTTON, self.DoStop)
        sizer.Add(self.btnStop, pos=(6,3), flag=wx.RIGHT|wx.BOTTOM, border=5)
        self.btnStop.Disable()
        panel.SetSizer(sizer)
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.DoTimer)

    def DoStartup(self, e):
        self.btnStartup.Disable()
        self.btnStop.Enable()
        self.start1 = time.time()
        self.timer.Start(100)

    def DoStop(self, e):
        self.timer.Stop()
        self.btnStartup.Enable()
        self.btnStop.Disable()

    def DoTimer(self, e):
        tonow = (int((time.time() - self.start1)*10))/10.0
        self.lblcountup.SetLabel(str(tonow))


def main():
    app = wx.App()
    ex = wxStopwatch(None, title='Welcome to wxStopwatch V1.01')
    ex.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()