I made some received tcp packet dialog by pyqt5.
This code are load dialog, call receive tcp packet in qthread.
I want to send packet data to dialog.
How to send?
It's my code.
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socketserver
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
try:
data = self.rfile.read(28)
# how to send packet data to dialog?
except Exception as e:
print('MyTCPHandler.handle exception error: ', e)
class TestThread(QThread):
HOST, PORT = '192.168.0.100', 8484
def __init__(self, parent=None):
super().__init__()
def receive_packet(self):
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((self.HOST, self.PORT), MyTCPHandler)
server.serve_forever()
def run(self):
print('run thread')
self.receive_packet()
class TestGUI(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.btn1 = QPushButton("start thread", self)
self.textbox1 = QLineEdit(self)
vertBox = QVBoxLayout()
vertBox.addWidget(self.btn1)
vertBox.addWidget(self.textbox1)
self.setLayout(vertBox)
self.setGeometry(700, 500, 300, 100)
self.btn1.clicked.connect(self.threadStart)
self.show()
self.th = TestThread(self)
#pyqtSlot()
def threadStart(self):
self.th.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = TestGUI()
app.exec_()
To send the information of a thread you can use QMetaObject.invokeMethod(), so it is necessary to pass the GUI, and in this case we take advantage that the parent of the QThread is the GUI.
You can set a new property to pass the GUI to the handler, and from the handler access that property through self.server.
Finally, we implemented a slot that receives the information.
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socketserver
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
try:
data = self.rfile.read(28)
QMetaObject.invokeMethod(self.server.w, "setData",
Qt.QueuedConnection, Q_ARG(bytes, data))
except Exception as e:
print('MyTCPHandler.handle exception error: ', e)
class TestThread(QThread):
HOST, PORT = '192.168.0.102', 8000
def __init__(self, parent=None):
super().__init__(parent)
def receive_packet(self):
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((self.HOST, self.PORT), MyTCPHandler)
server.w = self.parent()
server.serve_forever()
def run(self):
print('run thread')
self.receive_packet()
class TestGUI(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.btn1 = QPushButton("start thread", self)
self.textbox1 = QLineEdit(self)
vertBox = QVBoxLayout()
vertBox.addWidget(self.btn1)
vertBox.addWidget(self.textbox1)
self.setLayout(vertBox)
self.setGeometry(700, 500, 300, 100)
self.btn1.clicked.connect(self.threadStart)
self.show()
self.th = TestThread(self)
#pyqtSlot(bytes)
def setData(self, data):
print(data)
#pyqtSlot()
def threadStart(self):
self.th.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = TestGUI()
sys.exit(app.exec_())
#Plus: with signals
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socketserver
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
try:
data = self.rfile.read(28)
self.server.qthread.dataChanged.emit(data)
except Exception as e:
print('MyTCPHandler.handle exception error: ', e)
class TestThread(QThread):
HOST, PORT = '192.168.0.102', 8000
dataChanged = pyqtSignal(bytes)
def __init__(self, parent=None):
super().__init__(parent)
def receive_packet(self):
socketserver.TCPServer.allow_reuse_address = True
server = socketserver.TCPServer((self.HOST, self.PORT), MyTCPHandler)
server.qthread = self
server.serve_forever()
def run(self):
print('run thread')
self.receive_packet()
class TestGUI(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.btn1 = QPushButton("start thread", self)
self.textbox1 = QLineEdit(self)
vertBox = QVBoxLayout()
vertBox.addWidget(self.btn1)
vertBox.addWidget(self.textbox1)
self.setLayout(vertBox)
self.setGeometry(700, 500, 300, 100)
self.btn1.clicked.connect(self.threadStart)
self.show()
self.th = TestThread(self)
self.th.dataChanged.connect(self.setData)
#pyqtSlot(bytes)
def setData(self, data):
print(data)
#pyqtSlot()
def threadStart(self):
self.th.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
form = TestGUI()
sys.exit(app.exec_())
Related
when clicking a button in the PyQt5 Ui iam starting a mitmproxy. When the proxy is started, i try to change a listWidget with Data from the Proxy.
main.py
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
from MainWindow import Ui_MainWindow
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, *args, obj=None, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
self.pushButton.clicked.connect(self.Start)
def Start(self):
x = threading.Thread(target=self.StartMITM)
x.start()
def StartMITM(self):
os.system("mitmweb -s mitmproxy.py -q --no-web-open-browser")
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
mitmproxy.py - this is the error part
from mitmproxy import http
from main import MainWindow
def response(flow):
MainWindow.listWidget.addItem(flow.request.pretty_url)
Can I connect to the Widgets from another File?
It has 2 independent processes: The GUI and the mitmproxy script. And communicating both processes does not imply importing modules since you would be creating another widget, also objects should not be accessed through classes (I recommend you check your basic python notes).
In this the solution is to use some Inter process communication (IPC), in this case you can use Qt Remote Objects (QtRO):
main.py
from functools import cached_property
from PyQt5 import QtCore, QtRemoteObjects, QtWidgets
class Bridge(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
#QtCore.pyqtSlot(str)
def add_message(self, message):
self.messageChanged.emit(message)
class MitmwebManager(QtCore.QObject):
logChanged = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
# self.process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
self.process.readyReadStandardOutput.connect(self.handle_log)
self.process.setProgram("mitmweb")
#cached_property
def process(self):
return QtCore.QProcess()
def start(self, arguments):
self.process.setArguments(arguments)
self.process.start()
def stop(self):
self.process.kill()
def handle_log(self):
data = self.process.readAllStandardOutput()
codec = QtCore.QTextCodec.codecForName("UTF-8")
message = codec.toUnicode(data)
self.logChanged.emit(message)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.button = QtWidgets.QPushButton("Start", checkable=True)
self.listwidget = QtWidgets.QListWidget()
self.logview = QtWidgets.QPlainTextEdit(readOnly=True)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QGridLayout(central_widget)
lay.addWidget(self.button, 0, 0, 1, 2)
lay.addWidget(self.listwidget, 1, 0)
lay.addWidget(self.logview, 1, 1)
self.register_node = QtRemoteObjects.QRemoteObjectRegistryHost(
QtCore.QUrl("local:registry")
)
self.source_node = QtRemoteObjects.QRemoteObjectHost(
QtCore.QUrl("local:replica"), QtCore.QUrl("local:registry")
)
self.source_node.enableRemoting(self.bridge, "bridge")
self.button.toggled.connect(self.handle_toggled)
self.mitmweb.logChanged.connect(self.handle_log_changed)
self.bridge.messageChanged.connect(self.handle_message_changed)
self.resize(640, 480)
#cached_property
def mitmweb(self):
return MitmwebManager()
#cached_property
def bridge(self):
return Bridge()
def handle_toggled(self, checked):
if checked:
self.mitmweb.start(["-s", "script.py", "--no-web-open-browser"])
self.button.setText("Stop")
else:
self.mitmweb.stop()
self.button.setText("Start")
def handle_log_changed(self, message):
self.logview.insertPlainText(message)
def closeEvent(self, event):
super().closeEvent(event)
self.mitmweb.stop()
def handle_message_changed(self, message):
item = QtWidgets.QListWidgetItem(message)
self.listwidget.addItem(item)
def main():
app = QtWidgets.QApplication([])
view = MainWindow()
view.show()
app.exec_()
if __name__ == "__main__":
main()
script.py
from mitmproxy import http
from PyQt5 import QtCore, QtRemoteObjects
class Bridge(QtCore.QObject):
def __init__(self, bridge, parent=None):
super().__init__(parent)
self._message = ""
self._bridge = bridge
self.bridge.initialized.connect(self.handle_initialized)
#property
def bridge(self):
return self._bridge
#property
def message(self):
return self._message
def send_message(self, message):
self._message = message
#QtCore.pyqtSlot()
def handle_initialized(self):
self.bridge.add_message(self._message)
QtCore.QTimer.singleShot(0, QtCore.QCoreApplication.quit)
def send_qt(message):
qt_app = QtCore.QCoreApplication([])
replica_node = QtRemoteObjects.QRemoteObjectNode(QtCore.QUrl("local:registry"))
replica_bridge = replica_node.acquireDynamic("bridge")
bridge = Bridge(replica_bridge)
bridge.send_message(message)
qt_app.exec_()
def response(flow):
send_qt(flow.request.pretty_url)
The simple idea is that user inputs duration in seconds, and presses a PyQt button, that calls a function that creates a python subprocess and runs windump via it. Then time sleep is used to wait for user defined duration and then process.terminate(), terminates it (code below)
def windump_exec(duration):
p = s.Popen(['windump', '-i', '3', '-w', 'packets.pcap'], stdout=s.PIPE)
time.sleep(duration)
p.terminate()
Now once this is done, scapy reads .pcap file and I show stuff on the screen in short. While this is happening QWaitingSpinner is running, and to handle this I run the above logic (including scapy) using QRunnable (code below)
class ThreadRunnable(QRunnable):
def __init__(self, _time, filler):
QRunnable.__init__(self)
self.time = _time
self.filler = filler
self.signal = RunnableSignal()
def run(self):
windump_exec(self.time)
packets = parse_data()
self.filler(packets)
self.signal.result.emit()
The Problem is that the windump code works fine on it's own, but inside the QThread it doesn't create an output file and hence scapy has nothing to read (open), and it gives error.
Instead of using Popen with QThread you can use QProcess, in my test I have used tcpdump but I suppose that changing to windump should have the same behavior:
import os
from PyQt5 import QtCore, QtGui, QtWidgets
from scapy.all import rdpcap
import psutil
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class DumpProcesor(QtCore.QObject):
started = QtCore.pyqtSignal()
finished = QtCore.pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self._process = QtCore.QProcess()
self._timer = QtCore.QTimer(singleShot=True)
self._timer.timeout.connect(self.handle_timeout)
self._pid = -1
#property
def process(self):
return self._process
#property
def timer(self):
return self._timer
#QtCore.pyqtSlot()
def start(self):
self.started.emit()
status, self._pid = self._process.startDetached()
if status:
self._timer.start()
else:
self.finished.emit()
#QtCore.pyqtSlot()
def handle_timeout(self):
if self._pid > 0:
p = psutil.Process(self._pid)
p.terminate()
QtCore.QTimer.singleShot(100, self.finished.emit)
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.log_te = QtWidgets.QTextEdit(readOnly=True)
self.time_sb = QtWidgets.QSpinBox(minimum=1)
self.start_btn = QtWidgets.QPushButton(self.tr("Start"))
grid_layout = QtWidgets.QGridLayout(self)
grid_layout.addWidget(self.log_te, 0, 0, 1, 3)
grid_layout.addWidget(QtWidgets.QLabel("Time (seg):"), 1, 0)
grid_layout.addWidget(self.time_sb, 1, 1)
grid_layout.addWidget(self.start_btn, 1, 2)
self.dump_procesor = DumpProcesor(self)
self.dump_procesor.process.setProgram("tcpdump")
filename = os.path.join(CURRENT_DIR, "packets.pcap")
self.dump_procesor.process.setArguments(["-i", "3", "-w", filename])
self.start_btn.clicked.connect(self.start)
self.dump_procesor.finished.connect(self.on_finished)
#QtCore.pyqtSlot()
def start(self):
self.log_te.clear()
self.start_btn.setDisabled(True)
self.dump_procesor.timer.setInterval(self.time_sb.value() * 1000)
self.dump_procesor.start()
#QtCore.pyqtSlot()
def on_finished(self):
self.start_btn.setDisabled(False)
filename = os.path.join(CURRENT_DIR, "packets.pcap")
packets = rdpcap(filename)
for packet in packets:
t = packet.show(dump=True)
self.log_te.append(t)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
i want to create QLabels dynamically at runtime, and change the Text afterwards.
I did it like that:
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.counter = 0
self.items = self.read_hosts()
self.layout = QGridLayout()
for item in self.items:
self.item = QLabel(item, self)
self.item.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.item.setAlignment(Qt.AlignCenter)
self.item.setStyleSheet("QLabel {background-color: green;}")
self.layout.addWidget(self.item,self.counter, 0)
self.counter += 1
self.setLayout(self.layout)
self.startWorker()
self.show()
def change_txt(self, lb, i):
self.item.setText("{}".format(i))
It won't work.
I understand why it would change just the text of the last label. I'm doing something wrong during assignment.
How can I create all labels completely variably and subsequently change the texts?
I am using:
PyQT5
on Windows 10
Thanks!
Here is my whole code:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import os
class Worker(QObject):
update = pyqtSignal(str,int)
exception = pyqtSignal(str)
def read_hosts(self):
#try:
file = open("hosts.ini", "r")
return file.readlines()
#except Exception as ex:
#self.exception.emit(str(ex))
def check_ping(self):
#try:
hosts = self.read_hosts()
while True:
for host in hosts:
print(host)
params = " -l 1000"
response = os.system("ping " + host + params)
print("weiter")
self.update.emit(host, response)
#except Exception as ex:
#self.exception.emit(str(ex))
class Window(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.counter = 0
self.items = self.read_hosts()
self.layout = QGridLayout()
for item in self.items:
self.item = QLabel(item, self)
self.item.setObjectName("label" + str(self.counter))
print("label" + str(self.counter))
self.item.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.item.setAlignment(Qt.AlignCenter)
self.item.setStyleSheet("QLabel {background-color: green;}")
self.layout.addWidget(self.item,self.counter, 0)
self.counter += 1
self.setLayout(self.layout)
self.startWorker()
self.show()
def startWorker(self):
self.thread = QThread()
self.obj = Worker()
self.thread = QThread()
self.obj.moveToThread(self.thread)
self.obj.update.connect(self.onUpdate)
self.obj.exception.connect(self.onException)
self.thread.started.connect(self.obj.check_ping)
self.thread.start()
def read_hosts(self):
#try:
file = open("hosts.ini", "r")
return file.readlines()
#except Exception as ex:
#self.exception.emit(str(ex))
def onException(self, msg):
QMessageBox.warning(self, "Eine Exception im Worker wurde geworfen: ", msg)
def onUpdate(self, lb, i):
label = lb
self.label0.setText("{}".format(i))
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
hosts.ini:
192.168.1.1
192.168.1.2
192.168.1.30
I have improved your code in the following aspects:
If your layout has a column it is not necessary to use QGridLayout, just QVBoxLayout.
Having a read_hosts() method for each class is a waste, so I've created a unique function.
self.item is an attribute of the class that is continually being overwritten so it is not necessary to create them.
The objectName should be the name of the host, that is, the IP, since that is the information that the thread has.
To find the label through the objectName you can use findChild().
import sys
import os
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
def read_hosts():
file = open("hosts.ini", "r")
return file.readlines()
class Worker(QObject):
update = pyqtSignal(str, int)
exception = pyqtSignal(str)
def check_ping(self):
hosts = read_hosts()
while True:
for host in hosts:
params = "-l 1000"
response = os.system("ping {} {}".format(host, params))
print("weiter")
self.update.emit(host, response)
class Window(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.layout = QVBoxLayout(self)
hosts = read_hosts()
for host in hosts:
label = QLabel(host)
label.setObjectName(host)
label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
label.setAlignment(Qt.AlignCenter)
label.setStyleSheet("QLabel {background-color: green;}")
self.layout.addWidget(label)
self.startWorker()
def startWorker(self):
self.thread = QThread()
self.obj = Worker()
self.thread = QThread()
self.obj.moveToThread(self.thread)
self.obj.update.connect(self.onUpdate)
self.obj.exception.connect(self.onException)
self.thread.started.connect(self.obj.check_ping)
self.thread.start()
def onException(self, msg):
QMessageBox.warning(self, "Eine Exception im Worker wurde geworfen: ", msg)
def onUpdate(self, host, value):
label = self.findChild(QLabel, host)
label.setText("{}".format(value))
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
Can Anyone help me with this code? How can i add P2P chat code to this program?
I want to be able to chat between two PCs over a LAN
import sys, os
import socket
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget,
QLineEdit, QFileDialog, QInputDialog, QFileDialog,QMessageBox, QMenuBar)
from PyQt5.QtGui import QIcon
from PyQt5.Qt import QPushButton, QTextEdit, QMessageBox
class myApp (QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.main_text = QtWidgets.QTextEdit(self)
self.send_box = QtWidgets.QLineEdit(self)
self.connect_btn = QtWidgets.QPushButton('Connect', self)
self.browse_btn = QtWidgets.QPushButton('Browse',self)
ip_add_box = QtWidgets.QLineEdit(self)
port_box = QtWidgets.QLineEdit(self)
self.main_text.setGeometry(25, 30, 360, 350)
self.send_box.setGeometry(25, 400, 360, 60)
ip_add_box.setGeometry(25, 530, 150, 30)
port_box.setGeometry(180, 530, 90,30)
self.connect_btn.setGeometry(25, 500, 70, 25)
self.browse_btn.setGeometry(100,500,70,25)
self.setGeometry(200,100,400,580)
self.setWindowTitle('My Application')
self.setWindowIcon(QIcon('icon.png'))
# CODE BLOCK FOR EVENTS
self.send_box.returnPressed.connect(self.sendData) # EnterPressed Event for SendBox LineEdit
self.connect_btn.clicked.connect(self.connectNet) # Socket Connect event for the connect Button
self.browse_btn.clicked.connect(self.BrowseFile) # Browse file connect event for Browse Button
self.show()
def sendData(self):
self.main_text.append( self.send_box.text() )
self.send_box.clear()
def connectNet(self):
host = 'localhost'
port = 12345
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (host, port)
print ("Connecting to %s port %s" % server_address)
sock.connect(server_address)
try:
message = "Test message. This will be echoed"
print ("Sending %s" % message)
sock.sendall(message)
# Look for the response
amount_received = 0
amount_expected = len(message)
while amount_received < amount_expected:
data = sock.recv(16)
amount_received += len(data)
print ("Received: %s" % data)
finally:
print ("Closing connection to the server")
sock.close()
def BrowseFile (self):
filePath = QtWidgets.QFileDialog.getOpenFileName(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = myApp()
sys.exit(app.exec_())
Finally fixed this. Client side is here
from PyQt5 import QtCore, QtWidgets, QtWidgets, QtNetwork
class Client(QtWidgets.QDialog):
def __init__(self, parent=None):
super(Client, self).__init__(parent)
self.main_text = QtWidgets.QTextEdit(self)
self.main_text.setReadOnly(True)
self.send_box = QtWidgets.QLineEdit(self)
self.send_button = QtWidgets.QPushButton("Send", self)
self.browse_btn = QtWidgets.QPushButton("Browse", self)
# EVENT FOR SEND BUTTON
self.send_button.clicked.connect(self.sendData)
self.server_ip = QtWidgets.QLineEdit(self)
self.server_ip.setPlaceholderText("IP address")
self.port_address = QtWidgets.QLineEdit(self)
self.port_address.setPlaceholderText("port")
self.userName = QtWidgets.QLineEdit(self)
self.userName.setPlaceholderText("username")
self.connectButton = QtWidgets.QPushButton("Connect",self)
#Connect Button EVENT
self.connectButton.clicked.connect(self.connection)
self.connectButton.setDefault(True)
self.setGeometry(400,50,500,600)
self.main_text.setGeometry(25,50,450,400)
self.send_box.setGeometry(25,470,450,40)
self.send_button.setGeometry(385,520,90,30)
self.browse_btn.setGeometry(25,520,100,30)
self.server_ip.setGeometry(150,10,130,25)
self.port_address.setGeometry(300,10,70,25)
self.connectButton.setGeometry(385,10,90,25)
self.userName.setGeometry(25,10,120,25)
#NetworkInput
self.socket = QtNetwork.QTcpSocket(self)
self.socket.readyRead.connect(self.readData)
self.socket.error.connect(self.displayError)
def closeEvent(self, event):
self.socket.disconnectFromHost()
def connection(self):
self.socket.connectToHost(self.server_ip.text(), int(self.port_address.text()))
if self.socket.waitForConnected(1000):
self.user = self.userName.text()
self.send("login %s" % self.user)
self.connectButton.setEnabled(False)
self.send_button.setDefault(True)
self.send_box.setFocus()
#self.setWindowTitle("<%s>" % self.user)
def readData(self):
message = self.socket.readLine().data().decode("utf-8")
self.main_text.append(message)
def send(self, message):
self.socket.write(message.encode("utf-8"))
def sendData(self):
message = "say %s" % (self.send_box.text())
self.send(message)
self.send_box.clear()
self.send_box.setFocus()
def displayError(self):
QtWidgets.QMessageBox.information(self, "Connection", "Connection error")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
client = Client()
sys.exit(client.exec_())
I'm trying to write a pyqt5 application with a long running, but not CPU intensive process. I'd like to be able to run it without hanging the UI, so I'm trying to use threading, but since it doesn't seem like I can just run a thread and have it stop after its gone through its code so that it can be run again, I've tried setting up the thread to wait for a variable to change before running.
I know this can't be the correct pattern for running long processes in a pyqt app.
import time
import threading
from PyQt5 import QtWidgets, uic
class MyApp(QtWidgets.QMainWindow):
_run_thread = False
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = uic.loadUi('myapp.ui', self)
self.ui.start_thread_button.clicked.connect(self._run_thread_function)
self._thread = threading.Thread(target=self._run_thread_callback)
self._thread.daemon = True
self._thread.start()
self.ui.show()
def _run_thread_callback(self):
while True:
if self._run_thread:
print("running thread code...")
time.sleep(10)
print("thread code finished")
self._run_thread = False
def _run_thread_function(self):
print("starting thread...")
self._run_thread = True
def main():
app = QtWidgets.QApplication(sys.argv)
MyApp()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Below is a simple demo showing how to start and stop a worker thread, and safely comminucate with the gui thread.
import sys
from PyQt5 import QtCore, QtWidgets
class Worker(QtCore.QThread):
dataSent = QtCore.pyqtSignal(dict)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self._stopped = True
self._mutex = QtCore.QMutex()
def stop(self):
self._mutex.lock()
self._stopped = True
self._mutex.unlock()
def run(self):
self._stopped = False
for count in range(10):
if self._stopped:
break
self.sleep(1)
data = {
'message':'running %d [%d]' % (
count, QtCore.QThread.currentThreadId()),
'time': QtCore.QTime.currentTime(),
'items': [1, 2, 3],
}
self.dataSent.emit(data)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.edit = QtWidgets.QPlainTextEdit()
self.edit.setReadOnly(True)
self.button = QtWidgets.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.edit)
layout.addWidget(self.button)
self._worker = Worker()
self._worker.started.connect(self.handleThreadStarted)
self._worker.finished.connect(self.handleThreadFinished)
self._worker.dataSent.connect(self.handleDataSent)
def handleThreadStarted(self):
self.edit.clear()
self.button.setText('Stop')
self.edit.appendPlainText('started')
def handleThreadFinished(self):
self.button.setText('Start')
self.edit.appendPlainText('stopped')
def handleDataSent(self, data):
self.edit.appendPlainText('message [%d]' %
QtCore.QThread.currentThreadId())
self.edit.appendPlainText(data['message'])
self.edit.appendPlainText(data['time'].toString())
self.edit.appendPlainText(repr(data['items']))
def handleButton(self):
if self._worker.isRunning():
self._worker.stop()
else:
self._worker.start()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 100, 400, 400)
window.show()
sys.exit(app.exec_())