【问题标题】:Using QLineEdit.setText() freezes the window but background tasks works fine使用 QLineEdit.setText() 冻结窗口,但后台任务工作正常
【发布时间】:2018-10-29 00:45:11
【问题描述】:

我正在尝试创建一个聊天室程序作为一个休闲项目,以更好地理解 PyQt5 和多线程,但我面临着相当奇怪的问题。在 sWindow 类中,在 createserver() 函数中,setText() try 块中的显示小部件中的 em> 冻结屏幕,但后台进程工作正常。 except 块中的 setText 函数工作正常。如果我尝试将语句打印到 cmd,它会完美运行。

这是我遇到问题的代码sn-p:

def createServer(self):
    try:
        self.display.setText("Creating server") #doesn't display text, whole screen freezes
        print("Creating Server") #works fine
        self.s.bind((self.host, self.port))
        self.s.listen(10)
        print("Server Created") #works fine
        self.display.append("Server Created.") #doesn't print anything
        self.display.append("Started listening to clients")
        self.Listen() #goes into the listen function as well without display widget printing anything
    except Exception as e:
        print(e)
        self.display.setText("Error occured")

这是整个代码:

class sWindow(QMainWindow):
def __init__(self, title = "File Sharing", l = 0, t = 0, r = 800, b = 600):
    super().__init__()
    self.left = l
    self.right = r
    self.top = t
    self.bottom = b
    self.title = title
    self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    self.host = ""
    self.port = 15478
    self.ip_add = socket.gethostbyname(socket.getfqdn())
    self.initUi()
    self.shutting = False
    self.mode = 0
    self.address = {}
    self.clientaddr = {}
    self.buffsize = 2048
    self.encoding = 'utf8'




def closeEvent(self, event):
    reply = QMessageBox.question(self, "Message Box", "Are you sure?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
    if reply == QMessageBox.Yes:
        self.shutting = True
        self.s.close()
        event.accept()
    else:
        event.ignore()


def createServer(self):
    try:
        self.display.setText("Creating server")
        print("Creating Server")
        self.s.bind((self.host, self.port))
        self.s.listen(10)
        print("Server Created")
        self.display.append("Server Created.")
        self.display.append("Started listening to clients")
        self.Listen()
    except Exception as e:
        print(e)
        self.display.setText("Error occured")


def Listen(self):
    try:
        print("started listenting")
        while self.shutting == False:
            client, addr = self.s.accept()
            self.address[client] = addr
            message = "Enter your name: "
            client.send(message.encode(encoding))
            threading.Thread(target = handleclient, args = (client,)).start()
    except Exception as e:
        print('Error occured while creating server. ',e)



def handleclient(self, client):
    try: 
        name = client.recv(buffsize).decode(encoding)
        self.display.append(name + " connected from address ", addr)
        self.clientaddr[client] = name
        message = "Welcome " + name + "!! You have entered the chatroom."
        client.send(message.encode(encoding))
        message = name + "has entered the chatroom. "
        self.broadcast(message, client)
        while self.shutting == False:
            data = client.recv(buffsize).decode(encoding)
            self.display.append(name + ': ' + data)
            self.broadcast(data, client)


    except Exception as e:
        del self.address[client]
        del self.clientaddr[client]
        self.display.append('Error occured while connecting to client. ',e)
        message = name + " entered the chatroom."
        self.broadcast(message.encode(encoding), False)
        return


def broadcast(self, mesage, client):
    try:
        for sock in self.address.keys():
            if sock != client:
                sock.send(message)
    except Exception as e:
        self.display.append("Error occured while broadcasting the message to clients. ", e)
        print(e)
        return




def centr(self):
    qr = self.frameGeometry()
    cr = QDesktopWidget().availableGeometry().center()
    qr.moveCenter(cr)
    self.move(qr.topLeft())

def initUi(self):
    ## For window

    self.setGeometry(self.left, self.top, self.right, self.bottom)
    self.centr()
    self.setWindowTitle(self.title)
    self.setWindowIcon(QIcon("fSharing.jpg"))

    ## For layout

    self.setCentralWidget(QWidget(self))
    self.grid = QGridLayout()
    self.grid.setSpacing(10)
    self.centralWidget().setLayout(self.grid)

#def createServer(self):


def mainWindow(self):
    ## buttons
    global ip
    self.connect = QPushButton("Connect")
    self.connect.setToolTip("Connect to the server")
    self.connect.resize(self.connect.sizeHint())

    self.create = QPushButton("Create Server")
    self.create.setToolTip("Create server for others to connect")
    self.create.resize(self.create.sizeHint())
    self.create.clicked.connect(self.createServer)

    self.chooseFile = QPushButton("Choose File")
    self.chooseFile.setToolTip("Choose the file you want to send.")
    self.chooseFile.setEnabled(False)
    self.chooseFile.resize(self.chooseFile.sizeHint())

    self.send = QPushButton("Send")
    self.send.setEnabled(False)
    self.send.resize(self.send.sizeHint())

    self.sendFile = QPushButton("Send File")
    self.sendFile.setToolTip("Send the selected file")
    self.sendFile.setEnabled(False)
    self.sendFile.resize(self.sendFile.sizeHint())


    ## text Fields

    self.ip = QLineEdit()
    self.ip.setPlaceholderText("Ex: 192.168.0.1")

    self.display = QTextEdit()
    self.display.setReadOnly(True)

    self.sendText = QTextEdit()

    ## Label

    self.lbl = QLabel()
    self.lbl.setText(" Or ")

    self.grid.addWidget(self.create, 0,3 )
    self.grid.addWidget(self.lbl, 0,5)
    self.grid.addWidget(self.ip, 0,7)
    self.grid.addWidget(self.connect,0,9)
    self.grid.addWidget(self.display, 1, 1, 8, 7)
    self.grid.addWidget(self.chooseFile, 2, 9)
    self.grid.addWidget(self.sendFile, 4, 9)
    self.grid.addWidget(self.sendText, 9, 1, 2, 7)
    self.grid.addWidget(self.send, 9,9)
    self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = sWindow()
    win.mainWindow()
    sys.exit(app.exec())

【问题讨论】:

    标签: python multithreading python-3.x sockets pyqt5


    【解决方案1】:

    Qt 能够处理需要完成的事件和任务,创建一个事件循环,简单来说,事件循环是一个 while True,它在其中审查不同的事件,例如鼠标、键盘、事件由用户等创建。您通过创建一个阻塞事件循环的循环来创建服务器,这意味着 GUI 无法处理这些任务并在您发出信号时冻结。

    就像您正在创建线程,以便与客户端的通信不会阻塞服务器,就 GUI 而言应该对服务器执行相同的操作,但这带来了不便:无法从另一个线程更新 GUI(在您的情况下self.display.setText()self.display.append() 重绘GUI 的文本),Qt 建议使用信号,为此最好分开,在这种情况下,我创建了一个从QObject 继承的 Manager 类,以便它可以创建信号并管理服务器:

    import sys
    import socket
    import threading
    
    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    
    class Manager(QObject):
        logSignal = pyqtSignal(str)
        def __init__(self, parent=None):
            QObject.__init__(self, parent)
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.address = {}
            self.clientaddr = {}
            self.shutting = False
            self.encoding = 'utf8'
            self.buffsize = 2048
    
        def create_server(self, host, port):
            try:
                self.logSignal.emit("Creating server")
                print("Creating Server")
                self.s.bind((host, port))
                self.s.listen(10)
                print("Server Created")
                self.logSignal.emit("Server Created.")
                self.logSignal.emit("Started listening to clients")
                self.listen()
            except Exception as e:
                print(e)
                self.logSignal.emit("Error occured")
    
        def listen(self):
            try:
                print("started listenting")
                while self.shutting == False:
                    client, addr = self.s.accept()
                    self.address[client] = addr
                    message = "Enter your name: "
                    client.send(message.encode(self.encoding))
                    threading.Thread(target =self.handleclient, args = (client,), daemon=True).start()
            except Exception as e:
                print('Error occured while creating server. ',e)
    
        def handleclient(self, client):
            try: 
                name = client.recv(self.buffsize).decode(self.encoding)
                self.logSignal.emit(name + " connected from address " + str(self.address[client]))
                self.clientaddr[client] = name
                message = "Welcome " + name + "!! You have entered the chatroom."
                client.send(message.encode(self.encoding))
                message = name + "has entered the chatroom. "
                self.broadcast(message, client)
                while self.shutting == False:
                    data = client.recv(self.buffsize).decode(self.encoding)
                    self.logSignal.emit(name + ': ' + data)
                    self.broadcast(data, client)
    
            except Exception as e:
                del self.address[client]
                del self.clientaddr[client]
                self.logSignal.emit('Error occured while connecting to client. ' + str(e))
                message = name + " entered the chatroom."
                self.broadcast(message.encode(self.encoding), False)
    
        def broadcast(self, mesage, client):
            try:
                for sock in self.address.keys():
                    if sock != client:
                        sock.send(message)
            except Exception as e:
                self.logSignal.emit("Error occured while broadcasting the message to clients. ", e)
                print(e)
    
        def stop(self):
            self.shutting = True
            self.s.close()
    
    class sWindow(QMainWindow):
        def __init__(self, title = "File Sharing"):
            super().__init__()
            self.initUi()
    
            self.port = 15478
            self.manager = Manager()
            self.manager.logSignal.connect(self.display.append)
    
        def initUi(self):
            self.resize(800, 600)
            self.setWindowTitle("File Sharing")
            self.setWindowIcon(QIcon("fSharing.jpg"))
            self.setCentralWidget(QWidget(self))
            self.grid = QGridLayout(self.centralWidget())
            self.grid.setSpacing(10)
            self.setup()
    
        def setup(self):
            self.connect = QPushButton("Connect")
            self.connect.setToolTip("Connect to the server")
            self.connect.resize(self.connect.sizeHint())
    
            self.create = QPushButton("Create Server")
            self.create.setToolTip("Create server for others to connect")
            self.create.resize(self.create.sizeHint())
            self.chooseFile = QPushButton("Choose File")
            self.chooseFile.setToolTip("Choose the file you want to send.")
            self.chooseFile.setEnabled(False)
            self.chooseFile.resize(self.chooseFile.sizeHint())
    
            self.send = QPushButton("Send")
            self.send.setEnabled(False)
            self.send.resize(self.send.sizeHint())
    
            self.sendFile = QPushButton("Send File")
            self.sendFile.setToolTip("Send the selected file")
            self.sendFile.setEnabled(False)
    
            self.ip = QLineEdit()
            self.ip.setPlaceholderText("Ex: 192.168.0.1")
    
            self.display = QTextEdit()
            self.display.setReadOnly(True)
    
            self.sendText = QTextEdit()
    
            self.lbl = QLabel()
            self.lbl.setText(" Or ")
    
            self.grid.addWidget(self.create, 0,3 )
            self.grid.addWidget(self.lbl, 0,5)
            self.grid.addWidget(self.ip, 0,7)
            self.grid.addWidget(self.connect,0,9)
            self.grid.addWidget(self.display, 1, 1, 8, 7)
            self.grid.addWidget(self.chooseFile, 2, 9)
            self.grid.addWidget(self.sendFile, 4, 9)
            self.grid.addWidget(self.sendText, 9, 1, 2, 7)
            self.grid.addWidget(self.send, 9,9)
            self.centr()
    
            self.create.clicked.connect(self.on_create)
    
        def on_create(self):
            threading.Thread(target = self.manager.create_server, daemon=True, args = (self.ip.text(), self.port)).start()
    
        def closeEvent(self, event):
            reply = QMessageBox.question(self, "Message Box", "Are you sure?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.manager.stop()
                event.accept()
            else:
                event.ignore()
    
        def centr(self):
            qr = self.frameGeometry()
            cr = QDesktopWidget().availableGeometry().center()
            qr.moveCenter(cr)
            self.move(qr.topLeft())
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = sWindow()
        win.show()
        sys.exit(app.exec())
    

    注意:另一方面,如果您不想处理线程,Qt 提供了诸如 QTcpServerQTcpSocket 之类的类来处理连接,而不会以 Qt 友好的方式阻塞事件循环。您可以找到使用这些课程进行聊天的示例:http://doc.qt.io/qt-5/qtnetwork-network-chat-connection-cpp.html

    【讨论】:

      猜你喜欢
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-04
      • 1970-01-01
      • 2016-04-29
      • 2019-09-28
      相关资源
      最近更新 更多