Python带有界面的聊天程序

文章讲述了如何使用Tkinter库开发一个图形化的聊天程序,涉及服务器端的Socket编程,以及客户端的用户注册、登录流程。数据存储不依赖数据库,而是通过文件方式,并使用SHA512加密密码。
摘要由CSDN通过智能技术生成

想要编写带有界面的聊天程序我决定用tkinter来实现图形化功能,由于比较简易我就不利用数据库来进行用户账户密码的存储而是利用文件的方式,注册时需自己手动同意,所以是个比较私有化的程序

服务端

import json
import socket
import threading

class Chat_Server:

    def __init__(self):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.s.bind(('0.0.0.0', 4001))
        self.s.listen(5)
        self.accept_idea()

    def accept_idea(self):
        while True:
            threading.Thread(target = self.login_idea, args = (self.s.accept(), ), daemon = True).start()

    def login_idea(self, accept_data):
        conn, addr = accept_data
        print(conn, addr)

        while True:
            if conn.recv(1).decode('utf-8') == '1':
                header_size = int(conn.recv(8).decode('utf-8'))
                header = json.loads(conn.recv(header_size).decode('utf-8'))
                with open('loginuser.ini', 'r', encoding = 'utf-8')as f1:
                    for line1 in f1:
                        if header['username'] == line1.strip().split()[0] and header['password'] == line1.strip().split()[1]:
                            conn.sendall('1'.encode('utf-8').zfill(1))
                            self.s_chat_idea(conn)
                            return
                        else:
                            pass
            else:
                header_size = int(conn.recv(8).decode('utf-8'))
                header = json.loads(conn.recv(header_size))
                with open('check.log', 'a', encoding = 'utf-8')as f1:
                    f1.write(header['username'] + ' ' + header['password'] + '\n')
                continue
            conn.sendall('0'.encode('utf-8').zfill(1))

    def s_chat_idea(self, conn):
        threading.Thread(target = self.s_recv_chat_idea, args = (conn, ), daemon = True).start()
        threading.Thread(target = self.s_send_chat_idea, args = (conn, ), daemon = True).start()

    def s_recv_chat_idea(self, conn):
        with open('data.log', 'r', encoding = 'utf-8') as f:
            f.seek(0, 2)
            while True:
                res = f.readline()
                if res:
                    conn.sendall(res.encode('utf-8'))

    def s_send_chat_idea(self, conn):
        with open('data.log', 'a+', encoding = 'utf-8') as f:
            while True:
                f.write(conn.recv(1024).decode('utf-8'))
                f.flush()


Chat_Server()

客户端

import os
import json
import time
import socket
import tkinter
import hashlib
import threading
import tkinter.messagebox

class Chat_Client:
    def __init__(self):
        self.root = tkinter.Tk()
        self.root.geometry('800x600')
        self.root.title('星聊天')
        self.username = ''
        
        self.c = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.run()

        self.root.mainloop()

    def create_chat_text(self):
        self.chat_recv_frame = tkinter.Frame(self.root)
        self.chat_recv_frame.pack(anchor = tkinter.W)

        self.chat_recv_text = tkinter.Text(self.chat_recv_frame, state = tkinter.DISABLED)
        self.chat_recv_text.pack(side = tkinter.LEFT, padx = 30, pady = 15)

        self.chat_send_frame = tkinter.Frame(self.root)
        self.chat_send_frame.pack(anchor = tkinter.W)

        self.chat_send_text = tkinter.Text(self.chat_send_frame, state = tkinter.DISABLED)
        self.chat_send_text.pack(side = tkinter.LEFT, padx = 30, pady = 10)

        self.chat_send_button = tkinter.Button(self.chat_send_frame, text = '发送', width = 20, height = 10, command = self.c_send_chat_idea)
        self.chat_send_button.pack(side = tkinter.LEFT, padx = 10)

    def create_menu_idea(self):
        self.menu = tkinter.Menu(self.root)
        self.root.config(menu = self.menu)

        self.choose_menu = tkinter.Menu(self.menu, tearoff = False)
        self.menu.add_cascade(label = '选项', menu = self.choose_menu)

        self.choose_menu.add_command(label = '登录', command = self.login_window_button)
        self.choose_menu.add_command(label = '连接地址', command = self.connect_server_button)
        
    def connect_server_button(self):
        self.toplevel1 = tkinter.Toplevel(self.root)
        self.toplevel1.geometry('300x100')
        self.toplevel1.title('连接地址')

        self.toplevel1_frame = tkinter.Frame(self.toplevel1)
        self.toplevel1_frame.pack(pady = 30)

        self.toplevel1_host_label = tkinter.Label(self.toplevel1_frame, text = 'IP地址:')
        self.toplevel1_host_label.pack(side = tkinter.LEFT)

        self.toplevel1_host_entry = tkinter.Entry(self.toplevel1_frame)
        self.toplevel1_host_entry.pack(side = tkinter.LEFT)

        self.toplevel1_port_label = tkinter.Label(self.toplevel1_frame, text = '端口号:')
        self.toplevel1_port_label.pack(side = tkinter.LEFT)

        self.toplevel1_port_entry = tkinter.Entry(self.toplevel1_frame)
        self.toplevel1_port_entry.pack(side = tkinter.LEFT)

        self.toplevel1_button = tkinter.Button(self.toplevel1, text = '设置并连接', command = self.set_ipport_button)
        self.toplevel1_button.pack()

    def set_ipport_button(self):
        with open('config.ini', 'w', encoding = 'utf-8')as f1:
            f1.write(self.toplevel1_host_entry.get().strip() + ' ' + self.toplevel1_port_entry.get().strip())
        self.connect_server()

    def connect_server(self):
        try:
            with open('config.ini', 'r+', encoding = 'utf-8')as f1:
                self.host, self.port = f1.read().strip().split()
                self.c.connect((self.host, int(self.port)))
                tkinter.messagebox.showinfo(title = '提示', message = '服务器连接成功')
        except:
            tkinter.messagebox.showwarning(title = '提示', message = '服务器连接失败')

    def login_window_button(self):
        self.toplevel = tkinter.Toplevel(self.root)
        self.toplevel.geometry('300x300')
        self.toplevel.title('登录')

        self.toplevel_frame_1 = tkinter.Frame(self.toplevel)
        self.toplevel_frame_1.pack(pady = 30)

        self.toplevel_username_Label = tkinter.Label(self.toplevel_frame_1, text = '账户:')
        self.toplevel_username_Label.pack(side = tkinter.LEFT)

        self.toplevel_username_entry = tkinter.Entry(self.toplevel_frame_1)
        self.toplevel_username_entry.pack(side = tkinter.LEFT)

        self.toplevel_frame_2 = tkinter.Frame(self.toplevel)
        self.toplevel_frame_2.pack()

        self.toplevel_password_Label = tkinter.Label(self.toplevel_frame_2, text = '密码:')
        self.toplevel_password_Label.pack(side = tkinter.LEFT)

        self.toplevel_password_entry = tkinter.Entry(self.toplevel_frame_2, show = '*')
        self.toplevel_password_entry.pack(side = tkinter.LEFT)

        self.toplevel_login_button = tkinter.Button(self.toplevel, text = '登录', width = 10, command = self.login_button)
        self.toplevel_login_button.pack(pady = 10)

        self.toplevel_register_button = tkinter.Button(self.toplevel, text = '注册', width = 10, command = self.register_button)
        self.toplevel_register_button.pack(pady = 50)

    def login_button(self):
        try:
            self.c.sendall('1'.encode('utf-8').zfill(1))
            header = {
                'username': self.toplevel_username_entry.get().strip(),
                'password': hashlib.sha512(self.toplevel_password_entry.get().strip().encode('utf-8')).hexdigest()
            }
            self.c.sendall(bytes(str(len(json.dumps(header))), 'utf-8').zfill(8))
            self.c.sendall((json.dumps(header).encode('utf-8')))
            if self.c.recv(1).decode('utf-8') == '1':
                tkinter.messagebox.showinfo(title = '登录提示', message = '登陆成功')
                self.toplevel_username_entry.delete(0, tkinter.END)
                self.toplevel_password_entry.delete(0, tkinter.END)
                self.choose_menu.entryconfigure(0, state = tkinter.DISABLED)
                self.toplevel.destroy()
                self.username = header['username']
                self.username_label = tkinter.Label(self.chat_recv_frame, text = f'用户: {self.username}', font=("Helvetica", 15))
                self.username_label.pack(side = tkinter.LEFT)
                self.chat_send_text.config(state = tkinter.NORMAL)
                threading.Thread(target = self.c_recv_chat_idea, daemon = True).start()
            else:
                tkinter.messagebox.showerror(title = '登录提示', message = '登陆失败')
                self.toplevel.destroy()
        except:
            tkinter.messagebox.showerror(title = '提示', message = '未连接服务器')
            self.toplevel.destroy()

    def register_button(self):
        try:
            self.c.sendall('0'.encode('utf-8').zfill(1))
            header = {
                'username': self.toplevel_username_entry.get().strip(),
                'password': hashlib.sha512(self.toplevel_password_entry.get().strip().encode('utf-8')).hexdigest()
            }
            self.c.sendall(bytes(str(len(json.dumps(header))), 'utf-8').zfill(8))
            self.c.sendall((json.dumps(header).encode('utf-8')))
            tkinter.messagebox.showinfo(title = '提示', message = '注册中请等待管理员同意')
            self.toplevel.destroy()
        except:
            tkinter.messagebox.showerror(title = '提示', message = '未连接服务器')
            self.toplevel.destroy()
    def check_config(self):
        if not os.path.isfile('./config.ini'):
            with open('config.ini', 'w', encoding = 'utf-8'):
                pass
    
    def c_send_chat_idea(self):
        if self.chat_send_text.get('1.0', tkinter.END).strip() != '':
            self.c.sendall(f'{self.username}:{self.chat_send_text.get("1.0", tkinter.END).strip()}\n'.encode('utf-8'))
            self.chat_send_text.delete('1.0', tkinter.END)
        else:
            tkinter.messagebox.showwarning(title = '提示', message = '发送内容不能为空')
        
    def c_recv_chat_idea(self):
        while True:
            data = self.c.recv(1024).decode('utf-8')
            self.chat_recv_text.config(state = tkinter.NORMAL)
            self.chat_recv_text.insert(tkinter.END, data)
            self.chat_recv_text.config(state = tkinter.DISABLED)
            self.chat_recv_text.see(tkinter.END)

    def run(self):
        self.check_config()
        self.create_menu_idea()
        self.create_chat_text()
        self.connect_server()

Chat_Client()

使用方法

由于服务端没有图形化所以会比较简洁,在使用本程序时需要创建3个文件放置在服务端,分别是check.log、data.log、loginuser.ini

首先将服务端和客户端开启,客户端左上角有一个链接地址点击它然后设置服务器的地址,如果连接成功便会提示,这里我的服务器就是本机的4001端口

接下里点击登录

进入后会出现一个弹窗,然后把你想要注册的账户名密码填上去然后点击注册

接下来账户密码就会出现在服务端的check.log里面

由于我使用了sha512加密所以很难直接看出密码是啥

接下来只需要把账户名密码复制到loginuser.ini就可以了,然后再去程序里把账号密码填上然后点击登录就OK了,如果显示登录失败或者未连接服务器请重启客户端

接下来用户名会出现在右侧,登录按钮会变得不可用

然后就可以发消息试试啦

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python聊天程序的图形界面可以使用PyQt、Tkinter、wxPython等GUI工具包来实现。 以下是一个使用PyQt实现的简单聊天程序图形界面的示例代码: ```python import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QLineEdit, QPushButton class ChatWindow(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle('Chat Window') # 创建聊天历史记录框和输入框 self.chat_history = QTextEdit() self.chat_input = QLineEdit() # 创建发送按钮 send_button = QPushButton('Send') send_button.clicked.connect(self.send_message) # 创建水平布局,将输入框和发送按钮放在一起 input_layout = QHBoxLayout() input_layout.addWidget(self.chat_input) input_layout.addWidget(send_button) # 创建垂直布局,将聊天历史记录框和输入框+发送按钮放在一起 main_layout = QVBoxLayout() main_layout.addWidget(self.chat_history) main_layout.addLayout(input_layout) self.setLayout(main_layout) def send_message(self): # 获取输入框中的文本 message = self.chat_input.text() # 将输入框中的文本添加到聊天历史记录框中 self.chat_history.append(message) # 清空输入框 self.chat_input.clear() if __name__ == '__main__': app = QApplication(sys.argv) window = ChatWindow() window.show() sys.exit(app.exec_()) ``` 这个示例代码实现了一个带有聊天历史记录框、输入框和发送按钮的聊天界面。用户在输入框中输入文本后,点击发送按钮,文本会被添加到聊天历史记录框中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值