tkinter variable does not change when changing Checkbutton state - python

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()

Related

Object oriented Tkinter

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:

How to get variable from a class to another Tkinter window

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()

Open a new window with the button

I am building a real time monitoring project where information is given in the first window and that's keep on updating in the second window. I am trying to monitor the updated information in parallel from a different window using the same code, but as I pressed the new button and given the new information it is updating in the previous window also but I wanted monitor window to be completely different, so that I can monitor the different information in parallel using the same code. Please have a look at the sample code and help me with the ideas.
The sample code:
import time
import threading
import tkinter.messagebox
from tkinter import ttk
import queue
from tkinter import *
class Demo1:
data=[]
def __init__(self, master):#Python scrollbar on text widget
self.master = master
self.t=tkinter.Text(self.master,height=20,width=50)
self.t.grid(row=1, column=1)
self.button = tkinter.Button(self.master,height=3,width=10, text="OK", command = self.new_window)
self.button.grid(row=2,column=1)
def new_window(self):
self.inputValue=self.t.get("1.0",'end-1c')
Demo1.data1=self.inputValue.split("\n")
self.master.destroy() # close the current window
self.master = tkinter.Toplevel() # create another Tk instance
self.app = Demo2(self.master) # create Demo2 window
self.master.mainloop()
class Demo2:
t1 = []
s1 = True
display = []
display1 = []
i=0
kas = True
num=0
j1=0
def __init__(self, master):
self.master = master
self.button = tkinter.Button(self.master,height=2,width=11, text="new",command=self.new).place(x=0,y=0)
self.label = tkinter.Label(self.master, text="monitor", font=("Arial",20)).grid(row=0, columnspan=3)
cols = ('aa','bb')
self.listBox = ttk.Treeview(self.master, columns=cols)
for col in cols:
self.listBox.heading(col, text=col)
self.listBox.column(col,minwidth=0,width=170)
self.listBox.grid(row=1, column=0)
self.a()
def a(self):
self._img=tkinter.PhotoImage(file="green1.gif")
a=Demo1.data1
for i,(a) in enumerate(a): #here I have some function which runs repeatedlly
self.listBox.insert('', 'end',image=self._img, value=(a))
threading.Timer(1.0, self.a).start()
def new(self):
main()
def main():
root = tkinter.Toplevel()
app = Demo1(root)
root.mainloop()
if __name__ == '__main__':
main()
I have given the pppp information to monitor but as a pressed new button and added the xx information its updating in the previous window also. Please help me with the idea so that the link between these window will be vanished.
Output:
You have some major issues with your program. Including how you are trying to use your classes. The Toplevel() object was giving me issue, so I used Tk(). This should show you how to properly use the classes with the window. Most importantly your second window needs to be created from global not the first window. Also Demo1.data is a reference to your class definition not the actual data you loaded. I hope this example is helpful.
from tkinter import *
# your second window should be created in global
def create_demo2():
global app, app2
root2 = Tk()
app2 = Demo2(root2, app)
class Demo1:
def __init__(self, window):
self.window = window
self.data = ""
self.button = Button(self.window, text="New Window",
command=create_demo2)
self.button.pack()
def set_data(self):
self.data = "data"
class Demo2:
# you could just use app from global scope, but I like to pass so that it is explicit.
def __init__(self, window, app1):
self.window = window
self.button_set = Button(self.window, text="Set", command=app1.set_data)
self.button_use = Button(self.window, text="Use", command=self.use_data)
self.app = app1
self.label = Label(self.window)
self.button_set.pack()
self.button_use.pack()
self.label.pack()
def use_data(self):
self.label.configure(text=self.app.data)
root = Tk()
app = Demo1(root)
app2 = None
root.mainloop()

Modifying a Window Class in Tkinter (Python 3)

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")
...

import a python file that create a window when main window button clicks

I am creating 2 window in my program and i am using two class, since the code is complex, i separate it in 2 different python file. After i imported the second window file, how can i make sure it open without having this error which show in this picture
The original result should look like this after the new window button clicked:
Coding for Main Window:
from tkinter import *
import classGUIProgram
class Window(Tk):
def __init__(self, parent):
Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.geometry("600x400+30+30")
self.wButton = Button(self, text='newWindow', command = self.OnButtonClick)
self.wButton.pack()
def OnButtonClick(classGUIProgram):
classGUIProgram.top = Toplevel()
master = Tk()
b = classGUIProgram.HappyButton(master)
master.mainloop()
if __name__ == "__main__":
window = Window(None)
window.title("title")
window.mainloop()
Coding for Second Window:
from tkinter import *
class HappyButton:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.printButton = Button(frame, text="Print message", command=self.printMessage)
self.printButton.pack(side=LEFT)
self.quitButton = Button(frame, text="Quit", command= quit)
self.quitButton.pack(side=LEFT)
self.downloadHistoryCB=Checkbutton(frame, text="Download History")
self.downloadHistoryCB.pack(side=LEFT)
def printMessage(self):
print("Wow this actually worked!")
master = Tk()
b = HappyButton(master)
master.mainloop()
You're creating extra Tk windows. Here is an example of using Toplevel widgets and another file.
mainWindow.py
import tkinter as tk
import secondWindow as sW
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Main Window")
self.geometry("600x400+30+30")
tk.Button(self, text = "New Window", command = self.new_window).pack()
tk.Button(self, text = "Close Window", command = self.close).pack()
self._second_window = None
def new_window(self):
# This prevents multiple clicks opening multiple windows
if self._second_window is not None:
return
self._second_window = sW.SubWindow(self)
def close(self):
# Destory the 2nd window and reset the value to None
if self._second_window is not None:
self._second_window.destroy()
self._second_window = None
if __name__ == '__main__':
window = MainWindow()
window.mainloop()
secondWindow.py
import tkinter as tk
class SubWindow(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("Sub Window")
self.geometry("400x300+30+30")
# Change what happens when you click the X button
# This is done so changes also reflect in the main window class
self.protocol('WM_DELETE_WINDOW', master.close)
tk.Button(self, text = "Print", command = self.printMessage).pack()
def printMessage(self):
print("Wow this actually worked!")
When using another file be sure to not have any global code you don't want running. Your classes don't have to inherit from Tk and Toplevel, this is just an example. But you need to ensure you only ever have one instance of Tk otherwise you get the behaviour you encountered

Categories