I am trying to learn pygtk and I am developing a GUI for axel.
My problem is I need to show progress of download in my main window.
I used two threads one for downloading and second for progress calculation. Download is done by axel
but my second thread is not running until first thread stops. and second thread is not updating the gui
I used gobject.idel_add it stucks the main window upon download start
tried to use Gtk.gdk.thread_init()/thread_enter()/thread_leave() it says no module gdk in Gtk. On top of the page Gtk is imported from gi.repository
By the way I am using quickly to develop the app
Is there any way to solve the problem. or any similiar examples.
Check this thread separate threads in pygtk application may be usefully for you.
Here left a pygtk manage multithreading example to you.
import threading
import random, time
import gtk
#Initializing the gtk's thread engine
gtk.threads_init()
class FractionSetter(threading.Thread):
"""This class sets the fraction of the progressbar"""
#Thread event, stops the thread if it is set.
stopthread = threading.Event()
def run(self):
"""Run method, this is the code that runs while thread is alive."""
#Importing the progressbar widget from the global scope
global progressbar
#While the stopthread event isn't setted, the thread keeps going on
while not self.stopthread.isSet() :
# Acquiring the gtk global mutex
gtk.threads_enter()
#Setting a random value for the fraction
progressbar.set_fraction(random.random())
# Releasing the gtk global mutex
gtk.threads_leave()
#Delaying 100ms until the next iteration
time.sleep(0.1)
def stop(self):
"""Stop method, sets the event to terminate the thread's main loop"""
self.stopthread.set()
def main_quit(obj):
"""main_quit function, it stops the thread and the gtk's main loop"""
#Importing the fs object from the global scope
global fs
#Stopping the thread and the gtk's main loop
fs.stop()
gtk.main_quit()
#Gui bootstrap: window and progressbar
window = gtk.Window()
progressbar = gtk.ProgressBar()
window.add(progressbar)
window.show_all()
#Connecting the 'destroy' event to the main_quit function
window.connect('destroy', main_quit)
#Creating and starting the thread
fs = FractionSetter()
fs.start()
gtk.main()
Related
I am building a Tkinter GUI and I have a process that takes a while to complete, so I threaded it to prevent the GUI from hanging. Lets call the threaded function foo. Once foo is completed, I need to call another function, bar. bar needs to be called from the main thread (it uses a matplotlib method that does not work inside of a thread).
I can't seem to wrap my head around how I might do this. I thought about joining the thread, but that just causes the GUI to hang. I also thought about using a signal variable that I would change in the last line of foo to tell the rest of my program that it is done and its time to execute bar, but then I couldn't figure out how I could continuously check that variable in the main thread without hanging the GUI. Any ideas?
Using Python 3.7
You can use threading.Event() object to notify the main thread and use after() to call a function periodically to check the Event() object to determine when to call bar().
Below is a simple example:
import tkinter as tk
import threading
import time
def foo(event):
print('foo started')
time.sleep(5)
print('foo done')
# notify main thread
event.set()
def bar():
print('hello')
def check_event(event, callback):
print('.', end='')
if event.is_set():
# thread task is completed
callback()
else:
# check again 100 ms (adjust this to suit your case) later
root.after(100, check_event, event, callback)
root = tk.Tk()
# create the `Event()` object
event = threading.Event()
# start the checking
check_event(event, bar)
# start the thread task
threading.Thread(target=foo, args=(event,)).start()
root.mainloop()
How can I architect code to run a pyqt GUI multiple times consecutively in a process?
(pyqtgraph specifically, if that is relevant)
The context
A python script that performs long running data capture on measurement equipment (a big for loop). During each capture iteration a new GUI appear and displays live data from the measurement equipment to the user, while the main capture code is running.
I'd like to do something like this:
for setting in settings:
measurement_equipment.start(setting)
gui = LiveDataStreamGUI(measurement_equipment)
gui.display()
measurement_equipment.capture_data(300) #may take hours
gui.close()
The main issue
I'd like the data capture code to be the main thread. However pyqt doesn't seems to allow this architecture, as its app.exec_() is a blocking call, allowing a GUI to be created only once per process (e.g., in gui.display() above).
An application is an executable process that runs on one or more foreground threads each of which can also start background threads to perform parallel operations or operations without blocking the calling thread. An application will terminate after all foreground threads have ended, therefore, you need at least one foreground thread which in your case is created when you call the app.exec_() statement. In a GUI application, this is the UI thread where you should create and display the main window and any other UI widget. Qt will automatically terminate your application process when all widgets are closed.
IMHO, you should try to follow the normal flow described above as much as possible, the workflow could be as follows:
Start Application > Create main window > Start a background thread for each calculation > Send progress to UI thread > Show results in a window after each calculation is finished > Close all windows > End application
Also, you should use ThreadPool to make sure you don't run out of resources.
Here is a complete example:
import sys
import time
import PyQt5
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QRunnable, pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QDialog
class CaptureDataTaskStatus(QObject):
progress = pyqtSignal(int, int) # This signal is used to report progress to the UI thread.
captureDataFinished = pyqtSignal(dict) # Assuming your result is a dict, this can be a class, a number, etc..
class CaptureDataTask(QRunnable):
def __init__(self, num_measurements):
super().__init__()
self.num_measurements = num_measurements
self.status = CaptureDataTaskStatus()
def run(self):
for i in range(0, self.num_measurements):
# Report progress
self.status.progress.emit(i + 1, self.num_measurements)
# Make your equipment measurement here
time.sleep(0.1) # Wait for some time to mimic a long action
# At the end you will have a result, for example
result = {'a': 1, 'b': 2, 'c': 3}
# Send it to the UI thread
self.status.captureDataFinished.emit(result)
class ResultWindow(QWidget):
def __init__(self, result):
super().__init__()
# Display your result using widgets...
self.result = result
# For this example I will just print the dict values to the console
print('a: {}'.format(result['a']))
print('b: {}'.format(result['b']))
print('c: {}'.format(result['c']))
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.result_windows = []
self.thread_pool = QtCore.QThreadPool().globalInstance()
# Change the following to suit your needs (I just put 1 here so you can see each task opening a window while the others are still running)
self.thread_pool.setMaxThreadCount(1)
# You could also start by clicking a button, menu, etc..
self.start_capturing_data()
def start_capturing_data(self):
# Here you start data capture tasks as needed (I just start 3 as an example)
for setting in range(0, 3):
capture_data_task = CaptureDataTask(300)
capture_data_task.status.progress.connect(self.capture_data_progress)
capture_data_task.status.captureDataFinished.connect(self.capture_data_finished)
self.thread_pool.globalInstance().start(capture_data_task)
def capture_data_progress(self, current, total):
# Update progress bar, label etc... for this example I will just print them to the console
print('Current: {}'.format(current))
print('Total: {}'.format(total))
def capture_data_finished(self, result):
result_window = ResultWindow(result)
self.result_windows.append(result_window)
result_window.show()
class App(QApplication):
"""Main application wrapper, loads and shows the main window"""
def __init__(self, sys_argv):
super().__init__(sys_argv)
self.main_window = MainWindow()
self.main_window.show()
if __name__ == '__main__':
app = App(sys.argv)
sys.exit(app.exec_())
If you want your GUI to keep updating in realtime and to not be freezed, you have two main ways to do it:
Refresh the GUI from time to time calling QApplication.processEvents() inside your time consuming function.
Create a separate thread (I mean, QThread) where you run your time consuming function
My personal preference is to go for the latter way. Here is a good tutorial for getting started on how to do multi-threading in Qt.
Having a look at your code:
...
gui.display()
measurement_equipment.capture_data(300) #may take hours
gui.close()
...
it seems you are calling app.exec_ inside gui.display. Its very likely you will have to decouple both functions and call app.exec_ outside of gui.display and after calling capture_data. You will also have to connect the finished signal of the new thread to gui.close. It will be something like this:
...
gui.display() # dont call app.exec_ here
thread = QThread.create(measurement_equipment.capture_data, 300)
thread.finished.connect(gui.close)
app.exec_()
...
I hope this can help you and to not be late!!
You can have only One graphic GUI thread. This would imply to have some Threads to capture data and sync data with the graphic application when needed.
We need to know if the GUI data display is displaying realtime data or only oneshot.
I'm using PyQt for Python, and am building a gui. I had a problem a few weeks ago where I had a function outside of the gui module modifying widgets within the gui (advanced progress bar, updating strings, etc.), and those modifications were not reflected in the gui until the function that had made the changes finished running.
The solution to this was to simply call app.processEvents() after doing whatever modifications I wanted, which would immediately update the graphics of the window.
But now I am wondering, is there a way to do this everytime the window is brought forward?
Let's say I have called a function that will be modifying the progress bar, but this function takes quite a while to run. Inbetween calling the function, and the progress bar modification, the app processes no events. So, it during this time, I pull up a Chrome window (or anything else), and then close it, my gui window is blank, just gray, until app.processEvents() is called again.
Is ther functionality in PyQt that allows me to detect whenever the window is brought to the front of all current windows?
You should look into QThread.
Threads allow you to run long, complicated tasks in a worker thread while a background thread keeps the GUI responsive, such as updating a QProgressBar, ensuring it responds to motion events.
The basic idea is this:
# load modules
import time
from PySide import QtCore, QtGui
# APPLICATION STUFF
# -----------------
APP = QtGui.QApplication([])
# THREADS
# -------
class WorkerThread(QtCore.QThread):
'''Does the work'''
def __init__(self):
super(WorkerThread, self).__init__()
self.running = True
def run(self):
'''This starts the thread on the start() call'''
# this goes over 1000 numbers, at 10 a second, will take
# 100 seconds to complete, over a minute
for i in range(1000):
print(i)
time.sleep(0.1)
self.running = False
class BackgroundThread(QtCore.QThread):
'''Keeps the main loop responsive'''
def __init__(self, worker):
super(BackgroundThread, self).__init__()
self.worker = worker
def run(self):
'''This starts the thread on the start() call'''
while self.worker.running:
APP.processEvents()
print("Updating the main loop")
time.sleep(0.1)
# MAIN
# ----
def main():
# make threads
worker = WorkerThread()
background = BackgroundThread(worker)
# start the threads
worker.start()
background.start()
# wait until done
worker.wait()
if __name__ == '__main__':
main()
The output you get is something like this, showing how it takes turns at doing the long calculation and updating the main loop:
0
Updating the main loop
1
Updating the main loop
2
Updating the main loop
3
Updating the main loop
4
Updating the main loop
5
Updating the main loop
6
Updating the main loop
Updating the main loop7
8
Updating the main loop
9
This along with a QFocusEvent override should allow you to do whatever you wish. But it's better to separate updating the GUI and running your desired long thread.
As for overriding the QFocusEvent you can do something as follows:
def focusInEvent(self, event):
event.accept()
# insert your code here
And if you choose to implement threads to avoid GUI blocking, you should read about the basics of threading (as threads have a lot of nuances unless you know about their potential pitfalls).
i'm creating a application in Python, that uses GTK to build the UI, and i'm little confuse about handle GTK Objects on Threads, for example, the GtkProgressBar object.
Here is the context:
I try to do a download on a Main Thread, and i add a GObject.timeout_add to pulse the bar until the Download ends. But after the first pulse the UI froze.
Until there OK, the Thread froze until the download is complete, so any component will be updated. Solution: Create a new Thread.
I've created this new thread to make the Download, and other things. On this thread, i receive the progress bar to do the updates. But while the download is running and i add the GObject.timeout_add to pulse the bar, the UI froze again. New Solution: Create a third thread.
So my threads is looking lke this:
Main-Thread
'---- Thread 1
'------Thread 2
So, on Thread 1, i make another things and update the UI, while in Thread 2 i make the download and add the GObject.timeout_add and in there i can update the progress bar. And in Thread 1 i join the Thread 2
I handle the Gtk Objects using GObject.idle_add function.
But i'm very confuse about why the download and the update of the progress bar works well on the Thread 2 and not on Thread 1
Someone can explain to me why that's happen, or if i miss a something about handle GTK objects.
Thank you
If you block the Gtk main loop, the window and the widgets become unresponsive. That's why you cannot make the download in the main thread. Updating the Gtk.ProgressBar from the download thread could work depending on how you implemented it.
This is one way to download something and have a working ProgressBar:
from gi.repository import Gtk, GLib
import threading
import urllib.request
class Window(Gtk.Window):
def __init__(self):
super().__init__()
self.connect('delete-event', Gtk.main_quit)
self.progress = Gtk.ProgressBar()
self.add(self.progress)
self.show_all()
# Start download
self.thread = threading.Thread(target=self.download)
self.thread.start()
GLib.timeout_add_seconds(1, self.download_pulse)
def download(self):
urllib.request.urlretrieve('<link>')
def download_pulse(self):
if self.thread.is_alive():
self.progress.pulse()
return True
return False
win = Window()
Gtk.main()
okay so now i am almost finished with my little project with some bits left, that's running my background task and then showing my GUI.
class myGUIApp:
def __init()__:
....
def createwidgets():
....
if __name__ == "__main__":
import myBackgroundTasks
x = myBackgroundTasks()
x.startbackground1() <----- this is background task that doesn't need user interaction
x.startbackground2() <----- this is background task that doesn't need user interaction
MainWindow = myGUIApp()
MainWindow.show() <---- this is Pyside GUI
The issue is this, the GUI doesn't "show" until my 2 background tasks are finished, which can take quite some time as they are doing I/O jobs and grabber files from the internet. How should i go about this? Using python's multithread (inside the background task, i am also using multithreading)? Qthread? or multiprocessing module? or others? thanks for answering.
You could put it on a thread. Since the Qt gui runs in its own thread this is efficient use. Use a queue to pass back the results of x. The only trick is where and when do you need x? If you need it inside your gui then the best thing to do is use the gui's after method, if it has one, or whatever it's equivalent is. The point is you don't hog up all the resources continuously checking the queue for the output. If you put a while loop inside your gui, it will probably cause the gui to freeze.
from threading import Thread
from Queue import Queue
class myGUIApp:
def __init()__:
....
def createwidgets():
....
if __name__ == "__main__":
import myBackgroundTasks
QUEUE = Queue()
def queue_fun(q):
x = myBackgroundTasks()
x.startbackground1() <----- this is background task that doesn't need user interaction
x.startbackground2() <----- this is background task that doesn't need user interaction
q.put(x)
THREAD = Thread(target=queue_fun, args=QUEUE)
THREAD.start()
MainWindow = myGUIApp()
MainWindow.show() <---- this is Pyside GUI
# if you can wait until after mainloop terminates to get x, put this here
while THREAD.is_alive()
try:
x = QUEUE.get_nowait()
except Queue.Empty:
continue
# if you need this inside Pyside, then you should put this inside Pyside,
# but don't use while loop, use Qt after function and call self.wait
def wait(self):
try:
x = QUEUE.get_nowait()
except Queue.Empty:
self.after(5, self.wait)