How i can close all parents and classes in python tkinter - python

I'm trying to make this code close all windows which are opened on windows class. At this moment it only closes few of them. Code works perfectly, but it just dont remove all windows which needs to be removed. Can someone please help me to make this script working?
import tkinter as tk
from tkinter import *
from PIL import Image, ImageTk
import webbrowser
import time
import random
def closeall():
global close
close = True
self = selfstr
parent = parentstr
Window.__init__(self, parent)
class Window(tk.Toplevel):
def __init__(self, parent):
if close == True:
self.destroy()
else:
global selfstr
selfstr = self
global parentstr
parentstr = parent
plusminus_list = ["+", "-"]
plusminus = random.choice(plusminus_list)
plusminus2 = random.choice(plusminus_list)
location = random.randint(1, 1000)
location2 = random.randint(1, 1000)
super().__init__(parent)
self.geometry(f'256x256{plusminus}{location}{plusminus2}{location2}')
self.title('NOESCAPE')
frame = Frame(self, width=256, height=256)
frame.pack()
frame.place(anchor='center', relx=0.5, rely=0.5)
img_list = ["jeff2", "jeff3", "jeff4", "jeff5", "jeff6"]
randomimg = random.choice(img_list)
img = ImageTk.PhotoImage(Image.open(f"{randomimg}.jpg"))
label = Label(frame, image = img)
label.pack()
def on_closing():
plusminus_list = ["+", "-"]
onlywayout()
plusminus = random.choice(plusminus_list)
plusminus2 = random.choice(plusminus_list)
location = random.randint(1, 1000)
location2 = random.randint(1, 1000)
img_list = ["jeff2", "jeff3", "jeff4", "jeff5", "jeff6"]
randomimg = random.choice(img_list)
img = ImageTk.PhotoImage(Image.open(f"{randomimg}.jpg"))
Window(self)
self.protocol("WM_DELETE_WINDOW", on_closing)
self.mainloop()

So you cannot access all Window instances you created for some reason? Then you could save a list with all instances in the Window class as a class attribute. With this list you should be able to destroy all Window instances. Your approach with using the constructor and global variables is a very bad coding style and you shouldn't do anything like this.
Here I created a classmethod so that you don't need the extra function closeall():
import tkinter
class Window(tkinter.Toplevel):
instances = [] # class attribute
def __init__(self, parent):
super().__init__(parent)
self.instances.append(self) # save current instance to the list
# ...
#classmethod
def destroy_all_instances(cls):
for window in cls.instances:
window.destroy()
# Destroy all Window instances:
Window.destroy_all_instances()

Related

How to update lables in a python frame class with (after) code

I'm attempting a Python frame class with a lable that updates every time period. I can't seem to get he configure thing to work for me. Thanks
from tkinter import * # get base widget set
from tkinter.messagebox import askokcancel
from datetime import datetime
class SensorUpdate(Frame) :
def __init__(self) :
Frame.__init__(self)
self.pack()
Label(self, text="Sensors").pack(anchor=NW)
self.timeLabel = Label(self,
text=datetime.now().strftime("%H:%M:%S")).pack(anchor=NW)
Button(self, text="Shut Down",command=(lambda :self.shutDown())).pack(side=RIGHT)
self.after(500,self.updateImage) #updates Frame Image
def shutDown(self):
print("in shutDown")
ans = askokcancel('Verify', "Really quit?")
if ans:
print("made it here")
SensorUpdate.quit(self)
def updateImage(self):
print(" Updating Image")
now = datetime.now()
current_time = now.strftime("%H:%M:%S")
print(current_time)
#self().timeLabel.configure(text='Test') # object not callable
#self.timeLabel.configure(text='Test') # object has no attribute timeLabel
#timeLabel.configure(text='Test') # timeLabel not found
self.after(10,self.updateImage)
root = Tk()
app = SensorUpdate()
app.mainloop()
root.destroy()
After spending some quality time with 3000 pages of Lutz texts, I've found a solution using two classes one for the basics of display and one for the update using the (after) command. Any help on my first method is apprecieated. I can make this work at least even if I'm not sure why the first one doesn't
from tkinter import * # get base widget set
from tkinter.messagebox import askokcancel
from datetime import datetime
class SensorConfig:
size = 200
bg, fg = 'beige', 'brown'
class IndicatorDisplay(Frame):
def __init__(self, parent, cfg):
Frame.__init__(self, parent)
self.timeLabel = Label(self)
self.timeLabel.config(bd=20,relief=SUNKEN,bg=cfg.bg,fg=cfg.fg)
self.timeLabel.pack(side=LEFT)
Button(self, text="Shut Down",command=(lambda :self.shutDown())).pack(side=RIGHT)
def shutDown(self):
print("in shutDown")
ans = askokcancel('Verify', "Really quit?")
if ans:
print("made it here")
self.quit()
def onUpdate(self, curTime):
self.timeLabel.config(text=curTime)
class Sensor(Frame):
def __init__(self, config=SensorConfig, parent=None):
Frame.__init__(self, parent)
self.pack(expand=YES, fill = BOTH)
self.cfg = config
self.display = IndicatorDisplay(self, self.cfg)
self.display.pack(side=TOP)
self.updateLabels()
def updateLabels(self):
print("make time change")
self.display.onUpdate(datetime.now().strftime("%H:%M:%S"))
self.after(500, self.updateLabels)
if __name__ == '__main__':
config = SensorConfig()
mySensor = Sensor(config)
mySensor.mainloop()

Getting input from another window in tkinter

I am having a little trouble with this project that I am working on. My project is this GUI application. In my test.py file, I call another file that contains instructions for another GUI window. This is where I am having trouble. In the test.py file, if you click run, a small window will appear. Click TEST in the small window. Then another window will appear that contains text fields if you enter numbers into the text fields for the window and then click enter. My IDE gets these error messages. It says that " ValueError: could not convert string to float: ' ' " My question is how do I fix this so that I do not get this error message? It is supposed to print the input that was entered into the window. I have two files, test.py, and model_objects.py. If you run model_objects.py by itself, it works perfectly. But when I try to import this file into test.py, it does not want to work right. This is programmed in Python. Also, my model_objects.py file is placed in a folder called util in the project. The values that I entered are floating-point values. I am having trouble with this. If you can help, I would greatly appreciate it.
Here is my code:
model_objects.py (This is in a folder called util in the project.)
import tkinter as tk
from tkinter import ttk
from tkinter.ttk import Style
import numpy as np
from util import InputData
class Harmonic_Oscillator:
def __init__(self):
self.type = 0
self.name = "Harmonic Oscillator"
self.nparam = 2
self.label = ["\u03BC", "k"]
self.param = np.zeros(self.nparam, float)
def set_param(self, param_list):
for i in range(self.nparam):
self.param[i] = param_list[i]
return
class Morse_Oscillator:
def __init__(self):
self.type = 1
self.name = "Morse Oscillator"
self.nparam = 3
self.label = ["\u03BC", "De", "a"]
self.param = np.zeros(self.nparam, float)
def set_param(self, param_list):
for i in range(self.nparam):
self.param[i] = param_list[i]
return
class Test_Oscillator:
def __init__(self):
self.type = 2
self.name = "Test Oscillator"
self.nparam = 4
self.mu = 0
self.label = ["a", "b", "c", "d"]
self.param = np.zeros(self.nparam, float)
def set_param(self, param_list):
for i in range(self.nparam):
self.param[i] = param_list[i]
return
def model_prompt(potential_model):
window1 = tk.Tk()
style = Style()
window1.title('PyFGH Parameters')
box_length = 103
for q in range(3):
box_length = box_length + 33 * potential_model[q].nparam
box_len_str = '300x' + str(box_length)
window1.geometry(box_len_str)
entries = []
qvar = np.empty(3, dtype=list)
for i in range(3):
qvar[i] = []
j = 0
y = 5
for q in range(3):
for qparam in range(potential_model[q].nparam):
qvar[q].append(tk.StringVar())
ttk.Label(window1, text=potential_model[q].label[qparam] + " for Q:" + str(q + 1) + ":",
font=("Times New Roman", 15)).place(x=50, y=y)
# set text variable as q1var[j] , each entry will have separate index in the list
a1 = ttk.Entry(window1, textvariable=qvar[q][qparam], font=("Times New Roman", 10)).place(x=140, y=y)
j += 1
y += 35
def enter_button():
for q in range(3):
param_list = []
for qparam in range(potential_model[q].nparam):
param_list.append(qvar[q][qparam].get())
potential_model[q].set_param(param_list) # This is giving me error. Not working properly!!!
for q in range(3):
for qparam in range(potential_model[q].nparam):
print(potential_model[q].param[qparam])
InputData.output.items.model_data = potential_model
print(InputData.output.items.model_data)
window1.destroy()
enter = tk.Button(window1, text='Enter', bd='20', bg='green', fg='white',
command=enter_button).place(x=110, y=y)
window1.mainloop()
def output2():
sections = []
for i in range(3):
if InputData.output.items.v[i] == "Model-Harmonic Oscillator":
sections.append(Harmonic_Oscillator())
elif InputData.output.items.v[i] == "Model-Morse Oscillator":
sections.append(Harmonic_Oscillator())
elif InputData.output.items.v[i] == "Model-Test Oscillator":
sections.append(Harmonic_Oscillator())
#test = [Harmonic_Oscillator(), Morse_Oscillator(), Test_Oscillator()]
#model_prompt(test)
Here is another file called test.py
from util import InputData
from util import model_objects
from util import model_objects
from util.model_objects import Harmonic_Oscillator, Morse_Oscillator, Test_Oscillator, model_prompt
import tkinter as tk
def write_slogan():
test = [Harmonic_Oscillator(), Morse_Oscillator(), Test_Oscillator()]
model_prompt(test)
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame,
text="QUIT",
fg="red",
command=quit)
button.pack(side=tk.LEFT)
slogan = tk.Button(frame,
text="TEST",
command=write_slogan)
slogan.pack(side=tk.LEFT)
root.mainloop()
It's a bit esoteric, but the issue is this line in your model_prompt function:
qvar[q].append(tk.StringVar())
The quick fix would be:
qvar[q].append(tk.StringVar(window1))
The tkinter variable types' constructors accept an optional parameter - a handle to the window to which the associated entry widget is attached. If none is supplied, by default, it will pick the first tk.Tk window that was instantiated (in your case, that was root in test.py). The StringVars will update themselves whenever the event queue of the bound window has cleared. Since you spawned a new tk.Tk, this interrupted your root event queue, and since your variables were bound to root, they did not update despite text being entered in the entries.
If you wanted to be super proper, you should look into tk.Toplevel or tkinter.simpledialog to spawn child windows. Your program should never have more than one tk.Tk window in the first place.
i made a little example, i hope it matches yourr mainproblem. if you want to work with classes, in the most cases you need references.
from tkinter import *
class MainWindow(Tk):
def __init__(self):
super(MainWindow, self).__init__()
self.test = Test(self) # test class import
self.outputLbl = Label(self) # output Label
self.outputLbl.pack(side="top", fill="x", ipady=20)
class Test(Frame):
def __init__(self, parent):
super(Test, self).__init__()
self.parent = parent # you can use this way to call between classes
self._input = Entry(self.parent)
self._input.pack()
self._input.bind("<Return>", self.outputMW)
def outputMW(self, event): # function, when pressing return it gives changes the text in your label
var = self._input.get()
self.parent.outputLbl.config(text=var) # self.parent makes the reference to your other class
if __name__ == '__main__':
mw = MainWindow()
mw.geometry("500x500")
mw.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()

How to save askdirectory result in a variable I can use using tkinter with OOP?

I have ran into some trouble.
I'm quite new to OOP and working with tkinter and GUI's in general.
I have managed to find some code on the Internet and meshed it all together to create something and I'm nearly where I want to be.
So what I want is some help figuring this out.
How can I assign results of askdirectory to a variable I can use elsewhere?
# coding=utf-8
import tkinter as tk
from tkinter import font as tkfont
from tkinter import filedialog
class MainApp(tk.Tk):
....
class SelectFunction(tk.Frame):
....
class FunctionChangeName(tk.Frame):
....
a = Gui(self)
# this gets me the askdirectory but how to add it to a variable?
Above is the call to run askdirectory code, and it works, just need to find out how to save it to a variable so I can use it, I have tried to print it in several ways, but all I get is something along the lines .!frame.!functionchangename.!gui.
class SelectDir:
def __init__(self, container, title, initial):
self.master = container
self.initial = initial
self.selected = initial
self.options = {'parent': container,'title': title,'initialdir':initial,}
def show(self):
result = filedialog.askdirectory()
if result:
self.selected = result
def get(self):
return self.selected
class Gui(tk.Frame):
def __init__(self, container):
tk.Frame.__init__(self, container)
frame = tk.Frame(container)
frame.pack()
self.seldir = SelectDir(self, "Select directory", "D:\\MyPgm\\Python\\Tiles_8")
button = tk.Button(frame, text="Select directory", command=self.select_dir)
button.grid(column=0, row=0)
self.act_dir = tk.StringVar()
self.act_dir.set("D:\\MyPgm\\Python\\Tiles_8")
entry = tk.Entry(frame, textvariable=self.act_dir, width=30)
entry.grid(column=0, row=1)
def select_dir(self):
self.seldir.show()
self.act_dir.set(self.seldir.get())
# or
# result = seldir.show()
# self.act_dir.set(result)
if __name__ == "__main__":
app = MainApp()
app.mainloop()
I had an idea:
example, if you have f inside a function, you can make it global to have access as variable
def print_path():
# select working directory
global f #make f global to access the path
f = filedialog.askdirectory(parent=root, initialdir="/", title='Select Dir')

OptionMenu on multiple windows with single mainloop

import Tkinter as tk
import guiOpMenu2
class Omenu(object):
def __init__(self):
self.app = tk.Tk()
self.app.title('test1')
self.OpMenu()
self.btn()
def OpMenu(self):
self.op = tk.StringVar()
self.opt =['1', '2']
self.men = tk.OptionMenu(self.app, self.op, *self.opt)
self.men.pack()
def btn(self):
self.btn_btn = tk.Button(self.app, text='newGui', command=self.test)
self.btn_btn.pack()
def test(self):
win = guiOpMenu2.Omenu2()
win = Omenu()
win.app.mainloop()
import Tkinter as tk
class Omenu2(object):
def __init__(self):
self.app = tk.Tk()
self.app.title('test1')
self.OpMenu2()
def OpMenu2(self):
self.op2 = tk.StringVar()
self.opt2 =['2', '3']
self.men2 = tk.OptionMenu(self.app, self.op2, *self.opt2, command=self.test)
self.men2.pack()
def test(self, num):
print self.op2.get()
print num
Clicking on the button newGui displays the new window, but the selected value does not show up on the second optionmenu widget. When I do print self.op2.get() or print num it does print the selected value though. What can be done to make the second optionmenu behave properly?
The issue occurs because in Omenu2 class, you are creating a new Tk() app.
Also, unless the .mainloop() is called for the application owning a component, its widgets would not get updated.
Also, I don't think you really need a new Tk() app itself. If you want the new option menu to come in a new window, you should use tk.Toplevel() .
Example -
The guiOpmenu2.py -
import Tkinter as tk
class Omenu2(object):
def __init__(self, app):
self.app = app
self.master = tk.Toplevel(app)
self.master.title('test1')
self.OpMenu2()
def OpMenu2(self):
self.op2 = tk.StringVar()
self.opt2 =['2', '3']
self.men2 = tk.OptionMenu(self.master, self.op2, *self.opt2, command=self.test)
self.men2.pack()
def test(self, num):
print self.op2.get()
print num
Your main script -
import Tkinter as tk
import guiOpMenu2
class Omenu(object):
def __init__(self):
self.app = tk.Tk()
self.app.title('test1')
self.OpMenu()
self.btn()
def OpMenu(self):
self.op = tk.StringVar()
self.opt =['1', '2']
self.men = tk.OptionMenu(self.app, self.op, *self.opt)
self.men.pack()
def btn(self):
self.btn_btn = tk.Button(self.app, text='newGui', command=self.test)
self.btn_btn.pack()
def test(self):
win = guiOpMenu2.Omenu2(self.app)
win = Omenu()
win.app.mainloop()

Categories