如何将传入消息从套接字客户端发送到 GUI (pyqt5) [英] how to send incoming messages from socket client to GUI (pyqt5)

查看:61
本文介绍了如何将传入消息从套接字客户端发送到 GUI (pyqt5)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用套接字库制作聊天应用程序.我有三个文件 server.pyclient.pygui.py客户端和服务器的侦听过程由无限循环提供.因为 server.py 在另一个终端窗口中运行.但是客户端和 gui 正在一个终端窗口中运行.问题是当我调用包含无限循环的函数时,它会卡在那里,其余的代码将无法运行.我什至尝试使用 multiprocessingthreading.Threadthreading.TimerQThreadQueue,但仍然没有成功.我想也许我没有正确使用这些库,所以我决定寻求一些帮助.使用 threading.Timer 问题是说,新的父线程正在另一个线程中运行,我不能在主线程之外更改 gui 对象.值得一提的是我以某种方式解决了这个问题,服务器发送的所有传入消息最终都会出现在 GUI 上.问题仍然存在,当我按 ctrl+c 打破 incoming message 函数的无限循环时,我得到了结果.这花费了我太多时间并导致头痛,如果有人能帮助我克服这些问题,我将不胜感激.谢谢你这是一个最小的代码:(为了测试这段代码,我将客户端和 gui 合并在一起)

I am trying to make a chat application using socket library. I have three files server.py, client.py and gui.py listening process for client and server are provided by infinite loops. because of that server.py runs in another terminal window. but client and gui are running in one terminal window. the problem is when I call functions containing infinite loop it stuck there and the rest of code won't run. I even tried using multiprocessing, threading.Thread, threading.Timer, QThread and Queue, but still no success. I thought that maybe I am not using these libraries correctly, so I decided to ask for some help. using threading.Timer problem was it was saying that, new parent is running in another thread, and I can not change gui objects outside main thread. it's good to mention I somehow solved the problem, and all incoming messages sent by server, eventually appear on GUI. again problem still existed and I got results when I broke the infinite loop of incoming message function by pressing ctrl+c. it's taking too much time of me and causing painful headache, I appreciate if anyone could help me get over this problems. thank you here is a minimal code:(for testing this code, I merged client and gui together)

这里是 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)

client 和 gui together.py:

client and gui together.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_()

推荐答案

我终于可以以某种方式解决问题了.我相信我们只能编辑已经在消息布局中的小部件,我们不能向任何布局添加任何小部件,因为布局是 thread-safeprocess-safe.当然,这个问题也可能有解决方案,但现在我没有想到.这是通过代码的解决方案:server.py如上所述,gui+client.py如下:

Finally I could somehow go around situation. I believe we can only edit a widget that is already inside message layout, and we can not adding any widget to any layout, because layouts are thread-safe and process-safe. off-course there maybe a solution to that problem too, but right now nothing come to my mind. here is the solution via code:server.py is as mentioned above and gui+client.py is as follows:

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_()

这篇关于如何将传入消息从套接字客户端发送到 GUI (pyqt5)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆