GUI Wizard: flow control?

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
 
D

David Chan

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,
 
D

djw

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,

I always code this sort of situation up as a simple state machine. Each
NEXT/BACK button controls the next state, each state being the display of a
particular dialog. A state machine can be a good candidate for flow control
that has to go "forwards" and "backwards".

-Don
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top