上一篇Python网络编程-socket介绍了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)
然后创建两个线程,分别处理消息和图形界面。
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事件循环
消息发送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