如何将套接字客户端的传入消息发送到GUI(pyqt5)

问题描述 投票:0回答:1

我正在尝试使用套接字库制作一个聊天应用程序。我有三个文件server.pyclient.pygui.py无限循环提供了客户端和服务器的侦听过程。因为server.py在另一个终端窗口中运行。但是client和gui在一个终端窗口中运行。问题是当我调用包含无限循环的函数时,它陷入了死循环,其余代码将无法运行。我什至尝试使用multiprocessingthreading.Threadthreading.TimerQThreadQueue,但仍然没有成功。我以为也许我没有正确使用这些库,所以我决定寻求帮助。使用threading.Timer的问题是说new parent is running in another thread, and I can not change gui objects outside main thread。值得一提的是,我以某种方式解决了该问题,并且服务器发送的所有传入消息最终都出现在GUI上。再一次问题仍然存在,当我按下incoming message打破ctrl+c函数的无限循环时,我得到了结果。这花费了我太多时间,并造成了令人头痛的头痛,如果有人可以帮助我解决这个问题,我将不胜感激。谢谢这是最小的代码:(为测试此代码,我将客户端和gui合并在一起)

这里是server.py:

import socket, json, select
class Server():
    def __init__(self):
        self.connected_sockets = []# for saving sockets
        self.connected_clients = {}# for saving sockets and related usernames
        self.password = '21709'
        self.server_name = 'SERVER1'

    def start(self, host, port):
        # create the socket, AF_INET == ipv4, SOCK_STREAM == TCP
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_socket.bind( (host, port) )
        self.connected_sockets.append( self.server_socket )
        self.connected_clients[self.server_name] = self.server_socket
        self.server_socket.listen()
        self.connect_clients()

    def connect_clients(self):
        next_msg = b''
        while True:
            read_sockets, _, exception_sockets = select.select(self.connected_sockets, [], self.connected_sockets)
            for socket in read_sockets:
                if socket == self.server_socket:
                    #new connecttion
                    client_socket, address = self.server_socket.accept()
                    from_client = client_socket
                    msg, next_msg = self.receive_data( from_client )
                    data = json.loads(msg)['data']
                    username = data['username']
                    password = data['password']
                    from_user = self.server_name
                    to_user = username
                    BCC = self.server_socket
                    msg_type = "string"
                    if username in self.connected_clients:
                        self.transfer_data( f"username {username} is not valid, try again", msg_type, from_user, to_user, BCC )
                        client_socket.close()
                    else:
                        if ( password == self.password ):
                            self.connected_sockets.append( client_socket )
                            self.connected_clients[username] = client_socket
                            self.transfer_data( 'password was correct, wellcome', msg_type, from_user, to_user, BCC )
                            print(f"Connection from {address} has been established.")
                            #send welcome phrase to client just joined from the server
                            self.transfer_data( "Hey there!!! it's a json", msg_type, from_user, to_user, BCC )
                            self.transfer_data( "Wellcome to this server", msg_type, from_user, to_user, BCC )
                            self.transfer_data( "here you can", msg_type, from_user, to_user, BCC )
                            self.transfer_data( "connect to others", msg_type, from_user, to_user, BCC )
                        else:
                            self.transfer_data( "password was incorrect, sorry", msg_type, from_user, client_socket, BCC )
                            client_socket.close()
                else:
                    #old connection and receive_message from them
                    for user,user_socket in self.connected_clients.items():
                        if user_socket == socket:
                            username_related_to_socket = user
                            break
                    try:
                        msg,next_msg = self.receive_data(socket,next_msg)
                        msg = json.loads(msg)
                        from_user = msg['from_user']
                        to_user = msg['to_user']
                        if to_user != self.server_name:
                            self.transfer_data(msg['data'], msg['type'], msg['from_user'], msg['to_user'], msg['BCC'] )
                        else:
                            print( f"{msg['from_user']}: \x1b[6;30;42m {msg['data']} \x1b[0m" )
                    except:
                        print(f'\n client \x1b[6;30;42m {username_related_to_socket} \x1b[0m disconnected \n')
                        self.connected_sockets.remove( socket )
                        del self.connected_clients[username_related_to_socket]

    def receive_data(self, from_user, next_msg=b""):
        from_client = from_user
        full_msg = next_msg
        while True:
            msg = from_client.recv(7)
            try:# because the Ӛ has length of 2, so it may happen that, only one of them exist in the msg received
                index = msg.decode("utf-8").find('Ӛ')
            except:
                msg += from_client.recv(1)
                index = msg.decode("utf-8").find('Ӛ')
            if ( (index != -1 ) or (len(msg) <= 0) ):
                full_msg += msg[:index]
                next_msg = msg[index+2:]
                break
            else:
                full_msg += msg
        full_msg = full_msg.decode("utf-8")
        return(full_msg, next_msg)

    def transfer_data(self, msg, msg_type, from_user, to_user, BCC):
        from_client = self.connected_clients[from_user]
        if type(to_user) is str:
            try:
                to_client = self.connected_clients[to_user]
                result = True
            except:
                msg = f"{to_user} is offline. try later"
                to_client = self.connected_clients[from_user]
                to_user = from_user
                from_user = self.server_name
                result = False
        else:
            to_client = to_user
            to_user = from_user
            from_user = self.server_name
            result = False
        if msg_type == 'string':
            msg = msg.strip()
        msg = {'type':msg_type, 'size':len(msg), 'data':msg, 'from_user':from_user, 'to_user':to_user, 'BCC':str(BCC), 'result':result}
        # turn a dictionary into a string to transfere with socket
        data_string = json.dumps(msg)
        to_client.send( bytes(data_string,"utf-8") )
        to_client.send( bytes('Ӛ',"utf-8") )

server = Server()
server.start(host='127.0.0.1', port=1234)

客户端和gui在一起。py:

import os, socket, json
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class ConnectPage(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        prev_info = ['','','','']
        if os.path.isfile("previous_login.txt"):
            saved_login_file = open("previous_login.txt",'r')
            lines = saved_login_file.readlines()
            if len(lines) > 0:
                for i in range(0,len(lines)):
                    prev_info[i] = lines[i]
            saved_login_file.close()
        self.username = QLineEdit(prev_info[0])
        self.password = QLineEdit(prev_info[1])
        self.host = QLineEdit(prev_info[2])
        self.port = QLineEdit(prev_info[3])
        self.port.setValidator( QIntValidator() )#takes only numbers
        self.login_button = QPushButton('Login')
        self.login_button.clicked.connect(self.login_button_clicked)
        self.clear_form = QPushButton('Clear Form')
        self.clear_form.clicked.connect(self.clear_form_clicked)
        self.status_bar = QLabel()

        self.main_layout = QGridLayout()
        self.main_layout.addWidget( QLabel('Username:'),0,0 )
        self.main_layout.addWidget( self.username,0,1 )
        self.main_layout.addWidget( QLabel('Password:'),1,0 )
        self.main_layout.addWidget( self.password,1,1 )
        self.main_layout.addWidget( QLabel('Host:'),2,0 )
        self.main_layout.addWidget( self.host,2,1 )
        self.main_layout.addWidget( QLabel('Port:'),3,0 )
        self.main_layout.addWidget( self.port,3,1 )
        self.main_layout.addWidget( self.clear_form,4,0 )
        self.main_layout.addWidget( self.login_button,4,1 )
        self.main_layout.addWidget( self.status_bar,5,0,2,1 )
        self.setLayout(self.main_layout)

    def clear_form_clicked(self):
        username = self.username.setText('')
        password = self.password.setText('')
        host = self.host.setText('')
        port = self.port.setText('')

    def login_button_clicked(self):
        username = self.username.text().strip()
        password = self.password.text().strip()
        host = self.host.text().strip()
        port = self.port.text().strip()
        saved_login_file = open("previous_login.txt",'w')
        saved_login_file.write( username + '\n' + password + '\n' + host + '\n' + port )
        saved_login_file.close()
        result,msg = controller.client_connect(host, int(port), username, password)
        if result:
            controller.connect_page.close()
            controller.chat_page.show()
            controller.chat_page.start_listening()
        else:
            controller.connect_page.status_bar.setText(msg['data'])

class ChatPage(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.next_msg = b''

        #create a scrolled window to put info_grid in it
        self.msg_layout = QVBoxLayout()
        msg_layout_widget = QWidget()
        msg_layout_widget.setLayout(self.msg_layout)
        self.scrolled = QScrollArea()
        self.scrolled.setWidget(msg_layout_widget)
        self.scrolled.setWidgetResizable(True)
        self.scrolled.setFixedHeight(400)
        scroll_layout = QHBoxLayout()
        scroll_layout.addWidget(self.scrolled)
        msg_layout_groupbox = QGroupBox()
        msg_layout_groupbox.setLayout(scroll_layout)

        self.new_msg = QTextEdit()
        send_button = QPushButton('Send icon')
        #send_button.clicked.connect( self.send_button_clicked )
        self.send_layout = QHBoxLayout()
        self.send_layout.addWidget(self.new_msg)
        self.send_layout.addWidget(send_button)

        self.right_layout = QVBoxLayout()
        self.right_layout.addWidget(msg_layout_groupbox)
        self.right_layout.addLayout(self.send_layout)

        self.main_layout = QHBoxLayout()
        self.main_layout.addLayout(self.right_layout)
        self.setLayout(self.main_layout)

    def start_listening(self):
        controller.client.listen_to_incoming_messages()

    def insert_into_chat_area(self,data):
        from_user = data['from_user']
        to_user = data['to_user']
        msg = data['data']
        if msg != '':
            if data['type'] == 'string':
                self.msg_layout.addWidget( QLabel(msg) )
        else:
            print( "unknown data recieved (not a dictionary)" )



class Client(QObject):
    def __init__(self,host,port,username,password):
        QObject.__init__(self)
        self.BCC = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.next_msg = b''

    def connect(self):
        audience = "SERVER1"
        self.BCC.connect( (self.host, self.port) )
        msg = {'username':self.username,'password':self.password}
        msg_type = 'login'
        to_user = audience
        from_user = self.username
        self.transfer_data(msg, msg_type, from_user, to_user, self.BCC)
        msg,self.next_msg = self.recieve_data(self.BCC)
        msg = json.loads(msg)
        print( f"{msg['from_user']}: \x1b[6;30;42m {msg['data']} \x1b[0m" ) #syntax => print(f'\x1b[6;30;42m {colored text} \x1b[0m')
        result = msg['result']
        return( result,msg )

    def listen_to_incoming_messages(self):
        while True:
            msg,self.next_msg = self.recieve_data(self.BCC,self.next_msg)
            msg = json.loads(msg)
            print( f"{msg['from_user']}: \x1b[6;30;42m {msg['data']} \x1b[0m" )
            controller.chat_page.insert_into_chat_area(msg)

    def outgoing_messages(self,from_user,to_user,BCC):
        while True:
            msg = input(f'{self.username}> ')
            self.transfer_data(msg,'string',from_user,to_user,BCC)

    def transfer_data(self, msg, msg_type, from_user, to_user, BCC):
        if msg_type == 'string':
            msg = msg.strip()
        result = True
        msg = {'type':msg_type, 'size':len(msg), 'data':msg, 'from_user':from_user, 'to_user':to_user, 'BCC':str(BCC), 'result':result}
        data_string = json.dumps(msg)
        BCC.send( bytes(data_string,"utf-8") )
        BCC.send( bytes('Ӛ',"utf-8") )

    def recieve_data(self, from_server, next_msg=b""):
        full_msg = next_msg
        while True:
            msg = from_server.recv(7)
            try:
                index = msg.decode("utf-8").find('Ӛ')
            except:
                msg += from_server.recv(1)
                index = msg.decode("utf-8").find('Ӛ')
            if ( (index != -1 ) or (len(msg) <= 0) ):
                full_msg += msg[:index]
                next_msg = msg[index+2:]
                break
            else:
                full_msg += msg
        full_msg = full_msg.decode("utf-8")
        return(full_msg, next_msg)


# this class controls moving between all screens
class Controller:
    def __init__(self):
        self.connect_page = ConnectPage()
        self.connect_page.show()
        self.chat_page = ChatPage()

    def client_connect( self, host, port, username, password ):
        self.client = Client(host, port, username, password)
        return( self.client.connect() )


app = QApplication([])
controller = Controller()
app.exec_()
python-3.x multithreading pyqt5 serversocket
1个回答
0
投票

最后我可以以某种方式解决这种情况。我相信我们只能编辑已经在消息布局中的窗口小部件,并且不能将任何窗口小部件添加到任何布局中,因为布局是thread-safeprocess-safe。当然,也许也可以解决该问题,但是现在我什么都没想到。这是通过代码的解决方案:server.py如上所述,gui+client.py如下:

import threading, time
import os, socket, json
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class ConnectPage(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        prev_info = ['','','','']
        if os.path.isfile("previous_login.txt"):
            saved_login_file = open("previous_login.txt",'r')
            lines = saved_login_file.readlines()
            if len(lines) > 0:
                for i in range(0,len(lines)):
                    prev_info[i] = lines[i]
            saved_login_file.close()
        self.username = QLineEdit(prev_info[0])
        self.password = QLineEdit(prev_info[1])
        self.password.setEchoMode(QLineEdit.Password)
        self.host = QLineEdit(prev_info[2])
        self.port = QLineEdit(prev_info[3])
        self.port.setValidator( QIntValidator() )#takes only numbers
        self.login_button = QPushButton('Login')
        self.login_button.clicked.connect(self.login_button_clicked)
        self.clear_form = QPushButton('Clear Form')
        self.clear_form.clicked.connect(self.clear_form_clicked)
        self.status_bar = QLabel()

        self.main_layout = QGridLayout()
        self.main_layout.addWidget( QLabel('Username:'),0,0 )
        self.main_layout.addWidget( self.username,0,1 )
        self.main_layout.addWidget( QLabel('Password:'),1,0 )
        self.main_layout.addWidget( self.password,1,1 )
        self.main_layout.addWidget( QLabel('Host:'),2,0 )
        self.main_layout.addWidget( self.host,2,1 )
        self.main_layout.addWidget( QLabel('Port:'),3,0 )
        self.main_layout.addWidget( self.port,3,1 )
        self.main_layout.addWidget( self.clear_form,4,0 )
        self.main_layout.addWidget( self.login_button,4,1 )
        self.main_layout.addWidget( self.status_bar,5,0,2,1 )
        self.setLayout(self.main_layout)

    def clear_form_clicked(self):
        username = self.username.setText('')
        password = self.password.setText('')
        host = self.host.setText('')
        port = self.port.setText('')

    def login_button_clicked(self):
        username = self.username.text().strip()
        password = self.password.text().strip()
        host = self.host.text().strip()
        port = self.port.text().strip()
        saved_login_file = open("previous_login.txt",'w')
        saved_login_file.write( username + '\n' + password + '\n' + host + '\n' + port )
        saved_login_file.close()
        result,msg = controller.client_connect(host, int(port), username, password)
        if result:
            controller.connect_page.close()
            controller.chat_page.show()
            controller.chat_page.start_listening()
        else:
            controller.connect_page.status_bar.setText(msg['data'])

class ChatPage(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.next_msg = b''
        #create a scrolled window to put info_grid in it
        self.msg_layout = QVBoxLayout()
        self.new_msg_label = QLineEdit()
        self.new_msg_label.setHidden(True)
        self.new_msg_label.textChanged.connect( self.new_incoming_msg_text_changed )
        self.msg_layout.addWidget( self.new_msg_label )
        msg_layout_widget = QWidget()
        msg_layout_widget.setLayout(self.msg_layout)
        self.scrolled = QScrollArea()
        self.scrolled.setWidget(msg_layout_widget)
        self.scrolled.setWidgetResizable(True)
        self.scrolled.setFixedHeight(400)
        scroll_layout = QHBoxLayout()
        scroll_layout.addWidget(self.scrolled)
        msg_layout_groupbox = QGroupBox()
        msg_layout_groupbox.setLayout(scroll_layout)

        self.new_msg = QTextEdit()
        send_button = QPushButton('Send icon')
        #send_button.clicked.connect( self.send_button_clicked )
        self.send_layout = QHBoxLayout()
        self.send_layout.addWidget(self.new_msg)
        self.send_layout.addWidget(send_button)

        self.right_layout = QVBoxLayout()
        self.right_layout.addWidget(msg_layout_groupbox)
        self.right_layout.addLayout(self.send_layout)

        self.main_layout = QHBoxLayout()
        self.main_layout.addLayout(self.right_layout)
        self.setLayout(self.main_layout)

    def start_listening(self):
        t = threading.Timer(1,controller.client.listen_to_incoming_messages,(self.new_msg_label,) )
        t.start()

    def new_incoming_msg_text_changed(self,text):
        if text:
            widget = QLabel( text )
            self.msg_layout.addWidget( widget )
            self.new_msg_label.setText('')

class Client:
    def __init__(self,host,port,username,password):
        self.BCC = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.next_msg = b''

    def connect(self):
        audience = "SERVER1"
        self.BCC.connect( (self.host, self.port) )
        msg = {'username':self.username,'password':self.password}
        msg_type = 'login'
        to_user = audience
        from_user = self.username
        self.transfer_data(msg, msg_type, from_user, to_user, self.BCC)
        msg,self.next_msg = self.recieve_data(self.BCC)
        msg = json.loads(msg)
        print( f"{msg['from_user']}: \x1b[6;30;42m {msg['data']} \x1b[0m" ) #syntax => print(f'\x1b[6;30;42m {colored text} \x1b[0m')
        result = msg['result']
        return( result,msg )

    def listen_to_incoming_messages(self, obj):
        while True:
            data,self.next_msg = self.recieve_data(self.BCC,self.next_msg)
            data = json.loads(data)
            from_user = data['from_user']
            to_user = data['to_user']
            msg = data['data']
            print( f"{from_user}: \x1b[6;30;42m {msg} \x1b[0m" )
            if msg != '':
                time.sleep(1) # this line is redundant, I added it to see how it works
                obj.setText( f"<font color=red>{from_user}</font> => <font color=blue>{msg}</font>" )

    def outgoing_messages(self,from_user,to_user,BCC):
        while True:
            msg = input(f'{self.username}> ')
            self.transfer_data(msg,'string',from_user,to_user,BCC)

    def transfer_data(self, msg, msg_type, from_user, to_user, BCC):
        if msg_type == 'string':
            msg = msg.strip()
        result = True
        msg = {'type':msg_type, 'size':len(msg), 'data':msg, 'from_user':from_user, 'to_user':to_user, 'BCC':str(BCC), 'result':result}
        data_string = json.dumps(msg)
        BCC.send( bytes(data_string,"utf-8") )
        BCC.send( bytes('Ӛ',"utf-8") )

    def recieve_data(self, from_server, next_msg=b""):
        full_msg = next_msg
        while True:
            msg = from_server.recv(7)
            try:
                index = msg.decode("utf-8").find('Ӛ')
            except:
                msg += from_server.recv(1)
                index = msg.decode("utf-8").find('Ӛ')
            if ( (index != -1 ) or (len(msg) <= 0) ):
                full_msg += msg[:index]
                next_msg = msg[index+2:]
                break
            else:
                full_msg += msg
        full_msg = full_msg.decode("utf-8")
        return(full_msg, next_msg)

# this class controls moving between all screens
class Controller:
    def __init__(self):
        self.connect_page = ConnectPage()
        self.connect_page.show()
        self.chat_page = ChatPage()

    def client_connect( self, host, port, username, password ):
        self.client = Client(host, port, username, password)
        return( self.client.connect() )

app = QApplication([])
controller = Controller()
app.exec_()
© www.soinside.com 2019 - 2024. All rights reserved.