我正在尝试创建一个聊天室程序作为一个临时项目来更好地理解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())
答案 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