我有一个客户端服务器聊天程序,我正在尝试连接到GUI(pyqt5)。我试图使用同时运行客户端和GUI的“主”文件中的变量将文本从GUI移至客户端。
基本上,GUI文件中有to_server
(称为“ ichi.py”),该文本以行编辑形式编写,而在“ main.py”中,我将该文本附加到客户端将要发送的消息列表中发送到服务器。客户端将消息发送到服务器,然后将消息传播到所有连接的套接字。当客户端收到消息时,它将文本保存在to_gui
中,该文本将进入GUI的变量from_server
。在GUI中,有一种方法可以打印from_server
中的所有内容。
问题在于,随着程序的继续,我无法使所述变量保持最新状态。
服务器:
import sys
import socket
import select
import datetime
# from urnd import Users_DB
class Server(object):
def __init__(self):
# self.users_db = Users_DB()
self.server_socket = socket.socket()
self.open_sockets = []
self.sockets_names = {}
self.admins = []
self.muted_sockets = []
self.public_msgs = []
self.private_msgs = []
self.rlist = []
self.wlist = []
self.xlist = []
def open(self):
self.server_socket.bind(('0.0.0.0', 2303))
self.server_socket.listen(5)
def run(self):
self.rlist, self.wlist, self.xlist = select.select([self.server_socket] + self.open_sockets, self.open_sockets, [])
self.handle_new_connection()
self.handle_receive_data()
self.handle_sending_msgs()
def close(self):
del self.public_msgs[:]
del self.private_msgs[:]
self.public_msgs.append((None, "bye"))
self.handle_sending_msgs()
self.server_socket.close()
@staticmethod
def get_current_time():
"""Returns hh:mm"""
now = datetime.datetime.now()
time = f'{now.hour}:{now.minute}'
return time
def get_name(self, user_socket):
"""Returns the name of the user_socket, if he is an admin he gets @ before his name"""
name = self.sockets_names[user_socket]
if len(self.admins) != 0:
if user_socket in self.admins:
name = "@" + name
return name
def get_data_length(self, data):
"""Returns the length of the data as string with length 3"""
length = str(len(data))
while len(length) < 3:
length = "0" + length
return length
def get_socket_by_name(self, name):
"""Returns the socket with the name, if none exists return None"""
if len(self.sockets_names) != 0:
for socket_pair, socket_name in self.sockets_names.items():
if name == socket_name:
return socket_pair
return None
def get_admins_as_string(self):
admins_names_lst = []
for admin_socket in self.admins:
admins_names_lst.append(self.sockets_names[admin_socket])
return str(admins_names_lst)[1:-1]
def remove_socket(self, removed_socket):
self.open_sockets.remove(removed_socket)
if removed_socket in self.admins:
self.admins.remove(removed_socket)
del self.sockets_names[removed_socket]
def handle_new_connection(self):
for new_connection in self.rlist:
if new_connection is self.server_socket:
(new_socket, address) = self.server_socket.accept()
self.open_sockets.append(new_socket)
self.sockets_names[new_socket] = "Anonymous"
if len(self.admins) == 0:
self.admins.append(new_socket)
print("New Connection And Admin")
else:
print("New Connection")
def handle_receive_data(self):
for current_socket in self.rlist:
if current_socket is not self.server_socket:
data_length = int(current_socket.recv(3).decode('utf-8'))
data = current_socket.recv(data_length).decode('utf-8')
if data[0] == '/':
self.handle_commands(current_socket, data[1:])
else:
self.private_msgs.append((current_socket, "You: " + data))
if current_socket in self.muted_sockets:
self.private_msgs.append((current_socket, """"You are muted and so can't send msgs to everyone.
You can ask one of the self.admins to unmute you in a private msg"""))
else:
self.public_msgs.append((current_socket, self.get_name(current_socket) + ": " + data))
def handle_sending_msgs(self):
for message in self.public_msgs:
(sender_socket, data) = message
data = self.get_current_time() + " " + data
for receiver_socket in self.wlist:
if receiver_socket is not sender_socket:
receiver_socket.send(bytes(self.get_data_length(data), 'utf8'))
receiver_socket.send(bytes(data, 'utf8'))
if message in self.public_msgs:
self.public_msgs.remove(message)
for message in self.private_msgs:
(receiver_socket, data) = message
data = self.get_current_time() + " " + data
if receiver_socket in self.wlist:
receiver_socket.send(self.get_data_length(data).encode('utf-8'))
receiver_socket.send(data.encode('utf-8'))
if message in self.private_msgs:
self.private_msgs.remove(message)
if data.split(' ')[1] == "bye":
self.remove_socket(receiver_socket)
def handle_commands(self, current_socket, data):
command = data.split(' ')[0].lower()
data = ' '.join(data.split(' ')[1:])
if command == "exit":
self.public_msgs.append((current_socket, self.get_name(current_socket) + " left the chat."))
self.private_msgs.append((current_socket, "bye"))
print("Connection with " + self.get_name(current_socket) + " closed.")
elif command == 'rename' or command == 'setname':
if data not in self.sockets_names.values():
if data.lower() != "you" and data.lower() != "server" and data.lower()[0] != "@":
try:
# self.users_db.change_name(data, self.sockets_names[current_socket])
self.sockets_names[current_socket] = data
self.private_msgs.append((current_socket, "Your name has been successfully changed to " + data + "."))
except MemoryError:
err = sys.exc_info()[0]
print(f"Error: {err}")
else:
self.private_msgs.append((current_socket, data + " is not a valid name."))
else:
self.private_msgs.append((current_socket, "This name is already taken."))
elif command == 'setadmin' or command == "promote":
if current_socket in self.admins:
if data not in self.sockets_names.values():
self.private_msgs.append((current_socket, "This name doesn't exist in this server."))
else:
new_admin_socket = self.get_socket_by_name(data)
self.admins.append(new_admin_socket)
self.private_msgs.append((current_socket, data + " has been promoted to admin."))
self.public_msgs.append(
(current_socket, self.get_name(current_socket) + " promoted " + data + " to admin."))
else:
self.private_msgs.append((current_socket, "You don't have access to this command."))
elif command == 'kick' or command == 'remove':
if current_socket in self.admins:
if data not in self.sockets_names.values():
self.private_msgs.append((current_socket, "This name doesn't exist in this server."))
else:
kicked_socket = self.get_socket_by_name(data)
self.private_msgs.append((current_socket, data + " has been successfully kicked and removed."))
self.public_msgs.append((current_socket, self.get_name(current_socket) + " kicked and removed " + data))
self.private_msgs.append((kicked_socket, self.get_name(current_socket) + " kicked you."))
self.private_msgs.append((kicked_socket, "bye"))
elif command == 'mute':
if current_socket in self.admins:
if data not in self.sockets_names.values():
self.private_msgs.append((current_socket, "This name doesn't exist in this server."))
else:
muted_socket = self.get_socket_by_name(data)
self.muted_sockets.append(muted_socket)
self.private_msgs.append((current_socket, data + " has been successfully muted."))
self.public_msgs.append((current_socket, self.get_name(current_socket) + " muted " + data))
self.private_msgs.append((muted_socket, self.get_name(current_socket) + " muted you."))
else:
self.private_msgs.append((current_socket, "You are not an admin and so you have no such permissions."))
elif command == 'unmute':
if current_socket in self.admins:
if data not in self.sockets_names.values():
self.private_msgs.append((current_socket, "This name doesn't exist in this server."))
else:
unmute_socket = self.get_socket_by_name(data)
if unmute_socket not in self.muted_sockets:
self.private_msgs.append((current_socket, "This user isn't muted."))
else:
self.muted_sockets.remove(unmute_socket)
self.private_msgs.append((current_socket, data + " has been successfully unmuted."))
self.public_msgs.append((current_socket, self.get_name(current_socket) + " unmuted " + data))
self.private_msgs.append((unmute_socket, self.get_name(current_socket) + " unmuted you."))
else:
self.private_msgs.append((current_socket, "You are not an admin and so you have no such permissions."))
elif command == 'msg' or command == 'message' or command == "prvmsg" or command == "privatemessage":
send_to_name = data.split(' ')[0]
data = ' '.join(data.split(' ')[1:])
if send_to_name not in self.sockets_names.values():
self.private_msgs.append((current_socket, "This name doesn't exist in this server."))
else:
send_to_socket = self.get_socket_by_name(send_to_name)
self.private_msgs.append(
(current_socket, "You -> " + send_to_name + ": " + data))
self.private_msgs.append(
(send_to_socket, self.get_name(current_socket) + " -> " + send_to_name + ": " + data))
elif command == 'admin' or command == "admins" or command == "adminlist" or command == "adminslist":
self.private_msgs.append((current_socket, "self.admins: " + self.get_admins_as_string()))
elif command == 'users' or command == "userslist" or command == 'user' or command == "userlist":
self.private_msgs.append((current_socket, "Users: " + str(self.sockets_names.values())[1:-1]))
elif command == 'help' or command == '?':
commands = """/rename <name> - change your name.\n/msg <user> <msg> - will send <msg> as a private massage that only <user>
can see.\n/users - returns the names of all the connected users.\n/self.admins - returns the names of all the connected self.admins.\n/exit -
will disconnect you.\n\nself.admins' Commends Only:\n/kick <user> - kick the <user> from the server.\n/promote <user> - will ser <user> to
be an admin.\nmute <user> - will no let him send public msgs, only privates.\nunmute <user> - will cancel the mute on this user."""
self.private_msgs.append((current_socket, "\nCommands:\n" + commands + "\n"))
# elif command == 'login':
# info = data.split()
# if len(info) is 2:
# # db_response = self.users_db.check_user_info(info[0], info[1])
# if db_response is "Connected Successfully!":
# pass # close login gui
# elif db_response is "No such user exists!":
# pass # unhide hidden label in login gui
else:
self.private_msgs.append((current_socket, command + " is not a valid command."))
server = Server()
try:
server.open()
while True:
server.run()
finally:
server.close()
客户:
import socket
import select
import sys
from ichi import ui_mainwindow, QtWidgets
class Client(object):
def __init__(self, ip, port):
self.__my_socket = socket.socket()
self.close = False
self.IP = ip
self.PORT = port
self.msgs = []
self.to_gui = ''
def send(self, msg):
self.msgs.append(msg)
def run(self):
try:
self.__my_socket.connect((self.IP, self.PORT))
except ConnectionRefusedError:
print("Connection error. Rerun program")
while not self.close:
rlist, wlist, xlist = select.select([self.__my_socket], [self.__my_socket], [])
if self.__my_socket in wlist:
for msg in self.msgs:
# send to server the message
self.__my_socket.send(self.get_msg_length(msg).encode('utf-8'))
self.__my_socket.send(msg.encode('utf-8'))
if self.__my_socket in rlist:
data_length = int(self.__my_socket.recv(3))
data = self.__my_socket.recv(data_length).decode('utf-8')
if data.split(" ")[1] == "bye":
# self.input_from_server("\nDisconnecting, Bye!")
self.__my_socket.close()
self.close = True
elif data == "Connected Successfully!":
# close login_gui
pass
else:
self.to_gui = data
@staticmethod
def get_msg_length(message):
"""Returns the length of the msg as string with length 3"""
length = str(len(message))
while len(length) < 3:
length = "0" + length
return length
# client = Client("127.0.0.1", 2303)
# client.run()
GUI:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ichi.ui'
#
# Created by: PyQt5 UI code generator 5.13.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5.QtGui import QTextCursor
from PyQt5 import QtCore, QtWidgets
class ui_mainwindow(object):
def __init__(self, MainWindow):
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.messages_spot = QtWidgets.QTextEdit(self.centralwidget)
self.cursor = QTextCursor(self.messages_spot.document())
self.typingspot = QtWidgets.QLineEdit(self.centralwidget)
self.Enter = QtWidgets.QPushButton(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.to_server = 'open'
self.from_server = 'open'
def setup_ui(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget.setObjectName("centralwidget")
self.messages_spot.setGeometry(QtCore.QRect(140, 60, 411, 261))
self.messages_spot.setReadOnly(True)
self.messages_spot.setObjectName("messages_spot")
self.messages_spot.setTextCursor(self.cursor)
self.typingspot.setGeometry(QtCore.QRect(140, 320, 371, 31))
self.typingspot.setObjectName("typingspot")
self.Enter.setGeometry(QtCore.QRect(510, 320, 41, 31))
self.Enter.setInputMethodHints(QtCore.Qt.ImhNone)
self.Enter.setCheckable(False)
self.Enter.setChecked(False)
# self.Enter.hide()
self.Enter.setObjectName("Enter")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.Enter.clicked.connect(lambda: self.on_enter())
self.typingspot.returnPressed.connect(self.Enter.click)
self.print_from_server(self.from_server)
self.retranslate_ui(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslate_ui(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.Enter.setText(_translate("MainWindow", "Enter"))
def on_enter(self):
text = self.typingspot.text()
if text.isspace() is False and text != '' and text != 'open':
self.to_server = text
self.messages_spot.insertPlainText(f'• You: {text}\r\n')
self.messages_spot.verticalScrollBar().setValue(self.messages_spot.verticalScrollBar().maximum())
self.typingspot.clear()
def print_from_server(self, text):
if text.isspace() is False and text != '' and text != 'open':
self.messages_spot.insertPlainText(f'• {text}\r\n')
self.messages_spot.verticalScrollBar().setValue(self.messages_spot.verticalScrollBar().maximum())
主要:
from PyQt5 import QtWidgets
from ichi import ui_mainwindow
import sys
from threading import Thread
from client import Client
from login import Ui_login_screen
if __name__ == "__main__":
# create client
global client
client = Client("127.0.0.1", 2303)
t = Thread(target=client.run)
t.start()
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = ui_mainwindow(MainWindow)
ui.setup_ui(MainWindow)
MainWindow.show()
app.exec_()
client.send(ui.to_server)
ui.from_server = client.to_gui
t.join()
答案 0 :(得分:1)
您有几个错误的假设:
仅在Qt完成工作时(在您的情况下是在关闭窗口之后),才会执行exec_()之后的代码。
假定上述内容为假,则它不起作用,因为ui.from_server = client.to_gui
仅将变量“ to_gui”的值分配给“ from_server”,如果在“ to_gui”之后更改了“ from_server”没有通知。
由于我假设OP已经对其进行了测试并且应该可以正常工作,所以我将不分析服务器代码。我的解决方案建议使用QTcpSocket通过信号处理数据,从而在这种情况下消除不必要的复杂性:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork
class Client(QtCore.QObject):
messageChanged = QtCore.pyqtSignal(str)
def __init__(self, ip, port, parent=None):
super().__init__(parent)
self._socket = QtNetwork.QTcpSocket(self)
self.socket.readyRead.connect(self.onReadyRead)
self.socket.connectToHost(ip, port)
@property
def socket(self):
return self._socket
@QtCore.pyqtSlot()
def onReadyRead(self):
data_length = int(self.socket.read(3))
data = self.socket.read(data_length).decode("utf-8")
words = data.split(" ")
if len(words) >= 2 and words[1] == "bye":
self.socket.disconnectFromHost()
self.close = True
elif data == "Connected Successfully!":
pass
else:
self.messageChanged.emit(data)
def send(self, message):
self.socket.write(Client.get_msg_length(message).encode("utf-8"))
self.socket.write(message.encode("utf-8"))
@staticmethod
def get_msg_length(message):
"""Returns the length of the msg as string with length 3"""
length = str(len(message))
while len(length) < 3:
length = "0" + length
return length
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.client = Client("127.0.0.1", 2303)
self.messages_spot = QtWidgets.QTextEdit(readOnly=True)
self.typingspot = QtWidgets.QLineEdit()
self.enter_button = QtWidgets.QPushButton(
self.tr("Enter"), inputMethodHints=QtCore.Qt.ImhNone
)
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
grid_layout = QtWidgets.QGridLayout(central_widget)
grid_layout.addWidget(self.messages_spot, 0, 0, 1, 2)
grid_layout.addWidget(self.typingspot, 1, 0)
grid_layout.addWidget(self.enter_button, 1, 1)
self.enter_button.clicked.connect(self.on_enter)
self.typingspot.returnPressed.connect(self.enter_button.click)
self.client.messageChanged.connect(self.print_from_server)
self.resize(640, 480)
def on_enter(self):
text = self.typingspot.text()
if text.isspace() is False and text != "" and text != "open":
self.client.send(text)
self.messages_spot.insertPlainText(f"• You: {text}\r\n")
self.messages_spot.verticalScrollBar().setValue(
self.messages_spot.verticalScrollBar().maximum()
)
self.typingspot.clear()
def print_from_server(self, text):
if text.isspace() is False and text != "" and text != "open":
self.messages_spot.insertPlainText(f"• {text}\r\n")
self.messages_spot.verticalScrollBar().setValue(
self.messages_spot.verticalScrollBar().maximum()
)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
w = MainWindow()
w.show()
sys.exit(app.exec_())