Pyqt5 qprogressbar different behavior with QSplashScreen and QWidget [duplicate] - python

I can't use time.sleep in my pyqt application because that freezes the GUI thread, so the GUI will be completely frozen during this time.I have been looking for a way to handle this.
I tried to use QTimer, but it seemed like they need to be linked to another function? Like wait ten seconds then run some function. Is there a way to just have it wait then continue with the current function?
def num(self):
for i in range(1,999):
print i
#Add some sleep here
def testSleep(self):
QtCore.QTimer.singleShot(2000, self.num)

Actually i was looking for time.sleep alternative to use in pyqt without using any thread concepts.
And the solution i figured out is:
from PyQt4 import QtTest
QtTest.QTest.qWait(msecs)
This works similar to time.sleep making GUI responsive.
Thankyou for your answers.

Maybe it could be done better but you can always use singleShot to run function with delay, and lambda to run function with argument.
import sys
from PyQt4 import QtGui, QtCore
#def num(self, i):
def num(i):
print i
i += 1
if i < 999:
# run again after 2000ms with argument
QtCore.QTimer.singleShot(2000, lambda:num(i))
#QtCore.QTimer.singleShot(2000, lambda:self.num(i))
app = QtGui.QApplication(sys.argv)
# run first time with start argument
num(1)
#QtCore.QTimer.singleShot(2000, lambda:num(1))
sys.exit(app.exec_())

Another option would be to process Qt events while waiting:
def num():
for i in range(1, 999):
print(i)
# Sleep five seconds in total
for _ in range(5 * 10):
# Process events between short sleep periods
QtWidgets.QApplication.processEvents()
time.sleep(0.1)

You cannot use time.sleep in the pyqt main event loop as it would stop the GUI event loop from responding.
A solution in pyqt could look like this, using QTimer
import sys
from PyQt4 import QtGui, QtCore
application = QtGui.QApplication(sys.argv)
i=0
timer = QtCore.QTimer()
def num():
global i, timer
if i <999:
print ( i )
i += 1
else:
timer.stop()
timer.timeout.connect(num)
timer.start(2000)
sys.exit(application.exec_())

I believe you are asking how to keep the GUI responsive if num() takes several seconds to run? You have two options:
if num() consists of many small chunks of "work", you can call application.processEvents() between the chunks, this will allow the GUI to respond to events. An easy situation to deal with is when most of the num() time is spent in a loop, then at the start or end of each iteration, call application.processEvents(). In your real application, if you don't have access to application, import qApp from PyQt4.
the better approach is to execute num() in a separate thread. There are many examples of this on SO (like this one). One way of doing that is
instantiate a QThread,
define a class (say NumberCruncher) that derives from QObject and defines num(self) and defines a signal 'done' emitted by num() before returning
call numberCruncher.moveToThread(thread)
connect the thread started signal to num
start the thread

Related

How to show timer in a Quiz Application using pyqt5 python [duplicate]

I can't use time.sleep in my pyqt application because that freezes the GUI thread, so the GUI will be completely frozen during this time.I have been looking for a way to handle this.
I tried to use QTimer, but it seemed like they need to be linked to another function? Like wait ten seconds then run some function. Is there a way to just have it wait then continue with the current function?
def num(self):
for i in range(1,999):
print i
#Add some sleep here
def testSleep(self):
QtCore.QTimer.singleShot(2000, self.num)
Actually i was looking for time.sleep alternative to use in pyqt without using any thread concepts.
And the solution i figured out is:
from PyQt4 import QtTest
QtTest.QTest.qWait(msecs)
This works similar to time.sleep making GUI responsive.
Thankyou for your answers.
Maybe it could be done better but you can always use singleShot to run function with delay, and lambda to run function with argument.
import sys
from PyQt4 import QtGui, QtCore
#def num(self, i):
def num(i):
print i
i += 1
if i < 999:
# run again after 2000ms with argument
QtCore.QTimer.singleShot(2000, lambda:num(i))
#QtCore.QTimer.singleShot(2000, lambda:self.num(i))
app = QtGui.QApplication(sys.argv)
# run first time with start argument
num(1)
#QtCore.QTimer.singleShot(2000, lambda:num(1))
sys.exit(app.exec_())
Another option would be to process Qt events while waiting:
def num():
for i in range(1, 999):
print(i)
# Sleep five seconds in total
for _ in range(5 * 10):
# Process events between short sleep periods
QtWidgets.QApplication.processEvents()
time.sleep(0.1)
You cannot use time.sleep in the pyqt main event loop as it would stop the GUI event loop from responding.
A solution in pyqt could look like this, using QTimer
import sys
from PyQt4 import QtGui, QtCore
application = QtGui.QApplication(sys.argv)
i=0
timer = QtCore.QTimer()
def num():
global i, timer
if i <999:
print ( i )
i += 1
else:
timer.stop()
timer.timeout.connect(num)
timer.start(2000)
sys.exit(application.exec_())
I believe you are asking how to keep the GUI responsive if num() takes several seconds to run? You have two options:
if num() consists of many small chunks of "work", you can call application.processEvents() between the chunks, this will allow the GUI to respond to events. An easy situation to deal with is when most of the num() time is spent in a loop, then at the start or end of each iteration, call application.processEvents(). In your real application, if you don't have access to application, import qApp from PyQt4.
the better approach is to execute num() in a separate thread. There are many examples of this on SO (like this one). One way of doing that is
instantiate a QThread,
define a class (say NumberCruncher) that derives from QObject and defines num(self) and defines a signal 'done' emitted by num() before returning
call numberCruncher.moveToThread(thread)
connect the thread started signal to num
start the thread

How do I run multiple PyQt QTimers in a true concurrency manner?

I want to have two PyQt QTimers in my application where one performs the regular tasks that do not require much computation but, the other one performs some heavy workload. The two timers should run independently without anyone causing a delay in other's execution.
Currently, I have initialized two periodic timers such that, each times out after 10 milliseconds and calls its respective slot function. The code is shown below:
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QCoreApplication
import sys
import time
def first_timer_function():
# Some heavy task to be performed in this function
time.sleep(10)
print "First Timer Fired"
def second_timer_function():
# Regular tasks to be performed in this function
print "Second Timer Fired"
if __name__ == "__main__":
app = QCoreApplication.instance()
if app is None:
app = QApplication(sys.argv)
first_timer = QTimer()
first_timer.timeout.connect(first_timer_function)
first_timer.start(10)
second_timer = QTimer()
second_timer.timeout.connect(second_timer_function)
second_timer.start(10)
sys.exit(app.exec_())
The "first_timer" Timer has to perform the heavy computation simulated by the 10-second sleep. When I run this code, the "second_timer" suffers from a delay equal to the time taken by the "first_timer" slot function to perform its task. I read the documentation of QTimers and it turns out that all the QTimers run in one thread, therefore, the execution time of "first_timer" delays the "second_timer".
Should I just create two separate threads each running one of these Timers. What is ideally the best practice for achieving the true parallelism of QTimers?
First you need to convert your QThread into a python thread.
import threading
from threading import Timer, Thread, Event
class VideoCam(QThread):
def __init__(self):
super(VideoCam, self).__init__()
def run(self):
# do something
# add below
def run(self):
#"Launches the audio recording function using a thread"
self.thread = threading.Thread(target=self.run)
self.thread.start()
self.thread.join(1)
And there is a similar question about multiple timed event and there is also a good answer for it.
Can I run multiple Timers in Python simultaneously?
If I use this idea into my PyQt app, it will replace PyQt timer.
def start_painting(self):
#self.app_timer = QtCore.QTimer()
#self.app_timer.setInterval(control_values.timer_value)
#self.app_timer.timeout.connect(self.draw_2D_plot)
#self.app_timer.start()
timer_base = TimedEvent(1, self.draw_2D_plot)
timer_base.start()
timer_3d_painter = TimedEvent(0.3, self.draw_3D_plot)
timer_3d_painter.start()
Then adjust those (1 or 0.3) according to your app.
Hope this help.

using 'stop button' to quit function

So I'm trying to create a timer or sorts, the start button starts the timer, i need to stop the timer with the stop button and then record the time... I can't seem to figure out how to stop the timer function once its started. Ive tried if statements, disconnect(), and many others...
I realize that there is a timer in qt already but I'm trying to figure it out this way. thanks.
import sys
import time
from PyQt4 import QtCore, QtGui, uic
form_class = uic.loadUiType("/Users/Home/Desktop/Timer/timer.ui")[0]
class MyWindowClass(QtGui.QMainWindow, form_class):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.startButton.clicked.connect(self.timer)
def timer(self):
timeL = 0
self.stopButton.clicked.connect(False)
while True:
self.timeView.display(timeL)
timeL = timeL + 1
time.sleep(1)
app.processEvents()
app = QtGui.QApplication(sys.argv)
myWindow = MyWindowClass(None)
myWindow.show()
app.exec_()
TL;DR: You're after QElapsedTimer, not QTimer.
The operating system is already measuring the passage of time for you. You won't do any better job at it yourself.
"I'm trying to figure it out this way" - it will be less accurate than using QElapsedTimer because you're assuming that exactly as much time has passed as you wished to sleep for. This is almost never true: the actual amount of time that passed is different than the argument to sleep. Worse yet, the errors are usually of a systematic kind too, so your time accumulation will have a bias and will get less accurate as time passes. So, don't do it. It makes no sense. Perhaps you're not telling us exactly what you're trying to do: if you're asking about a particular solution that doesn't work, it helps to say what problem the solution is supposedly to. Why are you trying to figure it out this (wrong) way?
There are conceptually three different kinds of timers in Qt:
QElapsedTimer is like a stopwatch: it is an interface to the operating system's way of measuring the passage of time. That's the class you should be using to measure how much time has passed between the button clicks.
QTime is like a wall clock: you can ask it what time it is through currentTime(), and take difference between two readings of time to obtain elapsed time. Use this class only if you need to know the absolute time, otherwise QElapsedTimer will offer better resolution for elapsed time measurements.
QTimer is a source of timeouts: it is a way to periodically call your code. This is not meant to be used in measuring time, but merely to let your code run periodically, e.g. when you wish to refresh screen display, or implement a metronome that beeps periodically. There are no guarantees that your code will be called on time, and no guarantees that some ticks won't be missed. You want it guaranteed, you need to write a kernel driver, no way around that.
Below is a complete example using PyQt4, for Python 3.5. It uses QElapsedTimer to measure the passage of time between button presses, and QTimer to keep the time display refreshed.
#!/usr/bin/env python
#https://github.com/KubaO/stackoverflown/tree/master/questions/interval-timer-38036583
# -*- coding: utf-8 -*-
import sys
from PyQt4 import QtCore, QtGui
if __name__ == "__main__":
running = False
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
layout = QtGui.QVBoxLayout(w)
label = QtGui.QLabel()
button = QtGui.QPushButton('Start')
timer = QtCore.QElapsedTimer()
updateTimer = QtCore.QTimer()
layout.addWidget(label)
layout.addWidget(button)
def onTimeout():
label.setText('Elapsed: {0}ms'.format(timer.elapsed()))
def onClicked():
global running
if running:
onTimeout()
updateTimer.stop()
button.setText('Start')
else:
timer.start()
updateTimer.start()
button.setText('Stop')
running = not running
updateTimer.setInterval(1000/25) # ~25fps update rate
updateTimer.timeout.connect(onTimeout)
button.clicked.connect(onClicked)
w.show()
sys.exit(app.exec_())
Use a flag to control the loop. Then reset the flag in the slot connected to the stop button:
self.startButton.clicked.connect(self.timer)
self.stopButton.clicked.connect(self.stop)
def stop(self):
self._timer_flag = False
def timer(self):
timeL = 0
self._timer_flag = True
while self._timer_flag:
self.timeView.display(timeL)
timeL = timeL + 1
time.sleep(1)
app.processEvents()
A QTimer is better, because there is no lag in updating the ui. But you can improve your example by using an inner loop to call processEvents more frequently:
def timer(self):
timeL = 0
self._timer_flag = True
while self._timer_flag:
self.timeView.display(timeL)
timeL = timeL + 1
for _ in range(10):
# process events for up to 50 milliseconds
app.processEvents(QtCore.QEventLoop.AllEvents, 50)
time.sleep(0.1)
Sounds like you want to use QTimer on a QThread.
This answer should give you everything you need.
https://stackoverflow.com/a/18960953/5757280

Display busy progressBar for long process, no thread

I'm writing a PyQt GUI application, and I would like to display a busy progress bar for one of my function, which takes time to finish. Here is the code:
self.progress = QtGui.QProgressDialog("Canceling...", None, 0, 0, self)
self.progress.setWindowTitle("Canceling refresh")
self.progress.show()
timer = QtCore.QTimer()
timer.singleShot(0, self.loadNotifications)
while timer.isActive():
app.processEvents()
For now, I tried something with a QTimer, but it doesn't work. The CPU-bound function is loadNotifications.
I would like to start it in a sort of a thread, and while it's running, update the QProgressBar.
I could of course create a QThread class around the function loadNotifications, but it's a bit overkill for what I want to do: simply display a smooth progressBar while the function is running.
Do you have any idea ?
I've no experience with PyQT, but in tkinter, I usually accomplish this by making my slow function periodically call some update_progbar function passed to it. This update_progbar function then updates the progress bar in the same thread as the slow function (which is therefore temporarily on hold).
Rudimentary:
def update_progbar(prog):
print(prog)
def slow_function(prog_func):
for i in range(100000):
if i%1000 == 0:
prog_func(i / 100000.0)
do_something()

pyside qapplication exec with while loop

Basically I have a program that will create a basic hello world program in PySide qt framework. The difference is that it does print("loop") in a while loop before exec_() is called. The problem with that the loop won't finish until the user is done with the program, so it will only call exec_() when the loop's done.
My problem is that if you run it like this, print("loop") will run, but the window won't respond, and doesn't display "Hello, loop!"). If you indent qt_app.exec_() under while running:, then the window will respond, but print("loop") only executes once before closing the window, and executes only once after closing it.
I need to be able to have the main window be responding while its printing "loop" to the console multiple times.
import sys
from PySide.QtCore import *
from PySide.QtGui import *
qt_app = QApplication(sys.argv)
label = QLabel('Hello, loop!')
label.show()
running = True #only set to False when user is done with app in the real code.
while running:
#I am handling connections here that MUST be in continual while loop
print("loop")
qt_app.exec_()
If you want to have a GUI application you have to let the GUI event loop take over the main thread.
The solution for your problem would be to create a separate thread that will do the printing while you let the qt event loop take over the main thread.
Your thread will be running in the background, doing it's stuff and (since I set it to be daemon) it will stop when the application finishes, or the running variable is set to False.
import sys
import time
import threading
from PySide.QtCore import *
from PySide.QtGui import *
qt_app = QApplication(sys.argv)
label = QLabel('Hello, loop!')
label.show()
running = True #only set to False when user is done with app in the real code.
def worker():
global running
while running:
#I am handling connections here that MUST be in continual while loop
print("loop")
time.sleep(0.5)
thread = threading.Thread(target=worker)
thread.setDaemon(True)
thread.start()
qt_app.exec_()
But this is a bad example since you shouldn't use global mutable variables in thread without locking, etc. etc. etc... But that's all in the docs.

Categories

Resources