I'm creating a Wizard in Tkinter. Almost each of the steps shoud I have the same footer with the button for navigation and cancelling. How can I achieve this? Should I create a Frame? And in general, should all the steps be created as different frames?
-
Not really clear, can you tell a bit more how your wizard is structured and what the interface looks like, also what is exactly the role of the footer? The function of the buttons seems already obvious by their title. – Jacob Vlijm Dec 26 '16 at 22:09
-
1see [Switch between two frames in tkinter](http://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter). it uses `Frame` to create `Pages` - you can use `Frame` in the same way or use `Frame` to create widget with buttons (and place for other elements) and then use this widget to build `Pages`. – furas Dec 26 '16 at 22:55
2 Answers
The answer to this question is not much different than Switch between two frames in tkinter. The only significant difference is that you want a permanent set of buttons on the bottom, but there's nothing special to do there -- just create a frame with some buttons as a sibling to the widget that holds the individual pages (or steps).
I recommend creating a separate class for each wizard step which inherits from Frame
. Then it's just a matter of removing the frame for the current step and showing the frame for the next step.
For example, a step might look something like this (using python 3 syntax):
class Step1(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
header = tk.Label(self, text="This is step 1", bd=2, relief="groove")
header.pack(side="top", fill="x")
<other widgets go here>
Other steps would be conceptually identical: a frame with a bunch of widgets.
Your main program or you Wizard class would either instantiate each step as needed, or instantiate them all ahead of time. Then, you could write a method that takes a step number as a parameter and adjust the UI accordingly.
For example:
class Wizard(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.current_step = None
self.steps = [Step1(self), Step2(self), Step3(self)]
self.button_frame = tk.Frame(self, bd=1, relief="raised")
self.content_frame = tk.Frame(self)
self.back_button = tk.Button(self.button_frame, text="<< Back", command=self.back)
self.next_button = tk.Button(self.button_frame, text="Next >>", command=self.next)
self.finish_button = tk.Button(self.button_frame, text="Finish", command=self.finish)
self.button_frame.pack(side="bottom", fill="x")
self.content_frame.pack(side="top", fill="both", expand=True)
self.show_step(0)
def show_step(self, step):
if self.current_step is not None:
# remove current step
current_step = self.steps[self.current_step]
current_step.pack_forget()
self.current_step = step
new_step = self.steps[step]
new_step.pack(fill="both", expand=True)
if step == 0:
# first step
self.back_button.pack_forget()
self.next_button.pack(side="right")
self.finish_button.pack_forget()
elif step == len(self.steps)-1:
# last step
self.back_button.pack(side="left")
self.next_button.pack_forget()
self.finish_button.pack(side="right")
else:
# all other steps
self.back_button.pack(side="left")
self.next_button.pack(side="right")
self.finish_button.pack_forget()
The definition of the functions next
, back
, and finish
is very straight-forward: just call self.show_step(x)
where x
is the number of the step that should be shown. For example, next
might look like this:
def next(self):
self.show_step(self.current_step + 1)

- 1
- 1

- 370,779
- 53
- 539
- 685
-
thanks, how about 1) the footer, almost all the steps should share? 2) where should I create `root = Tk()`? – Jodooomi Dec 27 '16 at 03:31
-
@Jodooomi: the "footer" is `self.button_frame`. You can put anything you want in there, or create multiple footers or headers or anything you want. You create the root window whenever/wherever you want like you normally do. – Bryan Oakley Dec 27 '16 at 03:53
-
yeah, where should I normaly create the root, could you show me? in "main"? – Jodooomi Dec 27 '16 at 03:59
-
I don't understand. Why is Wizard inherited from Frame? Should there be one more main class inherited from Tk? In the link you've given me the class hierarchy is different and isn't applicable to your solution. That's why I asked you to give me an example of how to initialize the `root` in your solution. – Jodooomi Dec 27 '16 at 18:15
-
@Jodooomi: _why is wizard inherited from frame?_ No particular reason. I find it to be a convenient pattern. _Should there be one more main class inherited from Tk?_ Absolutely not. _"In the link you've given me the class hierarchy is different and isn't applicable to your solution. "_ Why do you say that? It's exactly the same, just different names for classes. Just replace `MainApplication` with `Wizard` (or visa versa). – Bryan Oakley Dec 27 '16 at 21:01
-
By "absolute not" in reference to creating "one more main class inherited from Tk", I was assuming you meant "one more _in addition to the first one_". I realize now you might have meant something different. You can have one class inherit from `Tk` if you use that for the root window, you just can't have two root windows. – Bryan Oakley Dec 27 '16 at 21:26
-
-
@Jodooomi: take the example I pointed you to earlier. Combine it with the code in this answer into a single file. Adjust the import statement in that linked-to example to work for python 3. Literally add this one line inside of `MainApplication` where it says "create the rest of your gui here": `Wizard(self).pack(fill="both", expand=True)`. – Bryan Oakley Dec 28 '16 at 03:05
-
-
**Literally add this one line inside of MainApplication where it says "create the rest of your gui here": Wizard(self).pack(fill="both", expand=True)** -- alright. Where should I do "root = Tk()" -- I've asked that a few times already! – Jodooomi Dec 28 '16 at 06:28
-
@Jodooomi: it's in the example I linked to in an earlier comment. – Bryan Oakley Dec 28 '16 at 06:41
-
-
-
what's up with your "shop_step" implementation, why can "current_step" be integer and Frame depending on the context? – Jodooomi Dec 29 '16 at 10:18
I recommend using one main window with the buttons and put the rest of the widgets in different labelframes that appear and disappear upon execution of different functions by the buttons

- 1
- 2