简体   繁体   中英

PyQt5 : Destroyed while thread is still running

Tried to add exceptions, what should the program (client) do when the server is disconnected.

The idea is simple. If the server is turned off - the client should try to connect to the server until the server is turned on.

When server is turn off - program try to connect. But when I turned server on, client is connected, and after that connection drops, and in client app I saw that - QThread: Destroyed while thread is still running This is simple example of what I want (minimal reproducible example for client):

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import socket


class ListeningThread(QtCore.QThread):
    mysignal = QtCore.pyqtSignal(str)
    def __init__(self, server_socket, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.server_socket = server_socket
        self.message = None

    def run(self):
        try:
            while True:
                    self.message = self.server_socket.recv(4096)
                    self.mysignal.emit(self.message.decode('utf-8'))
        except:
             Push()

class Push(QtWidgets.QMainWindow):
    def __init__(self):
        super(Push, self).__init__()
        print('now connecting...')
        self.connect_server()
    def connect_server(self):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            self.server_socket.connect(('127.0.0.1', 5555))
            self.message_monitor = ListeningThread(self.server_socket)
            self.message_monitor.mysignal.connect(self.init_UI)
            self.message_monitor.start()
        except:
            Push()

    def init_UI(self, message):
        print(message)

app =QtWidgets.QApplication([])
application = Push()
sys.exit(app.exec_())

I tried that in ListeningThread:

 class ListeningThread(QtCore.QThread):
    mysignal = QtCore.pyqtSignal(str)
    def __init__(self, server_socket, parent=None):
        QtCore.QThread.__init__(self, parent)
        self.server_socket = server_socket
        self.message = None

    def run(self):
        try:
            while not self.isInterruptionRequested():
                    self.message = self.server_socket.recv(4096)
                    self.mysignal.emit(self.message.decode('utf-8'))
        except:
            print('error in thread')
            self.requestInterruption()
            self.wait()
            Push()

But the problem is still the same. I think I should close the thread before it starts in Push.connect_server but idk how.

For minimal reproducible example u can use that server:

import socket
import threading

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('127.0.0.1', 5555))
server_socket.listen()
clients = []

def accept_socket():
    while True:
        client_socket, addr = server_socket.accept()
        print(f'connection from {addr}')
        print(client_socket)
        if client_socket not in clients:
            clients.append(client_socket)

def sending_message(clients, data):
    for client_socket in clients:
        try:
            client_socket.send(data.encode("utf-8"))
        except:
            pass

accept_thread = threading.Thread(target= accept_socket)
accept_thread.start()


while True:
    data = input('Enter_message:\n')
    sending_message(clients, data)

The problem is that when an exception occurs you are creating a new "Push" object that has a limited scope since it is a local variable so it will be destroyed, and objects such as the thread will also be destroyed, and that is what it indicates. the error message.

Instead of complicating your life with the handling of threads (IMO they are the last option) you can use a QTcpSocket that allows to handle the sockets in a simple way through signals and that uses the Qt eventloop.

from functools import cached_property
import sys

from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork


class Client(QtCore.QObject):
    messageChanged = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.socket.stateChanged.connect(self.handle_state_changed)
        self.socket.errorOccurred.connect(self.handle_error_occurred)
        self.socket.readyRead.connect(self.handle_ready_read)

    @cached_property
    def socket(self):
        return QtNetwork.QTcpSocket()

    def try_connect(self):
        self.socket.connectToHost("127.0.0.1", 5555)

    def handle_state_changed(self, state):
        print(f"state: {state}")
        if state == QtNetwork.QAbstractSocket.UnconnectedState:
            print("disconnected")
            QtCore.QTimer.singleShot(1000, self.try_connect)
        elif state == QtNetwork.QAbstractSocket.ConnectedState:
            print("connected")

    def handle_error_occurred(self, error):
        print(f"error code {error}, message: {self.socket.errorString()}")

    def handle_ready_read(self):
        codec = QtCore.QTextCodec.codecForName("UTF-8")
        message = codec.toUnicode(self.socket.readAll())
        self.messageChanged.emit(message)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.client.messageChanged.connect(self.handle_message_changed)
        self.client.try_connect()

    @cached_property
    def client(self):
        return Client()

    def handle_message_changed(self, message):
        print(f"client message: {message}")


def main():

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM