QThread: Threading with GUI python - python

I am making mini chat application to improve my socket and GUI skills in Python. But my QThread is dumping every time when I'm launching application.
I need to run Thread with GUI
Here it is client.py
import socket
import sys
from colorama import Fore, Back, Style
from os import system, name
import sys
from chat import Ui_Form as Ui_Form
from login import Ui_Form as Ui_Form1
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
app = QtWidgets.QApplication(sys.argv)
Form_login = QtWidgets.QWidget()
ui_login = Ui_Form1()
ui_login.setupUi(Form_login)
Form_login.show()
Form_chat = QtWidgets.QWidget()
ui_chat = Ui_Form()
ui_chat.setupUi(Form_chat)
history = ''
class listen_thread(QtCore.QObject):
running = False
listen_var = QtCore.pyqtSignal(str)
def run():
print('checkpoint')
while True:
listen_var.emit('message')
QtCore.QThread.msleep(1000)
def connect_pressed():
username = ui_login.lineEdit.text()
Form_login.hide()
Form_chat.show()
sock.connect(('127.0.0.1', 10000))
thread = QtCore.QThread()
listen1 = listen_thread()
listen1.moveToThread(thread)
listen1.listen_var.connect(update_chat_history)
thread.started.connect(listen1.run)
thread.start()
#QtCore.pyqtSlot(str)
def update_chat_history(message):
print(message)
ui_chat.textEdit_2.append(message)
def send_pressed():
message = ui_login.lineEdit.text() + ' > ' + ui_chat.lineEdit.text()
sock.send(bytes(str(message),'utf-8'))
# update_chat_history(message)
ui_chat.lineEdit.setText('')
def listen(some):
while True:
try:
data = sock.recv(1024)
except:
pass
else:
update_chat_history(str(data))
ui_login.pushButton.clicked.connect(connect_pressed)
ui_chat.pushButton.clicked.connect(send_pressed)
sys.exit(app.exec_())
When I'm launching it the output is:
QThread: Destroyed while thread is still running
Aborted (core dumped)
Can somebody help???

The explanation of your problem is simple: the thread created in connect_pressed has no reference outside that function, so it gets immediately garbage collected as soon as the function returns.
The "simple" solution would be to create the thread outside of that function and run the thread only there, or add the thread to a container (for instance, a list):
threads = []
def connect_pressed():
# ...
threads.append(thread)
But, the reality, is that your code has other serious issues, which would probably create other problems as soon as you try to expand your code even minimally, and that's also because Qt is easier to deal with when implementing the program using subclasses; even the pyqtSlot decorator (which is normally unnecessary) can only correctly work if it's set on a method of a QObject subclass.
Using only anonymous functions is usually discouraged, as they cannot have a direct reference to instances they must work with. Also, your run() function is wrong, as it doesn't have the self argument, which will result in a crash.
A better version of your code could be similar to this:
class LoginWindow(QtWidgets.QWidget, Ui_Form1):
def __init__(self, listenThread):
super().__init__()
self.setupUi(self)
self.pushButton.clicked.connect(listenThread.start)
class ChatWindow(QtWidgets.QWidget, Ui_Form):
def __init__(self, listenThread, loginWindow):
super().__init__()
self.listenThread = listenThread
self.loginWindow = loginWindow
self.setupUi(self)
self.listenThread.listen_var.connect(self.update_chat_history)
def update_chat_history(self, message):
print(message)
self.textEdit_2.append(message)
def send_pressed(self):
message = self.loginWindow.lineEdit.text() + ' > ' + self.lineEdit.text()
sock.send(bytes(str(message),'utf-8'))
self.lineEdit.setText('')
class ListenThread(QtCore.QThread):
listen_var = QtCore.pyqtSignal(str)
def run(self):
print('checkpoint')
while True:
listen_var.emit('message')
QtCore.QThread.msleep(1000)
listenThread = ListenThread()
loginWindow = LoginWindow(listenThread)
chatWindow = ChatWindow(listenThread, loginWindow)
sys.exit(app.exec_())

Related

PyQt5 black window while threads are working

I am making window application in PyQt5 and I want to parse data from XML file in the backgrond and send it to second class. I want to use threads with queue to handle between them. When I want to display window app i see black window. It would be nice to use python threads but i tried to do it on QThread and it is not working too idk why...
This is code example
import queue
import sys
import threading
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import *
class Test_generator:
def __init__(self, queue_obj):
super().__init__()
self.queue_obj = queue_obj
#self.parser_thread = threading.Thread(target=self.parser())
def sender(self):
for _ in range(100):
string ="test"
self.queue_obj.put(string)# send to queue
time.sleep(1)
#print(string)
class Test_collectioner:
def __init__(self,queue_obj):
self.queue_obj = queue_obj
def get_data(self):
collection = []
while self.queue_obj.empty() is False:
print("xd")
print(self.queue_obj.get(), "xd")
#I found this example in the internet(Not working)
class Threaded(QThread):
result = pyqtSignal(int)
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
#pyqtSlot(int)
def run(self):
while True:
print("test")
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.GUI()
self.setWindowTitle("PyQt5 app")
global q
q = queue.Queue()
# BLACKSCREEN (for Test_generator)
test_generator_obj = Test_generator(q)
test_collectioner_obj = Test_collectioner(q)
t1 = threading.Thread(target=test_generator_obj.sender())
t2 = threading.Thread(target=test_collectioner_obj.get_data())
t1.start()
t2.start()
# BLACKSCREEN TOO
"""self.thread = QThread()
self.threaded = Threaded()
self.thread.started.connect(self.threaded.run())
self.threaded.moveToThread(self.thread)
qApp.aboutToQuit.connect(self.thread.quit)
self.thread.start()"""
def GUI(self):
self.showMaximized()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
There are two main problems in your code:
you're executing the methods used for the thread, but you should use the callable object instead; you did the same mistake for the QThread, since connections to signals also expects a callable, but you're actually executing the run function from the main thread (completely blocking everything), since you're using the parentheses; those lines should be like this:
t1 = threading.Thread(target=test_generator_obj.sender)
t2 = threading.Thread(target=test_collectioner_obj.get_data)
or, for the QThread:
self.thread.started.connect(self.threaded.run)
due to the python GIL, multithreading only releases control to the other threads whenever it allows to, but the while cycle you're using prevents that; adding a small sleep function ensures that control is periodically returned to the main thread;
Other problems also exist:
you're already using a subclass of QThread, so there's no use in using another QThread and move your subclass to it;
even assuming that an object is moved to another thread, the slot should not be decorated with arguments, since the started signal of a QThread doesn't have any; also consider that slot decorators are rarely required;
the thread quit() only stops the event loop of the thread, but if run is overridden no event loop is actually started; if you want to stop a thread with a run implementation, a running flag should be used instead; in any other situation, quitting the application is normally enough to stop everything;
Note that if you want to interact with the UI, you can only use QThread and custom signals, and basic python threading doesn't provide a suitable mechanism.
class Threaded(QThread):
result = pyqtSignal(int)
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
def run(self):
self.keepGoing = True
while self.keepGoing:
print("test")
self.msleep(1)
def stop(self):
self.keepGoing = False
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.threaded = Threaded()
self.threaded.start()
qApp.aboutToQuit.connect(self.threaded.stop)

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.

Get notification when QThreadPool of QRunners in PyQt4 have completed execution

I've got this quick and dirty test I setup where I'm running a thread of QRunners in a QThreadPool one by one in PyQt4 on Python 2.7. Basically it looks like it's running fine, but the threads/pool don't seem to stop the QThreadPool once all threads have completed execution.
I'm wondering if it's as simple as returning some built in method from the QRunnable (ProductImporter here) when it's done executing it's code, but I can't seem to find anything in the documentation.
I'm moving some code over to this setup from a QThread structure, since I can't have concurrent threads running at the same time.
Any ideas on how I could have the ProductImportTasks aware that it's tasks have all completed, so I can execute more code afterwards, or after that class is called? Any help would be greatly appreciated!
import sys
from PyQt4.QtGui import QApplication
from PyQt4.QtCore import QThreadPool, QObject, QRunnable, pyqtSignal
class WorkerSignals(QObject):
productResult = pyqtSignal(str)
class ProductImporter(QRunnable):
def __init__(self, product):
super(ProductImporter, self).__init__()
self.product = product
self.signals = WorkerSignals()
def run(self):
self.signals.productResult.emit(self.product['name'])
return
class ProductImportTasks(QObject):
def __init__(self, products):
super(ProductImportTasks, self).__init__()
self.products = products
self.pool = QThreadPool()
self.pool.setMaxThreadCount(1)
def process_result(self, product):
return
def start(self):
for product in self.products:
worker = ProductImporter(product)
worker.signals.productResult.connect(view.text)
self.pool.start(worker)
self.pool.waitForDone()
class ViewController(QObject):
def __init__(self, parent=None):
super(ViewController, self).__init__(parent)
##pyqtSlot(str)
def text(self, message):
print "This is the view.text method: " + message
return
if __name__ == "__main__":
app = QApplication(sys.argv)
view = ViewController()
main = ProductImportTasks([{"name": "test1"}, {"name": "test2"}, {"name": "test3"}])
main.start()
sys.exit(app.exec_())
Here's what your script does:
calls main.start()
creates runnables and starts threadpool
waits for all the runnables to finish
returns from main.start()
starts the application event-loop
Once the event-loop has started, the signals that were emitted by the runnables will be processed, and the messages will be printed. This is because signals sent across thread are queued by default. Normally, signals are sent synchronously, and don't require a running event loop.
If you change the connection type of the signals, and add a few print statements, it should be clear what's going on:
worker.signals.productResult.connect(view.text, Qt.DirectConnection)
self.pool.start(worker)
self.pool.waitForDone()
print('finished')
...
main.start()
print('exec')
sys.exit(app.exec_())

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!

PyQt GUI control access from another thread

i'm trying to create an client server application in python. When the server closes i wish the gui of the client wich is on a separate thread to close, but the application crushes with Xlib error: bad implementation... I've searched and seems to be from accessing GUI interface from other thread. What should I do?python gui access from other thread
this might help you..
from PyQt4 import QtGui as gui
from PyQt4 import QtCore as core
import sys
import time
class ServerThread(core.QThread):
def __init__(self, parent=None):
core.QThread.__init__(self)
def start_server(self):
for i in range(1,6):
time.sleep(1)
self.emit(core.SIGNAL("dosomething(QString)"), str(i))
def run(self):
self.start_server()
class MainApp(gui.QWidget):
def __init__(self, parent=None):
super(MainApp,self).__init__(parent)
self.label = gui.QLabel("hello world!!")
layout = gui.QHBoxLayout(self)
layout.addWidget(self.label)
self.thread = ServerThread()
self.thread.start()
self.connect(self.thread, core.SIGNAL("dosomething(QString)"), self.doing)
def doing(self, i):
self.label.setText(i)
if i == "5":
self.destroy(self, destroyWindow =True, destroySubWindows = True)
sys.exit()
app = gui.QApplication(sys.argv)
form = MainApp()
form.show()
app.exec_()

Categories