I have a button that when it is clicked, it starts a GObject thread that calls a function every 2 seconds. I want to have another button that pauses or stops the calling of this function. Can anyone lead me in the right direction?
Using Python GTK 3
def on_refreshbutton_clicked(self,widget):
print("Data..")
self.th = GObject.timeout_add(2000,self.refresh_screen)
def on_pausebutton_clicked(self,widget):
print("Paused..")
Found the answer. Revome the source!
def on_refreshbutton_clicked(self,widget):
print("Data..")
self.th = GObject.timeout_add(2000,self.refresh_screen)
def on_pauseButton_clicked(self, widget):
print("Paused main screen.")
GObject.source_remove(self.th)
Related
I have a script that uses user input and based on that it generates some data (code below). The script uses threads so the GUI does not freeze while the background tasks are running.
My question is if it is possible to pause the worker thread, ask the user for some input, send it to the worker thread and then to continue the operation?
from tkinter.constants import LEFT, RIGHT, S
import tkinter.messagebox
import tkinter as tk, time, threading, random, queue
from tkinter import NORMAL, DISABLED, filedialog
class GuiPart(object):
def __init__(self, master, client_instance):
canvas = tk.Canvas(master, height=600, width=1020, bg="#263D42")
canvas.pack()
self.button1 = tk.Button(master, text="Mueller Matrix - MMP mode", padx=10,
pady=5, bd = 10, font=12, command=client_instance.start_thread1)
self.button1.pack(side = LEFT)
def files(self):
self.filena = filedialog.askopenfilenames(initialdir="/", title="Select File",
filetypes = (("comma separated file","*.csv"), ("all files", "*.*")))
return self.filena
def processing_done(self):
tkinter.messagebox.showinfo('Processing Done', 'Process Successfully Completed!')
return
class ThreadedClient(object):
def __init__(self, master):
self.running=True
self.gui = GuiPart(master, self)
def start_thread1(self):
thread1=threading.Thread(target=self.worker_thread1)
thread1.start()
def worker_thread1(self):
if self.running:
self.gui.button1.config(state=DISABLED) # can I disable the button from the main thread?
user_input = self.gui.files() # can I pause the Worker thread here so ask the user_input from main loop?
time.sleep(5) # to simulate a long-running process
self.gui.processing_done()
self.gui.button1.config(state=NORMAL)
root = tk.Tk()
root.title('test_GUI')
client = ThreadedClient(root)
root.mainloop()
I am asking this as from time to time my script ends abruptly saying that the GUI is not part of the main loop. Thank you for your help!
In general, you should run the GUI from the main thread.
There is some confusion whether tkinter is thread-safe. That is, if one can call tkinter functions and methods from any but the main thread. The documentation for Python 3 says “yes”. Comments in the C source code for tkinter say “its complicated” depending on how tcl is built.
You should be able to call tkinter functions and methods from additional threads in Python 3 if your tkinter was built with thread support. AFAIK, the tkinter that comes with the downloads from python.org have threading enabled.
If you want to be able to pause additional threads, you should create a global variable, e.g. pause = False.
The extra threads should regularly read the value of this and pause their calculations when set to true, e.g.:
def thread():
while run:
while pause:
time.sleep(0.2)
# do stuff
Meanwhile the GUI running in the main thread should be the only one to write the value of pause, e.g. in a widget callback.
Some sample callbacks:
def start_cb():
pause = False # So it doesn't pause straight away.
run = True
def stop_cb():
pause = False
run = False
def pause_cb():
pause = True
def continue_cb():
pause = False
If you have more complicated global data structures that can be modified be several threads, you should definitely guard access to those with a Lock.
Edit1
Threaded tkinter programs without classes:
Simple threaded tkinter example program
Real-world example for unlocking ms-excel files
Edit2:
From the python source code for _tkinter:
If Tcl is threaded, this approach won't work anymore. The Tcl
interpreter is only valid in the thread that created it, and all Tk
activity must happen in this thread, also. That means that the
mainloop must be invoked in the thread that created the
interpreter. Invoking commands from other threads is possible;
_tkinter will queue an event for the interpreter thread, which will
then execute the command and pass back the result. If the main
thread is not in the mainloop, and invoking commands causes an
exception; if the main loop is running but not processing events,
the command invocation will block.
You can read the whole comment here.
so im making a gui miner for a coin called duinocoin, very new to tkinter.
So i integrated mining into a function, so when you press a button it mines. The problem is when I click mine I cant click anyother buttons because its in that mining loop. How do I fix this?
-Eth guy
Using threading, to create a separate thread from the UI thread:
from tkinter import *
from threading import Thread
from time import sleep
root = Tk()
def run(): #the loop function
b1.config(command=Thread(target=run).start) #or b1.config(state=DISABLED)
while True:
print('Hey')
sleep(2) #pause for 2 seconds.
def step(): #the in between function
print('This is being printed in between the other loop')
b1 = Button(root,text='Loop',command=Thread(target=run).start)
b1.pack()
b2 = Button(root,text='Separate function',command=step)
b2.pack()
root.mainloop()
Take a look at this example, first run the loop button, then press the next button and you will notice, it gets executed in between the while loop. But sleep() might still freeze your GUI out.
Explanation:
Threading is nothing much, just like a normal thread, imagine cars going through a thread and they got into accident, so the thread might break, just like that, your tkinter runs on one thread and your while loop runs with it causing the thread to freeze, but with threading you make a new thread for that function with while and hence the thread with tkinter is going smooth, while the thread with while loop is frozen, and it doesnt matter for the other thread.
Alternatively you could also use after() for this purpose, like:
from tkinter import *
from threading import Thread
from time import sleep
root = Tk()
def run():
global rep
print('Hey')
rep = root.after(2000,run) #run the same function every 2 seconds
def stop():
root.after_cancel(rep)
def step():
print('This is being printed in between the other loop')
b1 = Button(root,text='Loop',command=run)
b1.pack()
b2 = Button(root,text='Seperate function',command=step)
b2.pack()
b3 = Button(root,text='Stop loop',command=stop)
b3.pack()
root.mainloop()
Here when you press the loop button, it will start to loop, when you press the separate button, it prints a function in between the pseudo loop and when you press stop, it stops the loop.
after() method takes two arguments mainly:
ms - time to be run the function
func - the function to run after the given ms is finished.
after_cancel() takes the variable name of after() only.
Hopefully, you understood better, do let me know if you have any doubts or errors.
Cheers
I have a GUI in PyQt with a function addImage(image_path). Easy to imagine, it is called when a new image should be added into a QListWidget. For the detection of new images in a folder, I use a threading.Thread with watchdog to detect file changes in the folder, and this thread then calls addImage directly.
This yields the warning that QPixmap shouldn't be called outside the gui thread, for reasons of thread safety.
What is the best and most simple way to make this threadsafe? QThread? Signal / Slot? QMetaObject.invokeMethod? I only need to pass a string from the thread to addImage.
You should use the built in QThread provided by Qt. You can place your file monitoring code inside a worker class that inherits from QObject so that it can use the Qt Signal/Slot system to pass messages between threads.
class FileMonitor(QObject):
image_signal = QtCore.pyqtSignal(str)
#QtCore.pyqtSlot()
def monitor_images(self):
# I'm guessing this is an infinite while loop that monitors files
while True:
if file_has_changed:
self.image_signal.emit('/path/to/image/file.jpg')
class MyWidget(QtGui.QWidget):
def __init__(self, ...)
...
self.file_monitor = FileMonitor()
self.thread = QtCore.QThread(self)
self.file_monitor.image_signal.connect(self.image_callback)
self.file_monitor.moveToThread(self.thread)
self.thread.started.connect(self.file_monitor.monitor_images)
self.thread.start()
#QtCore.pyqtSlot(str)
def image_callback(self, filepath):
pixmap = QtGui.QPixmap(filepath)
...
I believe the best approach is using the signal/slot mechanism. Here is an example. (Note: see the EDIT below that points out a possible weakness in my approach).
from PyQt4 import QtGui
from PyQt4 import QtCore
# Create the class 'Communicate'. The instance
# from this class shall be used later on for the
# signal/slot mechanism.
class Communicate(QtCore.QObject):
myGUI_signal = QtCore.pyqtSignal(str)
''' End class '''
# Define the function 'myThread'. This function is the so-called
# 'target function' when you create and start your new Thread.
# In other words, this is the function that will run in your new thread.
# 'myThread' expects one argument: the callback function name. That should
# be a function inside your GUI.
def myThread(callbackFunc):
# Setup the signal-slot mechanism.
mySrc = Communicate()
mySrc.myGUI_signal.connect(callbackFunc)
# Endless loop. You typically want the thread
# to run forever.
while(True):
# Do something useful here.
msgForGui = 'This is a message to send to the GUI'
mySrc.myGUI_signal.emit(msgForGui)
# So now the 'callbackFunc' is called, and is fed with 'msgForGui'
# as parameter. That is what you want. You just sent a message to
# your GUI application! - Note: I suppose here that 'callbackFunc'
# is one of the functions in your GUI.
# This procedure is thread safe.
''' End while '''
''' End myThread '''
In your GUI application code, you should create the new Thread, give it the right callback function, and make it run.
from PyQt4 import QtGui
from PyQt4 import QtCore
import sys
import os
# This is the main window from my GUI
class CustomMainWindow(QtGui.QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
self.setGeometry(300, 300, 2500, 1500)
self.setWindowTitle("my first window")
# ...
self.startTheThread()
''''''
def theCallbackFunc(self, msg):
print('the thread has sent this message to the GUI:')
print(msg)
print('---------')
''''''
def startTheThread(self):
# Create the new thread. The target function is 'myThread'. The
# function we created in the beginning.
t = threading.Thread(name = 'myThread', target = myThread, args = (self.theCallbackFunc))
t.start()
''''''
''' End CustomMainWindow '''
# This is the startup code.
if __name__== '__main__':
app = QtGui.QApplication(sys.argv)
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('Plastique'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())
''' End Main '''
EDIT
Mr. three_pineapples and Mr. Brendan Abel pointed out a weakness in my approach. Indeed, the approach works fine for this particular case, because you generate / emit the signal directly. When you deal with built-in Qt signals on buttons and widgets, you should take another approach (as specified in the answer of Mr. Brendan Abel).
Mr. three_pineapples adviced me to start a new topic in StackOverflow to make a comparison between the several approaches of thread-safe communication with a GUI. I will dig into the matter, and do that tomorrow :-)
I have a question on multiprocessing and tkinter. I am having some problems getting my process to function parallel with the tkinter GUI. I have created a simple example to practice and have been reading up to understand the basics of multiprocessing. However when applying them to tkinter, only one process runs at the time. (Using Multiprocessing module for updating Tkinter GUI) Additionally, when I added the queue to communicate between processes, (How to use multiprocessing queue in Python?), the process won't even start.
Goal:
I would like to have one process that counts down and puts the values in the queue and one to update tkinter after 1 second and show me the values.
All advice is kindly appreciated
Kind regards,
S
EDIT: I want the data to be available when the after method is being called. So the problem is not with the after function, but with the method being called by the after function. It will take 0.5 second to complete the calculation each time. Consequently the GUI is unresponsive for half a second, each second.
EDIT2: Corrections were made to the code based on the feedback but this code is not running yet.
class Countdown():
"""Countdown prior to changing the settings of the flows"""
def __init__(self,q):
self.master = Tk()
self.label = Label(self.master, text="", width=10)
self.label.pack()
self.counting(q)
# Countdown()
def counting(self, q):
try:
self.i = q.get()
except:
self.label.after(1000, self.counting, q)
if int(self.i) <= 0:
print("Go")
self.master.destroy()
else:
self.label.configure(text="%d" % self.i)
print(i)
self.label.after(1000, self.counting, q)
def printX(q):
for i in range(10):
print("test")
q.put(9-i)
time.sleep(1)
return
if __name__ == '__main__':
q = multiprocessing.Queue()
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Multiprocessing does not function inside of the interactive Ipython notebook.
Multiprocessing working in Python but not in iPython As an alternative you can use spyder.
No code will run after you call mainloop until the window has been destroyed. You need to start your other process before you call mainloop.
You are calling wrong the after function. The second argument must be the name of the function to call, not a call to the function.
If you call it like
self.label.after(1000, self.counting(q))
It will call counting(q) and wait for a return value to assign as a function to call.
To assign a function with arguments the syntax is
self.label.after(1000, self.counting, q)
Also, start your second process before you create the window and call counting.
n = multiprocessing.Process(name='Process2', target=printX, args = (q,))
n.start()
GUI = Countdown(q)
GUI.master.mainloop()
Also you only need to call mainloop once. Either position you have works, but you just need one
Edit: Also you need to put (9-i) in the queue to make it count down.
q.put(9-i)
Inside the printX function
I wrote a PyQt5 GUI (Python 3.5 on Win7). While it is running, its GUI is not responsive. To still be able to use the GUI, I tried to use QThread from this answer: https://stackoverflow.com/a/6789205/5877928
The module is now designed like this:
class MeasureThread(QThread):
def __init(self):
super().__init__()
def get_data(self):
data = auto.data
def run(self):
time.sleep(600)
# measure some things
with open(outputfile) as f:
write(things)
class Automation(QMainWindow):
[...]
def measure(self):
thread = MeasureThread()
thread.finished.connect(app.exit)
for line in open(inputfile):
thread.get_data()
thread.start()
measure() gets called once per measurement but starts the thread once per line in inputfile. The module now exits almost immediately after starting it (I guess it runs all thread at once and does not sleep) but I only want it to do all the measurements in another single thread so the GUI can still be accessed.
I also tried to apply this to my module, but could not connect the methods used there to my methods: http://www.xyzlang.com/python/PyQT5/pyqt_multithreading.html
The way I used to use the module:
class Automation(QMainWindow):
[...]
def measure(self):
param1, param2 = (1,2)
for line in open(inputfile):
self.measureValues(param1, param2)
def measureValues(self, param1, param2):
time.sleep(600)
# measure some things
with open(outputfile) as f:
write(things)
But that obviously used only one thread. Can you help me to find the right method to use here( QThread, QRunnable) or to map the example of the second link to my module?
Thanks!