繁体   English   中英

将聊天 gui 连接到套接字客户端

[英]Connecting chat gui to socket client

我有一个客户端 - 服务器聊天程序,我试图连接到 GUI(pyqt5)。 我尝试使用运行客户端和 GUI 的“主”文件中的变量将文本从 GUI 移动到客户端。

to_server ,GUI 文件中有to_server (称为“ichi.py”),它获取在行编辑中写入的文本,并在“main.py”中将文本附加到客户端将发送到服务器的消息列表中。 客户端将消息发送到服务器,并将消息传播到所有连接的套接字。 当客户端收到一条消息时,它会将文本保存在to_gui ,该文本将转到 GUI 的变量from_server 在 GUI 中,有一种方法可以打印from_server任何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()

图形用户界面:

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

你有几个不正确的假设:

  • 放在 exec_() 之后的代码只会在 Qt 完成其工作时执行,在您的情况下是在窗口关闭之后。

  • 假设前面是假的,应该是不行的,因为ui.from_server = client.to_gui只是把变量“to_gui”的值赋给了“from_server”,如果在“to_gui”之后改了,那么“from_server”会提示no .

我不打算分析服务器代码,因为我认为 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_())

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM