Python: thread in PyQt, emit method to get the data - python

I try to get the value of Count_total_value in my user interface when I call Continusread method, (it should be "toto" according to what I want to do) but it is always the default value "azerty" which is displayed. Please can you tell me where I am wrong?
This is my code:
#!/usr/bin/env python3
from PyQt4 import QtCore, QtGui
import sys
import os
import subprocess
import time
import threading
from ctypes import *
import ctypes
#import Converted Python UI File
from test_error_rx import Ui_MainWindow
class MyThread(QtCore.QThread):
Count_total_valuechanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(MyThread, self).__init__(parent=parent)
self.Count_total_value = 'Azerty'
def run(self):
##do things to calculate Count_total_value
Count_total_value='toto'
self.Count_total_valuechanged.emit((self.Count_total_value))
time.sleep(0.1)
class Main( QtGui.QMainWindow,QtGui.QWidget):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
# Connect the Buttons
QtCore.QObject.connect(self.ui.Continusread,QtCore.SIGNAL("clicked()"),self.Continusread)
self.__thread = MyThread(self)
self.__thread.Count_total_valuechanged.connect(self.ui.Count_total.setText)
def Continusread(self):
self.__thread.start()
def main():
app = QtGui.QApplication(sys.argv)
window = Main()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

In the run() method of your thread class MyThread you set Count_total_value='toto' when it should be self.Count_total_value='toto'.
Note that when posting on stackoverflow you should:
post a minimilistic working example (you haven't included your UI in the above code so no-one can run your script)
Check the posted code has the correct indentation and fix any mistakes (your posted code is a mess of incorrect indentation)

Related

When using a QThread, how do i pass data to the function that is called with .connect()?

I am using a QThread and, when my worker class is dont with the big work, i want to call a function that does changes to my UI (in this case with .finished.connect(), but i dont fully understand how the .connect() step works and how i can pass variables to the function called.
As an example: A button that, when pressed, opens a Thread in which it counts up, and changes the button text to the number its on. When the counter is finished, it prints something. Very simple and it works just fine.
main.py:
from PyQt5 import QtWidgets
import sys
import worker
from PyQt5.QtCore import QThread
from start import test
class App(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)
self.init_ui()
self.initiate_buttons()
self.obj = worker.Worker()
self.thread = QThread()
self.obj.intReady.connect(self.onIntReady)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.obj.finished.connect(test)
self.thread.started.connect(self.obj.procCounter)
def init_ui(self):
self.pushButton = QtWidgets.QPushButton(self)
def onIntReady(self, i):
self.pushButton.setText("{}".format(i))
def initiate_buttons(self):
print("clicked")
self.pushButton.clicked.connect(lambda: self.thread.start())
def run():
application = QtWidgets.QApplication(sys.argv)
main_app = App()
main_app.show()
application.exec()
if __name__ == '__main__':
run()
worker.py:
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
import time
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
#pyqtSlot()
def procCounter(self):
for i in range(1, 5):
time.sleep(1)
self.intReady.emit(i)
self.finished.emit()
start.py:
def test(x="bar"):
print("test"+x)
So the thing i am talking about is start.py - i can put the function in its own module just fine and it worked. But what if i want to pass a parameter to it? I tried the obvious attempt, changing self.obj.finished.connect(test) to self.obj.finished.connect(test(x="foo")) - but that doesnt work. But i dont understand why it does not work, and how else i am supposed to do this. In the end, i want the module to do changes to my UI, just like onIntReady. I found this and this but did not understand how i can apply it.

QtWebPageRenderer SIGILL issue

My problem is summed in title. When I call method setHtml on instance of QtWebPageRenderer, SIGILL signal is emitted and my application goes down.
I'm aware that this issue is caused by bad Qt5 dynamic library but I installed it with:
sudo pip install PyQt5 --only-binary PyQt5
sudo pip install PyQtWebEngine --only-binary PyQtWebEngine
so I thought I will get correct precompiled library. When I tried to install PyQt5 without --only-binary, I always ended with some strange compilation error. Something like qmake is not in PATH even though it is and I'm able to call qmake from shell.
So my question is, how to make PyQt5 running on Fedora 31 without any SIGILLs.
EDIT:
Following code can replicate the issue. That information about SIGILL is little inaccurate because first signal is actually SIGTRAP, after I hit continue in gdb, I got SIGILL. This hints that Qt is actually trying to say something to me, although in not very intuitive way.
After some playing around with it, I found that without thread, its ok. Does this mean that Qt forces user to use QThread and not python threads? Or it means that I can't call methods of Qt objects outside of thread where event loop is running?
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
class Runner(threading.Thread):
def __init__(self, web_view):
self.web_view = web_view
threading.Thread.__init__(self)
self.daemon = True
def run(self):
self.web_view.load(QtCore.QUrl("https://www.worldometers.info/coronavirus/"))
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
web_view = WebView()
runner = Runner(web_view)
runner.start()
app.exec_()
if __name__ == "__main__":
main()
You have to have several restrictions:
A QObject is not thread-safe so when creating "web_view" in the main thread then it is not safe to modify it in the secondary thread
Since the QWebEnginePage tasks run asynchronously then you need a Qt eventloop.
So if you want to use python's Thread class then you must implement both conditions:
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
class Runner(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True
def run(self):
# The QWebEnginePage was created in a new thread and
# that thread has an eventloop
loop = QtCore.QEventLoop()
web_view = WebView()
web_view.load(QtCore.QUrl("https://www.worldometers.info/coronavirus/"))
loop.exec_()
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
runner = Runner()
runner.start()
app.exec_()
if __name__ == "__main__":
main()
In reality QThread and threading.Thread() are native thread handlers of the OS, so in practical terms it can be said that QThread is a threading.Thread() + QObject with an eventloop running on the secondary thread.
On the other hand, if your objective is to call a function from a thread to which it does not belong, then you should use asynchronous methods as pointed out in this answer.
In this case the simplest is to use pyqtSlot + QMetaObject:
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
#QtCore.pyqtSlot(QtCore.QUrl)
def load(self, url):
QWebEnginePage.load(self, url)
class Runner(threading.Thread):
def __init__(self, web_view):
self.web_view = web_view
threading.Thread.__init__(self)
self.daemon = True
def run(self):
url = QtCore.QUrl("https://www.worldometers.info/coronavirus/")
QtCore.QMetaObject.invokeMethod(
self.web_view,
"load",
QtCore.Qt.QueuedConnection,
QtCore.Q_ARG(QtCore.QUrl, url),
)
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
web_view = WebView()
runner = Runner(web_view)
runner.start()
app.exec_()
if __name__ == "__main__":
main()
Or functools.partial() + QTimer
from functools import partial
import signal
import sys
import threading
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5.QtWebEngineWidgets import QWebEnginePage
class WebView(QWebEnginePage):
def __init__(self):
QWebEnginePage.__init__(self)
self.loadFinished.connect(self.on_load_finish)
def print_result(self, data):
print("-" * 30)
print(data)
with open("temp.html", "wb") as hndl:
hndl.write(data.encode("utf-8"))
def on_load_finish(self):
self.toHtml(self.print_result)
class Runner(threading.Thread):
def __init__(self, web_view):
self.web_view = web_view
threading.Thread.__init__(self)
self.daemon = True
def run(self):
wrapper = partial(
self.web_view.load,
QtCore.QUrl("https://www.worldometers.info/coronavirus/"),
)
QtCore.QTimer.singleShot(0, wrapper)
def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtWidgets.QApplication(sys.argv)
web_view = WebView()
runner = Runner(web_view)
runner.start()
app.exec_()
if __name__ == "__main__":
main()

"Send" information from python app to other python app

I have two python programs opened at the same time, and i would like that when i click one button in the first app, "send" some information to the second app.
Im trying to do by signals from PySide. I get how to send it with this little code:
from PySide.QtGui import *
from PySide.QtCore import *
from PySide import QtGui, QtCore, QtUiTools
class Foo(object):
pass
class MyWidget(QWidget):
mysignal = QtCore.Signal(int, str)
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.hlayout = QHBoxLayout()
self.setLayout(self.hlayout)
self.b = QPushButton("Emit your signal!", self)
self.hlayout.addWidget(self.b)
self.b.clicked.connect(self.clickHandler)
self.mysignal.connect(self.mySignalHandler)
def clickHandler(self):
self.mysignal.emit(123, "")
def mySignalHandler(self, n):
print n
# print l
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec_())
but i dont know how can i "recieve" this signal in the other python app.
Thanks for your help!
You need to think about the apps communication ways, it's multi-thread or multi-process?

How to launch an event loop inside a pyside unittest?

I'm trying to make unit tests on classes that involve signals and QTimers. (see the answer to this question for an idealization of one of the classes). I'm having a hard time trying to understand where to put the app.exec_() call without freezing everything and executing the tests with the signals being emitted. Just like in the question I cite, I think the problem here is that I need an event loop but putting in the setUpClass definitely doesn't work. This is the smallest piece of code I made to reproduce my problem, which in reality is with a larger code base.
testSignals.py:
import time
from PySide import QtCore
class TestSignals(QtCore.QObject):
def __init__(self):
self.timer = QtCore.QTimer()
self.myTime = ''
self.timer.timeout.connect(self.updateTime)
self.timer.start(100)
def getTime(self):
return self.myTime
def updateTime(self):
self.myTime = time.strftime('%H:%M:%S')
def stop(self):
self.timer.stop()
unitTesting.py:
import unittest
from testSignals import TestSignals
from PySide import QtGui, QtCore
from testSignals import TestSignals
import sys
class test(unittest.TestCase):
#classmethod
def setUpClass(cls):
app = QtGui.QApplication(sys.argv)
assert id(app) == id(QtGui.qApp) and id(app) == id(QtCore.QCoreApplication.instance())
cls.test = TestSignals()
# app.exec_() # uncommenting this only causes the code to freeze
def testStarted(self):
mytime = self.test.getTime()
self.assertNotEqual(mytime,'','Time is still empty')
if __name__ == '__main__':
unittest.main()
and the result is a failure of the only test because "mytime" is still empty. Just for clarity, the class does work:
class testConnection():
def receiveMe(self):
print(self.test.getTime())
# print(self.test.getData())
# signal is emmited when data is changed
def __init__(self):
self.test = TestSignals()
self.test.timer.timeout.connect(self.receiveMe)
# self.test.start()
app = QtCore.QCoreApplication([])
test = testConnection()
timer = QtCore.QTimer()
timer.singleShot(4000,app.exit)
app.exec_()
produces:
>>>$ python3 testConection.py
14:50:21
14:50:22
14:50:23
14:50:24

PyQt : why adding a dummy class definition in my file make the application crash?

Consider the code below:
#!/usr/bin/env python
from PyQt4 import QtCore, QtGui
import os,sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.listWidget = QtGui.QListWidget(None)
self.setCentralWidget(self.listWidget)
if __name__ == '__main__':
app = QtGui.QApplication (sys.argv)
mainWin = MainWindow ()
mainWin.show ()
sys.exit (app.exec_())
Works ok.
Now if I add a dummy class (that inherits from a QtGui module's class) in the global scope ...
class MainWindow(QtGui.QMainWindow):
... # unchanged
class MyWidget(QtGui.QWidget):
def __init__(self):
super(MyWidget, self).__init__()
if __name__ == '__main__':
... # unchanged
... when i launch the script i get the error:
TypeError: argument 1 of
QMainWindow.setCentralWidget() has an
invalid type
This error message is cryptic for me as i can't connect it to the modification done.
Do you have an idea what could be the source of this error?
Can't reproduce the problem as reported: the following exact code
from PyQt4 import QtCore, QtGui
import os, sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.listWidget = QtGui.QListWidget(None)
self.setCentralWidget(self.listWidget)
class MyWidget(QtGui.QWidget):
def __init__(self):
super(MyWidget, self).__init__()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
runs just fine for me (showing an empty window of course). So I guess it's down to versions details! I'm using system-supplied Python 2.5.1 on Mac OS X 10.5.7 and adding a
print QtCore.PYQT_VERSION_STR
shows I'm on version 4.5.1 of PyQt. What about you?
I have not worked with PyQt before, but didn't you forget to call the constructor of the superclass here?
class MyWidget(QtGui.QWidget):
def __init__(self):
# Where is the call to QtGui.QWidget's init ?
pass

Categories