I want to set a function to repeat again again after the message box is shown.
A short example of code is shown below that what i exactly want to do
def setInterval(func,time,args):
e = threading.Event()
while not e.wait(time):
func(args)
def foo(data):
print data
aa("what")
def aa(a):
print(a)
tkMessageBox.showinfo("Regret","nope")
setInterval(foo,5,"fsrty")
All things are okay but the problem is only that once the messgae box is shown its give "not responding error".Anyone please help to find out the solution
You will need to use the .after method since threading never work well this tkinter and nor does a while loop.
import Tkinter as tk
import TkMessageBox
def setInterval(func,time,args):
func(args)
root.after(time, setInterval(func, time, args))
root.tk.Tk()
root.withdraw()
def foo(data):
print data
aa("what")
def aa(a):
print(a)
tkMessageBox.showinfo("Regret","nope")
setInterval(foo, 5, "fsrty")
root.mainloop()
Threading and Tk don't mix well, as they violate the Tcl/Tk threading model of using Tk just from one thread and your code doesnt work, because you occupied the main (one and only) thread!
While your print-functions keeps printing - GUI is unresponsible because of that and you can check it easily with another print in your code:
>>> print('**** Active threads: %d ****' % threading.active_count())
**** Active threads: 1 ****
So what you really need is just create another one!
try:
import tkinter as tk
from tkinter import messagebox as msgbox
except ImportError:
import Tkinter as tk
import TkMessageBox as msgbox
import threading
def setInterval(func,time,args):
e = threading.Event()
while not e.wait(time):
print('**** Active threads: %d ****' % threading.active_count())
func(args)
def foo(data):
print(data)
aa("what")
def aa(a):
print(a)
root = tk.Tk()
print('**** Active threads: %d ****' % threading.active_count())
thread = threading.Thread(target=setInterval, args=(foo, 5, "fsrty"))
msgbox.showinfo("Regret", "nope")
thread.start()
root.mainloop()
But here's another problem - threads keep running even after your close GUI (when you escaped mainloop)! So if you trying to achieve something simple - .after() is an option (quick and small alternative).
But if you stubborny or really need this - create (override) a custom subclass of the thread object, it's gives you more flexibility to control your flow!
try:
import tkinter as tk
from tkinter import messagebox as msgbox
except ImportError:
import Tkinter as tk
import TkMessageBox as msgbox
import threading
class Thread(threading.Thread):
def __init__(self):
super(Thread, self).__init__()
self.running = False
self.function_to_execute = None
self._stop = threading.Event()
def start_thread(self, func, *args, **kwargs):
self.function_to_execute = (func, args, kwargs)
self.running = True
self.start()
def run(self):
print('### STARTED ###')
while self.running:
try:
print('### RUNNING ###')
function, args, kwargs = self.function_to_execute
function(*args, **kwargs)
except:
self.stop()
def stop(self):
print('### STOPPING ###')
self.running = False
self._stop.set()
def setInterval(func, time, args):
e = threading.Event()
e.wait(time)
print('**** Active threads: %d ****' % threading.active_count())
func(args)
def foo(data):
print(data)
aa('what')
def aa(a):
print(a)
def clear():
thread.stop()
while True:
try:
thread.is_alive()
except TypeError:
root.destroy()
print('### STOPPED ###')
print('**** Active threads: %d ****' % threading.active_count())
break
root = tk.Tk()
thread = Thread()
print('**** Active threads: %d ****' % threading.active_count())
msgbox.showinfo("Regret", "nope")
thread.start_thread(setInterval, foo, 5, 'fsrty')
root.protocol('WM_DELETE_WINDOW', clear)
root.mainloop()
As you see - things are getting more complicated when we trying to clear out before exiting, but it's works!
Conclusion:
For simple things - .after() is a good option!
For complicated and long-executed things - threading is your choise!
Links:
after() method
Tkinter: How to use threads to preventing main event loop from “freezing”
Tkinter and Threads
Also, when things getting complicated: Best way to structure a tkinter application
Related
I want to change label every sec when i press the button in tk:
# --coding:utf-8 -----
from Tkinter import *
import time
import random
def test(a):
begin =time.time()
end =time.time()
while True:
ran = random.random()
after = time.time()
if(after-begin >1):
a.set(str(ran))
print a.get()
begin =after
if(after-end>10):
a.set('over')
break
t = Tk()
a = StringVar()
a.set('0')
b = Label(t,textvariable = a)
b.pack()
Button(t,text ='test',command = lambda x=a:test(a)).pack()
t.mainloop()
My console output is right,but it doesnot effect on windows.WHY?
You can start the test() function in a separate thread, like this:
# --coding:utf-8 -----
from Tkinter import *
import time
import random
import threading
def startThread(a):
threading.Thread(target=test, args=(a,)).start()
def test(a):
begin =time.time()
end =time.time()
while True:
ran = random.random()
after = time.time()
if(after-begin >1):
a.set(str(ran))
print a.get()
begin =after
if(after-end>10):
a.set('over')
break
t = Tk()
a = StringVar()
a.set('0')
b = Label(t,textvariable = a)
b.pack()
Button(t,text ='test',command = lambda x=a:startThread(a)).pack()
t.mainloop()
However, the thread won't end until the end of the 10 seconds, even if you close the window. You'll need to find some code for the while loop to check if the application is still running.
Creating a behavior that must execute for a given time period is a frequent problem; you can tackle it with a dedicated Waiter class; this avoids the use of threads that are poorly supported by tkinter.
The following example opens a window with a label and a button. When the button is pressed, the label will update every seconds for 10 seconds, then stop.
Pressing the button again within 10 seconds has no effect; after that, it restarts the process for 10 more seconds.
import Tkinter as tk # tkinter if python >= 3
import time
import random
class Waiter(object):
def __init__(self, waiting_time):
"""
:param waiting_time: int, in seconds
"""
self.waiting_time = waiting_time
self.expiring_time = time.time() + self.waiting_time
self.waiting = True
# print('waiter started')
def stop(self):
# print('waiter stopping')
self.expiring_time = None
self.waiting = False
def is_waiting(self):
"""returns True while waiting, false otherwise"""
if time.time() > self.expiring_time:
self.stop()
return self.waiting
def atest():
global waiter
if waiter is None:
waiter = Waiter(10)
_atest()
def _atest():
""" equivalent to:
while the waiter is waiting,
change the label every second),
then destroy the waiter
"""
global waiter
if waiter.is_waiting():
a.set(random.random())
# print(time.time())
t.after(1000, _atest)
else:
waiter = None
if __name__ == '__main__':
waiter = None
t = tk.Tk()
a = tk.StringVar()
a.set('0')
tk.Label(t, textvariable=a).pack()
tk.Button(t, text='test', command=atest).pack()
t.mainloop()
Note:
You could make _atest an inner function of atest, but maybe it is easier to understand as it is?
using import Tkinter as tk instead of from Tkinter import * prevents cluttering the namespace, and arguably makes the code clearer.
You should probably consider using python 3.
I have this simple program where I want to have a modal, non-blocking progress window (using a QProgressDialog) that is remotely updated. SIZE simply controls the maximum value of the QProgressDialog. However, if I set it to have a value of 4 or less, the window looks like this during the entire duration of the action:
In other words, the window is completely white and displays no text nor progress bar. If I set the value of SIZE to 5 or more, the display works correctly, but only after the 2-3 first iterations:
and later
import sys, time
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
SIZE = 5
def doGenerate(setValue):
for x2 in range(SIZE):
time.sleep(1)
setValue(x2 + 1)
print('Done')
class MainMenu(QMainWindow):
def __init__(self):
super().__init__()
self.genAudioButton = QPushButton('Generate', self)
self.genAudioButton.clicked.connect(self.generate)
self.setCentralWidget(self.genAudioButton)
self.show()
def generate(self):
try:
progress = QProgressDialog('Work in progress', '', 0, SIZE, self)
progress.setWindowTitle("Generating files...")
progress.setWindowModality(Qt.WindowModal)
progress.show()
progress.setValue(0)
doGenerate(progress.setValue)
except Exception as e:
errBox = QMessageBox()
errBox.setWindowTitle('Error')
errBox.setText('Error: ' + str(e))
errBox.addButton(QMessageBox.Ok)
errBox.exec()
return
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainMenu()
ret = app.exec_()
sys.exit(ret)
What is causing this and how can I fix it?
Additionally, is there a way to completely remove the cancel button, instead of having an empty button that still cancels the action? The PyQt4 docs (I am using PyQt5) indicate that an empty string should achieve this result, and the C++ docs for Qt5 indicate the same, but that clearly doesn't work here. I haven't found standalone documentation for PyQt5.
The GUI implements a mainloop through app.exec_(), this loop is used to perform tasks such as checking events, signals, calling some functions, etc. so if we interrupt the loop we can get unexpected behavior like the one you observe. in your case sleep() is a blocking function that should not be used, Qt offers alternatives to it, and one of them is to use a QEventLoop with a QTimer:
def doGenerate(setValue):
for x2 in range(SIZE):
loop = QEventLoop()
QTimer.singleShot(1000, loop.quit)
loop.exec_()
setValue(x2 + 1)
print('Done')
If you want the cancel button not to show, you must pass None:
progress = QProgressDialog('Work in progress', None, 0, SIZE, self)
If you want to use gTTS you must do it through threads, Qt offers several ways to implement it, in this case I will use QThreadPool with QRunnable. We will use the QMetaObject.invokeMethod to update the values of the GUI since Qt prohibits the update of the GUI from another thread that is not from the main thread.
import sys, time
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from gtts import gTTS
class GTTSRunnable(QRunnable):
def __init__(self, data, progress):
QRunnable.__init__(self)
self.data = data
self.w = progress
def run(self):
for i, val in enumerate(self.data):
text, filename = val
tts = gTTS(text=text, lang='en')
tts.save(filename)
QMetaObject.invokeMethod(self.w, "setValue",
Qt.QueuedConnection, Q_ARG(int, i+1))
QThread.msleep(10)
class MainMenu(QMainWindow):
def __init__(self):
super().__init__()
self.genAudioButton = QPushButton('Generate', self)
self.genAudioButton.clicked.connect(self.generate)
self.setCentralWidget(self.genAudioButton)
self.show()
def generate(self):
try:
info = [("hello", "1.mp4"), ("how are you?", "2.mp4"), ("StackOverFlow", "3.mp4")]
self.progress = QProgressDialog('Work in progress', '', 0, len(info), self)
self.progress.setWindowTitle("Generating files...")
self.progress.setWindowModality(Qt.WindowModal)
self.progress.show()
self.progress.setValue(0)
self.doGenerate(info)
except Exception as e:
errBox = QMessageBox()
errBox.setWindowTitle('Error')
errBox.setText('Error: ' + str(e))
errBox.addButton(QMessageBox.Ok)
errBox.exec()
return
def doGenerate(self, data):
self.runnable = GTTSRunnable(data, self.progress)
QThreadPool.globalInstance().start(self.runnable)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainMenu()
ret = app.exec_()
sys.exit(ret)
This might be useful to anyone using Quamash/asyncio for async applications.
It takes #eyllanesc example and dispatches a CPU bound task in an executor and removes the dependency on Gtts.
Also for my purpose, I don't know how long the CPU bound will take, so I have set the min and max value of the progress dialog both to zero. This has the nice effect of just animating the progress bar until the task is completed. However, one has to manually call the cancel() method when doing this because the progress dialog cannot know when it is completed. This is done in a callback attached to the future.
def main():
import sys
import time
import quamash
import asyncio
import concurrent
import logging
import random
import PyQt5
# Integrate event loops
app = PyQt5.QtWidgets.QApplication(sys.argv)
loop = quamash.QEventLoop(app)
asyncio.set_event_loop(loop)
loop.set_debug(False) # optional
# Config logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger('quamash').setLevel(logging.ERROR)
# Print exception before crash!
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
sys.excepthook = except_hook
class MainWindow(PyQt5.QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.exitRequest = asyncio.Event()
self.genAudioButton = PyQt5.QtWidgets.QPushButton('Generate', self)
self.genAudioButton.clicked.connect(self.generate)
self.setCentralWidget(self.genAudioButton)
self.show()
def generate(self):
self.progress = PyQt5.QtWidgets.QProgressDialog('Work in progress...', None, 0, 0, self)
self.progress.setWindowTitle("Calculation")
self.progress.setWindowModality(PyQt5.QtCore.Qt.WindowModal)
self.progress.show()
self.progress.setValue(0)
# As the loop to run the coroutine
loop = asyncio.get_event_loop()
loop.create_task(self.doGenerate())
def closeEvent(self, event):
""" Called when the windows closes.
"""
self.exitRequest.set()
def cpuBound(self):
""" Just wait 2s or raise an exception 50% of the time to test error handling.
"""
# %50 change of raising an exception
time.sleep(1.0)
if random.random() < 0.5:
time.sleep(1.0)
else:
raise RuntimeError(
("If the CPU bound task fails you can raise "
"an exception that can be caught and displayed"
" like this!")
)
def onComplete(self, future):
""" Callback which contains the future that has completed.
"""
# Dismiss the progress popup widget before we (possibly)
# display a popup with an error message.
self.progress.cancel()
# Check if we got a result or an exception!
try:
result = future.result()
except Exception as e:
errBox = PyQt5.QtWidgets.QMessageBox()
errBox.setWindowTitle('Error')
errBox.setText('Error: ' + str(e))
errBox.addButton(PyQt5.QtWidgets.QMessageBox.Ok)
errBox.exec()
async def doGenerate(self):
""" The coroutine that is added to the event loop when the button is pressed.
"""
loop = asyncio.get_event_loop()
with concurrent.futures.ThreadPoolExecutor() as pool:
future = loop.run_in_executor(pool, self.cpuBound)
# This call back handles the result or possible exception
future.add_done_callback(self.onComplete)
# Block here until complete
result = await future
# Startup application
_window = MainWindow()
_window.show()
with loop:
loop.run_until_complete(_window.exitRequest.wait())
if __name__ == '__main__':
main()
I was able to solve the same problem by calling QtGui.QApplication.processEvents() after i set the value to refresh the QProgressDialog
progress.setValue(i)
QApplication.processEvents()
Almost exactly the problem that led me here. Blank, white dialog, then suddenly it displays correctly but as if 2 or 3 iterations have taken place.
The solution for me makes little sense...
progress = QProgressDialog('Work in progress', '', 0, SIZE, self)
progress.setWindowTitle("Generating files...")
progress.setWindowModality(Qt.WindowModal)
progress.setValue(0)
progress.setValue(1)
progress.setValue(0)
It is almost as if the first setValue gives the blank dialog, and the next two perform the first two iterations so the first real iteration has a correctly displaying dialog to update...
These are my first steps with python and desktop programming. I worked with web all my life and now I need to do threading.
I tried work with tkinter, pyqt and now gtk(father of tkinter) with glade xml.
I tried different modules because I was failing every time on the same problem. The interface has an infinite loop so I read a little bit about it on Google and its my first time working with python, desktop and threading
so here's my code could someone say what is wrong and why?
# -*- coding: utf-8 -*-
#Parallelism modulos
import threading
import Queue
#GUI modulos
import gtk, sys
#webservice modulos
import urllib2, json, urllib
#rfid modulos
import MFRC522
import signal
#GPIO modulos
import RPi.GPIO as GPIO
#sleep modulos
from time import sleep
#database modulos
import psycopg2
class Janela(threading.Thread):
def on_window_destroy(self, data=None):
gtk.main_quit()
def __init__(self, queue):
threading.Thread.__init__(self)
self._queue = queue
def run(self):
builder = gtk.Builder()
builder.add_from_file("interface.glade")
self.window = builder.get_object("window1")
'''aluno informação'''
self.alunoimage = builder.get_object("alunoimage")
self.label = builder.get_object("nomecrianca")
'''Responsavel informação'''
builder.connect_signals(self)
imagefile = "anderson_caricatura.png"
self.alunoimage.set_from_file(imagefile)
self.label.set_text("Anderson Santana")
self.window.show()
gtk.main()
class RfID():
def __init__(self):
a = "343511711127"
self.read()
def beep(self):
GPIO.setup(11, GPIO.OUT)
GPIO.output(11, False)
sleep(0.05)
GPIO.output(11, True)
sleep(0.05)
GPIO.output(11, False)
sleep(0.05)
GPIO.output(11, True)
def regulariza_cartao(self,cartao):
string = ''.join(str(e) for e in cartao);
return string
def consulta_web_service(self,cartao):
req = urllib2.Request("http://192.168.1.161/ieduca/TheClientRfid.php?cartao="+cartao)
response = urllib2.urlopen(req)
the_page = response.read()
return the_page
def end_read(signal, frame):
MIFAREReader = MFRC522.MFRC522()
global continue_reading
continue_reading = False
print "Ctrl+C captured, ending read."
MIFAREReader.GPIO_CLEEN()
def read(self):
continue_reading = True
MIFAREReader = MFRC522.MFRC522()
while continue_reading:
(status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
(status,backData) = MIFAREReader.MFRC522_Anticoll()
if status == MIFAREReader.MI_OK:
self.beep()
string = self.regulariza_cartao(backData)
consome = self.consulta_web_service(string)
jsonreaded = json.loads(consome)
print "Tem responsavel? ",jsonreaded['have_responsavel']
print "nome do estudante: ",jsonreaded['estudante_nome']
print "estudante imagem: ",jsonreaded['estudante_imagem']
print "imagem caminho: ",jsonreaded['estudante_caminho']
urllib.urlretrieve("http://192.168.1.161/ieduca/"+jsonreaded['estudante_caminho']+jsonreaded['estudante_imagem'], jsonreaded['estudante_imagem'])
print "baixou a imagem"
queue = Queue.Queue()
print "abriu comunicador"
worker = Janela(queue)
print "instanciou a janela"
worker.start()
print "iniciou a janela"
queue.put('quit')
print "pede pra sair 02"
print "rala!"
def run():
GUI = RfID()
run()
this code seems with what is happening to my code
import threading
from time import sleep
#lets pretend this class is the rfid class and will be the first to run
class RFid(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
i=0
d=True
while d:
print "oi"
i = i+1
print i
if i==10:
t2 = Gtk()#after some conditions in it while other class will run this class is the gtk (window, label, images)obs:no buttons
t2.start()
t2.join()
sleep(5)
del t2
#self.run() (or something or something meaning it)
#for example
##if ctrl+c:
##d = False
class Gtk(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self):
while True:#here on the gtk theres a infinity while never ends but it should close the window after some secs and than back to while of first class
print "ola"
#to resume the rfid will open a window with information of the owner of the rfidcard on scream
#but when the information are opened gtk enter on a infinity loop wainting for a event it will never happen
#when it should close after 5 secs and when other card's owner pass show other window and this forever...
def main():
t1 = RFid()
t1.start()
if __name__ == '__main__':
main()
i hope you understand now
Below there is some fully functioning code.
I am planning to execute this code through command line, however I would like it to end after 60 seconds.
Does anyone know the best way of going about this?
Thanks in advance.
import time
class listener(StreamListener):
def on_data(self, data):
try:
print data
saveFile = open('twitDB.csv','a')
saveFile.write(data)
saveFile.write('\n')
saveFile.close()
return True
except BaseException, e:
print 'failed ondata,' ,str(e)
time.sleep(5)
def on_error(self, status):
print status
Try this out:
import os
import time
from datetime import datetime
from threading import Timer
def exitfunc():
print "Exit Time", datetime.now()
os._exit(0)
Timer(5, exitfunc).start() # exit in 5 seconds
while True: # infinite loop, replace it with your code that you want to interrupt
print "Current Time", datetime.now()
time.sleep(1)
There are some more examples in this StackOverflow question: Executing periodic actions in Python
I think the use of os._exit(0) is discouraged, but I'm not sure. Something about this doesn't feel kosher. It works, though.
You could move your code into a daemon thread and exit the main thread after 60 seconds:
#!/usr/bin/env python
import time
import threading
def listen():
print("put your code here")
t = threading.Thread(target=listen)
t.daemon = True
t.start()
time.sleep(60)
# main thread exits here. Daemon threads do not survive.
Use signal.ALARM to get notified after a specified time.
import signal, os
def handler(signum, frame):
print '60 seconds passed, exiting'
cleanup_and_exit_your_code()
# Set the signal handler and a 60-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(60)
run_your_code()
From your example it is not obvious what the code will exactly do, how it will run and what kind of loop it will iterate. But you can easily implement the ALARM signal to get notified after the timeout has expired.
This is my favorite way of doing timeout.
def timeout(func, args=None, kwargs=None, TIMEOUT=10, default=None, err=.05):
if args is None:
args = []
elif hasattr(args, "__iter__") and not isinstance(args, basestring):
args = args
else:
args = [args]
kwargs = {} if kwargs is None else kwargs
import threading
class InterruptableThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.result = None
def run(self):
try:
self.result = func(*args, **kwargs)
except:
self.result = default
it = InterruptableThread()
it.start()
it.join(TIMEOUT* (1 + err))
if it.isAlive():
return default
else:
return it.result
I hope this is an easy way to execute a function periodically and end after 60 seconds:
import time
import os
i = 0
def executeSomething():
global i
print(i)
i += 1
time.sleep(1)
if i == 10:
print('End')
os._exit(0)
while True:
executeSomething()
I am trying to connect raw_input() to a Tkinter gui, by using Queus and Threading. Here is my code:
import Tkinter
import time
import threading
import Queue
class UI(Tkinter.Frame):
def __init__(self, master=None, queue=None):
self.queue = queue
Tkinter.Frame.__init__(self, master)
self.master.title("what does console say")
self.master.minsize(40, 30)
swd = self.master.winfo_screenwidth()
fwd = 320
fht = 240
self.master.geometry('{0:d}x{1:d}+{2:d}+{3:d}'.format(
fwd, fht, swd - fwd, 0))
self.message = Tkinter.StringVar()
mesgbox = Tkinter.Label(master, textvariable=self.message)
mesgbox.pack(fill=Tkinter.BOTH, expand=1)
self.pack()
self.processqueue()
def processqueue(self):
try:
message = '\n'.join([self.message.get(),
self.queue.get_nowait()])
except Queue.Empty:
pass
else:
self.message.set(message)
self.master.after(100, self.processqueue)
class ThreadedTask(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.daemon = 1
self.queue = queue
def run(self):
i = 0
while True:
i += 1
message = raw_input('put some words:')
self.queue.put("[Message] {0:s}".format(message))
queue = Queue.Queue()
gui = UI(queue=queue)
job = ThreadedTask(queue)
job.start()
gui.mainloop()
However, I get the following error:
error in background error handler:
out of stack space (infinite loop?)
while executing
"::tcl::Bgerror {out of stack space (infinite loop?)} {-code 1 -level 0 -errorcode NONE -errorinfo {out of stack space (infinite loop?)
("after" sc..."
Could anyone help me with it? Thank you in advance!
Another thing is that, this code works if I do not use the raw_input(), but some machine generated text, i.e.:
def run(self):
i = 0
while True:
i += 1
time.sleep(0.5)
self.queue.put("[Message] {0:d}".format(i))
Could anyone explain why?
After a deep searching, I found this post: here
I am not sure if this is exactly the problem, but it inspired me, in the way that I reformed my code, by putting the gui mainloop() in a second thread.
In this way, it works like a charm, although I still don't exactly why there is such behaviour.