python 简易微信实现(注册登录+数据库存储+聊天+GUI+文件传输)

python socket+tkinter详解+简易微信实现

历经多天的努力,查阅了许多大佬的博客后终于实现了一个简易的微信O(∩_∩)O~~

简易数据库的实现

使用pands+CSV实现数据库框架搭建

import socket
import threading
from pandas import *
import pymysql
import csv
# 创建DataFrame对象
# 存储用户数据的表(状态没用上。。。)
df = pandas.DataFrame([['whs','123','1']], columns=['account','password','state'], index=[0])
df.to_csv('user.csv',index=0)
#储存好友关系
df = pandas.DataFrame([['whs','wyl']], columns=['friend1','friend2'], index=[0])
df.to_csv('link.csv',index=0)
#存储待添加好友关系的表
df = pandas.DataFrame([['whs','@@@']], columns=['friend1','friend2'], index=[0])
df.to_csv('to_be_link.csv',index=0)
#储存在线人
df = pandas.DataFrame([['whs','127.0.0.1']], columns=['name','addr'], index=[0])
df.to_csv('onlinelist.csv',index=0)
#在客户端用于存储最近收到的信息
df = pandas.DataFrame([['@@@','@@@','@@@']], columns=['name','title','message'], index=[0])
df.to_csv('resent_message.csv',index=0)
print(df)

通过对csv表的操作实现对好友操作

添加在线人
def addonlinelist(name,addr,connectionSocket):
   onlinelist = read_csv('onlinelist.csv', dtype={'name': str,'addr': str})
   #打开csv文件并指定格式
   flag = onlinelist[onlinelist.name == name].shape[0]
   #如果数据不存在
   if(flag!=0):
      k = onlinelist[onlinelist.name == name].index.tolist()[0]
      onlinelist=onlinelist.drop(k)
      #如果有之前的残余数据则删去
   connect_list.append(connectclient(name,addr,connectionSocket))
   new = DataFrame({'name': name, 'addr': str(addr)},index=[0])
   onlinelist = onlinelist.append(new,ignore_index=True)
   #存储,并且忽略index
   onlinelist.to_csv('onlinelist.csv',index=0)

对用户登录的检验

语法与之前几乎一样,不做赘述

def adduser(name,passwd,status):#添加使用者
   user = read_csv('user.csv', dtype={'name': str,'passwd': str})
   flag = user[user.name == name].shape[0]
   if(flag != 0):
      return False
   new = DataFrame({'name': name, 'passwd': passwd, 'status': status}, index=[0])
   user = user.append(new, ignore_index=True)
   user.to_csv('user.csv',index=0)
   return True
def checkexist(name):#查看用户是否存在
   user = read_csv('user.csv', dtype={'name': str,'passwd': str})
   flag = user[user.name==name].shape[0]
   if(flag==0):
      return False
   else:
      return True
def checkuser(name,passwd):#查看用户名密码正确性
   user = read_csv('user.csv', dtype={'name': str,'passwd': str})
   if(passwd==str(user[user.name == name].passwd.tolist()[0])):
      return True
   else:
      return False

对用户删除添加好友接受好友请求

def deletefriend(name1,name2):#删除好友
   link = read_csv('link.csv', dtype={'friend1': str,'friend2': str})
   flag = link[((link.friend1 == name1) & (link.friend2 == name2)) | ((link.friend1 == name2) & (link.friend2 == name1))].shape[0]
   if(flag == 0):
      return 'not_exist'
   k = link[((link.friend1 == name1) & (link.friend2 == name2)) | ((link.friend1 == name2) & (link.friend2 == name1))].index.tolist()[0]
   link = link.drop(k)
   link.to_csv('link.csv', index=0)
   return 'delete_ok'
def addfriend(name1,name2):#添加好友
   if(isfriend(name1,name2)==True):
      return 'already_friend'
   to_be_link = read_csv('to_be_link.csv', dtype={'friend1': str,'friend2': str})
   user = read_csv('user.csv')
   flag = user[(user.name == name2)].shape[0]
   if(flag == 0):
      return 'not_exist'
   new = DataFrame({'friend1':name1, 'friend2':name2}, index = [0])
   to_be_link = to_be_link.append(new,ignore_index=True)
   to_be_link.to_csv('to_be_link.csv',index=0)
   return 'add_to_be'
def acceptfriend(name1,name2):#接收好友请求
   link = read_csv('link.csv', dtype={'friend1': str,'friend2': str})
   to_be_link = read_csv('to_be_link.csv', dtype={'friend1': str,'friend2': str})
   print(type(name1))
   print(type(name1))
   if (istobefriend(name1, name2) == False):
      return 'not_exist'
   print('tttttttttt')
   new = DataFrame({'friend1': name1, 'friend2': name2}, index=[0])
   link = link.append(new, ignore_index=True)
   link.to_csv('link.csv', index=0)
   k = to_be_link[((to_be_link.friend1 == name1) & (to_be_link.friend2 == name2)) | ((to_be_link.friend1 == name2) & (to_be_link.friend2 == name1))].index.tolist()[0]
   to_be_link = to_be_link.drop(k)
   to_be_link.to_csv('to_be_link.csv',index=0)
   # connectionSocket.send(('添加好友成功').encode())
   return 'accept_ok'
def delete_to_be_friend(name1,name2):#从待接收列表中删除
   to_be_link = read_csv('to_be_link.csv', dtype={'friend1': str,'friend2': str})
   k = to_be_link[((to_be_link.friend1 == name1) & (to_be_link.friend2 == name2)) | (
            (to_be_link.friend1 == name2) & (to_be_link.friend2 == name1))].index.tolist()[0]
   to_be_link = to_be_link.drop(k)
   to_be_link.to_csv('to_be_link.csv', index=0)
def isfriend(name1,name2):#判断两个人是否是好友
   link = read_csv('link.csv')
   flag = link[((link.friend1 == name1) & (link.friend2 == name2)) | ((link.friend1 == name2) & (link.friend2 == name1))].shape[0]
   if (flag == 0):
      return False
   return True
def istobefriend(name1,name2):#判断是否发起好友请求
   to_be_link = read_csv('to_be_link.csv', dtype={'friend1': str,'friend2': str})
   flag = to_be_link[((to_be_link.friend1 == name1) & (to_be_link.friend2 == name2)) | (
            (to_be_link.friend1 == name2) & (to_be_link.friend2 == name1))].shape[0]
   if (flag == 0):
      return False
   return True


   

打印好友列表(待添加好友列表)

def printlist(name):#打印好友列表
   link = read_csv('link.csv', dtype={'friend1': str,'friend2': str})
   k = link[(link.friend1 == name)|(link.friend2 == name)].shape[0]
   if(k==0):
      return '.'
   # connectionSocket.send(('您的好友列表如下').encode())
   message = '/'.join(link[link.friend1 == name].friend2.tolist()+link[link.friend2 == name].friend1.tolist())
   # connectionSocket.send(message.encode())
   print(message)
   return '/' + message
def printtobelist(name):#打印待同意好友列表
   to_be_link = read_csv('to_be_link.csv', dtype={'friend1': str,'friend2': str})
   k = to_be_link[(to_be_link.friend2 == name)].shape[0]
   if (k == 0):
      return '.'
   # connectionSocket.send(('有如下的人要添加你为好友,想接受则输入该好友名字,否则输入/exit/退出').encode())
   print(to_be_link[to_be_link.friend2 == name].friend1.tolist())
   message = '/'.join(to_be_link[to_be_link.friend2 == name].friend1.tolist())
   return '/'+message

socket+thread实现多线程通信

通信协议

'''
2.0 报头
   服务器和客服端之间,使用自己定义的报头进行通信
协议整体大致呈树形结构,收到消息后进行一层层解析,得到最终的结果。
2.1 注册
注册发送
root/reg/name/passwd
注册接收
register_ok(注册成功),register_error(用户名以及存在)
2.2 登录
登录发送
root/reg/name/passwd
登录接收
noPassword,user,noAccount
2.3 获取好友列表
获取已经有的好友的列表
root/friend_op(操作类型)/print_friend_list(操作方式)/myname
获取向你提出好友申请的人的列表
root/friend_op(操作类型)/print_to_be_list(操作方式)/myname
2.4 添加好友
root/friend_op(操作类型)/add(操作方式)/myname/othername
服务器端发出的信息
root/friend_op/(delete)(add)/othername/state('not_exist''delete_ok''already_friend''not_exist''add_to_be')
用户不存在    成功
2.5 好友请求回复
root/friend_op(操作类型)/accept(操作方式)/myname/othername
2.6 删除好友
root/friend_op(操作类型)/delete(操作方式)/myname/othername
2.7 [服务器]好友请求
root/friend_op(操作类型)/add(操作方式)/othername
2.8 [服务器]好友请求结果
root/friend_op/add/othername/state('not_exist''already_friend''add_to_be')
状态分为分为该用户不存在,你们已经是朋友,成功发出好友请求三种。
2.9 发送消息和文件
从客服端传输信息的格式
root/chat/message/from_name/to_name/message_content
客户端发送文件的格式
root/chat/file/from_name/to_name
传输是否在线的系统消息
2.10 [服务器]消息和文件
root/chat/file/from_name/begin(end)
root/chat/message/to_name/message_content
2.11 离线
root/chat/info/system_message/from_name
(判断是否在线后发送)
'''

服务器的连接

while True:
   connectionSocket, addr = serverSocket.accept()
   print(addr)
   # connectionSocket.send(('连接主机成功').encode())
   # 每当客户端连接后启动一个线程为该客户端服务
   threading.Thread(target=server_target, args=(connectionSocket, addr,)).start()
serverSocket.close()

客户端的连接

if __name__ == '__main__':
    t = Client()
    main_login()

服务器端对消息的处理

def server_target(connectionSocket, addr):#服务器主要函数
   while(1):
      message = connectionSocket.recv(1024).decode()
      deal_message = message.split('/')
      print(deal_message)
      lenth = len(deal_message)
      print(deal_message[0])
      if(deal_message[0]=='un_root'):#收到则退出
         connectionSocket.send(('un_root').encode())
         break
      else:
         if(deal_message[1] == 'login'):#进行登录操作
            account = deal_message[2]
            password = deal_message[3]
            if(checkexist(account)==False):
               connectionSocket.send(('noAccount').encode())
            elif(checkuser(account,password)==False):
               connectionSocket.send(('noPassword').encode())
            else:
               connectionSocket.send(('user').encode())
               addonlinelist(account, addr, connectionSocket)
         elif(deal_message[1] == 'register'):#进行注册
            account = deal_message[2]
            password = deal_message[3]
            if(adduser(account, password, 1)):
               connectionSocket.send(('register_ok').encode())
               addonlinelist(account, addr, connectionSocket)
            else:
               connectionSocket.send(('register_error').encode())
         elif(deal_message[1] == 'chat'):#进行信息交互
            type = deal_message[2]
            account = deal_message[3]
            friend_account = deal_message[4]
            if(type == 'file'):
               for clients in connect_list:
                  if (str(clients.name) == str(friend_account)):
                     print(('root/chat/' + type + '/' + account + '/begin'))
                     try:
                        clients.connectionSocket.send(
                           ('root/chat/' + type + '/' + account + '/begin').encode())
                     except:
                        pass
               # 申请相同大小的空间存放发送过来的文件名与文件大小信息
               fileinfo_size = struct.calcsize('128sl')
               # 接收文件名与文件大小信息
               buf = connectionSocket.recv(fileinfo_size)
               for clients in connect_list:
                  if (str(clients.name) == str(friend_account)):
                     print(('root/chat/' + type + '/' + account + '/begin'))
                     try:
                        clients.connectionSocket.send(buf)
                     except:
                        pass
               # 判断是否接收到文件头信息
               if buf:
                  # 获取文件名和文件大小
                  filename, filesize = struct.unpack('128sl', buf)
                  fn = filename.strip(b'\00')
                  fn = fn.decode()
                  print ('file new name is {0}, filesize if {1}'.format(str(fn), filesize))

                  recvd_size = 0  # 定义已接收文件的大小
                  # 存储在该脚本所在目录下面
                  print ('start receiving...')
                  # 将分批次传输的二进制流依次写入到文件
                  while not (recvd_size == filesize):
                     if filesize - recvd_size > 1024:
                        data = connectionSocket.recv(1024)
                        recvd_size += len(data)
                     else:
                        data = connectionSocket.recv(filesize - recvd_size)
                        recvd_size = filesize
                     for clients in connect_list:
                        if (str(clients.name) == str(friend_account)):
                           print(data)
                           try:
                              clients.connectionSocket.send(data)
                           except:
                              pass
                  print('end receive...')
            elif(type == 'message'):
               message = deal_message[5]+'/'+deal_message[6]
               isfind = False
               for clients in connect_list:#遍历list发送信息
                  if(str(clients.name) == str(friend_account)):
                     print(('root/chat/' + type + '/' + account + '/' + message))
                     try:
                        clients.connectionSocket.send(('root/chat/'+type+'/'+account+'/'+ message).encode())
                        isfind = True
                     except:
                        pass
               if(isfind == False):
                  connectionSocket.send(('root/chat/info/'+friend_account+'/'+'对方不在线无法接收你的消息').encode())
                  # print(('root/chat/'+friend_account+'/'+account+'/'+'对方不在线无法接收你的消息'))
         elif(deal_message[1] == 'friend_op'):#对于好友命令进行操作
            print(deal_message[2])
            if(deal_message[2] == 'add'):
               account = deal_message[3]
               friend_account = deal_message[4]
               connectionSocket.send(('root/friend_op/add/' + friend_account + '/' + addfriend(account, friend_account)).encode())
            elif(deal_message[2] == 'accept'):
               account = deal_message[3]
               friend_account = deal_message[4]
               acceptfriend(account, friend_account)
               connectionSocket.send(('root/friend_op/accept/' + friend_account).encode())
            elif(deal_message[2] == 'delete'):
               account = deal_message[3]
               friend_account = deal_message[4]
               connectionSocket.send(('root/friend_op/delete/'+friend_account+'/'+deletefriend(account,friend_account)).encode())
            elif(deal_message[2] == 'reject'):
               account = deal_message[3]
               friend_account = deal_message[4]
               delete_to_be_friend(account,friend_account)
            elif(deal_message[2] == 'print_friend_list'):
               account = deal_message[3]
               print(printlist(account))
               connectionSocket.send((('root/friend_op/print_friend_list'+printlist(account)).strip('.')).encode())
               print(('root/friend_op/print_friend_list'+printlist(account)).strip('.'))
            elif(deal_message[2] == 'print_to_be_friend_list'):
               account = deal_message[3]
               connectionSocket.send((('root/friend_op/print_to_be_friend_list' + printtobelist(account)).strip('.')).encode())
               print(('root/friend_op/print_to_be_frined_list' + printtobelist(account)).strip('.'))

客户端对信息的处理

class Client(object):
    def __init__(self):
        self.connecntsocket = socket(AF_INET, SOCK_STREAM)
        self.connecntsocket.connect(('127.0.0.1', 12000))
        #连接ip和端口
        self.myqueue=queue.Queue()
        self.name  = ''
        self.flag = True
        self.friend_list = []
        self.to_be_friend_list = []
    def read_from_server(self):
        while True:
            if(self.flag==False):
                break
            message = self.connecntsocket.recv(1024).decode()
            print(message)
            deal_message = message.split('/')
            print(deal_message)
            lenth = len(deal_message)
            print(deal_message[0])
            if(deal_message[0] == 'un_root'):
                break
            else:
                if(deal_message[1] == 'chat'):
                    if(deal_message[2] == 'message'):#处理信息,放到该放的地方
                        print('message')
                        friend_account = deal_message[3]
                        friend_title = deal_message[4]
                        friend_message = deal_message[5]
                        resent_message = read_csv('resent_message.csv')
                        new = DataFrame({'name': friend_account, 'title': friend_title, 'message': friend_message}, index=[0])
                        resent_message = resent_message.append(new, ignore_index=True)
                        resent_message.to_csv('resent_message.csv', index=0)
                        isfind = False
                        for Chat_window in Chat_list:
                            if(Chat_window.name == friend_account):
                                try:
                                    Chat_window.insert_message(friend_title,friend_message)
                                except:
                                    pass
                                isfind = True
                        if(isfind == False):#to be continue
                            pass
                    elif(deal_message[2] == 'file'):
                        if(deal_message[4] == 'begin'):

                            # 申请相同大小的空间存放发送过来的文件名与文件大小信息
                            fileinfo_size = struct.calcsize('128sl')
                            # 接收文件名与文件大小信息
                            buf = t.connecntsocket.recv(fileinfo_size)
                            # 判断是否接收到文件头信息
                            if buf:
                                # 获取文件名和文件大小
                                filename, filesize = struct.unpack('128sl', buf)
                                fn = filename.strip(b'\00')
                                fn = fn.decode()
                                print ('file new name is {0}, filesize if {1}'.format(str(fn), filesize))

                                recvd_size = 0  # 定义已接收文件的大小
                                # 存储在该脚本所在目录下面
                                fp = open('./' + str('new' + fn), 'wb')
                                print ('start receiving...')

                                # 将分批次传输的二进制流依次写入到文件
                                while not recvd_size == filesize:
                                    if filesize - recvd_size > 1024:
                                        data = t.connecntsocket.recv(1024)
                                        recvd_size += len(data)
                                    else:
                                        data = t.connecntsocket.recv(filesize - recvd_size)
                                        recvd_size = filesize
                                    fp.write(data)
                                fp.close()
                                print ('end receive...')
                    elif(deal_message[2] == 'info'):
                        friend_account = deal_message[3]
                        system_info = deal_message[4]
                        isfind = False
                        for Chat_window in Chat_list:
                            if (Chat_window.name == friend_account):
                                Chat_window.insert_info(system_info)
                                isfind = True
                        if (isfind == False):
                            pass
                elif(deal_message[1] == 'friend_op'):
                    if(deal_message[2] == 'delete'):
                        if(deal_message[4] == 'not_exist'):
                            self.creatbox(deal_message[3] + '不在您的好友列表中')
                        elif(deal_message[4] == 'delete_ok'):
                            self.creatbox('已经把'+deal_message[3]+'从您的好友列表删除')
                    elif(deal_message[2] == 'add'):
                        if(deal_message[4] == 'already_friend'):
                            self.creatbox('你和' + deal_message[3]+'已经是好友了')
                        elif(deal_message[4] == 'not_exist'):
                            self.creatbox('用户' + deal_message[3] + '不存在')
                        elif(deal_message[4 == 'add_to_be']):
                            self.creatbox('已经发送对'+deal_message[3]+'的好友申请')
                    elif(deal_message[2] == 'accept'):
                        self.creatbox('已经把' + deal_message[3] + '添加到您的好友列表')
                    elif(deal_message[2] == 'print_friend_list'):
                        self.friend_list = deal_message[3:lenth]
                    elif(deal_message[2] == 'print_to_be_friend_list'):
                        self.to_be_friend_list = deal_message[3:lenth]
                        print(1)
    def creatbox(self,message):
        tkinter.messagebox.showinfo(title='miniWechat', message=message)
    def terminate(self):
        self.flag = False
    def run(self):
        self.flag = True
        self.THD = threading.Thread(target=self.read_from_server, args=()).start()

tkinter实现GUI

登录界面

class Login(object):
    def __init__(self):
        # 创建主窗口,用于容纳其它组件
        self.root = Tk()
        # 给主窗口设置标题内容
        self.root.title("miniWechat")
        #自定义关闭按钮,不然关闭GUI后socket还在运行中
        self.root.protocol('WM_DELETE_WINDOW', self.my_close)
        self.root.geometry('450x300')
        #运行代码时记得添加一个gif图片文件,不然是会出错的
        self.canvas = tkinter.Canvas(self.root, height=300, width=500)#创建画布
        self.image_file = tkinter.PhotoImage(file='1572079024477.gif')
        self.image = self.canvas.create_image(0,0, anchor='nw', image=self.image_file)#将图片置于画布上
        self.canvas.grid(row=0, column=0)#放置画布(为上端)
        #创建一个`label`名为`Account: `
        self.label_account = tkinter.Label(self.root, text='Account: ')
        #创建一个`label`名为`Password: `
        self.label_password = tkinter.Label(self.root, text='Password: ')
        # 创建一个账号输入框,并设置尺寸
        self.input_account = tkinter.Entry(self.root, width=30)
        # 创建一个密码输入框,并设置尺寸
        self.input_password = tkinter.Entry(self.root, show='*', width=30)
        # 创建一个登录系统的按钮
        self.login_button = tkinter.Button(self.root, command = self.backstage_interface, text = "Login", width=10)
        # 创建一个注册系统的按钮
        self.siginUp_button = tkinter.Button(self.root, command = self.siginUp_interface, text = "Sign up", width=10)
        # 完成布局
    def gui_arrang(self):
        self.label_account.place(x=60, y= 170)
        self.label_password.place(x=60, y= 195)
        self.input_account.place(x=135, y=170)
        self.input_password.place(x=135, y=195)
        self.login_button.place(x=140, y=235)
        self.siginUp_button.place(x=240, y=235)
     # 进入注册界面
    def siginUp_interface(self):
        # self.root.destroy()
        account = self.input_account.get()
        password = self.input_password.get()
        verifyResult =self.reg_verifyAccountData(account,password)
        if(verifyResult == 'register_ok'):
            t.name = account
            self.start()
        else:
            tkinter.messagebox.showinfo(title='注册信息提示', message='用户名已经被使用')
     # 进行登录信息验证
    def backstage_interface(self):
        account = self.input_account.get()#.ljust(10," ")
        password = self.input_password.get()#.ljust(10," ")
        #对账户信息进行验证,普通用户返回user,管理员返回master,账户错误返回noAccount,密码错误返回noPassword
        verifyResult =self.log_verifyAccountData(account,password)
        if verifyResult=='master':
            self.root.destroy()
            tkinter.messagebox.showinfo(title='miniWechat', message='进入管理界面')
        elif verifyResult=='user':
            t.name = account
            self.start()
        elif verifyResult=='noAccount':
            tkinter.messagebox.showinfo(title='miniWechat', message='该账号不存在请重新输入!')
        elif verifyResult=='noPassword':
            tkinter.messagebox.showinfo(title='miniWechat', message='账号/密码错误请重新输入!')
    def run(self):
        self.root.run()
    def start(self):
        self.root.destroy()
        t.run()
        main_user()
    def log_verifyAccountData(self,account,password):
        #向服务器发送信息
        t.connecntsocket.send(('root/'+'login'+'/'+account+'/'+password).encode())
        sentense = t.connecntsocket.recv(2048).decode()
        print(sentense)
        return sentense
    def reg_verifyAccountData(self,account,password):
        t.connecntsocket.send(('root/'+'register'+'/'+account+'/'+password).encode())
        sentense = t.connecntsocket.recv(2048).decode()
        print(sentense)
        return sentense
    def my_close(self):
        t.connecntsocket.send(('un_root').encode())
        self.root.destroy()
        sleep(1)
        t.connecntsocket.close()

用户界面

class User(object):
    def __init__(self):
        self.tk = Tk()
        self.tk.title('用户界面')
        self.tk.protocol('WM_DELETE_WINDOW', self.my_close)
        self.button_pos = 280
        self.friend_list = [None]*80
        self.friend_list_len = 0
        self.to_be_friend_list = [None] * 80
        self.to_be_friend_list_len = 0
        #好友列表
        self.friend_area = Canvas(self.tk, bg='pink')
        self.friend_area.place(x=5, y=260, heigh=340, width=340)
        self.friend_text = scrolledtext.ScrolledText(self.friend_area)
        self.friend_text.place(x=5, y=5, heigh=340, width=330)
        #待添加好友列表
        self.to_be_friend_area = Canvas(self.tk,bg='pink')
        self.to_be_friend_area.place(x=5, y=650, heigh=340, width=340)
        self.to_be_friend_text = scrolledtext.ScrolledText(self.to_be_friend_area)
        self.to_be_friend_text.place(x=5, y=5, heigh=340, width=340)
        self.tk.geometry('350x1000')
        # 运行代码时记得添加一个gif图片文件,不然是会出错的
        self.canvas = tkinter.Canvas(self.tk, height=200, width=350)  # 创建画布
        self.image_file = tkinter.PhotoImage(file='1572079024477.gif')
        self.image = self.canvas.create_image(0, 0, anchor='nw', image=self.image_file)  # 将图片置于画布上
        self.canvas.place(x=0,y=0)#pack(side='top')  # 放置画布(为上端)
        # self.friend_list = tkinter.Button(self.tk, text='添加好友', bg='skyblue', command=self.to_chat, font=('Arial', 16))
        # self.friend_list.place(x=20, y=280)
        #添加TEXT文本
        self.add_friend_label = tkinter.Label(self.tk, text='名字 ')
        self.friend_list_label = tkinter.Label(self.tk, text='好友列表 ',bg='blue',fg='black',font=("华文行楷", 16),width=32,height=2)
        self.to_be_friend_list_label = tkinter.Label(self.tk, text='待通过的好友请求 ',bg='blue',fg='black',font=("华文行楷", 16),width=32,height=2)
        #添加输入框
        self.input_friend_name = tkinter.Entry(self.tk, width=20)
        #添加按钮
        self.add_friend_button = tkinter.Button(self.tk, command=self.add_friend, text="确认添加", width=6)
        self.delete_friend_button = tkinter.Button(self.tk, command=self.delete_friend, text="确认删除", width=6)
        self.refresh_all_button = tkinter.Button(self.tk, command=self.refresh_all, text="刷新列表", width=10)
        #放到指定位置
        self.add_friend_label.place(x=0,y=181)
        self.input_friend_name.place(x=35,y=182)
        self.add_friend_button.place(x=180,y=180)
        self.delete_friend_button.place(x=230, y=180)
        self.refresh_all_button.place(x=280,y=180)
        self.friend_list_label.place(x=10,y=210)
        self.to_be_friend_list_label.place(x=10,y=600)
        self.refresh_friend_list()
        self.refresh_to_be_friend_list()
    def refresh_to_be_friend_list(self):
        self.to_be_friend_text.delete(0.0,END)
        t.connecntsocket.send(('root/friend_op/print_to_be_friend_list/' + t.name).encode())
        time.sleep(0.1)
        self.to_be_deal_message = t.to_be_friend_list  # deal_message中存放所有名字
        print(self.to_be_deal_message)
        self.to_be_friend_list_len = len(self.to_be_deal_message)
        for i in range(0, self.to_be_friend_list_len):
            self.create_to_be_friend_bottun(i, self.to_be_deal_message[i])

    def refresh_friend_list(self):#刷新好友列表
        self.friend_text.delete(0.0,END)
        t.connecntsocket.send(('root/friend_op/print_friend_list/'+t.name).encode())
        time.sleep(0.1)
        self.deal_message = t.friend_list#deal_message中存放所有名字
        self.friend_list_len = len(self.deal_message)
        for i in range(0, self.friend_list_len):
            self.create_friend_bottun(i, self.deal_message[i])

    def create_to_be_friend_bottun(self, i, name):
        self.to_be_friend_list[i] = tkinter.Button(self.to_be_friend_text, text=name, bg='skyblue', command=lambda:self.whether_accpet(name), font=('Arial', 16), width=33, height=1)
        self.to_be_friend_text.window_create(END, window=self.to_be_friend_list[i])
    def create_friend_bottun(self,i,name):
        self.friend_list[i] = tkinter.Button(self.friend_text, text=name, bg='skyblue', command=lambda:self.to_chat(name), font=('Arial', 16), width=23, height=1)
        self.friend_text.window_create(END, window=self.friend_list[i])
    def whether_accpet(self,name):
        flag = messagebox.askokcancel(title='miniWechat', message=('是否接受' + name + '的好友请求?'))
        if (flag == True):
            t.connecntsocket.send(('root/friend_op/accept/'+t.name+'/'+name).encode())
        else:
            t.connecntsocket.send(('root/friend_op/reject/'+t.name+'/'+name).encode())
    def to_chat(self,name):
        print(name)
        main_chat(name)
    def refresh_all(self):
        self.refresh_to_be_friend_list()
        self.refresh_friend_list()
    def delete_friend(self):
        name = self.input_friend_name.get()
        t.connecntsocket.send(('root/friend_op/delete/' + t.name + '/' + name).encode())
    def add_friend(self):
        name = self.input_friend_name.get()
        print(type(name))
        t.connecntsocket.send(('root/friend_op/add/'+t.name+'/'+name).encode())
        print(name)
    def handler(self,event, top, lb):
        """事件处理函数"""
        content = lb.get(lb.curselection())
        return messagebox.showinfo(title="Hey, you got me!", message="I am {0}".format(content), parent=top)
    def handler_adaptor(self,fun, **kwds):
        """事件处理函数的适配器,相当于中介"""
        return lambda event, fun=fun, kwds=kwds: fun(event, **kwds)
    def run(self):
        self.tk.mainloop()
    def my_close(self):
        t.connecntsocket.send(('un_root').encode())
        self.tk.destroy()
        sleep(1)
        t.connecntsocket.close()

聊天界面

class Chat(object):
    def __init__(self,name):
        self.root = Toplevel()
        self.name = name
        self.root.title(name)
        self.root.protocol('WM_DELETE_WINDOW', self.my_close)
        '''创建分区'''
        self.f_msglist = Frame(self.root,height=300, width=300)  # 创建<消息列表分区 >
        self.f_msgsend = Frame(self.root,height=300, width=300)  # 创建<发送消息分区 >
        self.f_floor = Frame(self.root,height=100, width=300)  # 创建<按钮分区>
        self.f_right = Frame(self.root,height=700, width=100)  # 创建<图片分区>
        '''创建控件'''
        self.txt_msglist = Text(self.f_msglist)  # 消息列表分区中创建文本控件
        self.txt_msglist.tag_config('green', foreground='blue')  # 消息列表分区中创建标签
        self.txt_msgsend = Text(self.f_msgsend)  # 发送消息分区中创建文本控件
        self.txt_msgsend.bind('', self.msgsendEvent)  # 发送消息分区中,绑定‘UP’键与消息发送。
        '''txt_right = Text(f_right) #图片显示分区创建文本控件'''
        self.button_send = Button(self.f_floor, text='发送', command=self.msgsend)  # 按钮分区中创建按钮并绑定发送消息函数
        self.button_cancel = Button(self.f_floor, text='取消', command=self.cancel)  # 分区中创建取消按钮并绑定取消函数
        self.button_send_file = Button(self.f_floor, text='上传', command=self.filesend)
        self.button_resent_message = Button(self.f_floor, text='最近消息', command=self.get_resent_message)
        # self.photo = PhotoImage(file='1572079024477.gif')
        # self.label = Label(self.f_right, image=self.photo)  # 右侧分区中添加标签(绑定图片)
        self.label = Label(self.f_right)
        # self.label.image = self.photo
        self.canvas = Canvas(self.root, height=300, width=500)  # 创建画布
        self.image_file = PhotoImage(file='15720790244771.gif')
        self.image = self.canvas.create_image(0, 0, anchor='nw', image=self.image_file)  # 将图片置于画布上
        self.canvas.grid(row=1, column=1)  # 放置画布(为上端)
        '''分区布局'''
        self.f_msglist.grid(row=0, column=0)  # 消息列表分区
        self.f_msgsend.grid(row=1, column=0)  # 发送消息分区
        self.f_floor.grid(row=2, column=0)  # 按钮分区
        self.f_right.grid(row=0, column=1, rowspan=3)  # 图片显示分区
        self.txt_msglist.grid()  # 消息列表文本控件加载
        self.txt_msgsend.grid()  # 消息发送文本控件加载
        self.button_send.grid(row=0, column=0, sticky=W)  # 发送按钮控件加载
        self.button_cancel.grid(row=0, column=1, sticky=W)  # 取消按钮控件加载
        self.button_send_file.grid(row=0, column=2, sticky=W)
        self.button_resent_message.grid(row=0, column=3, sticky=W)
        self.label.grid()  # 右侧分区加载标签控件
    def insert_message(self,massage_title,massage_content):
        self.txt_msglist.insert(END, massage_title+'\n', 'green')
        self.txt_msglist.insert(END, massage_content)
    def insert_info(self,system_info):
        self.txt_msglist.insert(END, system_info + '\n', 'red')
    def get_resent_message(self):
        resent_message = read_csv('resent_message.csv')
        k = resent_message[resent_message.name == self.name].shape[0]
        if (k == 0):
            return
        title1 = '/'.join(resent_message[resent_message.name == self.name].title.tolist())
        message1 = '/'.join(resent_message[resent_message.name == self.name].message.tolist())
        title = title1.split('/')
        message = message1.split('/')
        self.txt_msgsend.delete(0.0,END)
        lenth = len(title)
        for i in range(0,lenth):
            self.txt_msglist.insert(END, title[i] + '\n', 'green')
            self.txt_msglist.insert(END, message[i])
    def msgsend(self):
        self.msg = t.name + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        self.txt_msglist.insert(END, self.msg + '\n', 'green')  # 添加时间
        self.txt_msglist.insert(END, self.txt_msgsend.get('0.0', END))  # 获取发送消息,添加文本到消息列表
        message =self.msg+'/'+self.txt_msgsend.get('0.0', END)
        mm=('root/chat/'+t.name+'/'+self.name+'/'+message)
        print(mm)
        t.connecntsocket.send(('root/chat/message/'+t.name+'/'+self.name+'/'+message).encode())
        resent_message = read_csv('resent_message.csv')
        new = DataFrame({'name': self.name, 'title': self.msg + '\n', 'message': self.txt_msgsend.get('0.0', END)}, index=[0])
        resent_message = resent_message.append(new, ignore_index=True)
        resent_message.to_csv('resent_message.csv', index=0)
        self.txt_msgsend.delete('0.0', END)  # 清空发送消息
    '''定义取消发送 消息 函数'''
    def filesend(self):
        filepath = self.txt_msgsend.get('0.0', END)
        self.txt_msgsend.delete('0.0', END)  # 清空发送消息
        t.connecntsocket.send(('root/chat/file/'+t.name+'/'+self.name+'/begin').encode())
        filepath = filepath.strip('\n')
        if os.path.isfile(filepath):
            # 定义定义文件信息。128s表示文件名为128bytes长,l表示一个int或log文件类型,在此为文件大小
            fileinfo_size = struct.calcsize('128sl')
            # 定义文件头信息,包含文件名和文件大小
            fhead = struct.pack('128sl', os.path.basename(filepath).encode('utf-8'), os.stat(filepath).st_size)
            # 发送文件名称与文件大小
            t.connecntsocket.send(fhead)
            # 将传输文件以二进制的形式分多次上传至服务器
            fp = open(filepath, 'rb')
            while 1:
                data = fp.read(1024)
                if not data:
                    print ('{0} file send over...'.format(os.path.basename(filepath)))
                    break
                t.connecntsocket.send(data)
    def cancel(self):
        self.txt_msgsend.delete('0.0', END)  # 取消发送消息,即清空发送消息

    '''绑定up键'''
    def msgsendEvent(self,event):
        if event.keysym == 'Up':
            self.msgsend()
    def run(self):
        self.root.mainloop()
    def my_close(self):
        self.root.destroy()
        my_removw(self.name)
def my_removw(name):
    for c in Chat_list:
        if c.name == name:
            Chat_list.remove(c)
            return

运行效果图

登录界面

python 简易微信实现(注册登录+数据库存储+聊天+GUI+文件传输)_第1张图片

用户界面

python 简易微信实现(注册登录+数据库存储+聊天+GUI+文件传输)_第2张图片

聊天界面

python 简易微信实现(注册登录+数据库存储+聊天+GUI+文件传输)_第3张图片

你可能感兴趣的:(python)