I am trying to write a gui which as a class which is the main application. A single instance of that class is created in the main root. At the same time I want a submit button to be clicked where some values are verified before a further submission to write the data. I am trying to do this by creating a new class for the Toplevel pop up window. But I am not sure how best to structure this. Ideally an instance of the pop up window class would be created each time the button is selected. It seems like with the way I have structured it another instance of the main application class has been created. I am a little confused how to correctly do this using OOP.
Below is some sample code to illustrate the problem.
import tkinter as tk
from tkinter import ttk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.title = "TITLE"
self.master = master
self.submit = ttk.Button(self, text = 'SUBMIT', command = self.click_submit_button)
self.submit.grid(row = 0, column = 2, padx = 20, pady = 20)
def click_submit_button(self):
self.submit_pop_up = submit_button(self.master)
print('New PopUp')
class submit_button(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.title = 'TITLE'
if __name__ == "__main__":
root = tk.Tk()
app = Window(root)
app.pack()
root.mainloop()
There is something missing from my understanding of the best approach to using OOP to structure a program like this.
Solution
Just make the submit_button class inherit the Window class and instantiate only the submiit_button class. With this, you don't need to instantiate the Window class. This allows for a special trick so that you can access the attributes of the submit_class class in the Window class without creating an instance. Just use self since it is actually an instance of the submit_class passed on to the Window class. Here is your code with that. There are many other suggestions and to know, see the code.
Suggestions
In the first place, why are you making submit_button a separate class? You could include it as a method of Window class. If you have good reason, it is ok but otherwise make it a method.
And also, why are you creating a root window and making the window class save it as an attribute? Just make the Window class inherit tk.Tk instead of tk.Frame. You can then create frame inside the __init__ function. Here is the code working code with these rectifications:
Code
import tkinter as tk
from tkinter import ttk
class Window(tk.Tk):
def __init__(self):
super().__init__()
self.title("TITLE")
self.submit = ttk.Button(self, text = 'SUBMIT', command = self.click_submit_button)
self.submit.grid(row = 0, column = 2, padx = 20, pady = 20)
class submit_button(Window):
def __init__(self):
super().__init__()
self.submit_pop_up = tk.Toplevel(self)
self.submit_pop_up.withdraw()
print(self.submit_pop_up)
def click_submit_button(self):
self.submit_pop_up.deiconify()
print('New PopUp')
if __name__ == "__main__":
app = submit_button()
app.mainloop()
Is that what you want? You should used self.master for all widgets.
import tkinter as tk
from tkinter import ttk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
self.master.title( "TITLE")
self.submit = ttk.Button(self.master, text='SUBMIT', command=self.click_submit_button)
self.submit.grid(row=0, column=2, padx=20, pady=20)
def click_submit_button(self):
self.submit_pop_up = submit_button(self.master)
print('New PopUp')
class submit_button(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.master.title('TITLE')
if __name__ == "__main__":
root = tk.Tk()
app = Window(root)
#app.pack()
root.mainloop()
Result:
Related
The goal is to pass the variable string1 from class Display to be used in another Tkinter window.
So when the button named Next [in class Display load function] is clicked, it would open a new Tkinter window. And in the new window, the variable string1 from class Display needs to be retrieved for further action. May i know should i create another class Display2, or should i just add a method in the class Display?
Currently the string variable can be passed as reference from class Display to the class Action_Data. But how can it be passed to another Tkinter window when the button Next is clicked?
I am trying to get the variable via the callback function new_window. Just not sure if it's how it's done. Any pointer would be appreciated. Many thanks.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self):
self.master = tk.Tk() # create another Tk instance
var_string2 = Label(self, text="<<string1 value>>")
var_string2.pack()
print (var_string2)
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command=self.new_window)
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()
Issue
Now the new window is blank and cannot retrieve the value of variable string1 from the load function
Error
Use the lambda function to pass the button's command, that way you can pass the needed string as an argument.
You do not need to create two instances of Tk, if you need another window, create a Toplevel.
from tkinter import *
import tkinter as tk
#Application window
root = tk.Tk()
#Display Class
class Display (tk.Frame):
def __init__(self, master, display_data):
tk.Frame.__init__(self,master)
self.master = master
#passing data as reference
self.display= display_data
#button
self.load_button = tk.Button(self, text="Load", command=self.load)
self.load_button.pack()
def new_window(self, string):
#self.master.destroy() # close the current window
new_window = tk.Toplevel() # create toplevel
var_string2 = Label(new_window, text=string)
var_string2.pack()
new_window.focus()
print (var_string2.cget("text"))
def load(self):
#get value
string1='value1'
self.display.action1(string1)
self.acition_button = tk.Button(self, text="Next",
command= lambda : self.new_window(string1))
self.acition_button.pack()
#Action_Data Class
class Action_Data(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self,master)
def action1(self, path1):
var_path1 = path1
print(var_path1)
display= Action_Data(root)
display.pack()
reader = Display(root, display)
reader.pack()
pathlabel2 = Label(root)
root.mainloop()
I keep getting this error and I cant seem to fix it, if anybody could help me I would really appreciate it. I have been looking at it for quite a while now and I can't seem to get my head around it, I am still quite new to programming in an Object Oriented Way.
Thanks & Merry Christmas
Welcome Page
from External_Menu import *
from tkinter import *
class Welcome(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.welcome_button()
self.pack()
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=ExternalMenu.menu)
self.welcome.pack()
self.pack()
if __name__ == "__main__":
root = Tk()
main = Welcome(root)
main.mainloop()
External Menu
from tkinter import *
class ExternalMenu(Frame):
def __init__(self, root):
Frame.__init__(self, root)
self.menu()
self.pack()
def menu(self):
self.external_menu_lbl = Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = Button(self, text="Sign Up")
self.sign_up_button.pack()
self.pack()
Your menu is a method so it needs an object it can manipulate in order to work. But you're trying to call it without an object created for it, essentially you're calling it like a function. First you need to create an object of the class that method is defined on:
an_ex_men = ExternalMenu(root)
and then you are able to call menu method on an_ex_men:
an_ex_men.menu()
But since you already call menu under your ExternalMenu's __int__ method it is called as soon as an object instance for that class is created. Shortly, you creating an ExternalMenu object is enough to achieve that as in, even without putting it to a variable to refer later:
ExternalMenu(root)
Since you want to swap between two windows you need an additional method to either hide or completely destroy the other window. Let's say you want to destroy the other window, and for that you could use a method defined for your button that does those 2 actions:
def welcome(self):
self.destroy()
ExternalMenu(root)
As in:
...
def welcome_button(self):
self.welcome = Button(self, text="Welcome!", command=self.welcome)
self.welcome.pack()
def welcome(self):
self.destroy()
ExternalMenu(root)
...
Below example does exactly what you expect, first I created a parent class App as according to you the two frames you have are in the same level of hierarchy and having a parent for them is in my opinion better structured:
import tkinter as tk
class App(tk.Tk):
def __init__(self):
super().__init__()
self.welcome_frame = Welcome(self)
#I believe it's better to call geometry managers as parent
self.welcome_frame.pack()
#assigning parent method as button command as it affects siblings
self.welcome_frame.button['command'] = self.go_ex_men
def go_ex_men(self):
self.welcome_frame.destroy()
self.ex_men = ExternalMenu(self)
self.ex_men.pack()
class Welcome(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.button = tk.Button(self, text="Welcome!")
self.button.pack()
class ExternalMenu(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.external_menu_lbl = tk.Label(self, text="External Menu", font=("", 26))
self.external_menu_lbl.pack()
self.sign_in_button = tk.Button(self, text="Sign In")
self.sign_in_button.pack()
self.sign_up_button = tk.Button(self, text="Sign Up")
self.sign_up_button.pack()
if __name__ == "__main__":
root = App()
root.mainloop()
I've been trying to get to grips with OOP and tkinter in python 3. I'd really like to have sub-windows pop up during use, either for data, output, etc. However, I cannot figure out how to assign the title in my Windows class, depending on what sort of window is being made. Please find a simplified example of what I have done so far.
from tkinter import *
from tkinter import messagebox
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("ProgramName")
self.pack(fill = BOTH, expand = 1)
menu = Menu(self.master)
self.master.config(menu=menu)
prog_help = Menu(menu)
prog_help.add_command(label='Help', command=self.help_popup)
prog_help.add_command(label='About', command=self.version_popup)
menu.add_cascade(label='Help', menu=prog_help)
#Method 1: Using message.box
def version_popup(self):
messagebox.showinfo("About program", "Version 0.1")
return
#Method 2: Using another window
def help_popup(self):
helpwindow()
return
def helpwindow():
hwindow = Toplevel()
hwindow.geometry("100x100")
root = Tk()
root.geometry("400x300")
app = Window(root)
root.mainloop()
I think that I should be doing something in the definition of helpwindow() which alters a variable in self.master.title(x), but I cannot figure out what.
Many thanks for your time.
In your case, the help window isn't a subclass of anything. All you need to do is call the title method of the Toplevel
def helpwindow():
hwindow = Toplevel()
hwindow.title("I am the help window")
...
I have the following code (example of my real program):
from tkinter import *
def class1(Frame)
def nv(self,x):
self.vent=Toplevel(self.master)
self.app=class2(self.vent)
self.value=x
def __init__(self,master):
super().__init__(master)
self.master=master
self.frame=Frame(self.master)
self.btn=Button(self, text="example", command=lambda: self.nw(1))
self.btn.pack()
self.pack()
def class2(Frame):
def __init__(self, master):
super().__init__(master)
self.master=master
self.frame=Frame(self.master)
self.value=class1.nw.value.get()
root= Tk()
marco=Frame(root)
marco.pack
lf=class1(marco)
root.mainloop()
this last part is the problem, i cant use .get() properly for this problem, I want to get the value of x when I create the new window.
I use lambda so i can execute the command with parameters.
So the question is, is there a way for me to access the value of x in class 2?
You appear to be quite confused about the usage of classes when using tkinter. super() should not be used with tkinter as explained here and when declaring a class you should use the class keyword, not def. .get() is a method of a tkinter variable class such as tkinter.IntVar, tkinter.StringVar, etc. so is not needed in the example you have given.
What you need is the function in the Frame you are trying to get the value of x from (nv) and then parse that value to the __init__ method in the child Frame.
Here is my solution:
from tkinter import *
class Class1(Frame):
def nv(self,x):
self.vent = Toplevel(self.master)
self.app = Class2(self.vent,x)
def __init__(self,master):
Frame.__init__(self,master)
self.master = master
self.btn = Button(self, text="example", command=lambda: self.nv(1))
self.btn.pack()
self.pack()
class Class2(Frame):
def __init__(self, master, x):
Frame.__init__(self,master)
self.master=master
self.frame=Frame(self.master)
self.x_text = Label(self, text=str(x))
self.x_text.pack()
self.pack()
root = Tk()
marco = Frame(root)
marco.pack()
lf = Class1(marco)
root.mainloop()
I have a Window wich opens another window askig for the settings. But the BooleanVar I use to get the Checkbutton's state doesn't change. It does however when I call the settingswindow strait from the code without the other window.
This is the minimal code to get the
from tkinter import *
class MainWindow():
def __init__(self, master):
self.root = master
SettingsWindow()
self.root.mainloop()
class SettingsWindow():
def __init__(self):
rootSettings = Tk()
self.rebuild = BooleanVar()
chkRebuild = Checkbutton(rootSettings, text="rebuild", variable=self.rebuild, command=self.testFunc)
chkRebuild.pack()
rootSettings.mainloop()
def testFunc(self):
print(self.rebuild.get())
root = Tk()
mainWindow = MainWindow(root)
The output is always 0 when clicking on the checkbutton, evenso the output of BooleanVar is True or False.
What is the difference in calling SettingsWindow() from insite a class or outside? At least I think that is the reason it is not working.
I am using Pyhton3 in case there is a difference.
You can't have two instances of Tk. For your second window you need to create a Toplevel. You also should never call mainloop more than once in your entire program.
This is a sample example for how to solve the checkbutton's variable not changing issue.
Key: for the second window, should use Toplevel, not use Tk.
from tkinter import *
class MainWindow:
def __init__(self):
self.master = Tk()
self.fun = dict()
def set_ui(self):
Button(self.master, text='SecondWindow', command=self.fun).pack(side=LEFT)
self.master.mainloop()
class SecondWindow:
def __init__(self):
self.root = Toplevel()
self.var = BooleanVar()
self.set_ui()
def printf(self):
print(self.var.get())
def set_ui(self):
Checkbutton(self.root, text='press', variable=self.var, command=self.printf).pack(side=LEFT)
def call_second_window():
second_window = SecondWindow()
main_window = MainWindow()
main_window.fun = call_second_window
main_window.set_ui()