The following code will show a small Tkinter user interface, however my buttons will not trigger the loop called copy_loop. I have been trying for several hours and I cannot figure out how to fix this. Any help will be very appreciated.
import tkinter as tk
import threading
class App():
def __init__(self, master):
self.isrunning = False
self.button1 = tk.Button(main, text='start')
self.button1.bind = ("<Button-1>", self.startrunning)
self.button1.pack()
self.button2 = tk.Button(main, text='stop')
self.button2.bind = ("<Button-1>", self.stoprunning)
self.button2.pack()
def startrunning(self, event):
self.isrunning = True
t = threading.Thread(target=self.copy_loop)
t.start()
def stoprunning(self, event):
self.isrunning = False
def copy_loop(self):
while self.isrunning:
print("Running...")
main = tk.Tk()
app = App(main)
main.mainloop()
bind is a function, not an attribute. Where you have:
self.button1.bind = ("<Button-1>", self.startrunning)
you are actually overwriting the bind function on your instance. It is now a tuple.
Instead, do:
self.button1.bind("<Button-1>", self.startrunning)
And likewise for button2.
You have an equals sign where there shouldn't be one. The command should be:
self.button1 = tk.Button(main, text='start')
self.button1.bind("<Button-1>", self.startrunning)
Or the much neater:
self.button1 = tk.Button(main, text='start', command=self.startrunning)
And your method signatures should have event=None:
def startrunning(self, event=None):
Or even more flexible:
def startrunning(self, *args):
Related
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()
I created a GUI using Python, Tkinter. Everything seems to be fine. I have added several options to my Menu tab. In the File section I have added a feature to exit the GUI by clickinh onto the Exit button(not the button though, but a menu option). Here is the code that I have written.
from tkinter import *
from tkinter import filedialog, RIGHT
class Filedialog:
def directory(self):
content = filedialog.askdirectory()
return content
class Main(Frame):
def __init__(self):
self.opening_times = 0
self.current_image = 0
self.images = []
self.root = Tk()
self.root.title("Y")
self.root.configure(bg='pale turquoise')
self.root.geometry("800x800")
self.frame = Frame(self.root)
self.frame.pack(side=RIGHT)
self.frame = Frame.__init__(self, self.root)
self.widgets()
self.root.mainloop()
def widgets(self):
self.menu = Menu(self.root)
self.root.config(menu=self.menu)
self.filemenu = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_separator()
self.filemenu.add_command(label="Save")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.onExit)
#self.editmenu = Menu(self.menu, tearoff=0)
#self.menu.add_cascade(label='Edit', menu=self.editmenu)
#self.editmenu.add_command(label="Undo")
#self.editmenu.add_command(label="Redo")
def onExit(self):
self.root.quit()
if __name__ == "__main__":
Main()
I have almost succeeded. But the problem arises when I click on the exit. The function does what it has to. But the GUI window was not closing, also it freezes there. There was no response at all until I force the window to close externally.
I also tried doing:
self.quit()
/
self.exit()
But the response is the same as the GUI freezes and not letting me do any other activities. I had to close complete program to get the access again.
I am not getting the error at least, to know what is wrong.
Please help me find out what is wrong/solve this problem.
Thank you for your time.
Let me correct your code
from sys import exit
from tkinter import *
from tkinter import filedialog, RIGHT
class Filedialog:
def directory(self):
content = filedialog.askdirectory()
return content
class Main(Frame):
def __init__(self):
self.opening_times = 0
self.current_image = 0
self.images = []
self.root = Tk()
self.root.title("Y")
self.root.configure(bg='pale turquoise')
self.root.geometry("800x800")
self.frame = Frame(self.root)
self.frame.pack(side=RIGHT)
self.frame = Frame.__init__(self, self.root)
self.widgets()
self.root.mainloop()
def widgets(self):
self.menu = Menu(self.root)
self.root.config(menu=self.menu)
self.filemenu = Menu(self.menu, tearoff=False)
self.menu.add_cascade(label="File", menu=self.filemenu)
self.filemenu.add_separator()
self.filemenu.add_command(label="Save")
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.onExit)
#self.editmenu = Menu(self.menu, tearoff=0)
#self.menu.add_cascade(label='Edit', menu=self.editmenu)
#self.editmenu.add_command(label="Undo")
#self.editmenu.add_command(label="Redo")
def onExit(self):
self.root.destroy()
exit()
if __name__ == "__main__":
Main()
You need to use destroy() instead of quit() and you should use exit to quit from the console
The method you have to use is destroy. For example:
def onExit(self):
self.root.destroy()
Use destroy() method instead of quit.
You can use the method tk.destroy() to close a window. The built-in functions exit() and quit() close the Python console.
Working example
from tkinter import *
tk = Tk()
tk.title("Closing window demonstration")
def close_window():
tk.destroy()
quit = Button(tk, text = "Quit", command = close_window)
quit.pack()
tk.mainloop()
I'm trying to open few windows one after the other in tkinter. They are closing itself after set amount of time. Creating first window breaks my loop (it doesn't even reach print instruction). What am I doing wrong? Here is my code:
import tkinter as tk
class Subwindow:
def __init__(self, time):
self.time = time
self.window = tk.Toplevel()
self.window.geometry("300x300+1000+200")
self.window.wm_attributes("-topmost", 1)
tk.Label(self.window, text=time).pack(anchor=tk.CENTER)
self.update_time()
self.window.mainloop()
def update_time(self):
if self.time:
self.window.title(str(self.time))
self.time -= 1
self.window.after(1000, self.update_time)
else:
self.window.destroy()
class Window:
def __init__(self):
self.window = tk.Tk()
self.initialize()
self.window.mainloop()
def initialize(self):
buttons = tk.Frame(self.window)
tk.Button(self.window, text="Start", width=5, command=self.start).pack(in_=buttons, side="left")
tk.Button(self.window, text="Exit", width=5, command=self.close).pack(in_=buttons, side="left")
buttons.place(relx=0.97, rely=0.95, anchor=tk.SE)
def close(self):
self.window.destroy()
quit()
def start(self):
for x in [5,10,15]:
sub = Subwindow(x)
print(x)
Window()
Explain me please how can I fix it to get them opening one by one?
You should not call mainloop more than once. Remove the call to mainloop inside Subwindow.__init__.
I am trying to execute a Tkinter ttk Button method while the button is pressed, meaning I want the method to keep executing when the button is pressed and stop when I release it, but i can't quite figure it out. Here is the code.
from tkinter import *
from tkinter import ttk
class stuff (object):
def __init__(self, master):
master.title("Grid Master")
master.frame_1 = ttk.Frame(master)
master.frame_1.pack()
master.configure(background = "#FFFFFF")
self.button = ttk.Button(master, text = 'Press', command = self.callback)
self.button.pack()
def callback(self):
print ("Hello world")
def main():
root = Tk()
loop = stuff(root)
root.mainloop()
if __name__ == '__main__':
main()
You can see in the code that the method only prints "Hello world" and I want this function to execute and keep going, printing Hello world until I release the button.
You can use root.after() to repeatedly schedule a job to perform the required task. Note that I have changed the button event to activate when the button is pressed, and to terminate the "after" job when the button is released.
try:
from tkinter import *
from tkinter import ttk
except ImportError:
# Python 2, probably
from Tkinter import *
import ttk
class stuff (object):
def __init__(self, master):
self._master = master
master.title("Grid Master")
master.frame_1 = ttk.Frame(master)
master.frame_1.pack()
master.configure(background = "#FFFFFF")
self.button = ttk.Button(master, text = 'Press')
self.button.bind("<Button-1>", self.button_pressed)
self.button.bind("<ButtonRelease-1>", self.button_released)
self.button.pack()
self.hello_world_frequency = 1 # milliseconds
def hello_world(self):
print ("Hello world")
self._job = self._master.after(self.hello_world_frequency, self.hello_world)
def button_pressed(self, event):
print ("Button down")
self.hello_world()
def button_released(self, event):
print ("Button released")
self._master.after_cancel(self._job)
def main():
root = Tk()
loop = stuff(root)
root.mainloop()
if __name__ == '__main__':
main()
You can make a custom repeating ttk button class that inherits from ttk.Button but adds basic repeating functionality.
Try this. you can use it like
self.button = RepeatButton(master, text='Press', command=self.callback)
and you can set the repeatdelay and repeatinterval arguments, which default to 300 and 100.
class RepeatButton(ttk.Button):
def __init__(self, *args, **kwargs):
self.callback = kwargs.pop('command', None)
self.repeatinterval = kwargs.pop('repeatinterval', 100)
self.repeatdelay = kwargs.pop('repeatdelay', 300)
ttk.Button.__init__(self, *args, **kwargs)
if self.callback:
self.bind('<ButtonPress-1>', self.click)
self.bind('<ButtonRelease-1>', self.release)
def click(self, event=None):
self.callback()
self.after_id = self.after(self.repeatdelay, self.repeat)
def repeat(self):
self.callback()
self.after_id = self.after(self.repeatinterval, self.repeat)
def release(self, event=None):
self.after_cancel(self.after_id)
I have a basic python class that creates a window using the standard Tkinter library:
import Tkinter
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=self.lock_func)
self.processBtn.pack()
app = GUI(None)
app.mainloop()
when I hit the Process button, the window doesn't respond.
I want to be able to close the program (using the x button) whene the lock_func is runing.
You could use a generator to hold the state within the loop, and use yield to relinquish control back to the main loop. Then use self.after to repeatedly call the generator's next method to simulate the effect of while True -- but doing it in a way which is friendly to Tkinter's main loop.
import Tkinter as tk
class App(object):
def __init__(self, master):
self.master = master
self.initialize()
def lock_func(self):
def step():
while True:
print("blah")
self.nextstep_id = self.master.after(1, nextstep)
yield
nextstep = step().next
self.nextstep_id = self.master.after(1, nextstep)
def stop(self):
self.master.after_cancel(self.nextstep_id)
print("stopped")
def initialize(self):
self.nextstep_id = 0
self.process_button = tk.Button(self.master, text="Process",
command=self.lock_func)
self.stop_button = tk.Button(self.master, text="Stop",
command=self.stop)
self.process_button.pack()
self.stop_button.pack(expand='yes', fill='x')
root = tk.Tk()
app = App(root)
root.mainloop()
You can use the window.update() method too keep your GUI active and functional after every time you change something on it. During the roots mainloop, this happens automatically but if you're prolonging the mainloop it's probably a good idea to do it manually your self. Put the window.update() in the loop that is taking a while. Note: window is a Tk() object
One way is to use threading:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def lock_func(self):
while 1==1:
print "blah"
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.processBtn.pack()
app = GUI(None)
app.mainloop()
However, it will keep printing until you close the Python console.
To stop it, you can use another button that changes a variable:
import Tkinter
import thread
class GUI(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.shouldPrint = True
self.initialize()
def lock_func(self):
while self.shouldPrint:
print "blah"
def setShouldPrint(self, value):
self.shouldPrint = value
def initialize(self):
self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
self.stopBtn = Tkinter.Button(self, text = "Stop", command = lambda: self.setShouldPrint(False))
self.processBtn.grid(row = 1)
self.stopBtn.grid(row = 2)
app = GUI(None)
app.mainloop()