S
Steve Holden
David said:Hi,
I'm trying to use python to create GUI wizards, i.e. sequences of dialog
boxes with <BACK and NEXT> buttons. Since I want to re-use some of the
dialog boxes in different wizards, I want to have a main function which
calls each dialog box, much like this:
def select_item():
"""runs GUI wizard to select item"""
client = show_client_dialog()
job = show_job_dialog(client)
invoice, rate = show_invoice_dialog(job)
item = choose_item_dialog(invoice, rate)
return item
This works fine until you want to implement the <BACK button, which should
return to the previous dialog. Theoretically, what I'd really like is to be
able to jump backwards through the control flow, like this:
class GoBack(Exception): pass
def select_item():
"""runs GUI wizard to select item - can go <BACK"""
back = HERE
client = show_client_dialog()
try: job = show_job_dialog(client)
except GoBack: back.goto()
back = HERE
try: invoice, rate = show_invoice_dialog(job)
except GoBack: back.goto()
back = HERE
try: item = show_item_dialog(invoice, rate)
except GoBack: back.goto()
return item
But I couldn't find a way of doing anything like that. You could do
something almost as unhorrible with nested breaks:'
class GoBack(Exception): pass
def select_item():
"""runs GUI wizard to select item - can go <BACK"""
CLIENT: while 1:
client = show_client_dialog()
JOB: while 1:
try: job = show_job_dialog(client)
except GoBack: continue CLIENT
INVOICE: while 1:
try: invoice, rate = show_invoice_dialog(job)
except GoBack: continue JOB
try: item = show_item_dialog(invoice, rate)
except GoBack: continue INVOICE
break CLIENT
Or you could do all the flow control manually in a "do_wizard" function and
make the caller create a horrible data structure instead of writing readable
code:
calls = [
{'meth': show_client_dialog, 'args': [], 'ret': ['client']},
{'meth': show_job_dialog, 'args': ['client'], 'ret': ['job']},
{'meth': show_invoice_dialog, 'args': ['job'], 'ret': ['invoice', 'rate']},
{'meth': show_item_dialog, 'args': ['invoice', 'rate'], 'ret': ['item']},
] # this just says: "client = show_client_dialog()", etc.
do_wizard(calls, locals())
But this is too hideous to deploy, because I might have to remember what it
does in six months
Can anyone suggest something better, or should I abandon my goal of having a
main function which calls each dialog box?
Many thanks,
Caveat: I am assuming that your chosen GUI allows you to easily choose
which of a number of panels is displayed inside a dialog. This is
typically most easily done with the component usually known as a notebook.
You create a list a panels, each of which you have constructed using
your chosen GUI. You then associate the "Next" button with a function
that displays the next panel in the sequence, and the "Back" button with
a function that displays the preceding panel in the sequence.
The dialog then becomes just a list of the panels, plus a counter to
tell you which in the sequence is currently being displayed.
You *don't* want to try and control flow through the sequence in the way
you have attempted. Typically windows are actually just data structures,
and so data-driven methods will work best.
Naturally, each of the panels in the notebook will have callbacks
associated with the various widgets, and interaction with those widgets
will change the data that your program eventually sees when the user
clicks the "Finish" button.
If you want to reuse the panels then make sure they are properly
modularized so their creators can be called used in different programs.
regards
Steve