Python Forum
[Tkinter] Program with Multiple Windows - how to use Class:
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Program with Multiple Windows - how to use Class:
#1
Hi everyone,
This is my first post on this forum. I have been trying to learn Tkinter and find answers via web searches, but it only got me so far. I am writing code to create multiple windows, and then have them interact with each other. Here, to stay simple, I created a program that has a list of values, a main menu window, a window where you can change the values, a window that lets you select the values to display, and a window where you display them. I also played around with scrollbars and progress bars.

My main question is the following:

- To take this to the next level, I would like to move to object-oriented approach. I read online that the best way to do this is to create a class for each window. However, I am not quite sure where to start. Since each window has to display my values, and the windows interact with each other, would I have to insert every variable I am using as an argument for each window? If so, where do I define the variables? Do I make a "main()" function that defines the variables, then have each class use them as argument? Where do I put the ".mainloop()" command then?

And I have another couple of smaller questions about my code:

- On Lines 98 and 123, I was not able to automatically create Buttons and CheckButtons that would work for each i, and I then had to individually create the first few by hand. How do I do it all at once (it would be painful to have to create 50 of them by hand)?
- I was able to make the Scrollbar work in the "Change Values" window even if I scroll while the mouse is anywhere on the screen, but for the "Select Values" window, I need to be on the side on top of the scrollbar itself to work. What is the best way to make it so that, if I am in particular window, scrolling with the mouse anywhere on the window will make it scroll?

Here is the code. Thank you so much for the help!!

# Basic Tkinter project with multiple windows interacting with each other

from tkinter import *
from tkinter import ttk
import tkinter as tk

root = Tk()

# The number of values to store
n = 50
# A list 'values' to contain the 'n' values and set them equal to 1
values = [IntVar() for _ in range(n)]
for i in range(n):
    values[i].set(1)
# A list 'selectValues' to keep track of which values are selected
selectValues = [IntVar() for _ in range(n)]
for i in range(n):
    selectValues[i].set(0)

# Two variables to be used later
label = [0 for _ in range(n)]
progress = [0 for _ in range(n)]

# Show windowToShow and hide other windows
def showWindow(windowToShow):
    for W in (change, select, display):
        W.iconify()
    windowToShow.deiconify()
    return()

# To put a vertical scrollbar when windows are too long
def onFrameConfigure(canvas):
    '''Reset the scroll region to encompass the inner frame'''
    canvas.configure(scrollregion=canvas.bbox("all"))

# These allow to scroll with the mouse wheel
def _on_mousewheel(canvas, event):
    canvas.yview_scroll(int(-1*(event.delta/120)), "units")

# Increase value by 1
def increase(num):
    values[int(num)].set(values[int(num)].get() + 1)
    progress[int(num)]["value"] = values[int(num)].get()

# Update selected values
def updateSelection(num):
    if selectValues[int(num)].get() == 1:
        label[int(num)].grid()
        progress[int(num)]["value"] = values[int(num)].get()
        progress[int(num)].grid()
    else:
        label[int(num)].grid_remove()
        progress[int(num)].grid_remove()

# Create Main Menu
mainmenu = ttk.Frame(root, width=200, height=200, padding="100 100 100 100")
mainmenu.grid(column=0, row=0, sticky=(N, W, E, S))

# Create windows needed for game
change = tk.Toplevel(root)
change.geometry("600x600")
select = tk.Toplevel(root)
select.geometry("1250x700")
display = tk.Toplevel(root)
display.geometry("1250x700")

# Hide all windows to start the game
change.iconify()
select.iconify()
display.iconify()

# Widgets for the Main Menu
changeButton = ttk.Button(mainmenu, text="Change Values", command= lambda: showWindow(change))
changeButton.grid(column=1, row=1)
selectButton = ttk.Button(mainmenu, text="Select Values", command= lambda: showWindow(select))
selectButton.grid(column=1, row=11)
displayButton =  ttk.Button(mainmenu, text="Display Values", command= lambda: showWindow(display))
displayButton.grid(column=1, row=21)
# Add padding to all widgets in the Main Menu
for child in mainmenu.winfo_children():  
    child.grid_configure(padx=5, pady=5)

# Put a canvas in the Change Values window to be able to use a scrollbar
changeCanvas = tk.Canvas(change)
changeFrame = tk.Frame(changeCanvas)
vsb = tk.Scrollbar(change, orient="vertical", command=changeCanvas.yview)
changeCanvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
changeCanvas.pack(side="left", fill="both", expand=True)
changeCanvas.create_window((4,4), window=changeFrame, anchor="nw")
changeFrame.bind("<Configure>", lambda event, canvas=changeCanvas: onFrameConfigure(canvas))
changeCanvas.bind_all("<MouseWheel>",  lambda event, canvas=changeCanvas: _on_mousewheel(canvas, event))

# Widgets for the Change Values window
ttk.Button(changeFrame, text="Return to Main Menu", command=change.iconify).grid(column=0,row=0,columnspan=1000)
for i in range(n):
    ttk.Label(changeFrame, textvariable=values[i]).grid(column=10, row=10+i)
#    ttk.Button(changeFrame, text="increase by 1", command = lambda: increase(i)).grid(column=20, row=10+i)
ttk.Button(changeFrame, text="increase by 1", command = lambda: increase(0)).grid(column=20, row=10)
ttk.Button(changeFrame, text="increase by 1", command = lambda: increase(1)).grid(column=20, row=11)
ttk.Button(changeFrame, text="increase by 1", command = lambda: increase(2)).grid(column=20, row=12)
ttk.Button(changeFrame, text="increase by 1", command = lambda: increase(3)).grid(column=20, row=13)
ttk.Button(changeFrame, text="increase by 1", command = lambda: increase(4)).grid(column=20, row=14)

# Add padding to all widgets in the Change Values window
for child in changeFrame.winfo_children():  
    child.grid_configure(padx=5, pady=5)

# Put a canvas in the Select Values window to be able to use a scrollbar
selectCanvas = tk.Canvas(select)
selectFrame = tk.Frame(selectCanvas)
vsb = tk.Scrollbar(select, orient="vertical", command=selectCanvas.yview)
selectCanvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
selectCanvas.pack(side="left", fill="both", expand=True)
selectCanvas.create_window((4,4), window=selectFrame, anchor="nw")
selectFrame.bind("<Configure>", lambda event, canvas=selectCanvas: onFrameConfigure(selectCanvas))

# Widgets for the Select Values window
ttk.Button(selectFrame, text="Return to Main Menu", command=select.iconify).grid(column=0, row=0, columnspan=1000)
for i in range(n):
    ttk.Label(selectFrame, textvariable=values[i]).grid(column=10, row=10+i)
#    ttk.Checkbutton(selectFrame, text="Select", variable=selectValues[i], command=lambda: updateSelection(i)).grid(column=20, row=10+i)
ttk.Checkbutton(selectFrame, text="Select", variable=selectValues[0], command=lambda: updateSelection(0)).grid(column=20, row=10)
ttk.Checkbutton(selectFrame, text="Select", variable=selectValues[1], command=lambda: updateSelection(1)).grid(column=20, row=11)
ttk.Checkbutton(selectFrame, text="Select", variable=selectValues[2], command=lambda: updateSelection(2)).grid(column=20, row=12)
ttk.Checkbutton(selectFrame, text="Select", variable=selectValues[3], command=lambda: updateSelection(3)).grid(column=20, row=13)
ttk.Checkbutton(selectFrame, text="Select", variable=selectValues[4], command=lambda: updateSelection(4)).grid(column=20, row=14)
    
# Add padding to all widgets in the Select Values window
for child in selectFrame.winfo_children():    
    child.grid_configure(padx=5, pady=5)

# Widgets for the Display window
ttk.Button(display, text="Return to Main Menu", command=display.iconify).grid(column=0, row=0, columnspan=1000)
for i in range(n):
    label[i] = ttk.Label(display, textvariable=values[i])
    label[i].grid(column=10, row=10+i)
    progress[i] = ttk.Progressbar(display, orient=HORIZONTAL, length=100, mode='determinate')
    progress[i].grid(column=20, row=10+i)
    progress[i]["value"] = values[i].get()
    progress[i]["maximum"] = 10
    label[i].grid_remove()
    progress[i].grid_remove()

# Main Loop
root.mainloop()
Reply
#2
Edit: I was able to fix the problem for Lines 98 and 123 by adding an "i=i" after the "lambda".
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Tkinter multiple windows in the same window tomro91 1 786 Oct-30-2023, 02:59 PM
Last Post: Larz60+
Question [PyQt] Need help with class composition / multiple inheritance / polymorphism (?) Alfalfa 4 3,014 May-11-2021, 12:55 PM
Last Post: deanhystad
  [Tkinter] How to close all windows when exit program scratchmyhead 3 6,307 May-17-2020, 08:19 AM
Last Post: menator01
  Program strange behavior ( windows doesn't close) adam2020 2 2,159 May-12-2019, 09:19 PM
Last Post: adam2020
  Avoid multiple windows of the same kind backermaster 1 2,216 Oct-30-2018, 08:32 AM
Last Post: Gribouillis
  [Tkinter] How to exit when multiple root windows. weatherman 4 5,459 May-10-2018, 11:52 PM
Last Post: Larz60+
  Using a class to create instances of Tkinter Toplevel() windows nortski 2 10,874 Mar-27-2018, 11:44 AM
Last Post: nortski

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020