In PyQt5 How to call function or method after load aplication? - python

from PyQt5 import QtWidgets
import time
def show_message(self):
time.sleep(5)
self.label.setText("It's me")
class Main(QtWidgets.QMainWindow):
def __init__(self):
super(Main, self).__init__()
self.label = QtWidgets.QLabel('Hello', self)
#How to call this func after load application
show_message(self)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication([])
application = Main()
application.show()
sys.exit(app.exec_())
How to call show_message(self) after load application
Does pyqt5 have a function or method like self.afterLoad(application, show_message)
It seems to me something like this available on tkinter

I am guessing you want to start the application and then after 5 seconds the text in the label will get changed to the new text.
What you have right now is almost working, however the problem is that when you call time.sleep(5) then the whole execution of the program will get paused and nothing gets shown for that amount of time. If you still wanna be able to interact with the program during those 5 seconds then you will need to use a timer that is running in the background instead.
PyQt already has something like that in PyQt5.QtCore.QTimer. If you use that then your code could look something like this
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer # Importing QTimer
import time
def show_message(self):
self.label.setText("It's me")
# Stopping the timer. Otherwise it will run over and over again.
self.timer.stop()
class Main(QtWidgets.QMainWindow):
def __init__(self):
super(Main, self).__init__()
self.label = QtWidgets.QLabel('Hello', self)
# Create a new QTimer
self.timer = QTimer(self)
# Tell the timer that it should call show_message(self) when the time runs out
self.timer.timeout.connect(lambda: show_message(self))
# Start the timer which then starts running in the background for 5 seconds
self.timer.start(5000)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication([])
application = Main()
application.show()
sys.exit(app.exec_())
One last thing that I would recommend doing is putting your functions that belong to a class inside of it. So here it would probably be better to put show_message inside of the main class because right now you can call the function from every part of the code and that can lead to errors.

Related

How to call a function periodically while my MainWindow is active?

I have tried doing it with multiprocessing module to no avail. I get the following error:
TypeError: cannot pickle 'MainWindow' object
import time, multiprocessing
from PyQt5 import QtWidgets, QtGui
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initializeUI()
self.show()
def initializeUI(self):
# UI stuff
self.setLayout(QtWidgets.QGridLayout())
dummy_btn = QtWidgets.QPushButton("Ok")
self.layout().addWidget(dummy_btn)
updater = multiprocessing.Process(target=self.update_function, args=[])
updater.start()
def update_function(self):
time.sleep(2)
print("This text, again!")
self.update_function()
app = QtWidgets.QApplication([])
mw = MainWindow()
app.exec_()
the proper way to do this is to not use any sort of parallel mechanism, instead use QTimer.singleshot, as QT doesn't work well with multiprocessing or threading, and if you want to repeat it then you can just connect the function to a Qtimer.timeout signal and set the timer on repeat using Qtimer.start() as in this tutorial
import time, multiprocessing
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtCore import QTimer
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initializeUI()
self.show()
def initializeUI(self):
# UI stuff
self.setLayout(QtWidgets.QGridLayout())
dummy_btn = QtWidgets.QPushButton("Ok")
self.layout().addWidget(dummy_btn)
self.timer = QTimer(self)
# self.timer.singleShot(2000,self.update_function) # for one time call only
self.timer.timeout.connect(self.update_function)
self.timer.start(2000) # time in milliseconds.
def update_function(self):
# time.sleep(2) this is not needed
print("This text, again!")
self.update() # this had a typo
app = QtWidgets.QApplication([])
mw = MainWindow()
app.exec_()
Edit: to clarify on working with threads and multiprocessing, if you use multiprocessing for example there are many precautions, such as putting an if __name__ == "__main__": guard on your code, and not use anything that belong to QT inside the subprocesses, and just use it for running things that don't need QT, like reading files and doing calculations.
as for threading, using any QWidget object in another thread other than your main application thread is going to crash your application, you can emit signals from child threads for signaling, but you cannot update the GUI on another thread, so only use QT objects that don't touch the GUI inside threads. (like networking, reading files, and sharing the CPU for extra calculations)

PyQt5 multithreading still freezes. How to improve performance?

I'm trying to write a PyQt application for some image processing algorithms that I have written. The problem is, these take some time and make the app freezes (until it finishes but the user might be compelled to close the app in the mean time).
I'm trying to understand how to implement multi-threading but I just can't seem to make it work. This example just loads a large image on a different thread, but it still makes the whole app freeze. I'm sure I'm making a mistake somewhere.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class App(QWidget):
def __init__(self):
super().__init__()
self._unit_ui()
def _unit_ui(self):
label = QLabel(self)
label.resize(700, 700)
button = QPushButton(self)
self.th = Thread(self)
self.th.change_pixmap.connect(label.setPixmap)
button.pressed.connect(self.th.start)
self.show()
class Thread(QThread):
change_pixmap = pyqtSignal(QPixmap)
def __init__(self, parent=None):
QThread.__init__(self, parent=parent)
self.isRunning = True
def run(self):
pixmap = QPixmap('gridscan.jpg')
self.change_pixmap.emit(pixmap)
app = QApplication(sys.argv)
m = App()
sys.exit(app.exec_())
With the help of eyllanesc I managed to solve it.
I just stopped using QPixmap in my second thread and imported the image with another function, passed it (changing the pyqtSignal(QPixmap) to pyqtSignal(np.ndarray)) and now it works flawlessly!
Thanks!
You can try looking into this answer:
https://stackoverflow.com/a/38003561/9066493
Basically, self.th needs to be moved to a different thread like:
# Setup the worker object and the worker_thread.
self.worker = WorkerObject()
self.worker_thread = QtCore.QThread()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
And then transfer data between the main thread and the worker thread using signal and slots, which you need to connect to a specific method slot
Hope the example helps.

Is my threading proper ? if yes then why code is not working?

I am creating an alarm clock in python using PyQt4 and in that I am using LCD display widget, which display current updating time. For that I am using threading. But I am new to it so the problem is I have no clue how to debug that thing.
This is my code
import sys
from PyQt4 import QtGui, uic
import time
import os
from threading import Thread
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
uic.loadUi('AlarmClock_UI.ui', self)
self.show()
self.comboBox.setCurrentIndex(0)
self.comboBox.currentIndexChanged.connect(self.getSelection)
self.lineEdit.setText('Please select the reminder type')
timeThread = Thread(target = self.showTime())
timeThread.start()
def getSelection(self):
if self.comboBox.currentIndex() == 1:
self.lineEdit.setText('Select the alarm time of your choice')
elif self.comboBox.currentIndex() == 2:
self.lineEdit.setText('Use those dials to adjust hour and minutes')
else:
self.lineEdit.setText('Please select the reminder type')
def showTime(self):
showTime = time.strftime('%H:%M:%S')
self.lcdNumber.display(showTime)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
sys.exit(app.exec_())
I tried while loop in showTime() function then it was not even loading GUI just running in the background.
Thanks :)
As has been said elsewhere, you do not need to use threading for this, as a simple timer will do. Here is a basic demo script:
import sys
from PyQt4 import QtCore, QtGui
class Clock(QtGui.QLCDNumber):
def __init__(self):
super(Clock, self).__init__(8)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.showTime)
self.timer.start(1000)
self.showTime()
def showTime(self):
time = QtCore.QTime.currentTime()
self.display(time.toString('hh:mm:ss'))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Clock()
window.setWindowTitle('Clock')
window.setGeometry(500, 100, 400, 100)
window.show()
sys.exit(app.exec_())
Qt does not support doing GUI operations in threads other than the main thread. So when you call self.lcddisplay.display(showTime) from within the context of your spawned thread, that is an error and Qt will not work correctly.
As tdelaney suggested in his comment, the best way to handle this sort of thing is to use a QTimer to emit a signal at the appropriate intervals, and update your lcddisplay in the slot that signal is connected to.
(if you insist on using threads, however, e.g. as a learning exercise, then your spawned thread would need to send a message to the main thread to tell the main thread to do the display update, rather than trying to do the update itself)

Update LCD Number countdown

I'm very new to Python and I have made a very simple countdown timer. The GUI was created in Qt Designer. There is a spin box for input of seconds, a start button and an LCD number counter. The counter counts down fine using the code below:
def start_btn_clicked(self):
x = self.Minute_spinBox.value()
for i in xrange(x,0,-1):
time.sleep(1)
print (i)
So that I could see what was happening as I played around with it, I added the print instruction so that it shows the countdown in the Python console as it runs. I then thought I could maybe quite easily have the LCD number display the countdown with something like:
self.lcdNumber.display(i)("%SS")
But no matter what I try, I cant get it to show. With the line above, I get the first number displayed, but then I get an error saying:
self.lcdNumber.display(i)("%SS")
TypeError: 'NoneType' object is not callable
I have tried so many variations that I no longer know where I started and here was me thinking it would be simple. I'd love to know why I cant get it to display the countdown.
Just adding one line of code to my original code will allow the lcdNumber to display the count...
def start_btn_clicked(self):
x = self.Minute_spinBox.value()
for i in xrange(x,0,-1):
time.sleep(1)
app.processEvents() # just this one line allows display of 'i'
self.lcdNumber.display(i)`
And works perfectly
The display function returns None, so doing None("%SS") obviously isn't allowed.
self.lcdNumber.display(i) is enough to show the countdown!
To let Qt paint the widgets while looping run the countdown from another thread. See an example.
import time
from threading import Thread
from PyQt4.QtGui import QApplication, QMainWindow, QLCDNumber
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.lcdnumber = QLCDNumber(self)
self.resize(400, 400)
t = Thread(target=self._countdown)
t.start()
def _countdown(self):
x = 10
for i in xrange(x,0,-1):
time.sleep(1)
self.lcdnumber.display(i)
if __name__ == "__main__":
app = QApplication([])
window = Window()
window.show()
app.exec_()
The for loop is blocking the GUI.
The slot connected to the button's clicked signal is processed synchronously. This means the event-loop must wait for the slot to return before it can process any more events (including the paint events needed for updating the GUI).
So you need to find a way to process these events whilst the for loop is running. There are various ways of doing this, such as using a QTimer or a QThread. But the simplest way of fixing your particular example would be to use QCoreApplication.processEvents.
Here's an example that shows how to do that:
import sys, time
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.spinbox = QtGui.QSpinBox(self)
self.spinbox.setValue(5)
self.lcdnumber = QtGui.QLCDNumber(self)
self.button = QtGui.QPushButton('Start', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.spinbox)
layout.addWidget(self.lcdnumber)
layout.addWidget(self.button)
def handleButton(self):
for tick in range(self.spinbox.value(), -1, -1):
self.lcdnumber.display(tick)
self.button.setEnabled(not tick)
# continually process events for one second
start = time.time()
while time.time() - start < 1:
QtGui.qApp.processEvents()
time.sleep(0.02)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 300, 200)
window.show()
sys.exit(app.exec_())

how to start two thread using python?

i have 3 python Files:
file including Many class any methods for initializing Frame and other GUI using pyQt.
file including leap Motion Listener class that read data from leap motion.
Main file that used to start other classes.
now i want to start the GUI frame and the Leap Motion class together. i tried to start two thread in the main class but there is many problems.
this code is valid to run only the pyQt frame :
import sys
from PyQt4 import QtCore, QtGui
from Painter import GUI_animate
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = GUI_animate()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
and this is what i tried to do to run the pyQt frame and Leap Motion class :
import sys
from PyQt4 import QtCore, QtGui
from Painter import GUI_animate
import LeapMotion
from threading import Thread
class StartQT4(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
t1 = Thread(target=show_frame())
t2 = Thread(target=start_LeapMotion())
t1.start()
t2.start()
self.ui.setupUi(self)
def start_LeapMotion():
LeapMotion.main()
def show_frame():
StartQT4.ui = GUI_animate()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp = StartQT4()
myapp.show()
sys.exit(app.exec_())
but only Leap Motion class run, and after finish reading from leap motion, the frame show !
how can i run them together?
Don't put the paired parentheses after show_frame and start_LeapMotion when you specify them as target of the thread. Python interprets functionName as a variable referring to <function functionName at (memory location)>, whereas functionName() is a call to that function. When you specify the thread's target, you do not want to pass a call to that function; you want to pass the function itself. As explained in the API for the threading module, t1.start() invokes the Thread object's run() method, which, unless you've overridden it, "invokes the callable object passed to the object's constructor as the target argument"--note that the target argument should receive a callable object (i.e. the function itself, so no parentheses), rather than a call (which is what you are currently passing).

Categories

Resources