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.
Related
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)
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()
I have this sample code I am running. I am creating a window, and when I load the template page and click the "Click me" button, it adds 20 boxes on the screen. 10 rows, 2 wide. Column 1 is Car makes, and column 2 is Models.
When I click the Make box in row 1, and change it from Ford to Toyota, I want the model combobox in row 1 to change to show the Toyota models. But it only works for the last row. Is it possible to get each row to work?
import tkinter as tk
from tkinter import font as tkfont, filedialog, messagebox
from tkinter.ttk import Combobox
class SLS_v1(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# Setting up the root window
self.title('Test App')
self.geometry("200x300")
self.resizable(False, False)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
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 = {}
self.frames["MenuPage"] = MenuPage(parent=container, controller=self)
self.frames["template"] = template(parent=container, controller=self)
self.frames["MenuPage"].grid(row=0, column=0, sticky="nsew")
self.frames["template"].grid(row=0, column=0, sticky="nsew")
self.show_frame("MenuPage")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class MenuPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
template = tk.Button(self, text='Template', height=3, width=20, bg='white', font=('12'),
command=lambda: controller.show_frame('template'))
template.pack(pady=50)
class template(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.grid(columnspan=10, rowspan=10)
button = tk.Button(self, text='Click me', command= lambda: stored_functions().add_boxes(self))
button.grid(row=11, column=1)
class stored_functions():
make = ['Ford', 'Toyota', 'Honda']
models = [['F-150', 'Mustang', 'Explorer'], ['Camry', 'Corolla', 'Tacoma'], ['Civic', 'CRV', 'Accord']]
def add_boxes(self, main_window):
for row in range(10):
self.make_var = tk.StringVar(main_window)
self.make_options = self.make
self.make_var.set(self.make_options[0])
self.make_selection = tk.ttk.Combobox(main_window, value=self.make_options,
state='readonly', width=10)
self.make_selection.current(0)
self.make_selection.bind('<<ComboboxSelected>>', lambda event:
stored_functions.update_models(self, selection=self.make_selection))
self.make_selection.grid(row=row, column=1)
self.model_var = tk.StringVar(main_window)
self.model_options = self.models[0]
self.model_var.set(self.model_options[0])
self.model_selection = tk.ttk.Combobox(main_window, value=self.model_options,
state='readonly', width=10)
self.model_selection.current(0)
self.model_selection.grid(row=row, column=2)
def update_models(self, selection):
if selection.get() == 'Ford':
self.model_options = self.models[0]
if selection.get() == 'Toyota':
self.model_options = self.models[1]
if selection.get() == 'Honda':
self.model_options = self.models[2]
self.model_selection.config(values=self.model_options)
self.model_selection.current(0)
if __name__ == "__main__":
app = SLS_v1()
app.mainloop()
You have used same variables for car brands and models selection, so the variables refer the last set after the for loop.
You need to pass the model combobox to update_models() using default value of argument:
class stored_functions():
make = ['Ford', 'Toyota', 'Honda']
models = [['F-150', 'Mustang', 'Explorer'], ['Camry', 'Corolla', 'Tacoma'], ['Civic', 'CRV', 'Accord']]
def add_boxes(self, main_window):
for row in range(10):
self.make_var = tk.StringVar(main_window)
self.make_options = self.make
self.make_var.set(self.make_options[0])
self.make_selection = tk.ttk.Combobox(main_window, value=self.make_options,
state='readonly', width=10)
self.make_selection.current(0)
self.make_selection.grid(row=row, column=1)
self.model_var = tk.StringVar(main_window)
self.model_options = self.models[0]
self.model_var.set(self.model_options[0])
self.model_selection = tk.ttk.Combobox(main_window, value=self.model_options,
state='readonly', width=10)
self.model_selection.current(0)
self.model_selection.grid(row=row, column=2)
# pass the corresponding model combobox to bind function
self.make_selection.bind(
'<<ComboboxSelected>>',
lambda event, peer=self.model_selection: self.update_models(event.widget.get(), peer)
)
def update_models(self, selection, model_selection):
model_options = self.models[self.make.index(selection)]
model_selection.config(values=model_options)
model_selection.current(0)
Note that it is not necessary to use instance variables inside the for loop. Also those StringVars are not used at all, so the functions can be simplified as below:
class stored_functions():
make = ['Ford', 'Toyota', 'Honda']
models = [['F-150', 'Mustang', 'Explorer'], ['Camry', 'Corolla', 'Tacoma'], ['Civic', 'CRV', 'Accord']]
def add_boxes(self, main_window):
for row in range(10):
make_selection = tk.ttk.Combobox(main_window, value=self.make,
state='readonly', width=10)
make_selection.current(0)
make_selection.grid(row=row, column=1)
model_selection = tk.ttk.Combobox(main_window, value=self.models[0],
state='readonly', width=10)
model_selection.current(0)
model_selection.grid(row=row, column=2)
make_selection.bind(
'<<ComboboxSelected>>',
lambda event, peer=model_selection: self.update_models(event.widget.get(), peer)
)
def update_models(self, selection, model_selection):
model_options = self.models[self.make.index(selection)]
model_selection.config(values=model_options)
model_selection.current(0)
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)
I'm trying to create a modular class ( for some gui buttons ).
CoreButton should consist of most methods for a common button, including tk frame.
Goal is to inheret CoreButton - and to use its frame to build rest of button's GUI - it does not appear.
any help will be appriciated
class CoreButton(ttk.Frame):
def __init__(self, master,nickname, hw_in=[], hw_out=[],ip_in='', ip_out='', sched_vector=[]):
ttk.Frame.__init__(self, master)
self.master = master
if ip_in == '': ip_in = ip_out # in case remote input is not defined
self.grid()
#####Rest of code
and class that inherits:
class ToggleBut2(CoreButton):
def __init__(self, master, hw_in=[], hw_out=[],ip_in='', ip_out='', sched_vector=[]):
CoreButton.__init__(self, master, nickname="JOHM", hw_in=hw_in, hw_out=hw_out, ip_in=ip_in, ip_out=ip_out, sched_vector=sched_vector)
self.master = master
def build_gui(self, nickname='babe', height=3, width=13):
self.button = tk.Checkbutton(self, text=nickname, variable=self.but_var, indicatoron=0, height=height, width=width, command=self.sf_button_press)
self.button.grid(row=0, column=0)
I don't know what you try to do but I would do something like this
I don't use self.grid() inside class, so outside class I can use tb1.pack() or tb1.grid() depends on which layout manager I use in window.
In __init__ I execute self.build_gui() so I don't have to do it manually, but now all classes have to create self.build_gui() without arguments.
I add Label only for test - to display "selected"/"not selected". You don't need it.
import tkinter as tk
from tkinter import ttk
class CoreButton(ttk.Frame):
def __init__(self, master, nickname, hw_in=None, hw_out=None, ip_in=None, ip_out=None, sched_vector=None):
ttk.Frame.__init__(self, master)
self.nickname = nickname
self.hw_in = hw_in
if self.hw_in is None:
self.hw_in = []
#self.hw_in = hw_in or []
self.hw_out = hw_out
if self.hw_out is None:
self.hw_out = []
#self.hw_out = hw_out or []
self.ip_out = ip_out
self.ip_in = ip_in
if self.ip_in is None:
self.ip_in = self.ip_out # in case remote input is not defined
#self.ip_in = hw_in or self.ip_out
self.sched_vector = sched_vector
if sched_vector is None:
sched_vector = []
#self.sched_vector = sched_vector or []
self.build_gui() # <--- to build it automatically
def build_gui(self):
# you will overide it in child widgets
raise NotImplementedError('You have to override method build_gui()')
class ToggleBut2(CoreButton):
def __init__(self, master, hw_in=None, hw_out=None, ip_in=None, ip_out=None, sched_vector=None, height=3, width=13):
self.height = height
self.width = width
# `self.but_var` is used in `build_gui` so it has to be created before `__init__` which executes `build_gui`
# or create it directly in `build_gui`
#self.but_var = tk.StringVar()
CoreButton.__init__(self, master, "JOHM", hw_in, hw_out, ip_in, ip_out, sched_vector)
def build_gui(self, nickname='babe'):
self.but_var = tk.IntVar()
self.button = tk.Checkbutton(self, text=self.nickname, variable=self.but_var, indicatoron=0, height=self.height, width=self.width, command=self.sf_button_press)
self.button.grid(row=0, column=0)
self.label = tk.Label(self, text='[not selected]')
self.label.grid(row=1, column=0)
def sf_button_press(self):
print(self.but_var.get())
if self.but_var.get() == 0:
self.label['text'] = '[ not selected ]'
else:
self.label['text'] = '[ selected ]'
# --- main ---
root = tk.Tk()
tb1 = ToggleBut2(root, height=1, width=10)
tb1.pack()
tb2 = ToggleBut2(root, height=3, width=30)
tb2.pack()
tb2 = ToggleBut2(root, height=5, width=50)
tb2.pack()
root.mainloop()