Call functions from within a wxPython event handle

2019-07-21 07:27发布

问题:

I'm struggling to find a way to use function from within a wxPython event handler function. Say I have a button that when clicked it runs a function called OnRun using an event handler. However, the user forgot to click a RadionButton before the OnRun button and I want to pop-up a MessageDialog telling them they forgot a step. I'm going to reuse this MessageDialog several times, thus rather than doing a copy/paste of the same code I would like to just have this MessageDialog in a function and call this MessageDialog function if the user forgets to check a RadioButton.

If this wasn't a function used in an Event Handler I know I could simply put the function as an argument but I'm not seeing a way I can do this with these. Any help here would be appreciated.

回答1:

The following code shows how to create a little method that you can reuse to show custom dialogs and tells the user that they need to accept the agreement. You can change the conditionals to do whatever you want, of course. And you can change the "showMsg" method so that the icon changes too with just a little tweaking.

import wx

########################################################################
class TestFrame(wx.Frame):
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        wx.Frame.__init__(self, None, title="Test")

        panel = wx.Panel(self)
        self.radios = wx.RadioBox(panel, label="Choices",
                                  choices = ["None", "Accept", "Reject"])

        button = wx.Button(panel, label="Run")
        button.Bind(wx.EVT_BUTTON, self.onBtn)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.radios, 0, wx.ALL, 5)
        sizer.Add(button, 0, wx.ALL, 5)
        panel.SetSizer(sizer)

    #----------------------------------------------------------------------
    def onBtn(self, event):
        """"""
        btn = event.GetEventObject()
        btn.SetLabel("Running")
        radio_value = self.radios.GetStringSelection()
        if radio_value == "None":
            self.showMsg("Error", "Please Choose 'Accept' or 'Reject'!")
        elif radio_value == "Accept":
            self.showMsg("Message", "Thank you for accepting!")
        else:
            self.showMsg("Message", "We're sorry, but you cannot continue the install")

    #----------------------------------------------------------------------
    def showMsg(self, title, msg):
        """"""
        dlg = wx.MessageDialog(None, msg, title, wx.OK | wx.ICON_QUESTION)
        dlg.ShowModal()
        dlg.Destroy()



if __name__ == "__main__":
    app = wx.App(False)
    frame = TestFrame()
    frame.Show()
    app.MainLoop()


回答2:

I will make a stab at this, even if the answer seems too direct. I would set a property in the enclosing frame that flags whether the Radio Button has been clicked or not. Then when OnRun is called check that property. Should it be in the wrong state, call the MessageDialog and abort/pause/modify the OnRun.

EDIT Here is what I mean, a trivial example with two buttons, neither of which will lead to further action unless a user agreement is clicked.

import wx

class ButtonFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, 'Button Example', 
                          size=(300, 100))
        panel = wx.Panel(self, -1)
        self.radio = wx.RadioButton(panel, -1, "Accept user agreement", pos=(50, 10))
        self.button = wx.Button(panel, -1, "Run", pos=(50, 30))
        self.Bind(wx.EVT_BUTTON, self.OnRun, self.button)
        self.button.SetDefault()
        self.btn2 = wx.Button(panel, -1, "Walk", pos=(150, 30))
        self.Bind(wx.EVT_BUTTON, self.OnWalk, self.btn2)

    def OnRun(self, event):
        if not self.CheckRadio():
            return
        self.button.SetLabel("Running")

    def OnWalk(self, event):
        if not self.CheckRadio():
            return
        self.btn2.SetLabel("Walking")

    def CheckRadio(self):
        accepted = self.radio.GetValue()
        if not accepted:
            dlg = wx.MessageDialog(None, 'First accept the user agreement',
                                  'MessageDialog', wx.OK | wx.ICON_QUESTION)
            result = dlg.ShowModal() # result not used in this demo
            dlg.Destroy()
            return False
        else:
            return True

if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = ButtonFrame()
    frame.Show()
    app.MainLoop()

Code is adapted from Listing 7.11 of wxPython in Action. I hope this helps, if you have not already solved this n the time that has passed.



回答3:

You can create your own MessageDialog (inheriting), or you can use functools.partial/lambda to pass an additional argument to the event handler:

self.Bind(wx.MY_EVENT, lambda evt: self.OnEventX(evt, handler=foo), id=12)