GUI Wizard: flow control?

Steve Holden sholden at holdenweb.com
Tue Aug 31 15:26:36 CEST 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