so i have this code:
import thread
from Tkinter import *
import random
import time
Admin=Tk()
def moveit(number):
songas=Label(Admin,text=number,bg='red')
def ji():
plad=0.0
recount=0
times=0
while 1:
plad-=0.1
recount+=1
times+=1
time.sleep(0.5)
pls=0.0
pls+=plad
if recount==4:
pls=0
plad=0.0
recount=0
songas.place(relx=pls,rely=0.7)
thread.start_new_thread(ji,())
za=random.random()
button=Button(Admin,text='Press',command=lambda:moveit(str(za)))
button.place(relx=0.2)
Admin.mainloop()
And it starts to move to the left but if you press the 'press' button again it puts some more numbers on top of the old ones.
does any one know how to erase the old numbers to make it so there are only the knew ones?
Tkinter isn't thread safe -- you can't manipulate widgets in any thread except the main one or you'll get undefined results.
You don't need threads for this. Your code adds an infinite loop, but the application already has an infinite loop (the event loop) that you can take advantage of.
If you want to move some item create a function that does two things. First, it does whatever it is you want, such as move the item. Second, it uses the standard after method to call itself again in a short amount of time (for example, half a second or 500ms). This way you let your event loop drive the animation, you don't need threads, and your UI stays responsive.
Here's an example. I doubt it does exactly what you want because I'm not certain of exactly what you want.
import Tkinter as tk
import random
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
self._running = False
self._relx = None
tk.Tk.__init__(self, *args, **kwargs)
self.pack_propagate(False)
self.configure(width=400, height=400)
self.label = tk.Label(self, text="hello, world", background="red")
self.button = tk.Button(self, text="Start", command=self.toggle)
self.button.pack(side="top")
def toggle(self):
'''toggle animation on or off'''
self._running = not self._running
if self._running:
self.button.configure(text="Stop")
self.moveit()
else:
self.button.configure(text="Start")
def moveit(self):
'''Animate the label'''
if not self._running:
# animation has been stopped
# hide the label from view.
self.label.place_forget()
if self._running:
if not self.label.winfo_viewable():
# not visible; establish future locations
self._relx = [.5, .4, .3, .2, .1, 0]
relx = self._relx.pop(0)
self._relx.append(relx)
self.label.place(relx=relx, rely=0.7)
self.after(1000, self.moveit)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
You must signal the old thread to exit somehow. It is probably easiest to perform with locks - you create a lock when creating a new thread and acquire it. And you release it when the thread is no longer needed. The thread then only needs to check in the main loop whether its lock is still locked - if it isn't it will remove the label and exit. Here the modified version of your code (replace "Remove label here" comment by suitable code):
import thread
from Tkinter import *
import random
import time
Admin=Tk()
lock = None
def moveit(number):
global lock
songas=Label(Admin,text=number,bg='red')
def ji(lock):
plad=0.0
recount=0
times=0
while 1:
plad-=0.1
recount+=1
times+=1
time.sleep(0.5)
pls=0.0
pls+=plad
if recount==4:
pls=0
plad=0.0
recount=0
songas.place(relx=pls,rely=0.7)
if not lock.locked():
# Remove label here
break
if lock:
# Signal old thread to exit
lock.release()
lock = thread.allocate_lock()
lock.acquire()
thread.start_new_thread(ji,(lock,))
za=random.random()
button=Button(Admin,text='Press',command=lambda:moveit(str(za)))
button.place(relx=0.2)
Admin.mainloop()
Related
I want to stop a loop when I press a button. If I do it all in the same files I don´t have a problem but when I put the loop as a routine from another file It doesn´t work and I don´t know why. I hope you can help me! Thanks!!
First of all, this is subroutine.py, the file where is the loop allocated
subroutine.py
def routine (pressed):
while True:
print ("hello")
if pressed== 1:
break #Break while loop when stop = 1
And this is the "main" file
import tkinter as tk
import threading
import subroutine
pressed = 0
def Start_Scan():
global pressed
pressed = 0
# Create and launch a thread
t = threading.Thread (target = subroutine.routine (pressed))
t.start()
def Stop():
global pressed
pressed = 1
#main
master = tk.Tk()
app = tk.Frame(master)
app.grid()
start = tk.Button(app, text="Start Scan",command=Start_Scan)
stop = tk.Button(app, text="Stop",command=Stop)
start.grid()
stop.grid()
app.mainloop()
I use thread and a global variable, I thought about not using a global variable but I didn't find a way for the two threads to communicate that was not blocking. Thanks!!
You can use threading.Events to pass a flag between the main thread and the worker thread
# main.py
stop_event = threading.Event()
def Start_Scan():
# Create and launch a thread
t = threading.Thread (
target=subroutine.routine,
args=(stop_event,) # pass the Event object to the worker target
)
t.start()
def Stop():
stop_event.set()
Just update your subroutine to keep an eye on that event
# subroutine.py
def routine(stop_event):
while not stop_event.is_set(): # loop until the Event is 'set'
print ("hello")
I have a problem updating labels in a loop during run time. I think I need to use signals and so on, but I've tried everything I can think of now. What I want my program to do:
When I click the button a loop should start that run some function that take some time. While the function is running the corresponding label should update its text to say "running" and when its done it should say "done" and continue to the next function. I've created some dummy code that represent what I'm after!
I have represented my function with a dummy function that just take some time.
import sys
from PyQt5.QtWidgets import *
import time
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
# Set up an example ui
qbtn = QPushButton('Click', self)
qbtn.clicked.connect(self.changeLabels)
qbtn.resize(qbtn.sizeHint())
qbtn.move(100, 50)
# Add labels
self.labels = list()
self.labels.append(QLabel("Lbl1", self))
self.labels[-1].setFixedSize(50, 20)
self.labels.append(QLabel("Lbl2", self))
self.labels[-1].setFixedSize(50, 20)
self.labels[-1].move(0, 20)
self.labels.append(QLabel("Lbl3", self))
self.labels[-1].setFixedSize(50, 20)
self.labels[-1].move(0, 40)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Test')
self.show()
def changeLabels(self):
# Loop over all labels. For each label a function will be executed that will take some time. In this case
# I represent that with a dummy function to just take time. While the function is running the label should say
# "running" and when its finished it should say "done".
for lbl in self.labels:
orgTxt = lbl.text()
lbl.setText("%s Running" % orgTxt)
self.dummyFunction()
lbl.setText("%s Done" % orgTxt)
def dummyFunction(self):
time.sleep(1)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
The GUI does not support blocking tasks because the GUI needs some time to update some attributes of the window, a possible solution is to use a new thread and implement the dummy function, in the following example the implementation is shown with the use of signals. If your real function updates any GUI view you should not do it directly in the thread, you should do it through signals.
class DummyThread(QThread):
finished = pyqtSignal()
def run(self):
time.sleep(1)
self.finished.emit()
class Example(QWidget):
[...]
def changeLabels(self):
for lbl in self.labels:
orgTxt = lbl.text()
lbl.setText("%s Running" % orgTxt)
thread = DummyThread(self)
thread.start()
thread.finished.connect(lambda txt=orgTxt, lbl=lbl : lbl.setText("%s Done" % txt))
An easier way would be to call QApplication.processEvents() to force QT to process all the events present on the queue. But the UI won't be as responsive as it would be with another thread doing the work and sending signals to the main thread as stated in other answers.
I am trying to display a count variable from a background task in the main task which is my tkinter GUI. Why? I want to display that the long time taking background task is performing and later use this count variable to visualize it with a progress bar.
My problem is, that even when using a Queue, I am not able to display the count variable. Maybe I've got problems in understanding python and its behaviour with objects and/or threads.
import threading
import time
import Queue
import Tkinter as Tk
import Tkconstants as TkConst
from ScrolledText import ScrolledText
from tkFont import Font
import loop_simulation as loop
def on_after_elapsed():
while True:
try:
v = dataQ.get(timeout=0.1)
except:
break
scrText.insert(TkConst.END, str(v)) # "value=%d\n" % v
scrText.see(TkConst.END)
scrText.update()
top.after(100, on_after_elapsed)
def thread_proc1():
x = -1
dataQ.put(x)
x = loop.loop_simulation().start_counting()
# th_proc = threading.Thread(target=x.start_counting())
# th_proc.start()
for i in range(5):
for j in range(20):
dataQ.put(x.get_i())
time.sleep(0.1)
# x += 1
time.sleep(0.5)
dataQ.put(x.get_i())
top = Tk.Tk()
dataQ = Queue.Queue(maxsize=0)
f = Font(family='Courier New', size=12)
scrText = ScrolledText(master=top, height=20, width=120, font=f)
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15, expand=True)
th = threading.Thread(target=thread_proc1)
th.start()
top.after(100, on_after_elapsed)
top.mainloop()
th.join()
In thread_proc1() I want to get the value of the counter of background task. This is the background task:
import time
class loop_simulation:
def __init__(self):
self.j = 0
# self.start_counting()
def start_counting(self):
for i in range(0, 1000000):
self.j = i
time.sleep(0.5)
def get_i(self):
return str(self.j)
The reason the count variable isn't being displayed is due to the
x = loop.loop_simulation().start_counting()
statement in thread_proc1(). This creates a loop_simulation instance and calls its start_counting() method. However, other than already inserting a -1 into the dataQ, thread_proc1() doesn't do anything else until start_counting() returns, which won't be for a long time (500K seconds).
Meanwhile, the rest of your script is running and displaying only that initial -1 that was put in.
Also note that if start_counting() ever did return, its value of None is going to be assigned to x which later code attempts to use with: x.get_i().
Below is reworking of your code that fixes these issues and also follows the PEP 8 - Style Guide for Python Code more closely. To avoid the main problem of calling start_counting(), I changed your loop_simulation class into a subclass of threading.Thread and renamed it LoopSimulation, and create an instance of it in thread_proc1, so there are now two background threads in addition to the main one handling the tkinter-based GUI.
import loop_simulation as loop
from ScrolledText import ScrolledText
import threading
import Tkinter as Tk
import Tkconstants as TkConst
from tkFont import Font
import time
import Queue
def on_after_elapsed():
# removes all data currently in the queue and displays it in the text box
while True:
try:
v = dataQ.get_nowait()
scrText.insert(TkConst.END, str(v)+'\n')
scrText.see(TkConst.END)
except Queue.Empty:
top.after(100, on_after_elapsed)
break
def thread_proc1():
dataQ.put(-1)
ls = loop.LoopSimulation() # slow background task Thread
ls.start_counting()
while ls.is_alive(): # background task still running?
for i in range(5):
for j in range(20):
dataQ.put(ls.get_i())
time.sleep(0.1)
time.sleep(0.5)
dataQ.put('background task finished')
top = Tk.Tk()
dataQ = Queue.Queue(maxsize=0)
font = Font(family='Courier New', size=12)
scrText = ScrolledText(top, height=20, width=120, font=font)
scrText.pack(fill=TkConst.BOTH, side=TkConst.LEFT, padx=15, pady=15,
expand=TkConst.YES)
th = threading.Thread(target=thread_proc1)
th.daemon = True # OK for main to exit even if thread is still running
th.start()
top.after(100, on_after_elapsed)
top.mainloop()
loop_simulation.py module:
import threading
import time
class LoopSimulation(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True # OK for main to exit even if instance still running
self.lock = threading.Lock()
self.j = 0
start_counting = threading.Thread.start # an alias for starting thread
def run(self):
for i in range(1000000):
with self.lock:
self.j = i
time.sleep(0.5)
def get_i(self):
with self.lock:
return self.j
I want some basics on the problem of making some sort of "Stop" button that in my case terminates the series of beeps:
from tkinter import *
import winsound
from random import randint
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.widgets()
def widgets(self):
self.beep = Button(self, text = "Beep", command = play_beep)
self.beep.pack()
self.stop = Button(self, text = "Stop", command = stop_beep)
self.stop.pack()
go_on = True
def play_beep():
count = 10
while go_on == True and count != 0:
winsound.Beep(randint(100, 2500), 200)
count -= 1
def stop_beep():
go_on = False
root = Tk()
app = App(root)
root.mainloop()
When I press the "Beep" button it gets stuck as well as all the GUI until the beeps end. Could anyone tell me how to fix it?
I don't use TKinter, but I believe your button press is not creating a separate thread or process. The reason why your button gets stuck is because your play_beep loop is blocking your GUI execution loop. So we use threading. The thread executes at the same time as your GUI, so you can basically do two things at once (listen for GUI events and play beep noises).
import threading
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.is_playing = False
self.pack()
self.widgets()
def widgets(self):
self.beep = Button(self, text = "Beep", command = self.play_beep)
self.beep.pack()
self.stop = Button(self, text = "Stop", command = self.stop_beep)
self.stop.pack()
def play_beep(self):
self.is_running = True
self.beep_th = threading.Thread(target=self.run)
self.beep_th.start()
def run(self):
count = 10
while self.is_running == True and count != 0:
winsound.Beep(randint(100, 2500), 200)
count -= 1
def stop_beep(self):
try:
self.is_running = False
self.beep_th.join(0)
self.beep_th = None
except (AttributeError, RuntimeError): # beep thread could be None
pass
def closeEvent(self, event): # This is a pyside method look for a TKinter equivalent.
"""When you close the App clean up the thread and close the thread properly."""
self.stop_beep()
super().closeEvent(event)
First off, your question has nothing to do about threads or processes. Tkinter is single-threaded.
If you want to run some function periodically in a tkinter program, you must give the event loop a chance to process events. The typical solution is to do it like this:
def play_beep(count=10):
if go_on and count != 0:
winsound.Beep(randint(100, 2500), 200)
root.after(1000, play_beep, count=1)
This will cause the beep to play every second (1000ms) for ten iterations. In between each call, the event loop will have a chance to process other events.
Now, if the code you are running takes a long time, you're going to have to run that code in a separate thread or process. I know nothing about winsound.Beep so I don't know if that's necessary or not.
Second, to be able to interrupt it, you need to make go_on global, otherwise you're simply setting a local variable that never gets used.
def stop_beek():
global go_on
go_on = False
I would like the code to run in the background and to update my GUI periodically. How can I accomplish this?
For example, suppose I want to execute something like this in the background of the GUI code you can see below:
x = 0
while True:
print(x)
x = x + 1
time.sleep(1)
This is the GUI code:
class GUIFramework(Frame):
def __init__(self,master=None):
Frame.__init__(self,master)
self.master.title("Volume Monitor")
self.grid(padx=10, pady=10,sticky=N+S+E+W)
self.CreateWidgets()
def CreateWidgets(self):
textOne = Entry(self, width=2)
textOne.grid(row=1, column=0)
listbox = Listbox(self,relief=SUNKEN)
listbox.grid(row=5,rowspan=2,column=0,columnspan=4,sticky=N+W+S+E,pady=5)
listbox.insert(END,"This is an alert message.")
if __name__ == "__main__":
guiFrame = GUIFramework()
guiFrame.mainloop()
It is a little unclear what your code at the top is supposed to do, however, if you just want to call a function every second (or every the amount of seconds you want), you can use the after method.
So, if you just want to do something with textOne, you'd probably do something like:
...
textOne = Entry(self, width=2)
textOne.x = 0
def increment_textOne():
textOne.x += 1
# register "increment_textOne" to be called every 1 sec
self.after(1000, increment_textOne)
You could make this function a method of your class (in this case I called it callback), and your code would look like this:
class Foo(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.x = 0
self.id = self.after(1000, self.callback)
def callback(self):
self.x += 1
print(self.x)
#You can cancel the call by doing "self.after_cancel(self.id)"
self.id = self.after(1000, self.callback)
gui = Foo()
gui.mainloop()
If you truly want to run a distinct infinite loop you have no choice but to use a separate thread, and communicate via a thread safe queue. However, except under fairly unusual circumstances you should never need to run an infinite loop. Afte all, you already have an infinite loop running: the event loop. So, when you say you want an infinite loop you are really asking how to do an infinite loop inside an infinite loop.
#mgilson has given a good example on how to do that using after, which you should consider trying before trying to use threads. Threading makes what you want possible, but it also makes your code considerably more complex.