Introduction
You want to ask the user a question, but yes/no/ok/cancel doesn't fit your needs. This makes it easy to get a single-button response from the user to an arbitrary question.
What Objects are Involved
Just wx.Dialog and your favourite event binding technique.
Process Overview
Usage:
1 import QuestionDialog
2
3 ...
4 result = QuestionDialog.questionDialog('Do you like apples or oranges?', buttons=['Apples', 'Oranges', wx.ID_CANCEL])
5 if result == 'Apples':
6 print 'Yum!'
7 elif result == 'Oranges':
8 print 'Yuck!'
9 elif result == wx.ID_CANCEL:
10 print 'Eat more fruit!'
11 else:
12 print 'Answer the question!'
Code Sample
1 """ Dialog to ask a model question, with coder-specified list of buttons.
2 """
3
4 import wx
5 from utils import curry, dropArgs
6 from wx.lib.evtmgr import eventManager
7
8 class ModalQuestion(wx.Dialog):
9 """ Ask a question.
10
11 Modal return value will be the index into the list of buttons. Buttons can be specified
12 either as strings or as IDs.
13 """
14
15 def __init__(self, parent, message, buttons, **kw):
16 wx.Dialog.__init__(self, parent, **kw)
17
18 topSizer = wx.BoxSizer(orient=wx.VERTICAL)
19 self.SetSizer(topSizer)
20
21 topSizer.Add(wx.StaticText(self, label=message), flag=wx.ALIGN_CENTRE|wx.ALL, border=5)
22
23 line = wx.StaticLine(self, size=(20, -1), style=wx.LI_HORIZONTAL)
24 topSizer.Add(line, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, border=5)
25
26 buttonSizer = wx.BoxSizer(orient=wx.HORIZONTAL)
27 topSizer.Add(buttonSizer, flag=wx.ALIGN_CENTRE)
28
29 for i, button in enumerate(buttons):
30 if isinstance(button, (int, long)):
31 b = wx.Button(self, id=button)
32 else:
33 b = wx.Button(self, label=button)
34
35 eventManager.Register(dropArgs(curry(self.EndModal, i)), wx.EVT_BUTTON, b)
36 buttonSizer.Add(b, flag=wx.ALL, border=5)
37
38 self.Fit()
39
40 def questionDialog(message, buttons=[wx.ID_OK, wx.ID_CANCEL], caption=''):
41 """ Ask a question.
42
43 Return value will be the button the user clicked, in whatever form it was specified.
44 Allowable button specifications are strings or wxIDs of stock buttons.
45
46 If the user clicks the 'x' close button in the corner, the return value will be None.
47 """
48
49 dlg = ModalQuestion(None, message, buttons, title=caption)
50 try:
51 return buttons[dlg.ShowModal()]
52 except IndexError:
53 return None
54
55 class curry(object):
56 """Taken from the Python Cookbook, this class provides an easy way to
57 tie up a function with some default parameters and call it later.
58 See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549 for more.
59 """
60 def __init__(self, func, *args, **kwargs):
61 self.func = func
62 self.pending = args[:]
63 self.kwargs = kwargs
64
65 def __call__(self, *args, **kwargs):
66 if kwargs and self.kwargs:
67 kw = self.kwargs.copy()
68 kw.update(kwargs)
69 else:
70 kw = kwargs or self.kwargs
71 return self.func(*(self.pending + args), **kw)
72
73 class dropArgs(object):
74 """ Same as curry, but once the function is built, further args are ignored. """
75
76 def __init__(self, func, *args, **kwargs):
77 self.func = func
78 self.args = args[:]
79 self.kwargs = kwargs
80
81 def __call__(self, *args, **kwargs):
82 return self.func(*self.args, **self.kwargs)
Comments
If you prefer, use b.Bind rather than eventManager.Register.
Also, you can cut out curry and dropArgs and build the button callbacks the hard way if you want. I have them in a utilities module and just import them anyway.
-- JohnFouhy