GUI Wizard: flow control?
Steve Holden
sholden at holdenweb.com
Tue Aug 31 09:26:36 EDT 2004
David Chan wrote:
> 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
More information about the Python-list
mailing list