使用 QLineEdit.setText() 冻结窗口但后台任务工作正常 [英] Using QLineEdit.setText() freezes the window but background tasks works fine
问题描述
我正在尝试创建一个聊天室程序作为一个休闲项目,以更好地理解 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 提供了诸如 QTcpServer
和 QTcpSocket
之类的类来处理连接而不阻塞事件循环以 Qt 友好的方式.您可以找到使用这些类进行聊天的示例:http://doc.qt.io/qt-5/qtnetwork-network-chat-connection-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屋!