I am working on a program where there is a scrollable frame that will be containing a large quantity of items. But with my app it does not render all of them. Can someone possibly tell me why? And how I can fix it?
Code:
#700x650
from Tkinter import *
import ttk
class itemLoad:
def __init__(self):
pass
def item(self):
items = "Video File,Image File,None,King King"
return items
class App(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.pack(fill=BOTH)
self.loadItem = itemLoad()
self.one = None
self.create_widgets()
self.loadItems()
def create_widgets(self):
self.mainFrame = Frame(self, width=700, height=650)
self.mainFrame.pack_propagate(False)
self.mainFrame.pack()
self.menu = Frame(self.mainFrame, width=150, height=650, bg="Gray92")
self.menu.pack_propagate(False)
self.menu.pack(side=LEFT)
self.itemMenu = Frame(self.mainFrame, width=550, height=650)
self.itemMenu.pack_propagate(False)
self.itemMenu.pack(side=LEFT)
self.vScroller = ttk.Scrollbar(self.itemMenu, orient=VERTICAL)
self.vScroller.pack(side=RIGHT, fill=Y)
self.canvas = Canvas(self.itemMenu, bd=0, width=534, highlightthickness=0, yscrollcommand=self.vScroller.set)
self.canvas.pack_propagate(False)
self.canvas.pack(side=LEFT, fill=BOTH)
self.vScroller.config(command=self.canvas.yview)
self.innerFrame = Frame(self.canvas, width=550, height=650, bg="Pink")
self.canvas.create_window(0, 0, window=self.innerFrame, anchor=NW)
def update(event):
self.canvas.config(scrollregion=self.canvas.bbox("all"))
self.innerFrame.bind("<Configure>", update)
self.spacer = Frame(self.mainFrame, bg="Gray")
self.spacer.pack(side=LEFT, fill=Y)
frame = Frame(self.menu, bg="Gray92")
frame.pack(side=TOP, fill=X)
high = Frame(frame, bg="Gray92", width=10)
high.pack(side=LEFT, fill=Y)
self.bu1 = Label(frame, font=("Calibri", 14), text=" Main Folder", width=12, anchor=W, bg="Gray92")
self.bu1.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
frame2 = Frame(self.menu, bg="Gray92")
frame2.pack(side=TOP, fill=X)
high2 = Frame(frame2, bg="Gray92", width=10)
high2.pack(side=LEFT, fill=Y)
self.bu2 = Label(frame2, font=("Calibri", 14), text=" Favorited", width=12, anchor=W, bg="Gray92")
self.bu2.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
frame3 = Frame(self.menu, bg="Gray92")
frame3.pack(side=TOP, fill=X)
high3 = Frame(frame3, bg="Gray92", width=10)
high3.pack(side=LEFT, fill=Y)
self.bu3 = Label(frame3, font=("Calibri", 14), text=" Trash Can", width=12, anchor=W, bg="Gray92")
self.bu3.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
frame4 = Frame(self.menu, bg="Gray92")
frame4.pack(side=BOTTOM, fill=X)
high4 = Frame(frame4, bg="Gray92", width=10)
high4.pack(side=LEFT, fill=Y)
self.bu4 = Label(frame4, font=("Calibri", 14), text=" Log Out", width=12, anchor=W, bg="Gray92")
self.bu4.pack(side=LEFT, fill=X, ipadx=10, ipady=10)
def hover(event):
widg = event.widget
items = widg.winfo_children()
if items[1].cget("text") == self.one:
pass
else:
items[0].config(bg="Gray85")
items[1].config(bg="Gray85")
def unHover(event):
widg = event.widget
text = None
items = widg.winfo_children()
if items[1].cget("text") == self.one:
pass
else:
items[0].config(bg="Gray92")
items[1].config(bg="Gray92")
def clicked(event):
widg = event.widget
par = widg.winfo_parent()
par = self.menu._nametowidget(par)
for item in self.menu.winfo_children():
items = item.winfo_children()
items[0].config(bg="Gray92")
for item in par.winfo_children():
try:
self.one = item.cget("text")
except:
item.config(bg="lightBlue")
frame.bind("<Enter>", hover)
frame2.bind("<Enter>", hover)
frame3.bind("<Enter>", hover)
frame4.bind("<Enter>", hover)
frame.bind("<Leave>", unHover)
frame2.bind("<Leave>", unHover)
frame3.bind("<Leave>", unHover)
frame4.bind("<Leave>", unHover)
high.bind("<Button-1>", clicked)
self.bu1.bind("<Button-1>", clicked)
high2.bind("<Button-1>", clicked)
self.bu2.bind("<Button-1>", clicked)
high3.bind("<Button-1>", clicked)
self.bu3.bind("<Button-1>", clicked)
high4.bind("<Button-1>", clicked)
self.bu4.bind("<Button-1>", clicked)
def loadItems(self):
theItems = self.loadItem.item()
for i in range(0, 500):
none = Frame(self.innerFrame, width=200, height=500, bg="red")
none.pack_propagate(False)
none.pack(side=TOP, padx=10, pady=10)
let = Label(none, text=i)
let.pack(side=TOP)
root = Tk()
root.geometry("700x650")
root.resizable(0,0)
app = App(root)
root.mainloop()
I think you're exceeding the limits of the tkinter canvas. The frame you're trying to scroll is 250,000 pixels tall. I doubt the canvas can handle that.
When I make all of your inner widgets considerably smaller your code works fine.
Related
I feel like my question is pretty straightforward, but I just cant seem to figure out how to do this. I'm currently creating a GUI project with CustomTkInter and trying to make a button that navigates through to the next page, but I'm not sure how to do this? I'm trying to link different pages together, essentially just trying to have a multi-page project, I've tried to apply the solution for the normal tkinter to my customtkinter - but its not worked so far and its throwing me some errors (specifically an error about a master not being defined)
This is what I have so far
import tkinter
import tkinter.messagebox
import customtkinter
from PIL import Image
import os
customtkinter.set_appearance_mode("Dark") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("green") # Themes: "blue" (standard), "green", "dark-blue"
class Page(customtkinter.CTkFrame):
def __init__(self):
customtkinter.CTkFrame.__init__()
def show(self):
self.lift()
class Page1(Page):
def __init__(self):
Page.__init__()
class HomePage(customtkinter.CTkFrame):
def __init__(self):
super().__init__()
self.title("IMS")
self.geometry(f"{1300}x{800}")
self.rowconfigure((0), weight=1)
self.columnconfigure((0), weight=1)
self.rowconfigure((1, 2), weight=1)
self.columnconfigure((1,2), weight=1)
self.rowconfigure((3), weight=1)
image_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "images")
self.logoImage = customtkinter.CTkImage(Image.open(os.path.join(image_path, "logo.png")), size=(150, 66))
self.titleLogo = customtkinter.CTkLabel(master=self, text="", image=self.logoImage)
self.titleLogo.grid(row=0, column=1, padx=0, pady=0)
self.categoriesButton = customtkinter.CTkButton(master=self, border_width=2, text_color=("gray10", "#DCE4EE"),font=("",33), width=150, height=50, text="Categories", command=Page1.show())
self.categoriesButton.grid(row=2, column=0, padx=(50, 50), pady=(0, 40), sticky="nsew", columnspan=3)
if __name__ == "__main__":
app = HomePage()
app.mainloop()
Would appreciate any help, ty :)
here's a great CTk doc that could help with your problem https://felipetesc.github.io/CtkDocs/#/multiple_frames
i made some minor changes because i was getting some error running sample 1 with python 3.9.13
import tkinter
import customtkinter
DARK_MODE = "dark"
customtkinter.set_appearance_mode(DARK_MODE)
customtkinter.set_default_color_theme("dark-blue")
class App(customtkinter.CTk):
frames = {"frame1": None, "frame2": None}
def frame1_selector(self):
App.frames["frame2"].pack_forget()
App.frames["frame1"].pack(in_=self.right_side_container,side=tkinter.TOP, fill=tkinter.BOTH, expand=True, padx=0, pady=0)
def frame2_selector(self):
App.frames["frame1"].pack_forget()
App.frames["frame2"].pack(in_=self.right_side_container,side=tkinter.TOP, fill=tkinter.BOTH, expand=True, padx=0, pady=0)
def __init__(self):
super().__init__()
# self.state('withdraw')
self.title("Change Frames")
self.geometry("{0}x{0}+0+0".format(self.winfo_screenwidth(), self.winfo_screenheight()))
# contains everything
main_container = customtkinter.CTkFrame(self)
main_container.pack(fill=tkinter.BOTH, expand=True, padx=10, pady=10)
# left side panel -> for frame selection
left_side_panel = customtkinter.CTkFrame(main_container, width=150)
left_side_panel.pack(side=tkinter.LEFT, fill=tkinter.Y, expand=False, padx=10, pady=10)
# buttons to select the frames
bt_frame1 = customtkinter.CTkButton(left_side_panel, text="Frame 1", command=self.frame1_selector)
bt_frame1.grid(row=0, column=0, padx=20, pady=10)
bt_frame2 = customtkinter.CTkButton(left_side_panel, text="Frame 2", command=self.frame2_selector)
bt_frame2.grid(row=1, column=0, padx=20, pady=10)
# right side panel -> to show the frame1 or frame 2
self.right_side_panel = customtkinter.CTkFrame(main_container)
self.right_side_panel.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=True, padx=10, pady=10)
self.right_side_container = customtkinter.CTkFrame(self.right_side_panel,fg_color="#000811")
self.right_side_container.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=True, padx=0, pady=0)
App.frames['frame1'] = customtkinter.CTkFrame(main_container,fg_color="red")
bt_from_frame1 = customtkinter.CTkButton(App.frames['frame1'], text="Test 1", command=lambda:print("test 1") )
bt_from_frame1.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
App.frames['frame2'] = customtkinter.CTkFrame(main_container,fg_color="blue")
bt_from_frame2 = customtkinter.CTkButton(App.frames['frame2'], text="Test 2", command=lambda:print("test 2") )
bt_from_frame2.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER)
a = App()
a.mainloop()
i made a very basic template that you could use and modify as you want or just take the principle out of it.
import tkinter
import customtkinter
DARK_MODE = "dark"
customtkinter.set_appearance_mode(DARK_MODE)
customtkinter.set_default_color_theme("dark-blue")
class App(customtkinter.CTk):
def __init__(self):
super().__init__()
self.title("Change Frames")
# remove title bar , page reducer and closing page !!!most have a quit button with app.destroy!!! (this app have a quit button so don't worry about that)
self.overrideredirect(True)
# make the app as big as the screen (no mater wich screen you use)
self.geometry("{0}x{1}+0+0".format(self.winfo_screenwidth(), self.winfo_screenheight()))
# root!
self.main_container = customtkinter.CTkFrame(self, corner_radius=10)
self.main_container.pack(fill=tkinter.BOTH, expand=True, padx=10, pady=10)
# left side panel -> for frame selection
self.left_side_panel = customtkinter.CTkFrame(self.main_container, width=150, corner_radius=10)
self.left_side_panel.pack(side=tkinter.LEFT, fill=tkinter.Y, expand=False, padx=5, pady=5)
self.left_side_panel.grid_columnconfigure(0, weight=1)
self.left_side_panel.grid_rowconfigure((0, 1, 2, 3), weight=0)
self.left_side_panel.grid_rowconfigure((4, 5), weight=1)
# self.left_side_panel WIDGET
self.logo_label = customtkinter.CTkLabel(self.left_side_panel, text="Welcome! \n", font=customtkinter.CTkFont(size=20, weight="bold"))
self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 10))
self.scaling_label = customtkinter.CTkLabel(self.left_side_panel, text="UI Scaling:", anchor="w")
self.scaling_label.grid(row=7, column=0, padx=20, pady=(10, 0))
self.scaling_optionemenu = customtkinter.CTkOptionMenu(self.left_side_panel, values=["80%", "90%", "100%", "110%", "120%"],
command=self.change_scaling_event)
self.scaling_optionemenu.grid(row=8, column=0, padx=20, pady=(10, 20), sticky = "s")
self.bt_Quit = customtkinter.CTkButton(self.left_side_panel, text="Quit", fg_color= '#EA0000', hover_color = '#B20000', command= self.close_window)
self.bt_Quit.grid(row=9, column=0, padx=20, pady=10)
# button to select correct frame IN self.left_side_panel WIDGET
self.bt_dashboard = customtkinter.CTkButton(self.left_side_panel, text="Dashboard", command=self.dash)
self.bt_dashboard.grid(row=1, column=0, padx=20, pady=10)
self.bt_statement = customtkinter.CTkButton(self.left_side_panel, text="Statement", command=self.statement)
self.bt_statement.grid(row=2, column=0, padx=20, pady=10)
self.bt_categories = customtkinter.CTkButton(self.left_side_panel, text="Manage Categories", command=self.categories)
self.bt_categories.grid(row=3, column=0, padx=20, pady=10)
# right side panel -> have self.right_dashboard inside it
self.right_side_panel = customtkinter.CTkFrame(self.main_container, corner_radius=10, fg_color="#000811")
self.right_side_panel.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=True, padx=5, pady=5)
self.right_dashboard = customtkinter.CTkFrame(self.main_container, corner_radius=10, fg_color="#000811")
self.right_dashboard.pack(in_=self.right_side_panel, side=tkinter.TOP, fill=tkinter.BOTH, expand=True, padx=0, pady=0)
# self.right_dashboard ----> dashboard widget
def dash(self):
self.clear_frame()
self.bt_from_frame1 = customtkinter.CTkButton(self.right_dashboard, text="dash", command=lambda:print("test dash") )
self.bt_from_frame1.grid(row=0, column=0, padx=20, pady=(10, 0))
self.bt_from_frame2 = customtkinter.CTkButton(self.right_dashboard, text="dash 1", command=lambda:print("test dash 1" ) )
self.bt_from_frame2.grid(row=1, column=0, padx=20, pady=(10, 0))
# self.right_dashboard ----> statement widget
def statement(self):
self.clear_frame()
self.bt_from_frame3 = customtkinter.CTkButton(self.right_dashboard, text="statement", command=lambda:print("test statement") )
self.bt_from_frame3.grid(row=0, column=0, padx=20, pady=(10, 0))
# self.right_dashboard ----> categories widget
def categories(self):
self.clear_frame()
self.bt_from_frame4 = customtkinter.CTkButton(self.right_dashboard, text="categories", command=lambda:print("test cats") )
self.bt_from_frame4.grid(row=0, column=0, padx=20, pady=(10, 0))
# Change scaling of all widget 80% to 120%
def change_scaling_event(self, new_scaling: str):
new_scaling_float = int(new_scaling.replace("%", "")) / 100
customtkinter.set_widget_scaling(new_scaling_float)
# close the entire window
def close_window(self):
App.destroy(self)
# CLEAR ALL THE WIDGET FROM self.right_dashboard(frame) BEFORE loading the widget of the concerned page
def clear_frame(self):
for widget in self.right_dashboard.winfo_children():
widget.destroy()
a = App()
a.mainloop()
Basically I have a class that inherits from LabelFrame, I want it to show up on the window itself, but I don't see it at all, it doesn't even show up on the window no matter what I do
from tkinter import *
root = Tk()
root['bg'] = 'white'
root.state('zoomed')
class CharacterFrame(LabelFrame):
def __init__(self, master, skin_img, requirement, desc):
super().__init__(master=master, bg='grey29')
self.desc = Label(self, text=desc)
default_char_frame = CharacterFrame(master=root, desc='Default Character.', requirement=0, skin_img='')
default_char_frame.grid(row=0, column=0, sticky='nse')
root.mainloop()
ok, so i was stupid for not implementing the actual code itself, but ill do it now (This isnt actual source code, just the important code, still has same error as actual code):
from tkinter import *
root = Tk()
root['background'] = 'white'
root.state('zoomed')
class SkinFrame(LabelFrame):
def __init__(self, master, skin_img, requirement, desc):
super().__init__(master=master, bg='grey29')
self.desc = Label(self, text=desc)
self.desc.pack()
def show_chars():
button_grid.grid_forget()
skin_lib.grid(row=1, column=0, sticky='nws')
button_skin0 = Button(skin_lib, text="Skin1", relief="flat", width=15, fg="white", bg="darkgrey", command=lambda: show_char_frame(prev, default_char_frame), font=("Arial", 29, "italic"))
button_skin0.grid(row=0, column=0, pady=(0,10))
back = Button(skin_lib, text="Back", width=15, relief="flat", fg="white", bg="maroon", font=("Arial",29,"italic"),
command=lambda: [skin_lib.grid_forget(), button_grid.grid(row=1, column=0, sticky='nws')])
back.grid(row=100, column=0)
def show_char_frame(prev, char_frame):
skin_lib.grid_forget()
default_char_frame.grid(row=1, column=1, sticky='nse')
default_char_frame = SkinFrame(master=root, desc='Default Skin.', requirement=0, skin_img='')
Label(root, text="Game Title", width=68, relief="flat", bg="white", fg="grey", font=("Arial",26,"italic")).grid(row=0, column=0, sticky='n')
button_grid = LabelFrame(root, bg='grey29', relief="groove", pady=10, padx=10)
button_grid.grid(row=1, column=0, sticky='nws')
skin_lib = LabelFrame(root, bg='grey29', relief="groove", pady=10, padx=10)
skins = Button(button_grid, text="Skins", width=15, relief="flat", fg="white", bg="navy", command=show_chars, font=("Arial", 29, "italic"))
skins.grid(row=2, column=0, pady=(0,10))
previous_skinframe
root.mainloop()
you missed to pack() label to add in control
self.desc.pack()
this is complete code
from tkinter import *
root = Tk()
root['bg'] = 'white'
root.state('zoomed')
class CharacterFrame(LabelFrame):
def __init__(self, master, skin_img, requirement, desc):
super().__init__(master=master, bg='grey29')
self.desc = Label(self, text=desc)
self.desc.pack()
default_char_frame = CharacterFrame(master=root, desc='Default Character.', requirement=0, skin_img='')
default_char_frame.grid(row=0, column=0, sticky='nse')
root.mainloop()
in this code what I add self.desc.pack() to show
this is output
I have researched and tested a number of solutions on stackoverflow and other sites but I cannot get a scrollbar to work with my application.
I'm looking for a scrollbar capable of scrolling the SubFrame group that my application creates. I also need to delimit a minimum window height, and that this window can be extended, but without breaking the scrollbar.
Thank you in advance to all who will help me.
I admit not having understood which should go above the other, the Canvas or the Frame.
Here is a piece of code, with all the graphical widgets to visualize the scrollbar effect :
import tkinter as tk
from tkinter import ttk
class FrameStack(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.subframes = []
self.topFrame = tk.Frame(root)
self.topFrame.pack(side="top", fill="x")
self.groupOfFrames = tk.Frame(root)
self.groupOfFrames.pack(side="top", fill="both", expand=True, pady=(32,0))
self.scrollbar = tk.Scrollbar(self.groupOfFrames, orient="vertical")
self.scrollbar.pack(side="right",fill="y")
#self.canvas = tk.Canvas(self.groupOfFrames, yscrollcommand=self.scrollbar.set)
#self.canvas.pack()
#self.canvas_window = self.canvas.create_window(0, 0, anchor="nw", window=self.groupOfFrames)
self.create_widget()
def create_widget(self):
tk.Label(self.topFrame, text="BUS").grid(row=0, column=0)
tk.Label(self.topFrame, text="IP").grid(row=1, column=0)
tk.Label(self.topFrame, text="REG").grid(row=2, column=0)
tk.Label(self.topFrame, text="SIZE").grid(row=3, column=0)
self.combobox_bus = ttk.Combobox(self.topFrame, values=list(dic.keys()), width=40, justify='center', state="readonly")
self.combobox_bus.bind('<<ComboboxSelected>>', self.getUpdateDataIP)
self.combobox_bus.grid(row=0, column=1, sticky="nsew")
self.combobox_ip = ttk.Combobox(self.topFrame, justify='center', width=40, state="readonly")
self.combobox_ip.bind('<<ComboboxSelected>>', self.getUpdateDataReg)
self.combobox_ip.grid(row=1, column=1, sticky="nsew")
self.button_read = tk.Button(self.topFrame, text="Read", command=self.read)
self.button_write = tk.Button(self.topFrame, text="Write", command=self.write)
self.button_read.grid(row=0, column=2, columnspan=2, sticky="nsew")
self.button_write.grid(row=1, column=2, columnspan=2, sticky="nsew")
self.button_hsid = tk.Button(self.topFrame, text="HSID")
self.button_hsid.grid(row=0, column=4, columnspan=2, sticky="nsew")
self.button_add = tk.Button(self.topFrame, text="+", command=self.add_frame)
self.button_add.grid(row=1, column=4, columnspan=2, sticky="nsew")
self.combobox_reg_dl = ttk.Combobox(self.topFrame, width=40, justify='center', state="readonly")
self.combobox_reg_dl.bind('<<ComboboxSelected>>', self.getUpdateDisplayReg)
self.combobox_reg_dl.grid(row=2, column=1, sticky="nsew")
self.button_dump = tk.Button(self.topFrame, text="Dump", command=self.dump)
self.button_load = tk.Button(self.topFrame, text="Load", command=self.load)
self.button_dump.grid(row=2, column=2, columnspan=2, sticky="nsew")
self.button_load.grid(row=2, column=4, columnspan=2, sticky="nsew")
self.select_size = tk.StringVar()
self.select_size.set("0")
self.entry_size = tk.Entry(self.topFrame, textvariable=self.select_size, justify='center')
self.entry_size.grid(row=3, column=1, sticky="nsew")
tk.Scale(self.topFrame, from_=0, to=1024, variable=self.select_size)
self.select_read_size = tk.IntVar()
self.select_read_size.set(8)
self.radio_8 = tk.Radiobutton(self.topFrame, text=" 8", variable=self.select_read_size, value=8)
self.radio_16 = tk.Radiobutton(self.topFrame, text="16", variable=self.select_read_size, value=16)
self.radio_32 = tk.Radiobutton(self.topFrame, text="32", variable=self.select_read_size, value=32)
self.radio_64 = tk.Radiobutton(self.topFrame, text="64", variable=self.select_read_size, value=64)
self.radio_8.grid(row=3, column=2)
self.radio_16.grid(row=3, column=3)
self.radio_32.grid(row=3, column=4)
self.radio_64.grid(row=3, column=5)
def getUpdateDataIP(self, event):
self.combobox_ip['values'] = list(dic[self.combobox_bus.get()].keys())
self.combobox_ip.set('')
self.combobox_reg_dl['values'] = ['']
self.combobox_reg_dl.set('')
for f in self.subframes:
f.combobox_reg_rw['values'] = ['']
f.combobox_reg_rw.set('')
def getUpdateDataReg(self, event):
self.combobox_reg_dl['values'] = list(dic[self.combobox_bus.get()][self.combobox_ip.get()].keys())
self.combobox_reg_dl.set('')
for f in self.subframes:
f.combobox_reg_rw['values'] = list(dic[self.combobox_bus.get()][self.combobox_ip.get()].keys())
f.combobox_reg_rw.set('')
def getUpdateDisplayReg(self, event):
self.combobox_reg_dl.set(self.combobox_reg_dl.get()+" ("+dic[self.combobox_bus.get()][self.combobox_ip.get()][self.combobox_reg_dl.get()]+")")
def delete_frame(self, frame):
self.subframes.remove(frame)
frame.destroy()
def add_frame(self):
f = SubFrame(parent=self.groupOfFrames, controller=self)
if self.combobox_bus.get()!="" and self.combobox_ip.get()!="":
f.combobox_reg_rw['values'] = list(dic[self.combobox_bus.get()][self.combobox_ip.get()].keys())
self.subframes.append(f)
f.pack(side="top", fill="x", pady=(0,5))
def read(self):
pass
def write(self):
pass
def dump(self):
pass
def load(self):
pass
class SubFrame(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
self.parent = parent
self.controller = controller
self.create_widget()
def create_widget(self):
self.combobox_reg_rw = ttk.Combobox(self, width=47, justify='center', state="readonly")
self.combobox_reg_rw.bind('<<ComboboxSelected>>', self.getUpdateDisplayReg)
self.select_val = tk.StringVar()
self.entry_value = tk.Entry(self, bd=1.5, width=20, textvariable=self.select_val, justify='center')
self.button_write = tk.Button(self, text="Write", command=self.write)
self.button_read = tk.Button(self, text="Read", command=self.read)
self.button_help = tk.Button(self, text="?", command=self.help)
self.button_remove = tk.Button(self, text="-", command=self.remove)
self.combobox_reg_rw.grid(row=0, column=0, columnspan=2, sticky="ew")
self.entry_value.grid(row=0, column=2, columnspan=2, sticky="ew")
self.button_write.grid(row=1, column=0, sticky="ew")
self.button_read.grid(row=1, column=1, sticky="ew")
self.button_help.grid(row=1, column=2, sticky="nsew")
self.button_remove.grid(row=1, column=3, sticky="nsew")
def remove(self):
self.controller.delete_frame(self)
def getUpdateDisplayReg(self, event):
self.combobox_reg_rw.set(self.combobox_reg_rw.get()+" ("+dic[self.controller.combobox_bus.get()][self.controller.combobox_ip.get()][self.combobox_reg_rw.get()]+")")
def write(self):
print(self.entry_value.get())
def read(self):
print(self.combobox_reg_rw.get())
def help(self):
pass
if __name__ == "__main__":
#dic = extractor.main()
dic = {'1': {'1.1': {'1.1.1': 'A', '1.1.2': 'B'}, '1.2': {'1.2.1': 'C', '1.2.2': 'D'}}, '2': {'2.1': {'2.1.1': 'E', '2.2.2': 'F'}, '2.2': {'2.2.1': 'G', '2.2.2': 'H'}}}
root = tk.Tk()
root.title("XXXXXX")
root.resizable(False, True)
fs = FrameStack(root)
fs.pack(fill="both", expand=True)
root.mainloop()
It is the canvas that has the ability to scroll, so your self.groupOfFrames needs to go inside the canvas. And since you want the scrollbar and canvas to appear as a single complex object, they should go in a frame.
You need to make sure that when the window is resized, the frame inside the canvas is resized as well. And you also need to make sure that when you add something to self.groupOfFrames you also update the scrollregion. These can be done by binding to the <Configure> event of each widget.
Thus, I would create the FrameStack like the following. Please note that self.topFrame and self.bottomFrame are children of self rather than root. That is a mistake I didn't catch in the code I gave you in your previous question.
class FrameStack(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.subframes = []
self.topFrame = tk.Frame(self)
self.bottomFrame = tk.Frame(self)
self.topFrame.pack(side="top", fill="x")
self.bottomFrame.pack(side="bottom", fill="both", expand=True)
self.canvas = tk.Canvas(self.bottomFrame, bd=0)
vsb = tk.Scrollbar(self.bottomFrame, command=self.canvas.yview)
self.canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.groupOfFrames = tk.Frame(self.canvas)
self.canvas.create_window(0, 0, anchor="nw", window=self.groupOfFrames, tags=("inner",))
self.canvas.bind("<Configure>", self._resize_inner_frame)
self.groupOfFrames.bind("<Configure>", self._reset_scrollregion)
def _reset_scrollregion(self, event):
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def _resize_inner_frame(self, event):
self.canvas.itemconfig("inner", width=event.width)
This is a part of code from my school project.
from tkinter import *
from tkinter.font import Font
class student_window():
def __init__(self, master):
self.student_win = master
#window = Toplevel(self.master)
self.student_win.geometry("1280x720")
self.header1Font = Font(family='Helvetica', size=20)
self.optionFont = Font(family='Sans Serrif', size=20)
self.student_win.focus()
self.show_window()
def show_window(self):
print("ookk")
self.student_win.title("Student Window")
self.option_frame = Frame(self.student_win, width=200, height=720)
lbl_header = Label(self.option_frame,text="EXAMINATION", font=self.header1Font, fg='white', bg='#172D44').grid(row=0,column=0, sticky=NSEW)
lbl_welcome = Label(self.option_frame, text="Welcome,", fg='#E9F1F7', bg='#2A3F54').grid(row=1,column=0)
lbl_username = Label(self.option_frame, text="Username", fg='white', bg='#2A3F54').grid(row=2,column=0)
lbl_header2 = Label(self.option_frame, text="STUDENT CORNER", fg='white', bg='#2A3F54').grid(row=3, column=0)
self.btn_tests = Button(self.option_frame, text="Attempt Exam", fg='#E9F1F7', bg='#35495D', relief=FLAT)
self.btn_tests.grid(row=4,column=0, sticky=NSEW)
self.btn_attempts = Button(self.option_frame, text="Attempts", fg='#E9F1F7', bg='#2A3F54', relief=FLAT)
self.btn_attempts.grid(row=5, column=0, sticky=NSEW)
self.btn_result = Button(self.option_frame, text="Result", fg='#E9F1F7', bg='#2A3F54', relief=FLAT)
self.btn_result.grid(row=6, column=0, sticky=NSEW)
self.btn_goBack = Button(self.option_frame, text="Go Back", fg='#E9F1F7', bg='#2A3F54', relief=FLAT)
self.btn_goBack.grid(row=7, column=0, sticky=NSEW)
self.option_frame.configure(bg='#2A3F54')
self.option_frame.grid(row=0, column=0)
self.option_frame.grid_propagate(0)
self.main_frame = Frame(self.student_win, width=880, height=720)
self.main_result_frame = Frame(self.main_frame)
self.main_result_frame.grid(row=0,column=0)
self.attempts_frame = Frame(self.main_frame)
self.attempts_frame.grid(row=0, column=0)
self.test_frame = Frame(self.main_frame)
lbl_test = Label(self.test_frame, text="In test frame").pack()
self.test_frame.grid(row=0,column=0)
self.main_frame.grid(row=0,column=1)
self.main_frame.grid_propagate(0)
self.info_frame = Frame(self.student_win, width=200, height=720)
self.btn_username = Button(self.info_frame, text="Username", relief=FLAT)
self.btn_username.grid(row=0,column=0)
self.userInfo_frame = Frame(self.info_frame)
self.info_frame.grid(row=0, column=2)
self.info_frame.grid_propagate(0)
root = Tk()
student_window(root)
root.mainloop()
And it looks something like this.
The Student Panel for my project
The whole window is divided into three frames and want to expand each label and button of the left frame(self.option_frame) to fill it horizontally. I tried doing sticky=EW and sticky=NSEW but still some space is left. How do I fix that?
You need to call self.option_frame.columnconfigure(0, weight=1) to make column 0 to use all the available horizontal space.
I was just trying some things and what I have found to be working is to make the label width bigger than than the frame then anchoring the text to the left.
I'm trying to make a mini timer programme, but I have a question: each frame uses the same function called setTimer, so what frame should I reference in the setTimer function? Here is my code:
def createFrames(self):
self.timer1_frame = Frame(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = Frame(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = Frame(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = Frame(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
def createWidgets(self):
self.setTimer1_button = Button(self.timer1_frame, text="SET TIMER", command=self.setTimer)
self.setTimer1_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer2_button = Button(self.timer2_frame, text="SET TIMER", command=self.setTimer)
self.setTimer2_button.grid(row=0, column=1, padx=30, pady=20)
self.setTimer3_button = Button(self.timer3_frame, text="SET TIMER", command=self.setTimer)
self.setTimer3_button.grid(row=1, column=0, padx=30, pady=20)
self.setTimer4_button = Button(self.timer4_frame, text="SET TIMER", command=self.setTimer)
self.setTimer4_button.grid(row=1, column=1, padx=30, pady=20)
def setTimer(self):
self.hoursLabel = Label(root, text="Hours: ")
self.minutesLabel = Label(root, text="Minutes: ")
self.secondsLabel = Label(root, text="Seconds: ")
As you can see, I'm referencing root in my setTimer function but I don't think it's correct, what would I put there instead so it knows which frame I'm referring to, rather than having to write 4 lots of the same code (is this possible?)
You could create method with argument frame which is used in Label
def setTimer(self, frame):
self.hoursLabel = Label(frame, text="Hours: ")
# ... rest ...
and then you can use lambda in command= to assign function with argument
self.setTimer1_button = tk.Button..., command=lambda:self.setTimer(self.timer1_frame))
And it works.
But it has one problem: in all frames it assigns labels to the same variables self.hoursLabel, etc. so you can't access labels to change text (ie. to update time). You would have to use separated variables but it would need to keep them on list or dictionary and use frame as key.
import tkinter as tk
class Window():
def __init__(self):
# dictionary for labels
self.labels = {}
self.createFrames()
self.createWidgets()
def createFrames(self):
self.timer1_frame = tk.Frame(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = tk.Frame(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = tk.Frame(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = tk.Frame(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
def createWidgets(self):
self.setTimer1_button = tk.Button(self.timer1_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer1_frame))
self.setTimer1_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer2_button = tk.Button(self.timer2_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer2_frame))
self.setTimer2_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer3_button = tk.Button(self.timer3_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer3_frame))
self.setTimer3_button.grid(row=0, column=0, padx=30, pady=20)
self.setTimer4_button = tk.Button(self.timer4_frame, text="SET TIMER", command=lambda:self.setTimer(self.timer4_frame))
self.setTimer4_button.grid(row=0, column=0, padx=30, pady=20)
def setTimer(self, frame):
self.hoursLabel = tk.Label(frame, text="Hours: ")
self.minutesLabel = tk.Label(frame, text="Minutes: ")
self.secondsLabel = tk.Label(frame, text="Seconds: ")
self.hoursLabel.grid(row=1)
self.minutesLabel.grid(row=2)
self.secondsLabel.grid(row=3)
# remember labels in dictionary
self.labels[frame] = [self.hoursLabel, self.minutesLabel, self.secondsLabel]
# start update time
self.updateTimer(frame, 0)
def updateTimer(self, frame, seconds):
secondsLabel = self.labels[frame][2]
secondsLabel['text'] = "Seconds: {}".format(seconds)
seconds += 1
root.after(1000, self.updateTimer, frame, seconds)
root = tk.Tk()
Window()
root.mainloop()
EDIT:
It can be simpler to use Frame to create own widget with one Frame, one Button, all Labels and variables needed in timer. And later use this widget 4 times in main window.
import tkinter as tk
class MyTimer(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.hours = 0
self.minutes = 0
self.seconds = 0
self.button = tk.Button(self, text="SET TIMER", command=self.set_timer)
self.button.grid(row=0, column=0, padx=30, pady=20)
self.hours_label = None
self.minutes_label = None
self.seconds_label = None
def set_timer(self):
if self.hours_label is None:
self.hours_label = tk.Label(self, text="Hours: ")
self.minutes_label = tk.Label(self, text="Minutes: ")
self.seconds_label = tk.Label(self, text="Seconds: ")
self.hours_label.grid(row=1)
self.minutes_label.grid(row=2)
self.seconds_label.grid(row=3)
# reset time
self.hours = 0
self.minutes = 0
self.seconds = 0
# start updating time
self.update_timer()
def update_timer(self):
self.hours_label['text'] = "Hours: {}".format(self.hours)
self.minutes_label['text'] = "Minutes: {}".format(self.minutes)
self.seconds_label['text'] = "Seconds: {}".format(self.seconds)
self.seconds += 1
if self.seconds == 60:
self.seconds = 0
self.minutes += 1
if self.minutes == 60:
self.minutes = 0
self.hours += 1
# update again after 1000ms (1s)
root.after(1000, self.update_timer)
class Window():
def __init__(self):
self.createFrames()
def createFrames(self):
self.timer1_frame = MyTimer(root, width=300, height=300, background="red")
self.timer1_frame.grid_propagate(0)
self.timer1_frame.grid(row=0, column=0)
self.timer2_frame = MyTimer(root, width=300, height=300, background="blue")
self.timer2_frame.grid_propagate(0)
self.timer2_frame.grid(row=0, column=1)
self.timer3_frame = MyTimer(root, width=300, height=300, background="orange")
self.timer3_frame.grid_propagate(0)
self.timer3_frame.grid(row=1, column=0)
self.timer4_frame = MyTimer(root, width=300, height=300, background="yellow")
self.timer4_frame.grid_propagate(0)
self.timer4_frame.grid(row=1, column=1)
root = tk.Tk()
Window()
root.mainloop()