Python网络编程-GUI聊天(tkinter+socket)

上一篇Python网络编程-socket介绍了socket的原理和一个多人聊天的案例,今天给多人聊天加上一个图形界面。

服务器端的代码不需要改变,只需要在客户端做一些改变,将消息的接收和发送(的显示)从原来的命令行改为图形界面。

最终效果如下:

Python网络编程-GUI聊天(tkinter+socket)

client.py

class Client:
    def __init__(self, host, port):

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((host, port))

        # 创建消息窗口,获取用户名字
        msg = tkinter.Tk()
        msg.withdraw()
        self.name = simpledialog.askstring('name''input your username:', parent=msg)

        self.gui_done = False
        self.running = True
        gui_thread = threading.Thread(target=self.gui_loop) 
        receive_thread = threading.Thread(target=self.receive)
        gui_thread.start()
        receive_thread.start()

这次我们使用类来实现client。在初始化方法中,创建socket并和服务器连接。

self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port))

然后用tk的dialog获取用户的姓名:

msg = tkinter.Tk()
msg.withdraw()
self.name = simpledialog.askstring('name''input your username:', parent=msg)

Python网络编程-GUI聊天(tkinter+socket)

然后创建两个线程,分别处理消息和图形界面。

gui_thread = threading.Thread(target=self.gui_loop) 
receive_thread = threading.Thread(target=self.receive)
gui_thread.start()
receive_thread.start()

接下来在gui_loop中编写图形界面:

def gui_loop(self):
    self.win = tkinter.Tk()
    self.win.configure(bg='lightgray')

    self.chat_label = tkinter.Label(self.win, text='chat:', bg='lightgray')
    self.chat_label.config(font=('Arial'12))
    self.chat_label.pack(padx=20, pady=5)

    self.text_area = tkinter.scrolledtext.ScrolledText(self.win)
    self.text_area.pack(padx=20, pady=5)
    self.text_area.config(state='disabled')

    self.msg_label = tkinter.Label(self.win, text='message:', bg='lightgray')
    self.msg_label.config(font=('Arial'12))
    self.msg_label.pack(padx=20, pady=5)

    self.input_area = tkinter.Text(self.win, height=3)
    self.input_area.pack(padx=20, pady=5)

    self.send_btn = tkinter.Button(self.win, text='Send', command=self.write)
    self.send_btn.pack(padx=20, pady=5)

    # print('self.gui_done')
    self.gui_done = True
    self.win.protocol('WM_DELETE_WINDOW', self.stop)
    self.win.mainloop() #  tk事件循环

Python网络编程-GUI聊天(tkinter+socket)

消息发送write和接收receive

def write(self):
    message = f"{self.name}{self.input_area.get('1.0''end')}"
    self.sock.send(message.encode("utf-8"))
    self.input_area.delete('1.0''end')

def receive(self):
    while self.running:
        try:
            message = self.sock.recv(1024).decode('utf-8')
            if message == 'name:':
                self.sock.send(self.name.encode('utf-8'))
            else:
                if self.gui_done:
                    self.text_area.config(state='normal')
                    self.text_area.insert('end', message)
                    self.text_area.yview('end')
                    self.text_area.config(state='disabled')

        except ConnectionAbortedError:
            print(f"ConnectionAbortedError.  exit")
            break

        except Exception as e:
            print(f"exception{e} happened... exit")
            self.sock.close()
            break

def stop(self):
    # 程序退出时,关闭窗口和socket连接
    print('stop...')
    self.running = False
    self.win.destroy()
    self.sock.close()
    exit(0)


完整代码:

client.py

# client.py
import socket
import threading
import tkinter
import tkinter.scrolledtext
from tkinter import simpledialog


class Client:
    def __init__(self, host, port):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((host, port))

        msg = tkinter.Tk()
        msg.withdraw()
        self.name = simpledialog.askstring('name''input your username:', parent=msg)

        self.gui_done = False
        self.running = True
        gui_thread = threading.Thread(target=self.gui_loop)
        receive_thread = threading.Thread(target=self.receive)
        receive_thread.start()
        gui_thread.start()

    def gui_loop(self):
        self.win = tkinter.Tk()
        self.win.configure(bg='lightgray')

        self.chat_label = tkinter.Label(self.win, text='chat:', bg='lightgray')
        self.chat_label.config(font=('Arial'12))
        self.chat_label.pack(padx=20, pady=5)

        self.text_area = tkinter.scrolledtext.ScrolledText(self.win)
        self.text_area.pack(padx=20, pady=5)
        self.text_area.config(state='disabled')

        self.msg_label = tkinter.Label(self.win, text='message:', bg='lightgray')
        self.msg_label.config(font=('Arial'12))
        self.msg_label.pack(padx=20, pady=5)

        self.input_area = tkinter.Text(self.win, height=3)
        self.input_area.pack(padx=20, pady=5)

        self.send_btn = tkinter.Button(self.win, text='Send', command=self.write)
        self.send_btn.pack(padx=20, pady=5)

        # print('self.gui_done')
        self.gui_done = True
        self.win.protocol('WM_DELETE_WINDOW', self.stop)
        self.win.mainloop() #  tk事件循环

    def write(self):
        message = f"{self.name}{self.input_area.get('1.0''end')}"
        self.sock.send(message.encode("utf-8"))
        self.input_area.delete('1.0''end')

    def receive(self):
        while self.running:
            try:
                message = self.sock.recv(1024).decode('utf-8')
                if message == 'name:':
                    self.sock.send(self.name.encode('utf-8'))
                else:
                    if self.gui_done:
                        self.text_area.config(state='normal')
                        self.text_area.insert('end', message)
                        self.text_area.yview('end')
                        self.text_area.config(state='disabled')

            except ConnectionAbortedError:
                print(f"ConnectionAbortedError.  exit")
                break

            except Exception as e:
                print(f"exception{e} happened... exit")
                self.sock.close()
                break

    def stop(self):
        print('stop...')
        self.running = False
        self.win.destroy()
        self.sock.close()
        exit(0)


host = '127.0.0.1'
port = 55555

client = Client(host, port)

server.py

# server.py
import socket
import threading

host = '127.0.0.1'
port = 55555

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen()

clients = []
usernames = []


def broadcast(message):
    for client in clients:
        client.send(message)


def handle(client):
    while True:
        try:
            message = client.recv(1024)
            broadcast(message)
        except Exception:
            index = clients.index(client)
            clients.remove(client)
            username = usernames[index]
            broadcast(f"{username} left the chat!".encode('utf-8'))
            usernames.remove(username)
            break


def reveive():
    while True:
        client, address = server.accept()
        print(f"Connected with {address}")
        client.send("name:".encode('utf-8'))
        name = client.recv(1024).decode('utf-8')
        usernames.append(name)
        clients.append(client)

        print(f"user name is {name}")
        broadcast(f"{name} joined the chat!".encode("utf-8"))
        client.send(f"connected to the server!".encode('utf-8'))

        thread = threading.Thread(target=handle, args=(client,))
        thread.start()


print("server is listening...")
reveive()


参考:https://www.youtube.com/watch?v=sopNW98CRag


原文始发于微信公众号(一只大鸽子):Python网络编程-GUI聊天(tkinter+socket)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/237640.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!