使用 QLineEdit.setText() 冻结窗口但后台任务工作正常 [英] Using QLineEdit.setText() freezes the window but background tasks works fine

查看:67
本文介绍了使用 QLineEdit.setText() 冻结窗口但后台任务工作正常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a chatroom program as a casual project to better understand PyQt5 and multi-threading, but I am facing rather strange problem. Inside sWindow class, in the createserver() function, setText() in the display widget in the try block freezes the screen but the background processes work fine. setText function in the except block works fine. If I try to print the statements to the cmd, it works perfectly.

这是我遇到问题的代码片段:

Here is the code snippet where I am facing the problem:

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())

推荐答案

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

Qt to be able to handle the events and tasks that need to be done, create an event loop, in a simple description the event loop is a while True within which it reviews the different events such as the mouse, keyboard, events created by the user, etc. And you create your server by creating a loop that blocks the event loop, and that consequently means that the GUI can not handle those tasks and freeze as you signal.

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

Like you are creating threads so that communication with clients does not block the server the same should be done with the server with respect to the GUI, but this brings with it an inconvenience: the GUI can not be updated from another thread (in your case self.display.setText() or self.display.append() redraw the text of the GUI), Qt recommends using signals, and for that it is better to separate, in this case I have created a Manager class that inherits from QObject so that it can create the signals and administer the server:

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-c​​onnection-cpp.html


Note: On the other hand if you do not want to deal with the threads Qt offers classes such as QTcpServer and QTcpSocket to handle the connections without blocking the event loop in a Qt-friendly way. You can find an example of a chat made with those classes: http://doc.qt.io/qt-5/qtnetwork-network-chat-connection-cpp.html

这篇关于使用 QLineEdit.setText() 冻结窗口但后台任务工作正常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆