代码拉取完成,页面将自动刷新
from tkinter import *
from tkinter import messagebox
from socket import *
import json
from threading import Thread
WINTH = 800
HEIGHT = 500
class Friend:
def __init__(self, name) -> None:
self.name = name
self.history = []
def add_history(self, msg):
"""history: {"opp": T, "msg": msg}"""
self.history.append(msg)
class Chat:
def __init__(self, master, client_obj, width=WINTH, heigt= HEIGHT):
self.master = master
self.width = width
self.height = heigt
self.client = client_obj
self.name = ""
self.friend_dict = {} # name: Friend obj
self._geometry()
self._login_window()
self.master.protocol("WM_DELETE_WINDOW", self._handle_logout)
def _handle_logout(self):
try:
self.client.sendall(json.dumps({"from": self.name, "type": 0, "login": 0}).encode("utf-8"))
except ConnectionResetError:
pass
self.client.close()
self.master.destroy()
def _geometry(self):
win_width = self.master.winfo_screenwidth() # 屏幕的宽度
win_height = self.master.winfo_screenheight() # 屏幕的高度
x = (win_width-WINTH) // 2
y = (win_height-HEIGHT)//2
self.master.geometry(f"{WINTH}x{HEIGHT}+{x}+{y}")
self.master.resizable(False, False)
def _login_window(self):
name_label = Label(master=self.master, text="用户名:", bg="lightgray", width=10, font=("Timer", 15))
name_label.place(x=200, y= 200)
name_entry = Entry(self.master, width=30, font=("Timer", 15))
name_entry.place(x=320, y= 200)
def handle_sign():
name = name_entry.get()
if name == "":
messagebox.showwarning(title="Warning", message="用户名不能为空")
return
self.name = name
self.master.title(name)
first_data = {"from": name, "type": 0, "login": 1}
self.client.sendall(json.dumps(first_data).encode("utf-8"))
Thread(target=self.handle_listen).start()
for child in self.master.winfo_children():
child.destroy()
self._chat_window()
sign_in = Button(master=self.master, text=" 登录 ", font=("Timer", 15), command=handle_sign)
sign_in.place(x=330,y=260)
def _parse_data(self, data):
if data['type']: # {from: xxx, to: xxx, msg: xxx}
friend: Friend = self.friend_dict[data['from']]
friend.add_history({"opp": True, "msg": data['msg']})
if data['from'] != self.title.get():
for child in self.friend_list.winfo_children():
if getattr(child, "name") == data['from']:
child.config(bg="lightpink")
for inner_child in child.winfo_children():
inner_child.config(bg="lightpink")
break
return
self._update_content_frame(data['msg'], opp_flag=True)
else: # {type: 0, name:}
del data['type']
del data[self.name]
if not hasattr(self, "friend_list"):
self._chat_window()
for child in self.friend_list.winfo_children():
child.destroy()
new_friend_dict = {}
for name, _ in self.friend_dict.items():
if name in data.keys():
new_friend_dict[name] = self.friend_dict[name]
self.friend_dict = new_friend_dict
if self.title.get() not in new_friend_dict.keys():
self._clear_talk_frame()
for idx, name in enumerate(data.keys()):
if name not in self.friend_dict:
friend = Friend(name=name)
self.friend_dict[name] = friend
self.generate_friend(idx, name)
def _update_all_content(self, name):
friend = self.friend_dict[name]
for msg in friend.history:
self._update_content_frame(msg["msg"], opp_flag=msg["opp"])
def generate_friend(self, row_idx, name):
def callback(event:Event):
event.widget.configure(bg="#4c504c")
for child in event.widget.winfo_children():
child.configure(bg="#4c504c")
self._clear_talk_frame()
self.title.set(getattr(event.widget, "name"))
self._update_all_content(getattr(event.widget, "name"))
f1_frame = Frame(master=self.friend_list, bg="#4c504c", relief="solid",bd=0.5, width=240, height=75)
f1_frame.grid(row=row_idx, column=0)
setattr(f1_frame, "name", name)
f1_frame.bind("<Button-1>", callback)
name_label = Label(master=f1_frame, text=name, font=("微软雅黑 Light", 15), bg="#4c504c")
name_label.place(rely=0.05, relx=0.3)
#name_label.bind("<Button-1>", callback)
setattr(name_label, "name", name)
def handle_listen(self):
while True:
try:
data = self.client.recv(BUFF).decode("utf-8")
if not data:
continue
data = json.loads(data)
self._parse_data(data)
except ConnectionAbortedError:
exit(1)
def _clear_talk_frame(self):
self.title.set("")
for child in self.content_frame.winfo_children():
child.destroy()
self.enter_text.delete("1.0", "end")
def _update_content_frame(self, msg, opp_flag: bool):
if opp_flag:
bg="white"
side = "left"
else:
bg = "lightgreen"
side = "right"
msg_frame = Frame(master=self.content_frame, bg="lightgray")
msg_frame.pack(fill=X, padx=10, pady=3)
Label(master=msg_frame, text=msg, font=("宋体", 15), bg=bg).pack(side=side)
def _handle_send_btn(self):
to_name = self.title.get()
if not to_name:
return
text = self.enter_text.get("1.0", "end").strip()
data_pack = {"type": 1, "from": self.name, "to": to_name, "msg": text}
self.client.sendall(json.dumps(data_pack).encode("utf-8"))
self.enter_text.delete("1.0", "end")
self.friend_dict[to_name].add_history({"opp": False, "msg": text}) # 找到对象的好友,然后把聊天记录更新上去
self._update_content_frame(text, opp_flag=False)
def _chat_window(self):
self.friend_list = Frame(master=self.master, bg="#4c504c")
self.friend_list.place(x=0, y=0, relwidth=0.3, relheight=1)
talk_part = Frame(master=self.master, bg="#9aeda3")
talk_part.place(relx=0.3, relheight=1, relwidth=0.7, rely=0)
title_frame = Frame(master=talk_part, bg="lightgray", relief="solid", bd=0.5)
title_frame.place(relx=0, rely=0, relheight=0.12, relwidth=1)
self.title = StringVar()
title_label = Label(master=title_frame,textvariable=self.title, font=("微软雅黑 Light", 25),bg="lightgray")
title_label.place(x=15, y=6)
self.content_frame = Frame(master=talk_part, bg="lightgray")
self.content_frame.place(relx=0, rely=0.12, relheight=0.66, relwidth=1)
entry_frame = Frame(master=talk_part)
entry_frame.place(relx=0, rely=0.78, relheight=0.22, relwidth=1)
self.enter_text = Text(master=entry_frame, bg="lightgray")
self.enter_text.place(x=0, y=0, relwidth=1, relheight=1)
send_btn =Button(master=entry_frame, text=" 发送 ", command=self._handle_send_btn)
send_btn.place(relx=0.9, rely=0.7)
if __name__ == "__main__":
BUFF = 4096
client = socket(AF_INET, SOCK_STREAM)
client.connect(('localhost', 9999))
print("connect success!")
tk = Tk()
chat = Chat(master=tk, client_obj=client)
tk.mainloop()
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。