Passing a variable through frames in tkinter - python

I am trying to get the filename variable from the SearchFolderFrame class to the ResultFrame class but when the user changes the variable in SearchFolderFrame it doesn't change in ResultFrame.
class ImageGroupingApp(tkinter.Tk):
#initalise variables in the class
def __init__(self, *args, **kwargs):
#initalise tkinter
tkinter.Tk.__init__(self,*args,**kwargs)
#creating a frame to contain everything in the app
container = tkinter.Frame(self)
#declaring the position of the frame
container.pack(fill="both",expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#specifing a dictionary that contains all the frames
self.frames = {}
for F in (SearchFolderFrame,ResultFrame):
#setting frame to the first frame the user sees
frame = F(container,self)
self.frames[F] = frame
#sticky is used for alinment
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(SearchFolderFrame)
#creating a method to show frames
def show_frame(self,cont):
frame = self.frames[cont]
frame.tkraise()
class SearchFolderFrame(tkinter.Frame):
def __init__(self,parent, controller):
tkinter.Frame.__init__(self,parent)
self.filename = tkinter.StringVar()
def select_folder():
filePath = tkinter.filedialog.askdirectory()
self.filename.set(filePath)
#print(filePath)
def get_filename():
return self.filename
def searchBtn():
self.path = self.filename
controller.show_frame(ResultFrame)
selectFolderLabel = tkinter.Label(self, text = "Select Folder:")
selectFolderLabel.grid(row=0,column=0)
selectFolderLabel.columnconfigure(1,weight=1)
selectFolderLabel.rowconfigure(1,weight=1)
thresholdLabel = tkinter.Label(self, text = "Threshold(%):")
thresholdLabel.grid(row=1,column=0)
thresholdLabel.columnconfigure(1,weight=1)
thresholdLabel.rowconfigure(1,weight=1)
featuresLabel = tkinter.Label(self, text = "Features:")
featuresLabel.grid(row=2,column=0)
featuresLabel.columnconfigure(1,weight=1)
featuresLabel.rowconfigure(1,weight=1)
searchBtn = tkinter.Button(self,text="Search",
command=lambda: controller.show_frame(ResultFrame))
searchBtn.grid(row=3,column=1)
searchBtn.columnconfigure(1,weight=1)
searchBtn.rowconfigure(1,weight=1)
selectFolderBtn = tkinter.Button(self,text="...",command = select_folder)
selectFolderBtn.grid(row=0,column=2)
selectFolderBtn.columnconfigure(1,weight=1)
selectFolderBtn.rowconfigure(1,weight=1)
folderPathLabel = tkinter.Label(self, textvariable = self.filename)
folderPathLabel.grid(row=0,column=1)
folderPathLabel.columnconfigure(1,weight=1)
folderPathLabel.rowconfigure(1,weight=1)
class ResultFrame(tkinter.Frame):
def __init__(self,parent, controller):
tkinter.Frame.__init__(self,parent)
p1 = SearchFolderFrame(parent, controller)
self.path = p1.filename
self.path.set(p1.filename)
print(self.path)
pathLabel = tkinter.Label(self, textVariable = self.path)
pathLabel.grid(row=0,column=1)
pathLabel.columnconfigure(1,weight=1)
pathLabel.rowconfigure(1,weight=1)
if __name__ == '__main__':
app = ImageGroupingApp()
app.title("Image Grouping")
app.geometry('700x500')
app.mainloop()

You are creating a new SearchFolderFrame instance in ResultFrame. This new instance will also have a new filename variable, which defaults to being empty.
You can get the old SearchFolderFrame instance by doing p1 = controller.frames[SearchFolderFrame]. You also need to not call self.path.set(p1.filename):
class ResultFrame(tkinter.Frame):
def __init__(self,parent, controller):
tkinter.Frame.__init__(self,parent)
p1 = controller.frames[SearchFolderFrame]
self.path = p1.filename
print(self.path)
pathLabel = tkinter.Label(self, textVariable = self.path)
pathLabel.grid(row=0,column=1)
pathLabel.columnconfigure(1,weight=1)
pathLabel.rowconfigure(1,weight=1)

Related

Pass opened file name to function

I am building a small app where I am doing some calculations and want to save the variables and results into a *.csv file.
My desire is to open the *.csv file via a menu point in Tkinter and, after calculations done, save the results with a button to the *.csv file.
What I am not able to do is to save the data, as I am passing the file name in the wrong way.
I have tried to assign the file name to a variable, declare it as global but have not find any solution.
This is the code snippet requested, not just the 2 functions:
import tkinter as tk
from tkinter import *
# Used for styling the GUI
from tkinter import ttk
from math import *
import csv
from tkinter import filedialog as fd
from tkinter.filedialog import asksaveasfile
from tkinter.messagebox import showinfo
from datetime import datetime
# global database
class windows(tk.Tk):
def __init__(self, *args, **kwargs):
global database
tk.Tk.__init__(self, *args, **kwargs)
# Adding a title to the window
self.wm_title("calculator and database")
#
tire_menu = Menu(self)
self.config(menu=tire_menu)
def command_open():
filename = fd.askopenfilename(
title = 'Select database',
filetypes = [("CSV files", "*.csv")])
showinfo(
title = 'You have selected',
message = filename)
database = filename
return(database)
def command_new():
extensions = [("csv file(*.csv)", "*.csv")]
file = asksaveasfile(filetypes=extensions,
defaultextension=extensions)
headerlist = [
"Date",
"Track"]
database = file.name
with open(database, "w") as f:
writer = csv.writer(f)
writer.writerow(headerlist)
# create a menu item
file_menu = Menu(tire_menu)
db_menu = Menu(tire_menu)
tire_menu.add_cascade(label="File", menu = file_menu)
file_menu.add_command(label="Exit", command=self.quit)
tire_menu.add_cascade(label="Database", menu = db_menu)
db_menu.add_command(label="New DB", command=command_new)
db_menu.add_command(label="Open DB", command=command_open)
# creating a frame and assigning it to container
container = tk.Frame(self, height=600, width=800)
# specifying the region where the frame is packed in root
container.pack(side="top", fill="both", expand=True)
# configuring the location of the container using grid
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
# We will now create a dictionary of frames
self.frames = {}
# we'll create the frames themselves later but let's add the components to the dictionary.
for F in (MainPage, SidePage, CompletionScreen):
frame = F(container, self)
# the windows class acts as the root window for the frames.
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
# Using a method to switch frames
self.show_frame(MainPage)
def show_frame(self, cont):
frame = self.frames[cont]
# raises the current frame to the top
frame.tkraise()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
title_main = tk.Label(
self,
text="calculator")
title_main.config(font=("Times 18 bold"), bg="white")
title_main.grid(row=0, column=0, columnspan=2, pady=5)
def savetodb():
data = [
datetime.today().strftime('%Y-%m-%d'),
]
with open(database, "w") as f:
writer = csv.writer(f)
writer.writerow(data)
## Create reference tire pressures frame
frame_reference = tk.LabelFrame(
self,
text = "1: Enter reference data: ")
frame_reference.grid(row=1, column=0, padx=5, pady=5, columnspan = 2)
lbl_track = Label(
frame_reference,
text = "Track: ",
font = 'Times 11',
justify = tk.CENTER)
lbl_track.grid(row = 0, column = 0)
# entries for database save
ent_track = Entry(
frame_reference,
justify = tk.CENTER,
width = 23).grid(row=0, column=1)
btn_savetodb = Button(
frame_reference,
text="Save to DB!",
font = 'Times 11 bold',
command=savetodb)
btn_savetodb.grid(row=2, column=0, columnspan=2, padx=10, pady=5)
class SidePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="This is the Side Page")
label.pack(padx=10, pady=10)
switch_window_button = tk.Button(
self,
text="Go to the Completion Screen",
command=lambda: controller.show_frame(CompletionScreen),
)
switch_window_button.pack(side="bottom", fill=tk.X)
class CompletionScreen(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Completion Screen, we did it!")
label.pack(padx=10, pady=10)
switch_window_button = ttk.Button(
self, text="Return to menu", command=lambda: controller.show_frame(MainPage)
)
switch_window_button.pack(side="bottom", fill=tk.X)
if __name__ == "__main__":
testObj = windows()
testObj.mainloop()
Could you please provide some help?
You have declared global database in wrong place. It should be put inside command_open() as I said in my comment:
class windows(tk.Tk):
def __init__(self, *args, **kwargs):
...
def command_open():
global database
...
database = filename
return database
...
...
However I would suggest to use a class variable inside windows class instead of global variable, so that it can be accessed using controller.database inside MainPage class:
class windows(tk.Tk):
def __init__(self, *args, **kwargs):
...
self.database = None
...
def command_open():
...
self.database = filename
return self.database
...
...
...
class MainPage(tk.Frame):
def __init__(self, parent, controller):
...
def savetodb():
...
with open(controller.database, "w") as f:
...
...
...
Note that you have to change all occurrences of database to self.database inside windows class.

How do I reinitialize a frame before I raise it in tkinter?

I have a problem regarding updating frames in tkinter. When I am in the Search frame, I update a file with some information, and then switch frames to the ShowSearch class. However, when the mainloop runs for the first time, it runs and initializes all the classes, so that my ShowSearch frame will display information in the file from the previous run of the program. My question is if there is a way for me to send data to the file storeSearch.txt, and then when I have called controller.show_frame(ShowSearch), my ShowSearch class will reinitialize and retrieve the data from the file storeSearch.txt and then display it. I only need help with reinitializing/updating the class since I already have written the code that retrieves the data from the file and displays it in my ShowSearch class.
import tkinter as tk
import Projekt_KTH_GUI as PK
import functools
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
container = tk.Frame(self)
container.pack(side = "top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
self.frames = {}
for F in (Homescreen, Menuscreen, Create, ShowUpcoming, ShowAll, OpenList, Search, Edit, ShowSearch):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(Homescreen)
def show_frame(self, container):
frame = self.frames[container]
frame.tkraise()
class Search(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
tk.Label(self, text = "Sök efter packningslista", font = ("Times new Roman", 30)).place(x = 110, y = 0)
self.objectList = PK.main()
self.search = tk.StringVar()
tk.Label(self, text = "Var god mata in ditt sökord:").place(x = 150, y = 100)
search = tk.Entry(self, textvariable = self.search, width = 40).place(x = 60, y = 120)
search_button = tk.Button(self, text = "Enter", command = self.getList).place(x = 430, y = 122.5)
menuButton = tk.Button(self, text = "Meny", command = lambda: self.controller.show_frame(Menuscreen)).place(x = 100, y = 300)
def getList(self):
searchWord = self.search.get()
objectList = self.objectList
objectList = PK.search(objectList, searchWord)
PK.filesUpdate(objectList, "storeSearch.txt")
return self.controller.show_frame(ShowSearch)
I tried to use the methods .update() and .destroy(), one line before i execute frame.tkraise(), but the .update() method doesn't do anything and when I use the .destroy() method, I get an error saying _tkinter.TclError: bad window path name ".!frame.!showsearch".

How to reach and modify child class's variables from parent class when child class is one of many Tkinter frames made visible container and tkraise()

I am writing a Python GUI application with Tkinter where I have several frames that I want to manage separately. I want to put these "child" frames in separate classes (and ultimately in different files) to make the overall code more manageable. Each child class is basically a Tkinter frame with input elements. Based on selections from the main GUI, the relevant child class frame is shown. This is achieved using container and tkraise(). I want to reach child class variables from the main class but I cannot with my current code which is given below. I believe there is a problem with the initialization of child classes and/or the inheritance scheme of my app.
What is the correct way to structure a Python application in a setting where you have child classes being shown with container and tkraise() scheme and you want to reach child class variables form the main class? I appreciate your help.
import tkinter as tk
from tkinter import ttk
import math
# Padding values.
tab_padx = (10, 0)
tab_pady = (20, 0)
# Font settings.
font_1 = ("Arial", 13, "bold")
# Main class.
class Main_GUI(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title("DEMO")
self.frame_blue_circle = BlueCircle(self, self)
self.frame_green_square = GreenSquare(self, self)
# Available shapes.
self.available_shapes = ["CIRCLE", "SQUARE"]
# Available colors.
self.available_colors = ["BLUE", "GREEN"]
# Function to run when color is changed.
def color_change(*args):
self.color = self.option_var_color.get()
if self.color == "BLUE" and self.shape == "CIRCLE":
self.type = "BlueCircle"
self.show_frame("BlueCircle")
elif self.color == "GREEN" and self.shape == "SQUARE":
self.show_frame("GreenSquare")
else:
self.show_frame("Unimplemented")
print(f"{self.color} {self.shape}")
# Function to run when shape is changed.
def shape_change(*args):
self.shape = self.option_var_shape.get()
if self.color == "BLUE" and self.shape == "CIRCLE":
self.show_frame("BlueCircle")
elif self.color == "GREEN" and self.shape == "SQUARE":
self.show_frame("GreenSquare")
else:
self.show_frame("Unimplemented")
print(f"{self.color} {self.shape}")
#GUI tabs
self.nb = ttk.Notebook(self)
self.nb.grid(row=1, column=0, sticky="w", padx=10, pady=10)
#GUI tab1 - Type selection.
self.tab1 = tk.Frame(self.nb)
self.nb.add(self.tab1, text="Type")
#GUI tab2 - Unput for selected type.
self.tab2 = tk.Frame(self.nb)
self.nb.add(self.tab2, text="Input")
#GUI tab3 - Calculate result for selected type with its specific inputs.
self.tab3 = tk.Frame(self.nb)
self.nb.add(self.tab3, text="Result")
# Tab-1 types.
# Shapes.
self.Label_shape = tk.Label(self.tab1, text = "Shape: ", font=font_1)
self.Label_shape.grid(row=10, column=0, padx=tab_padx, pady=tab_pady, sticky="W")
# Setup variable for disk type dropdown menu.
self.option_var_shape= tk.StringVar()
self.option_var_shape.set(self.available_shapes[0])
self.option_var_shape.trace("w", shape_change)
self.shape = self.option_var_shape.get()
self.shape_dropdown_menu = tk.OptionMenu(self.tab1, self.option_var_shape, *self.available_shapes)
self.shape_dropdown_menu.grid(row=10, column=1, sticky="WE", padx=tab_padx, pady=tab_pady)
self.shape_dropdown_menu.config(font=font_1, width=20)
self.shape_dropdown_menu["menu"].config(font=font_1)
# Colors.
self.Label_color = tk.Label(self.tab1, text = "Color: ", font=font_1)
self.Label_color.grid(row=20, column=0, padx=tab_padx, pady=tab_pady, sticky="W")
# Setup variable for disk type dropdown menu.
self.option_var_color= tk.StringVar()
self.option_var_color.set(self.available_colors[0])
self.option_var_color.trace("w", color_change)
self.color = self.option_var_color.get()
self.color_dropdown_menu = tk.OptionMenu(self.tab1, self.option_var_color, *self.available_colors)
self.color_dropdown_menu.grid(row=20, column=1, sticky="WE", padx=tab_padx, pady=tab_pady)
self.color_dropdown_menu.config(font=font_1, width=20)
self.color_dropdown_menu["menu"].config(font=font_1)
# Tab-2. Show frame based on selection in Tab-1.
# Container for frames.
container = tk.Frame(self.tab2)
container.grid(row=0, column=0)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (BlueCircle, GreenSquare, Unimplemented):
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("BlueCircle")
# Tab-3. Calculate and display result based on Tab-1 and Tab-2.
# Label to display result.
result_text = "Result will be displayed here."
self.Label_result = tk.Label(self.tab3, text = result_text, font=font_1, fg="RED")
self.Label_result.grid(row=10, column=0, padx=tab_padx, pady=tab_pady, sticky="W")
self.button = tk.Button(self.tab3, text=f"Print", command=self.print_info)
self.button.grid(row=20, column=0, sticky="W")
# print(self.Label_result)
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
def print_info(self):
bc_text = f"Blue circle radius: {self.frame_blue_circle.radius}"
print(bc_text)
# Class defining GUI for BlueCircle.
class BlueCircle(tk.Frame):
def __init__(self, parent, controller, *args, **kwargs):
super().__init__(*args, **kwargs)
self.parent = parent
self.radius = 0
# Function to run when rim radius is changed.
def Entry_change(*args):
value = self.Entry_var_radius.get()
if value == "":
self.Entry_var_radius.set(".0")
else:
try:
self.radius = float(value)
print(self.radius)
except ValueError:
self.Entry_var_radius.set("")
print(f"Warning! Floating point number only!")
tk.Frame.__init__(self, parent)
self.controller = controller
self.label = tk.Label(self, text="Blue Circle", font=font_1, fg="BLUE")
self.label.grid(row=0, column=0)
self.label = tk.Label(self, text="Radius:")
self.label.grid(row=1, column=0)
# Setup variable for entry to use in callback trace.
self.Entry_var_radius = tk.StringVar()
self.Entry_var_radius.trace("w", lambda name, index, mode, sv=self.Entry_var_radius: Entry_change(self.Entry_var_radius))
# Entry.
self.Entry_radius = tk.Entry(self, font=font_1, textvariable=self.Entry_var_radius)
self.Entry_radius.grid(row=1, column=1)
self.radius = self.Entry_radius.get()
# Class defining GUI for GreenSquare.
class GreenSquare(tk.Frame):
def __init__(self, parent, controller):
super().__init__()
self.parent = parent
# Function to run when rim radius is changed.
def Entry_change(*args):
value = self.Entry_var_lenght.get()
if value == "":
self.Entry_var_lenght.set(".0")
else:
try:
self.lenght = float(value)
self.green_square_area = self.lenght**2
# print(f"Side lenght: {self.lenght}. Area: {self.green_square_area:.2f}")
except ValueError:
self.Entry_var_lenght.set("")
print(f"Warning! Floating point number only!")
# Inıtialize variable.
self.green_square_area = 0
tk.Frame.__init__(self, parent)
self.controller = controller
self.label = tk.Label(self, text="Green Squire", font=font_1, fg="GREEN")
self.label.grid(row=0, column=0)
self.label = tk.Label(self, text="Side lenght:")
self.label.grid(row=1, column=0)
# Setup variable for entry to use in callback trace.
self.Entry_var_lenght = tk.StringVar()
self.Entry_var_lenght.trace("w", lambda name, index, mode, sv=self.Entry_var_lenght: Entry_change(self.Entry_var_lenght))
# Entry.
self.lenght = tk.Entry(self, font=font_1, textvariable=self.Entry_var_lenght)
self.lenght.grid(row=1, column=1)
self.lenght = self.Entry_var_lenght.get()
# Class defining GUI for unimplemented options.
class Unimplemented(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.label = tk.Label(self, text="To be implemented...", font=font_1, fg="RED")
self.label.grid(row=0, column=0)
if __name__ == "__main__":
app = Main_GUI()
app.mainloop()
Note that self.frame_blue_circle is not the instance of BlueCircle shown inside the notebook, so self.frame_blue_circle.radius is not the one input inside the "Input" tab.
The correct instance should be self.frames['BlueCircle'], so you need to use self.frames['BlueCircle'].radius instead:
def print_info(self):
bc_text = f"Blue circle radius: {self.frames['BlueCircle'].radius}"
print(bc_text)
In your __init__() method you have: self.frame_blue_circle = BlueCircle(self, self). This means that anywhere in your Main_GUI class (I mean typically other methods) you can access this and then its radius attribute.
In fact, you already do this here:
def print_info(self):
bc_text = f"Blue circle radius: {self.frame_blue_circle.radius}"
print(bc_text)

Close Tkinter window while using try and except. (Window variable is self)

What I want my code to do, is when any exception happens it should close the program and allow me to call a function which opens up the "program has crashed".
When using a try and except around the whole program, I can't access my self variable inside the except because it's created inside the try
so, I'm trying by putting the try and except inside each class
The current error I get is this: "TclError: bad window path name ".!frame.!startpage"
I added a copy of the code below. (My full program is 6000 lines, this is a shortened down version of 100 lines to figure out the issue)
Save New Duplicate & Edit Just Text Twitter
import tkinter as tk
from tkinter import ttk
from tkinter import *
LARGE_FONT= ("Verdana", 12)
Information_Font = ("Verdana", 10)
new = ""
class Application(tk.Tk):
def __init__(self, *args, **kwargs, ):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.wm_title(self, "Title")
container = tk.Frame(self, width=1768, height=20000)
container.pack(side="top", fill='both' , expand = 1)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.geometry("500x500")
self.title("Program Title")
self.frames = {}
for F in (StartPage, PageOne):
frame = F(container, self)
self.frames[F] = frame
#frame.pack()
frame.grid(row=0, column=0)
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.grid(row=0, column=0, sticky='nsew')
frame.tkraise()
frame.update()
frame.event_generate("<<ShowFrame>>")
class StartPage(tk.Frame,):
def __init__(self, parent, controller):
try:
tk.Frame.__init__(self, parent)
label = ttk.Label(self, text="Start Page/Please Login", font=LARGE_FONT)
label.pack(pady=0,padx=100)
labelempty = ttk.Label(self, text="", font=LARGE_FONT)
labelempty.pack(pady=12,padx=100)
ConsolePasswordeLabel = Label(self, text="Console Password")
ConsolePasswordeLabel.pack(side = "top", pady=10,padx=10)
Console = StringVar()
ConsoleEntry = Entry(self, show = "*", textvariable=Console).pack(pady=2,padx=2)
EnablePasswordeLabel = Label(self, text="Enable Password")
EnablePasswordeLabel.pack(side = "top", pady=10,padx=10)
Enable = StringVar()
EnableEntry = Entry(self, show = "*", textvariable=Enable).pack(pady=2,padx=2)
def login():
Enable1 = Enable.get()
Console1 = Console.get()
print(Console1)
print(Enable1)
def dologin():
login()
controller.show_frame(PageOne)
def showpasswords():
pass
Login = tk.Button(self, text ="Login", command= dologin)
Login.pack(padx = 10, pady = 10)
Login.place(x = "260", y = "220", width = "90")
ShowButton = tk.Button(self, text ="Show passwords", command= "")
ShowButton.pack(padx = 10, pady = 10)
ShowButton.place(x = "140", y = "220")
print(ABCD) # Raising an error on purpose to test the try and except
except:
print("Excepting")
self.destroy()
print("1. Program should have closed")
print('2. "Program has crashed" page can now be opened, from seperate mainloop"')
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = ttk.Label(self, text="Switchport Menu", font=LARGE_FONT,) #background="red")
label.pack(pady=0,padx=100)
app = Application()
app.mainloop()
You need to override report_callback_exception method of Tk class, e.g.:
from tkinter import messagebox
class Application(tk.Tk):
...
def report_callback_exception(self, exc_type, exc_value, exc_traceback):
# The following line is optional, it will print info about the exception to the console (the default tkinter behaviour)
super().report_callback_exception(exc_type, exc_value, exc_traceback)
filename, line, *_ = traceback.extract_tb(exc_traceback).pop()
messagebox.showerror(
"Program has crashed",
f"{exc_type.__name__}: {exc_value}\n"
f"{filename}, Line: {line}"
)
self.quit()

how to add value to the other combobox from select query after selecting from the first combobox

i am trying to make a selection on a combobox1 thin it fill combobox2 with data from the sqlite3 db
i did the combobox1 but i don't know why it is not working with the combobox2
i tried to make event = none the error gone but no value on the combobox2
import tkinter as tk
from tkinter import ttk
import sqlite3
class SchoolProjict(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side = "top", fill = "both", expand = True)
container.grid_rowconfigure(0, weight = 1)
container.grid_columnconfigure(0, weight = 1)
self.frames = {}
for F in (StartPage,):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row = 0, column = 0, sticky = "nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def get_page(self, classname):
for page in self.frames.values():
if str(page.__class__.__name__) == classname:
return page
return None
class StartPage(tk.Frame):
def __init__(self, parent, controller):
self.controller = controller
tk.Frame.__init__(self, parent)
lablel = tk.Label(self, text = "Stuident Info")
lablel.grid(row = 1, columnspan = 3, pady=5, padx=5)
lable2 = tk.Label(self, text = "gread")
lable2.grid(row = 2, column = 2, pady=5, padx=5)
lable3 = tk.Label(self, text = "class")
lable3.grid(row = 3, column = 2, pady=5, padx=5)
lable4 = tk.Label(self, text = "Stuident Name")
lable4.grid(row = 4, column = 2, pady=5, padx=5)
self.number = tk.StringVar()
self.combobox1 = ttk.Combobox(self, width = 15)
self.combobox1.bind("<<ComboboxSelected>>", self.comboclass)
self.combobox1['value'] = self.combogread()
self.combobox1.grid(row = 2, column = 1, pady=5, padx=5)
self.combobox2 = ttk.Combobox(self, width = 15)
self.combobox2['value'] = self.comboclass()
self.combobox2.grid(row = 3, column = 1, pady=5, padx=5)
def combogread(self):
self.conn = sqlite3.connect("exeldata.db")
self.cur = self.conn.cursor()
self.cur = self.conn.execute('SELECT rowid, GradNumber FROM gradelevel')
result = []
for row in self.cur.fetchall():
result.append(row[1])
return result
def comboclass(self, event = None):
greadid = self.combobox1.get()
self.conn = sqlite3.connect("exeldata.db")
self.cur = self.conn.cursor()
self.cur = self.conn.execute('SELECT rowid, GradNumber FROM gradelevel WHERE GradNumber = (?)', (greadid,))
result = []
for row in self.cur.fetchall():
result.append(row[0])
self.cur = self.conn.execute('SELECT rowid , ClassNumb FROM classnumber WHERE GradID = (?)', (str(result),))
result = []
for row in self.cur.fetchall():
result.append(row[0])
return result
app = SchoolProjict()
app.mainloop()
my db is 3 tables with one to many relationship one for grade level one for classes in each level and the student info for each class
The problem is that the comboclass() function doesn't actually
update the values of the Combobox, this is an easy fix.
You just need to create a function that updates the values.
Which is basically just this line of code : self.combobox2['value'] = self.comboclass()
So this would be the code you would need to change/add:
class StartPage(tk.Frame):
def __init__(self, parent, controller):
"""All Init Code Here"""
self.combobox1 = ttk.Combobox(self, width = 15)
self.combobox1.bind("<<ComboboxSelected>>", self.update_combo) # Changed binds command to the update the combobox
self.combobox1['value'] = self.combogread()
self.combobox1.grid(row = 2, column = 1, pady=5, padx=5)
self.combobox2 = ttk.Combobox(self, width = 15)
self.combobox2['value'] = self.comboclass()
self.combobox2.grid(row = 3, column = 1, pady=5, padx=5)
def update_combo(self, event=None): # New function to update the combobox
self.combobox2['value'] = self.comboclass()
The Combobox 2 should now update whenever you select an option for Combobox 1

Categories