使用QLineEdit.setText()冻结窗口但后台任务工作正常

时间:2018-05-18 14:30:24

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

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

以下是我遇到问题的代码段:

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

1 个答案:

答案 0 :(得分:0)

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

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

QObject

注意:另一方面,如果你不想处理线程,Qt会提供诸如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()) QTcpServer之类的类来处理连接,而不会以Qt友好的方式阻塞事件循环。您可以找到与这些类进行聊天的示例:http://doc.qt.io/qt-5/qtnetwork-network-chat-connection-cpp.html