Continuously Check SQLAlchemy Database Connection on Pyside2 - python

First of all, I want to figure out how to check database status every second. so that the user will able to tell if the database is up or not without even clicking or triggering anything. I've read that this will create a problem as mentioned in the comments here
so here's my minimal reproducible example:
import sys
import os
import shiboken2
from PySide2 import QtCore, QtGui, QtWidgets
from PySide2.QtWidgets import QMainWindow, QFileDialog, QMessageBox, QWidget, QDialog, QProxyStyle
from sqlalchemy import create_engine, inspect
class MyWidget(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.resize(200, 200)
self.path = os.path.abspath(os.path.dirname(sys.argv[0]))
self.button = QtWidgets.QPushButton("Open File")
self.labelFile = QtWidgets.QLabel("empty")
self.labelData = QtWidgets.QLabel("None")
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.button)
self.layout.addWidget(self.labelFile)
self.layout.addWidget(self.labelData)
self.setLayout(self.layout)
self.button.clicked.connect(self.open_file)
self.process = None
self.CreateEngine = CreateEngine(self)
self.CreateEngine.result.connect(self.start_timer)
self.CreateEngine.start()
def open_file(self):
x = QFileDialog.getOpenFileName(self,"Just To Spice This Code",self.path,"CSV Files (*.csv)")
self.labelFile.setText(x[0]) #just to check that GUI doesn't freeze
def start_timer(self,engine): #callback from CreateEngine
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(lambda: self.continuously_check(engine))
self.timer.start(1000) #check connetion every second, as real-time as possible
def continuously_check(self,engine): #this gonna get called every second, yes it isn't effective i know
self.process = CheckConnection(self,engine)
self.process.result.connect(self.update_connection_label)
self.process.start()
def update_connection_label(self,x): #update connection status on GUI
self.labelData.setText("DB Status: "+str(x))
def closeEvent(self,event): #to handle QThread: Destroyed while thread is still running
print("begin close event")
if(self.process is not None):
if(shiboken2.isValid(self.process)): #to check whether the object is deleted. ->
self.process.wait() #-> this will get messy when the DB connection is down
self.process.quit() #-> (IMO):since i stack so many CheckConnection objects maybe?
print("end close event")
class CreateEngine(QtCore.QThread): #creating engine on seperate thread so that it wont block GUI
result = QtCore.Signal(object)
def __init__(self, parent):
QtCore.QThread.__init__(self, parent)
self.engine = None
def run(self):
self.engine = create_engine('mysql+pymysql://{}:{}#{}:{}/{}'.format("root","","localhost","3306","adex_admin"))
self.result.emit(self.engine)
class CheckConnection(QtCore.QThread): #constantly called every second, yes its not a good approach ->
result = QtCore.Signal(str) #-> i wonder how to replace all this with something appropriate
def __init__(self, parent,engine):
QtCore.QThread.__init__(self, parent)
self.engine = engine
def run(self):
try:
self.engine.execute('SELECT 1').fetchall()
self.result.emit("Connected")
except:
self.result.emit("Not Connected")
self.deleteLater() #somehow this doesn't do it job very well. maybe blocked?
#-> especially when the connection is busted. this thread gets stuck quite long to finish
if __name__ == "__main__":
#idk why when you start this without connection it's running really slow on showing the status of DB
#you must wait like 4 seconds until the connection status is showed up, which is really bad
#but once it's live. it could read database status really fast
app = QtWidgets.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
I've created this example just to reproduce the same problem I'm facing in my real app. so the problem is that closeEvent takes too long to terminate the checking process and also blocking the GUI. The reason why I create 'closeEvent' is that I had this problem which produce [QThread: Destroyed while thread is still running] when the app is closed.
also, whenever the database isn't reachable it makes the QThread finishes way longer than it should unlike when the database is reachable. but we can retrieve the status pretty much like we want (every second of live DB Status). I also tried a silly approach like this
...
def continuously_check(self,engine):
self.process = CheckConnection(self,engine)
self.process.result.connect(self.update_connection_label)
self.process.finished.connect(lambda: QtCore.QTimer.singleShot(1000,self.continuously_check))
self.process.start()
...
hoping that it won't keep creating objects before the thread even finished (ps: obviously this won't work). so what's the best approach when it comes to this? sorry for multiple problems at a time.

Related

Update a label while inside a loop. PyQT

So I have a TCP server, which just echo's back whatever I send to it. And I have a GUI client which sends the stuff. But since I have to maintain the connection I can't seem to get the label I want to change once in a certain amount of time, I was trying to use signals but I seem to be doing something terribly wrong since the application freezes as soon as I connect to the server on button click. Here's what I got so far. Also, eventually I need to get 2 servers to echo to the client the information, and I guess that will pose even more of a problem and I will have to use multithreading.
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import *
import sys
import socket
import time
class MyClient(QMainWindow):
updateText = QtCore.pyqtSignal(str)
def __init__(self):
super(MyClient, self).__init__()
self.setGeometry(200, 200, 300, 300)
self.setWindowTitle("Krs")
self.initSocket()
self.initUI()
def initSocket(self):
self.ClientSocket = socket.socket()
self.s1 = ['127.0.0.1', 1233]
def initUI(self):
self.label = QLabel(self)
self.label.setText("nuthin")
self.label.move(50,50)
self.updateText.connect(self.label.setText)
self.b1 = QtWidgets.QPushButton(self)
self.b1.setText("S1")
self.b1.clicked.connect(lambda: self.conntoS(self.s1))
def conntoS(self, addrs):
self.ClientSocket.connect((addrs[0], addrs[1]))
while True:
time.sleep(1)
self.ClientSocket.send(str.encode("Anything"))
Response = self.ClientSocket.recv(1024)
self.upd(Response.decode('utf-8'))
#QtCore.pyqtSlot()
def upd(self, txt):
self.updateText.emit(txt)
def window():
app = QApplication(sys.argv)
win = MyClient()
win.show()
sys.exit(app.exec_())
window()
You should not run while True loop with time.sleep() inside gui thread, because it freezes the eventloop: while it run, widgets have no chance to recieve any events, and no chance to handle them - no repaints, no signals can be emited, no handlers invoked. All long operations should be performed outside of the gui thread.
You need to create worker, move your blocking function to it and run it separate QThread, or in your case you can use QTcpSocket asyncronous signal-slot api.
Example of the right way to use QThread in PyQt?
Python QTcpSocket and QTcpServer receiving message

Update widget with data from another Python process, without blocking it

I'm running a Python script that extracts and localizes some files. I'd like to use a QDialog to show the progress status through a QProgressBar, and the list of files that are being copied.
Let me start by saying that the localization script cannot be integrated in the PyQt script - otherwise I'm aware that the solution would be quite straightforward. I need to keep the localization script separated from the UI and have them running at the same time.
I thought about running the UI from the localization script through a thread, to avoid it blocking the localization process. But at that point I have no idea how to update the UI elements as I don't have an instance I can call and update since I've launched it with the thread.
This is a simplified example of the dialog code:
from PyQt5 import QtCore, QtWidgets
import sys
class Ui_dialog_main(object):
def setupUi(self, dialog_main):
dialog_main.setWindowTitle("Test")
dialog_main.resize(390, 120)
self.progress_bar = QtWidgets.QProgressBar(dialog_main)
self.progress_bar.setGeometry(QtCore.QRect(10, 60, 371, 30))
self.label_localizing = QtWidgets.QLabel(dialog_main)
self.label_localizing.setGeometry(QtCore.QRect(10, 10, 81, 25))
self.label_localizing.setText("Package:")
QtCore.QMetaObject.connectSlotsByName(dialog_main)
def start():
app = QtWidgets.QApplication(sys.argv)
dialog_main = QtWidgets.QDialog()
ui = Ui_dialog_main()
ui.setupUi(dialog_main)
dialog_main.show()
sys.exit(app.exec_())
And this is how I'm starting the thread in the localizer file:
thread = Thread(target=LocManager.start)
thread.start()
where LocManager is the name of the ui .py file.
Of course this way the main script doesn't get stuck by the ui, which is one of my goals - but I have no idea how to update the progress bar and label in this situation. I've found several threads discussing similar problems, but nothing that would exactly help with mine.
I hope my description was clear enough.
UPDATE:
I found a solution for this here, using pipes. Even if I'm not sure this is the appropriate way to tackle this issue, it definitely did the trick. Instead of working with a "Bi-directional Communication" between two PyQt GUIs, as described in the link, I modified the code to have a bi-directional communication between my GUI and my localization script.
One way to solve this is to run the dialog in a separate process, and then use some form of IPC to send the updates. The solution below uses Qt's QLocalServer and QLocalSocket classes to pass a dict encoded with json to the dialog process. The server emits a signal whenever new data is received, which the dialog connects to in order to process updates. When the sending process exits, the server process is automatically shut down.
test.py:
import time
from dlg_server import send_data
for message in 'One Two Three Four Five'.split():
send_data(message=message)
time.sleep(2)
dlg_server.py:
import sys, os, json, atexit
from PyQt5 import QtCore, QtWidgets, QtNetwork
SERVER = 'dlg_server'
_tries = 0
def send_data(**data):
socket = QtNetwork.QLocalSocket()
socket.connectToServer(SERVER, QtCore.QIODevice.WriteOnly)
if socket.waitForConnected(500):
socket.write(json.dumps(data).encode('utf-8'))
if not socket.waitForBytesWritten(2000):
raise RuntimeError('could not write to socket: %s' %
socket.errorString())
socket.disconnectFromServer()
elif socket.error() == QtNetwork.QAbstractSocket.HostNotFoundError:
global _tries
if _tries < 10:
if not _tries:
if QtCore.QProcess.startDetached(
'python', [os.path.abspath(__file__)]):
atexit.register(lambda: send_data(shutdown=True))
else:
raise RuntimeError('could not start dialog server')
_tries += 1
QtCore.QThread.msleep(100)
send_data(**data)
else:
raise RuntimeError('could not connect to server: %s' %
socket.errorString())
else:
raise RuntimeError('could not send data: %s' % socket.errorString())
class Server(QtNetwork.QLocalServer):
dataReceived = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.newConnection.connect(self.handleConnection)
if not self.listen(SERVER):
raise RuntimeError(self.errorString())
def handleConnection(self):
data = {}
socket = self.nextPendingConnection()
if socket is not None:
if socket.waitForReadyRead(2000):
data = json.loads(str(socket.readAll().data(), 'utf-8'))
socket.disconnectFromServer()
socket.deleteLater()
if 'shutdown' in data:
self.close()
self.removeServer(self.fullServerName())
QtWidgets.qApp.quit()
else:
self.dataReceived.emit(data)
class Dialog(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setGeometry(50, 50, 200, 30)
layout = QtWidgets.QVBoxLayout(self)
self.label = QtWidgets.QLabel()
layout.addWidget(self.label)
def handleDataReceived(self, data):
self.show()
self.label.setText(data.get('message', ''))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = Dialog()
server = Server()
server.dataReceived.connect(dialog.handleDataReceived)
app.exec_()

Change Button Color in Qt Thread Python

I need to change the color of QPushButton, but an error occurred: "AttributeError: type object 'ProyectoTFM' has no attribute 'ui'".
I don't know hoy to acced to a ui variable from my thread.
This is my code:
import sys
import OpenOPC
import time
import threading
from proyectoQt import *
def actualizarDatosOPC():
while 1:
time.sleep(5)
if(itemsOPC[15])[1]!=0:
#Error on next line
ProyectoTFM.ui.AP08Button.setStyleSheet("background-color: red")
return
class ProyectoTFM(QtGui.QMainWindow):
def __init__(self,parent=None):
QtGui.QMainWindow.__init__(self,parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.startTheThread()
print('Init')
def startTheThread(self):
threadQt = threading.Thread(target = actualizarDatosOPC)
threadQt.start()
def clienteOPC():
opc=OpenOPC.client()
opc.connect('Kepware.KEPServerEX.V6')
global itemsOPC
while 1:
itemsOPC = opc.read(opc.list('PLC.PLC.TAGS'))
time.sleep(5)
return
threads = list()
threadOPC = threading.Thread(target=clienteOPC)
threads.append(threadOPC)
threadOPC.start()
time.sleep(5)
if __name__== "__main__":
app=QtGui.QApplication(sys.argv)
myapp = ProyectoTFM()
myapp.show()
sys.exit(app.exec_())
threadOPC.__delete()
Sorry for my English and thanks.
It is not correct to modify the view from a different thread to the main one, a way to solve the problem without using QThread is to create a signal that connects to some slot that changes the color of the button. To be able to emit the signal from the new thread we must pass the object to him through the parameter args.
def actualizarDatosOPC(obj):
while 1:
time.sleep(5)
if(itemsOPC[15])[1]!=0:
#Error on next line
obj.sendChangeColor.emit()
return
class ProyectoTFM(QtGui.QMainWindow):
sendChangeColor = QtCore.pyqtSignal()
def __init__(self,parent=None):
QtGui.QMainWindow.__init__(self,parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.startTheThread()
print('Init')
self.sendChangeColor.connect(lambda: self.ui.AP08Button.setStyleSheet("background-color: red"))
def startTheThread(self):
threadQt = threading.Thread(target = actualizarDatosOPC, args=(self,))
threadQt.start()
Even if you got this to work, you can't modify the UI from a thread directly.
A few things:
You never actually pass the UI to the function actualizarDatosOPC
so it doesn't know it exists.
Is there any reason you can't use PyQt's built in threading tools? If you are going to use PyQt it might make sense to buy into the whole framework.
def startTheThread(self):
self.threadQt = QThread()
d = actualizarDatosOPC(self)
d.moveToThread(self.threadQt)
self.threadQt.start()
def actualizarDatosOPC(widget):
.... widget.AP08Button.setStyleSheet("background-color: red")
If you do choose to go this route, I'd take a look at this thread which has a good example:
How to use QThread correctly in pyqt with moveToThread()?
Additionally, while the way you initialize your Window works, this is the more standard way to do it:
class ProyectoTFM(QMainWindow, Ui_MainWindow):
def __init__(self, parent):
# General Init Stuff
super(Login, self).__init__(parent)
self.setupUi(self)
After that, whenever you want to refer to something in the UI all you need to do is refer to self._____. For example, if you have a button named buttonA, self.buttonA would be the appropriate reference.
Edit:
As mentioned in another answer, the proper way to actually change the button color would be to emit a trigger that to the main thread which could then respond by changing the button color.

PyQt4 QWebview error after initial evaluateJavaScript() call

I've got a Python 2.7 application running with PyQt4 that has a QWebView in it, that has two way communication to and from Javascript.
The application is multithreaded via QThreadPool, QRunnables, so I'm communicating with a ViewController class with signals.
When I run the application, the QWebView loads my HTML with external JS and CSS just fine. I'm able to interact with the Javascript functions via the main program thread and ViewController class.
Once the user selects a directory and certain criteria are met, it starts looping through QRunnable tasks one at a time. During that time it calls back to the ViewController -> Javascript via Signal slots, just as expected. The problem is when I'm calling those ViewController methods that execute evaluateJavaScript, I get a Javascript error returned,
undefined line 1: SyntaxError: Parse error
I've done lots of trial error back and forth, but can't seem to figure out why evaluateJavaScript won't run in these instances. I've tried sending simple Javascript calls ranging from test functions that don't accept any arguments (thinking maybe it was some weird encoding issue), to just sending things like Application.main.evaluateJavaScript("alert('foo')"), which normally work outside of the threads. The only other thing I can think of is that maybe self.main.addToJavaScriptWindowObject('view', self.view) needs to be called in the threads again, but I've run a dir() on Application.main and it appears to have the evaluateJavaScript method attached to it already.
Any thoughts on why this could be occurring, when the scope seems to be correct, and the ViewController appears to be communicating just fine to the QWebView otherwise? Answers in Qt C++ will probably work as well, if you've seen this happen before!
I tried to simplify the code for example purposes:
# coding: utf8
import subprocess as sp
import os.path, os, sys, time, datetime
from os.path import basename
import glob
import random
import string
from PyQt4 import QtCore, QtGui, QtWebKit
from PyQt4.QtCore import QObject, pyqtSlot, QThreadPool, QRunnable, pyqtSignal
from PyQt4.QtGui import QApplication, QFileDialog
from PyQt4.QtWebKit import QWebView
from ImportController import *
class Browser(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.resize(800,500)
self.centralwidget = QtGui.QWidget(self)
self.mainLayout = QtGui.QHBoxLayout(self.centralwidget)
self.mainLayout.setSpacing(0)
self.mainLayout.setMargin(0)
self.frame = QtGui.QFrame(self.centralwidget)
self.gridLayout = QtGui.QVBoxLayout(self.frame)
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0)
self.html = QtWebKit.QWebView()
# for javascript errors
errors = WebPage()
self.html.setPage(errors)
self.main = self.html.page().mainFrame()
self.gridLayout.addWidget(self.html)
self.mainLayout.addWidget(self.frame)
self.setCentralWidget(self.centralwidget)
path = os.getcwd()
if self.checkNetworkAvailability() and self.checkApiAvailbility():
self.default_url = "file://"+path+"/View/mainView.html"
else:
self.default_url = "file://"+path+"/View/errorView.html"
# load the html view
self.openView()
# controller class that sends and receives to/from javascript
self.view = ViewController()
self.main.addToJavaScriptWindowObject('view', self.view)
# on gui load finish
self.html.loadFinished.connect(self.on_loadFinished)
# to javascript
def selectDirectory(self):
# This evaluates the directory we've selected to make sure it fits the criteria, then parses the XML files
pass
def evaluateDirectory(self, directory):
if not directory:
return False
if os.path.isdir(directory):
return True
else:
return False
#QtCore.pyqtSlot()
def on_loadFinished(self):
# open directory select dialog
self.selectDirectory()
def openView(self):
self.html.load(QtCore.QUrl(self.default_url))
self.html.show()
def checkNetworkAvailability(self):
#TODO: make sure we can reach the outside world before trying anything else
return True
def checkApiAvailbility(self):
#TODO: make sure the API server is alive and responding
return True
class WebPage(QtWebKit.QWebPage):
def javaScriptConsoleMessage(self, msg, line, source):
print '%s line %d: %s' % (source, line, msg)
class ViewController(QObject):
def __init__(self, parent=None):
super(ViewController, self).__init__(parent)
#pyqtSlot()
def did_load(self):
print "View Loaded."
#pyqtSlot()
def selectDirectoryDialog(self):
# FROM JAVASCRIPT: in case they need to re-open the file dialog
Application.selectDirectory()
def prepareImportView(self, displayPath):
# TO JAVASCRIPT: XML directory parsed okay, so let's show the main
Application.main.evaluateJavaScript("prepareImportView('{0}');".format(displayPath))
def generalMessageToView(self, target, message):
# TO JAVASCRIPT: Send a general message to a specific widget target
Application.main.evaluateJavaScript("receiveMessageFromController('{0}', '{1}')".format(target, message))
#pyqtSlot()
def startProductImport(self):
# FROM JAVASCRIPT: Trigger the product import loop, QThreads
print "### view.startProductImport"
position = 1
count = len(Application.data.products)
importTasks = ProductImportQueue(Application.data.products)
importTasks.start()
#pyqtSlot(str)
def updateProductView(self, data):
# TO JAVASCRIPT: Send product information to view
print "### updateProductView "
Application.main.evaluateJavaScript('updateProductView("{0}");'.format(QtCore.QString(data)) )
class WorkerSignals(QObject):
''' Declares the signals that will be broadcast to their connected view methods '''
productResult = pyqtSignal(str)
class ProductImporterTask(QRunnable):
''' This is where the import process will be fired for each loop iteration '''
def __init__(self, product):
super(ProductImporterTask, self).__init__()
self.product = product
self.count = ""
self.position = ""
self.signals = WorkerSignals()
def run(self):
print "### ProductImporterTask worker {0}/{1}".format(self.position, self.count)
# Normally we'd create a dict here, but I'm trying to just send a string for testing purposes
self.signals.productResult.emit(data)
return
class ProductImportQueue(QObject):
''' The synchronous threadpool that is going to one by one run the import threads '''
def __init__(self, products):
super(ProductImportQueue, self).__init__()
self.products = products
self.pool = QThreadPool()
self.pool.setMaxThreadCount(1)
def process_result(self, product):
return
def start(self):
''' Call the product import worker from here, and format it in a predictable way '''
count = len(self.products)
position = 1
for product in self.products:
worker = ProductImporterTask("test")
worker.signals.productResult.connect(Application.view.updateProductView, QtCore.Qt.DirectConnection)
self.pool.start(worker)
position = position + 1
self.pool.waitForDone()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
Application = Browser()
Application.raise_()
Application.show()
Application.activateWindow()
sys.exit(app.exec_())
You know, I love PyQt4 but after searching and searching, I believe this is actually a bug and not as designed.
I've since moved on and am trying to implement this in CEFPython with WxPython, which seems to have a much more elegant implementation for this specific purpose.

Using a QThread in PyQT for serial communication (w. pyserial)

I am pretty much a beginner when it comes to GUI programming.
I am using QT in combination with python bindings (PyQT4).
What I am trying to do:
Setting up a QThread to read from & write to a Serial Port with
pyserial.
The main application should be able to emit new serial data via a
signal to the running QThread. and receive serial data from the
QThread with a signal.
I started my own test implementation based on this code (Link).
Prior to this I read the basics about QThreads and tried to understand how they are intended to be used.
The following test code is what I have come up with. I m sorry, I tried to keep it minmal,
but it is still 75 lines of code:
from PyQt4 import QtCore, QtGui
import time
import sys
class SerialData(QtCore.QObject):
def __init__(self, message):
super(SerialData, self).__init__()
self.__m = message
def getMsg(self):
return self.__m
class SerialCon(QtCore.QObject):
finished = QtCore.pyqtSignal()
received = QtCore.pyqtSignal(SerialData)
def init(self):
super(SerialCon, self).__init__()
# TODO setup serial connection:
# setting up a timer to check periodically for new received serial data
self.timer = QtCore.QTimer()
self.timer.setInterval(400)
self.timer.timeout.connect(self.readData)
self.timer.start(200)
# self.finished.emit()
def readData(self):
self.received.emit(SerialData("New serial data!"))
print "-> serial.readLine() ..."
#QtCore.pyqtSlot(SerialData)
def writeData(self, data):
print "-> serial.write(), ", data.getMsg()
class MyGui(QtGui.QWidget):
serialWrite = QtCore.pyqtSignal(SerialData)
def __init__(self):
super(MyGui, self).__init__()
self.initUI()
def initUI(self):
bSend = QtGui.QPushButton("Send",self)
bSend.clicked.connect(self.sendData)
self.show()
#QtCore.pyqtSlot(SerialData)
def updateData(self, data):
print "Gui:", data.getMsg()
def sendData(self, pressed):
data = SerialData("Send me!")
self.serialWrite.emit(data)
def usingMoveToThread():
app = QtGui.QApplication(sys.argv)
guui = MyGui()
thread = QtCore.QThread()
serialc = SerialCon()
serialc.moveToThread(thread)
# connecting signals to slots
serialc.finished.connect(thread.quit)
guui.serialWrite.connect(serialc.writeData)
serialc.received.connect(guui.updateData)
thread.started.connect(serialc.init)
thread.finished.connect(app.exit)
thread.start()
sys.exit(app.exec_())
if __name__ == "__main__":
usingMoveToThread()
My Problems:
In test code the signal emitted from the SerialCon object (which has
been moved to the QThread) seems to be not received by the
corresponding slot (in MyGui, updateData)
Sooner or later the running test code always causes a Segmentation
fault (core dumped). Which makes me believe that I missed some
important bits.
What could cause this?
Maybe I m taking a completely wrong approach? -
so if you have a better idea how to achieve this, I d be very grateful to hear about it!
Thanks a lot!
At first I was only focussing on the new way, how QThreads should be used since QT4
(Link),
by creating a QObject, and then invoking moveToThread(), pretty much like in my first code sample (at least thats how I understood it).
However I just could not figure out, why I was not able to pass signals from the
QThread to the main application.
As I really needed a fast solution to my problem, I desperately tried varius things.
Here is some second code, that does seem to work the way I wanted:
from PyQt4 import QtCore, QtGui
import time
import sys
import math
class SerialCon(QtCore.QThread):
received = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
QtCore.QThread.__init__(self)
# specify thread context for signals and slots:
# test: comment following line, and run again
self.moveToThread(self)
# timer:
self.timer = QtCore.QTimer()
self.timer.moveToThread(self)
self.timer.setInterval(800)
self.timer.timeout.connect(self.readData)
def run(self):
self.timer.start()
#start eventloop
self.exec_()
def readData(self):
# keeping the thread busy
# figure out if the GUI remains responsive (should be running on a different thread)
result = []
for i in range(1,1000000):
result.append(math.pow(i,0.2)*math.pow(i,0.1)*math.pow(i,0.3))
#
self.received.emit("New serial data!")
#QtCore.pyqtSlot(object)
def writeData(self, data):
#print(self.currentThreadId())
print(data)
class MyGui(QtGui.QWidget):
serialWrite = QtCore.pyqtSignal(object)
def __init__(self, app, parent=None):
self.app = app
super(MyGui, self).__init__(parent)
self.initUI()
def initUI(self):
self.bSend = QtGui.QPushButton("Send",self)
self.bSend.clicked.connect(self.sendData)
self.show()
def closeEvent(self, event):
print("Close.")
self.serialc.quit();
#QtCore.pyqtSlot(object)
def updateData(self, data):
print(data)
def sendData(self, pressed):
self.serialWrite.emit("Send Me! Please?")
def usingMoveToThread(self):
self.serialc = SerialCon()
# binding signals:
self.serialc.received.connect(self.updateData)
self.serialWrite.connect(self.serialc.writeData)
# start thread
self.serialc.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
guui = MyGui(app)
guui.usingMoveToThread()
sys.exit(app.exec_())
I consider it a workaround for now, but it does not really answer the question for me.
Plus, as mentioned in the previously linked QT blog entry,
it is not really the intended way to use the QThread.
So I am still wondering how to get the first code in my question to work as expected.
If you have some ideas about what is wrong with my first code, please let my know!

Categories