简体   繁体   中英

How to update the state of a Toggle Button after process completion?

I want to execute a task when a Toggle Button is clicked(on) and toggle it off after the task is completed, I execute the task in a new process because I don't want it to block the UI, event.GetEventObject().SetValue(False) seems to update correctly the value of the Toggle but it doesn't reflect on the UI.

from  multiprocessing import Process
import time
import wx

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
        wx.Frame.__init__(self, *args, **kwds)
        self.toggle_button = wx.ToggleButton(self, wx.ID_ANY, "OK")
        control = Control()
        self.Bind(wx.EVT_TOGGLEBUTTON, control.action, self.toggle_button)
        self.SetTitle("Update UI with a process")
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.toggle_button, 0, 0, 0)
        self.SetSizer(sizer)
        self.Layout()

class Control():
    def update_toggle(self, duration, event):
        time.sleep(duration)
        event.GetEventObject().SetValue(False)
        print("Toggled")

    def action(self, event):
        if event.GetEventObject().GetValue():
            self.update_toggle_process = Process(target = self.update_toggle,
                                                args=(5, event,))
            self.update_toggle_process.start()
        else:
            print("UnToggled")

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, wx.ID_ANY, "")
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

if __name__ == "__main__":
    app = MyApp(0)
    app.MainLoop()

A call to event.GetEventObject().Update() or event.GetEventObject().Refresh() after changing the value of the Toggle doesn't seem to change anything.

EDIT :

If I use a Thread instead of Process , it works correctly, but I chose Process over Thread because I want the ability to kill it cleanly whenever I need to.

Python version: 3.7

WxPython version: 4.0.1

You must remember, that the your update_toggle runs in a new process. Simply put, it has got a copy of data, so if you call event.GetEventObject().SetValue(False) it happens in the new process, and the original one with the Window and Button won't know.

You must somehow pass a message from the new process to the original. I would suggest that the first thing you try is:

        self.update_toggle_process.start()
        self.update_toggle_process.join()
        print("the process has finished")

This will block, but at least you will see if the "update_toggle_process" has finished and if this approach works. After that, there are a few possibilities:

  • Set up a times and periodically call self.update_toggle_process.is_alive()
  • Create a new thread, call the update_toggle_process.start() from it, and also join(). When finished, tell the main thread to toggle the button (remember that you may only manipulate the UI from the main thread in wx)
  • Maybe you do not need a new process, a thread will be enough
  • Look at the multiprocessing IPC

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM