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).
Related
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.
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)
Problem Description
I'm trying to make an application that collects data, processes it, displays it, and some actuation (open/close valves, etc). As a practice for future applications where I have some stricter time constraints, I want to run the data capture and processing in a separate thread.
My current problem is that it's telling me I cannot start a timer from another thread.
Current code progress
import sys
import PyQt5
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal
# This is our window from QtCreator
import mainwindow_auto
#thread to capture the process data
class DataCaptureThread(QThread):
def collectProcessData():
print ("Collecting Process Data")
#declaring the timer
dataCollectionTimer = PyQt5.QtCore.QTimer()
dataCollectionTimer.timeout.connect(collectProcessData)
def __init__(self):
QThread.__init__(self)
def run(self):
self.dataCollectionTimer.start(1000);
class MainWindow(QMainWindow, mainwindow_auto.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self) # gets defined in the UI file
self.btnStart.clicked.connect(self.pressedStartBtn)
self.btnStop.clicked.connect(self.pressedStopBtn)
def pressedStartBtn(self):
self.lblAction.setText("STARTED")
self.dataCollectionThread = DataCaptureThread()
self.dataCollectionThread.start()
def pressedStopBtn(self):
self.lblAction.setText("STOPPED")
self.dataCollectionThread.terminate()
def main():
# a new app instance
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Any advice on how to get this to work would be appreciated!
You have to move the QTimer to the DataCaptureThread thread, in addition to that when the run method ends, the thread is eliminated so the timer is eliminated, so you must avoid running that function without blocking other tasks. QEventLoop is used for this:
class DataCaptureThread(QThread):
def collectProcessData(self):
print ("Collecting Process Data")
def __init__(self, *args, **kwargs):
QThread.__init__(self, *args, **kwargs)
self.dataCollectionTimer = QTimer()
self.dataCollectionTimer.moveToThread(self)
self.dataCollectionTimer.timeout.connect(self.collectProcessData)
def run(self):
self.dataCollectionTimer.start(1000)
loop = QEventLoop()
loop.exec_()
I have a MainWindow class which have a Gui application running on it and i want that every time i click on a button from my application a signal is emitted and caught by another thread. There is my example code (sorry for not posting my real code but it is real big now):
from PySide.QtGui import *
from PySide.QtCore import *
import sys
import mainGui #Gui file
class MainWindow(QMainWindow, mainGui.Ui_MainWindow):
mySignal = Signal()
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.newThread = workThread()
self.newThread.start()
#myButton is part of Gui application
self.myButton.clicked.connect(self.myfunction)
def myfunction(self):
self.mySignal.emit()
(...) #Other functions and methods
class workThread(QThread):
def __init__(self, parent=None):
super(workThread, self).__init__(parent)
#The problem:
MainWindow.mySignal.connect(self.printMessage)
def run(self):
(...)
def printMessage(self):
print("Signal Recived")
(...)
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__=="__main__":
main()
... and i get the following error:
MainWindow.mySignal.connect(self.printMessage)
AttributeError: 'PySide.QtCore.Signal' object has no attribute 'connect'
There is any ideia how can i solve this?
Thanks in advance!
Signals are just like methods - they must be bound to instances. They won't work correctly if you try to access them directly via the class.
One way to fix the example is to pass the instance of MainWindow in as the parent of the thread, like so:
self.newThread = workThread(self)
...
parent.mySignal.connect(self.printMessage)
I have a Python PyQt application that displays a simple UI.
When the user clicks a button in the UI it triggers a QThread.
The use of a thread prevents the UI from "freezing" while the thread runs.
I emit signals to pass information from the run thread back to the UI for status updates and to indicate completion. Everything works fine as described and I've created a simple class for my UI to call which creates the thread and runs my generic processing.
However, I would also like to create a command line version (No GUI) of my program and use the same processing QThread class. However, I get the following error when I try to connect my signals. It would seem that QThread was only meant for GUI programs?
AttributeError: MyClass instance has no attribute 'connect'
Is it possible to use QThread with a non-GUI program?
from PyQt4 import QtCore
from PyQt4.QtCore import *
#======================================
class MyProcess(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.quit()
self.wait()
def run(self):
print "do time intensive process here"
self.emit( SIGNAL('processdone'), "emitting signal processdone")
return
#======================================
class MyClass(QObject):
def __init__(self, parent=None): # All QObjects receive a parent argument (default to None)
super(MyClass, self).__init__(parent) # Call parent initializer.
thread1 = MyProcess() # uses QThread and emits signal 'processdone'
self.connect( thread1, SIGNAL("processdone"), self.thread1done)
thread1.start()
def thread1done(self):
print "done"
#======================================
if __name__ == "__main__":
MyClass()
The problem isn't QThread, the problem is that you're calling the connect method from a class which doesn't have it. You need to make MyClass inherits from QObject.
In the GUI this works because whatever widget you're working with (QDialog, QMainWindow, QWidget ...) it inherits (directly or indirectly) from QObject.
To make MyClass inherit from QObject you only have to:
class MyClass(QObject): # Specify the class your are specializing.
def __init__(self, parent=None): # All QObjects receive a parent argument (default to None)
super(MyClass, self).__init__(parent) # Call parent initializer.
# And countinue your code here...
I also recommend you to use the New-style Signal and Slot Support.
Everything works except the processdone signal is called but apparently it never triggers call to thread1done.
The problem I can spot is you don't have defined a processdone signal. That signal does not exist for Qt. Check the link I left you to learn about custom signals. Meanwhile you can add:
class MyProcess(QThread):
processdone = QtCore.pyqtSignal("QString")
at the beginning of the class.
And one last thing, but very important. You aren't working with GUI, but you are still using QObjects and the Qt singal mechanisms, which depends on the main Qt loop to work. Hence, you still need a QApplication object despite of your application is a non-gui program.
Here is your code, now working:
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.QtCore import *
class MyProcess(QThread):
processdone = QtCore.pyqtSignal("QString") # Define custom signal.
def __init__(self, parent = None):
QThread.__init__(self, parent)
def run(self):
print("do time intensive process here")
self.emit( SIGNAL('processdone'), "emitting signal processdone")
return
class MyClass(QObject):
def __init__(self, parent=None): # All QObjects receive a parent argument (default to None)
super(MyClass, self).__init__(parent) # Call parent initializer.
thread1 = MyProcess(self)
self.connect( thread1, SIGNAL("processdone"), self.thread1done)
thread1.start()
#QtCore.pyqtSlot("QString") # Tell Python this is a QTSLOT an receives a string
def thread1done(self, text):
print(text) # Print the text from the signal.
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv) # You still need a QApplication object.
a = MyClass()
sys.exit(app.exec())