Search code examples
pythonmultithreadingreturnwxpython

wxpython dialog return values with option to destroy


I have a wxpython dialog (wrapped in a function call which returns user selection) that Im using to prompt user for "ok", "cancel" type questions.

I have another thread that is running that allows the user to click a button and perform an emergency stop (also wxpython). when the emergency stop button is clicked, it performs some stuff and then displays its own user dialog.

Now, assume that a popup message has prompted the user with a yes/no question and at the very same time, the user sees something on fire and decides to click the emergency stop.

I need to be able to destroy the current dialog question and eventually display the dialog thats part of the estop thread.

Ive created code that starts the dialog in one thread and another that issues a pub.sendMessage(...) to kill the dialog thread if its displayed on the screen at the time of pressing the estop button.

This works just fine.

Problem is, i cant get the result from the user dialog (yes,no,cancel etc) because the thread never joins back to return what the user picked.

If i add a .join() to my code, i get the user response, but the dialog isnt destroyed if a pub.sendMessage(...) is sent.

heres pseudocode:

1)start up thread to monitor user emergency stop button push
2)start up test which may contain popup window (custom wxdialog) prompting user for answer
3)return user selection from dialog if selected 
  OR if user has clicked estop button: destroy current dialog and display new    dialog related to estop

simple enough, but with the implementation of wrapper functions (this is code for others to call and needs to provide simple function calls), its obviously a little weird. but reimplementing it might not fly right now

heres some code (both are part of larger class so thats why the self references are there)

    #the receiver of the pub message first:

def DestroyUI(self):
    print 'into destroy'
    #self.Hide()
    #self.Close()
    self.Destroy()

#these 2 fuctions are part of a different class 

def closeDialogs(self):
    print 'into closeDialogs'
    def destroyUI():
        if self.okToKill:
            print 'closing wx dialogs'
            pub.sendMessage("destroyUI")
    ts = threading.Thread(target = destroyUI)
    ts.start()
    ts.join()


def MB(self,blah,blah):
    def runMB(*args):
        self.okToKill= True
        self.response = self.PromptUser(*args)
        self.okToKill= False

    tr = threading.Thread(target = runMB,args=(blah,blah))
    tr.start()

    #if i add the join back in, i always return the user values
    #but the dialog is never destroyed (if needed) before user
    #clicks.
    #without the join, the destroy pub send works great, but the 
    #user values are never returned
    #tr.join()

    return self.response

I have tried using queues, multiprocess pool, but the problem with those is the q.get() and async_result().get() both block until user clicks and therefore wont allow the destroy to work as needed.

What id love to figure out is how to get the user values IF the user clicked buttons, but also be able to destroy the current dialog and display the new emergency stop dialog instead (with custom buttons).

I wish wxpython just had a closeAll() :)

Also, i have tried closing the windows based on title name, and while that works, it hoses up my wx instance for the next dialog.

Any ideas? thanks


Solution

  • since i dont really understand what you are asking for, I will instead address "I wish wxpython just had a closeAll() :)"

    def wx_closeAll():
        for win in wx.GetTopLevelWindows():
            win.Destroy()
    
    def closeAllButPrimary():
        for win in wx.GetTopLevelWindows():
            if win != wx.GetTopLevelWindow():
               win.Destroy()
    

    note this will close all frames and dialogs whose parent is None ... if you set a parent for the frame or dialog that dialog will be closed when its parent window is closed