我将使用python来制作一款用于局域网内部的聊天软件,名为Lan Chat(局域网聊天),高度仿照微信界面、控件布局、创建大小、颜色、登录窗口等,同时含有许多功能用于管理
你可以使用这款软件在内部进行聊天,发送文字、表情、上传文件、下载文件(暂时无法使用)、私信,这是都是基本功能,还有部分管理员才能使用的功能,如:设置公告、禁言、踢人、发送弹窗、添加黑名单等,此外还有一个最顶尖的身份,那就是服主。
服主用于最高权限,也是用于功能最多的用户,如:远程控制、授权身份、文件管理等。服主还可以进入开发者后台对服务器进行设置等操作
注:只有服务器的创建者才能是服主
需要更多功能尽情评论
废话不多说我们先来看下效果
这是登录窗口
登录窗口高度还原原版微信
加载界面
主要聊天窗口
这里就是我们主要的聊天窗口了,高度还原微信。右侧中间是Lan Chan的LOGO(网络上找的),左侧就是我们的聊天对象了,默认情况下我们都会含有一个全服的群聊,也就是所有人都会在这个群聊里面,在Lan Chat中,群聊是使用一对中文中括号括起来的,图标也会显示群聊图标
当我们在左侧单单击任意对象即可进入到该对象的对话窗口中,在右下方会显示你的身份,新用户默认都是普通用户,普通用户仅支持聊天等基本功能,你可以向服主索要更高的身份
我相信你们都用过微信,那么我就不再介绍了,更多功能可以自己下载去摸索哦
下面我就要开始讲解源码了
在源码中含有多个配置文件,我主要讲解头文件
import socket,sys,re,os,struct,hashlib,TKDnD,time,random,signal
import tkinter as tk
from datetime import *
from tkinter.ttk import *
from tkinter import messagebox,scrolledtext,simpledialog,filedialog
from DataManagement import DataManagement
from CServer import CServer
import threading
import pyperclip
import multiprocessing
from multiprocessing import Process
#第三方库
import ToolTip
from pynput import keyboard
from win11toast import toast
from SysTrayIcon import SysTrayIcon
首先这是我们需要导入的库,其中导入了其他第三方库
if __name__ == "__main__":
LogonWindows = tk.Tk()
LogonWindows.title("Lan Chat")
LogonWindows["background"] = "#FFFFFF"
#获取屏幕分辨率
screen_width = LogonWindows.winfo_screenwidth()
screen_height = LogonWindows.winfo_screenheight()
def CloseProcess():
#关闭进程
pid = os.getpid() # 获取当前进程的PID
os.kill(pid, signal.SIGTERM) # 主动结束指定ID的程序运行
LogonWindows_size = "280x350+%d+%d" % (screen_width / 2 - 280 /2, screen_height / 2 - 350 / 2)
LogonWindows.tk.call("wm", "iconphoto", LogonWindows._w, tk.PhotoImage(file="icon\\LanChat.png"))
LogonWindows.protocol("WM_DELETE_WINDOW", CloseProcess)
LogonWindows.geometry(LogonWindows_size)
LogonWindows.resizable(0,0)
#布局
HeadIcon = tk.PhotoImage(file="icon\\Head.png")
Head = tk.Label(LogonWindows, image=HeadIcon, cursor="hand2")
Head.place(x=100,y=50)
#名字
Name = tk.Label(LogonWindows, text=NativeIP, font=("微软雅黑",15), bg="#FFFFFF")
Name.place(x=138,y=178,anchor="s")
def EnterLanChatBG_Enter(event):
"""进入时的背景色"""
EnterLanChat["bg"] = "#38CD7F"
def EnterLanChatBG_Leave(event):
"""离开时的背景色"""
EnterLanChat["bg"] = "#07C160"
def Logon_LanChat():
"""登录LanChat"""
if EnterIng == True:
if Entering["text"] == "正在进入 .":
Entering["text"] = "正在进入 .."
elif Entering["text"] == "正在进入 ..":
Entering["text"] = "正在进入 ..."
else:
Entering["text"] = "正在进入 ."
LogonWindows.after(250,Logon_LanChat)
elif EnterIng == "已登录":
return
elif EnterIng == "连接失败":
FailureReason["text"] = "无法连接到服务器,请检查网络连接和防火墙设置"
FailureReason.place(x=0,y=330)
Entering.place(y=1770)
Name.place(y=178)
EnterLanChat.place(y=240)
else:
FailureReason["text"] = EnterIng
FailureReason.place(x=0,y=330)
Entering.place(y=1770)
Name.place(y=178)
EnterLanChat.place(y=240)
def CallLogon():
"""登录时的中转函数"""
global EnterIng
EnterIng = True
Entering.place(y=177)
Name.place(y=1780)
EnterLanChat.place(y=2400)
FailureReason.place(y=350)
Logon_LanChat()
Logon() #进入聊天窗口
#按钮
EnterLanChat = tk.Label(LogonWindows, text="进入Lan Chat", font=("微软雅黑",11), bg="#07C160", fg="#DAF6E7", cursor="hand2")
EnterLanChat.place(x=50,y=240,width=180,height=35)
#正在进入
Entering = tk.Label(LogonWindows, text="正在进入 .", font=("微软雅黑",11), fg="#1DC66E", bg="#FFFFFF")
Entering.place(x=138,y=1770,anchor="s")
#失败原因
FailureReason = tk.Label(LogonWindows, text="无法连接到服务器,请检查网络连接和防火墙设置", bg="#FFFFFF", fg="red")
FailureReason.place(x=0,y=350)
EnterLanChat.bind("<Enter>", EnterLanChatBG_Enter)
EnterLanChat.bind("<Leave>", EnterLanChatBG_Leave)
EnterLanChat.bind("<ButtonRelease-1>", lambda event : threading.Thread(target = CallLogon).start())
LogonWindows.mainloop()
这里就是我们登录窗口的代码
def Logon():
"""登录函数"""
global c,ip,myname,loginTime,identity,SupremeIdentity,name_list,Change_Number,object_list
global All_Objects,All_object_groups,EnterIng
## print("正在连接...")
#messagebox.showinfo("S",OldPID)
c = socket.socket() #创建socket对象
Connection_method = DataManagement.ReadData("Connect\\Connection method.json") #获取连接方式
if Connection_method == "默认":
## print("连接方式:默认")
if Network_segment == "11.11.11.":
host_number = ["11"]
elif Network_segment == "10.103.250.":
host_number = ["96","160","161"]
elif Network_segment == "10.103.252.":
host_number = ["160"]
elif Network_segment == "10.103.253.":
host_number = ["14","224","161","96"]
elif Network_segment == "10.103.254.":
host_number = ["32","33"]
elif Network_segment == "192.168.32.":
host_number = ["11"]
else:
host_number = ["32","161","96","16"]
for host in host_number:
ip = Network_segment + host
try:
c.connect((ip,port)) #连接服务器
except:
if host_number.index(host) == len(host_number)-1:
## print("无法连接到服务器,请查看连接设置或联系服主")
## connect_windows = tk.Tk()
## connect_windows.withdraw()
## messagebox.showerror("连接失败","无法连接到服务器\n请查看连接设置或联系服主")
EnterIng = "连接失败"
else:
continue
else:
GUI().send_msg(c, NativeIP.encode("utf-8"))
content = GUI().handle_client(c) #接收登录信息
if content[0:4] == "登录成功":
## print("已登录服务器:%s"%ip)
loginTime = datetime.now().strftime(time_format) #记录登录时间
identity = (GUI().handle_client(c)).strip() #存放我的当前身份
SupremeIdentity = (GUI().handle_client(c)).strip() #存放我的最高身份
name_list = (GUI().handle_client(c)).strip() #在线列表
Change_Number = (GUI().handle_client(c)).strip() #修改昵称次数
#FILE_LIST = ((GUI().handle_client(c))).split("*") if GUI().handle_client(c) != None else " " #文件列表
name_list = name_list.split(",")
myname = content[4:]
object_list = name_list.copy()
object_list.remove(myname)
All_Objects = object_list.copy()
All_Objects.insert(0,"【全服】")
All_object_groups = All_Objects.copy()
GUI().main()
else:
## connect_windows = tk.Tk()
## connect_windows.withdraw()
## messagebox.showerror("登录失败",content) #显示登录失败信息
EnterIng = content
break
else:
## print("连接方式:自定义")
Server_IP = DataManagement.ReadData("Connect\\Server IP.json") #服务器IP地址
Server_IP = Server_IP.split(",")
for host in Server_IP:
ip = host
try:
c.connect((ip,port)) #连接服务器
except:
if Server_IP.index(host) == len(Server_IP)-1:
## print("无法连接到服务器,请查看连接设置或联系服主")
## connect_windows = tk.Tk()
## connect_windows.withdraw()
## messagebox.showerror("连接失败","无法连接到服务器\n请查看连接设置或联系服主")
EnterIng = "连接失败"
else:
continue
else:
GUI().send_msg(c, NativeIP.encode("utf-8"))
content = GUI().handle_client(c) #接收登录信息
if content[0:4] == "登录成功":
## print("已登录服务器:%s"%ip)
loginTime = datetime.now().strftime(time_format) #记录登录时间
identity = (GUI().handle_client(c)).strip() #存放我的当前身份
SupremeIdentity = (GUI().handle_client(c)).strip() #存放我的最高身份
name_list = (GUI().handle_client(c)).strip() #在线列表
Change_Number = (GUI().handle_client(c)).strip() #修改昵称次数
#FILE_LIST = ((GUI().handle_client(c))).split("*") if GUI().handle_client(c) != None else " " #文件列表
name_list = name_list.split(",")
myname = content[4:]
object_list = name_list.copy()
object_list.remove(content[4:])
All_Objects = object_list.copy()
All_Objects.insert(0,"【全服】")
All_object_groups = All_Objects.copy()
GUI().main()
else:
## connect_windows = tk.Tk()
## connect_windows.withdraw()
## messagebox.showerror("登录失败",content) #显示登录失败信息
EnterIng = content
break
这里就是登录的函数,主要用于判断你是否可以登录
下面就是整个聊天窗口的函数了
class GUI():
"""用户图形界面类"""
screen_width, screen_height = 0, 0 #存放屏幕分辨率
def __init__(s):
s.SysTrayIcon = None # 判断是否打开系统托盘图标
s.emoji_name = [ file_name[:file_name.rfind(".")] for file_name in os.listdir("emoji") ] #使用列表推导式获取表情包文件夹内的表情文件名
s.emoji_pictrue = [] #表情包的数组
s.Emotion_button_array = [] #表情库按钮的数组
s.MessageBox = [] #存放所有的消息控件
s.MemberFrameBox = [] #存放成员列表框架
s.MemberHandBox = [] #存放成员头像的控件
s.MemberName = [] #存放成员列表的名字控件
s.MessageTime = [] #存放成员列表的消息时间
s.MemberSmallMessage = [] #存放成员列表预览消息的控件
s.MemberLine1 = [] #存放成员列表用于布局的辅助线1
s.MemberLine2 = [] #存放成员列表用于布局的辅助线2
s.CurrentObject = "" #当前对象
s.ContentRootFrame = [] #存放消息区的所有根框架
s.MessageRootFrame = [] #存放消息区显示消息的所有根框架
s.ContentCanvasFrame = [] #存放消息区画布的框架
"初始化图片"
s.HeadSculptureIcon = tk.PhotoImage(file="icon\\HeadSculpture.png")
s.PersonalProfilePictureIcon = tk.PhotoImage(file="icon\\PersonalProfilePicture.png") #个人头像,在线成员列表显示的
s.GroundIcon = tk.PhotoImage(file="icon\\Ground.png") #群聊头像,在线成员列表显示的
s.LanChat_Icon = tk.PhotoImage(file="icon\\Lan Chat.png") #背景图标
s.NoticeIcon = tk.PhotoImage(file="icon\\公告.png")
s.SettingsIcon = tk.PhotoImage(file="icon\\设置及其他.png")
s.emojiIcon = tk.PhotoImage(file="icon\\表情.png")
s.UploadFilesIcon = tk.PhotoImage(file="icon\\上传文件.png")
s.ChatIcon = tk.PhotoImage(file="icon\\聊天.png")
s.CollectIcon = tk.PhotoImage(file="icon\\收藏.png")
s.FileIcon = tk.PhotoImage(file="icon\\文件.png")
s.CircleFriendsIcon = tk.PhotoImage(file="icon\\朋友圈.png")
s.LockIcon = tk.PhotoImage(file="icon\\锁定.png")
s.MoreIcon = tk.PhotoImage(file="icon\\更多.png")
def send_msg(s, sock, msg):
"""发送消息"""
# 计算消息长度
msglen = len(msg)
# 将消息长度编码为4个字节的二进制数据
msglen_bytes = msglen.to_bytes(4, byteorder="little")
# 发送消息长度
sock.sendall(msglen_bytes)
# 发送消息内容
sock.sendall(msg)
def recv_msg(s,sock):
"""接收信息"""
# 先接收4个字节的消息长度
raw_msglen = sock.recv(4)
if not raw_msglen:
return None
# 将消息长度解码为整数
msglen = int.from_bytes(raw_msglen, byteorder="little")
# 接收实际的消息内容
msg = b""
while len(msg) < msglen:
chunk = sock.recv((msglen - len(msg)))
if not chunk:
return None
msg += chunk
return msg
def handle_client(s,conn):
"""处理消息"""
# 接收消息
data = s.recv_msg(conn)
if not data:
return
# 处理消息
message = data.decode()
return message
def main(s):
global All_object_groups,EnterIng
#窗口设置
s.root = tk.Toplevel(LogonWindows)
s.root.withdraw() #隐藏窗口,等待所有控件加载完毕
s.root.protocol("WM_DELETE_WINDOW",lambda:s.Hidden_window()) #点击关闭窗口时,将窗口隐藏至托盘
s.root.title("Lan Chat")
#获取屏幕分辨率
screen_width = s.root.winfo_screenwidth()
screen_height = s.root.winfo_screenheight()
window_size = "910x630+%d+%d"%(screen_width / 2 - 910 /2, screen_height / 2 - 630 / 2)
s.root.tk.call("wm", "iconphoto", s.root._w, tk.PhotoImage(file="icon\\LanChat.png"))
s.root.geometry(window_size)
s.root.resizable(0,0)
#s.root["background"] = "#F5F5F5"
#根框架
root_frame = tk.Frame(s.root, bg="#F5F5F5")
root_frame.place(x=0,y=0,width=1215,height=630)
"变量存储器---------------------------------------------------------------------------------------------------------"
people_ID = tk.StringVar() #当前身份存储
anonymous_values = tk.IntVar() #匿名存储器的值
automatically_send_value = tk.IntVar() #自动发送存储器 True开启 False关闭
CustomNicknameWordCount = tk.IntVar() #自定义的昵称字数
AllowChatting = tk.IntVar() #允许成员变量存储
AllowChatting.set(1) #默认值为1
AllowLogin = tk.IntVar() #允许登录服务器存储
AllowLogin.set(1) #默认值为1
AutoDisplay = tk.IntVar() #自动显示最新消息
AutoDisplay.set(1) #默认值为1
#功能区数据存储
DetailedInformationSwitch = tk.IntVar() #详细信息
UploadFile_Switch = tk.IntVar() #上传文件
DownloadFile_Switch = tk.IntVar() #下载文件
ChangeNickname_Switch = tk.IntVar() #修改昵称
GroupChatManagement_Switch = tk.IntVar() #群聊管理
ModifyIdentity_Switch = tk.IntVar() #修改身份
"初始化表情图片"
for expression in range(0,len(s.emoji_name)-1):
s.emoji_pictrue.append(tk.PhotoImage(file="emoji\\%s.png"%s.emoji_name[expression]))
def Offline():
"""下线"""
global EnterIng
#下线通知
Offline_notification = messagebox.askokcancel("退出登录","退出登录后将无法收到新消息,\n确定退出登录?",parent=s.root,default="cancel")
if Offline_notification:
## pid = os.getpid() # 获取当前进程的PID
## os.kill(pid, signal.SIGTERM) # 主动结束指定ID的程序运行
EnterIng = ""
c.close() #关闭套接字
s.root.destroy()
Entering.place(y=1770)
Name.place(y=178)
EnterLanChat.place(y=240)
LogonWindows.deiconify()
else:
return
由于这个函数多达3000多行,这里就展示一点点啦
软件我已经打包好放在我的超星(学习通)课程里面了,课程邀请码是:69346126,需要的可以进入我的课程前往资料里面下载啦,你们应该用超星(学习通)吧,不用也没关系,我来教你吧
如果你是手机的话直接在应用商店搜索“学习通”下载就可以了
如果你是网页版就在浏览器(随便一个,例如:Microsoft Edge)搜索URL(网址):https://i.chaoxing.com/
然后你就会进入登录界面
然后你需要登录或注册进入(注册不用我说了吧,应该都会)
进来后点击右上角的“输入邀请码”
接着输入邀请码:14115314,点击确定,然后点击加入课程就可以啦
回到主页面(我学的课)刷新一下,就会新增一个课程,点击进入,在左侧找到资料,第一个Lan Chat(局域网聊天)就是这款软件啦,直接下载就可以了,含有服务器和客户端,你需要把客户端给其他人安装再登录你的服务器就可以进行聊天了,有什么待改进或者BUG或者需要添加新功能的都可以再左侧的讨论发表你的意见喔
我也会在课程里面分享更多资源哦
软件持续更新中,最新版见超星课程资料