使用Python简单实现客户端界面

news/2024/11/8 17:05:18 标签: python

服务端实现

python">import threading
import time

import wx
from socket import socket, AF_INET, SOCK_STREAM


class LServer(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, id=1002, title='L服务器端界面', pos=wx.DefaultPosition, size=(400, 450))
        # 窗口中添加面板
        pl = wx.Panel(self)
        # 创建一个盒子
        box = wx.BoxSizer(wx.VERTICAL)
        # 创建可伸缩的网格布局
        fgz1 = wx.FlexGridSizer(wx.HSCROLL)  # 水平方向布局
        # 创建按钮
        start_server_btn = wx.Button(pl, size=(133, 40), label='启动服务')
        record_btn = wx.Button(pl, size=(133, 40), label='保存记录')
        stop_server_btn = wx.Button(pl, size=(133, 40), label='停止服务')
        # 将按钮添加到网络布局中
        fgz1.Add(start_server_btn, 1, wx.TOP | wx.LEFT)
        fgz1.Add(record_btn, 1, wx.TOP | wx.CENTRE)
        fgz1.Add(stop_server_btn, 1, wx.TOP | wx.RIGHT)
        # 将可伸缩的网格布局添加到box中
        box.Add(fgz1, 1, wx.ALIGN_CENTRE)

        # 只读文本框,用于显示聊天内容
        self.show_text = wx.TextCtrl(pl, size=(400, 410), style=wx.TE_MULTILINE | wx.TE_READONLY)
        box.Add(self.show_text, 1, wx.ALIGN_CENTRE)
        # 把盒子放到面板中
        pl.SetSizer(box)
        '''-----------------------------以上代码为界面的绘制代码-----------------------------------'''
        '''------------------------------设置服务器功能实现的必要属性----------------------------------'''
        self.isOn = False  # 存储服务器的启动状态,默认False没有启动
        # 服务器端绑定的IP地址和端口
        self.host_port = ('', 8888)  # 空的字符串代表的是本机的所有IP
        # 创建Socket对象
        self.server_socket = socket(AF_INET, SOCK_STREAM)
        # 绑定IP地址和端口
        self.server_socket.bind(self.host_port)
        # 监听
        self.server_socket.listen(5)
        # 创建一个字典,存储与客户端对话的会话线程
        self.session_thread_dict = {}  # key-value(客户端的名称key:绘画线程value)
        '''----------------------------------------------------------------'''
        # 当鼠标点击'启动服务'按钮时,需要执行的操作
        self.Bind(wx.EVT_BUTTON, self.start_server, start_server_btn)
        self.Bind(wx.EVT_BUTTON, self.save_record, record_btn)
        self.Bind(wx.EVT_BUTTON, self.stop_server, stop_server_btn)

    def stop_server(self,event):
        #
        print('服务器已停止服务')
        self.isOn=False

    def save_record(self,event):
        #获取只读文本框的内容
        record_data=self.show_text.GetValue()
        with open('record.log','w',encoding='utf-8') as file:
            file.write(record_data)

    def start_server(self, event):
        # 判断服务器是否已经启动,只有服务器没有启动时菜启动
        if not self.isOn:  # 等监狱self.isOn==False
            # 启动服务
            self.isOn = True
            # 创建主线程对象,函数式创建主线程
            main_thread = threading.Thread(target=self.do_work)
            # 设置守护线程,父线程执行结束(窗口界面)子线程也自动关闭
            main_thread.daemon = True
            # 启动主线程
            main_thread.start()

    def do_work(self):
        while self.isOn:
            # 接收客户端的连接请求
            session_socket, client_addr = self.server_socket.accept()
            # 客户端发送连接请求后,发送过啊里的第一条数据为客户端名称,客户端的名称去作为字典中的键
            user_name = session_socket.recv(1024).decode('utf-8')
            # 创建一个会话线程对象
            session_thread = SessionThread(session_socket, user_name, self)
            # 存储到字典中
            self.session_thread_dict[user_name] = session_thread
            # 启动会话线程
            session_thread.start()
            # 输出服务器的提示信息
            self.show_info_and_send_client('服务器通知', f'欢迎{user_name}进入聊天室!',
                                           time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))

        # 当self.isOn的值为False时,执行关闭Socket对象
        self.server_socket.close()

    def show_info_and_send_client(self, data_source, data, data_time):
        # 字符串拼接操作
        send_data = f'{data_source}:{data}\n时间:{data_time}'
        # 只读文本框
        self.show_text.AppendText('-' * 40 + '\n' + send_data + '\n')
        # 每一个客户端都发送一次
        for client in self.session_thread_dict.values():
            # 判断当前的会话是否为开启状态
            if client.isOn:
                client.client_socket.send(send_data.encode('utf-8'))


class SessionThread(threading.Thread):
    def __init__(self, client_socket, user_name, server):
        # 调用父类的初始化方法
        threading.Thread.__init__(self)
        self.client_socket = client_socket
        self.user_name = user_name
        self.server = server
        self.isOn = True  # 会话线程是否启动,当创建SessionThread对象是,会话线程就启动了,所以当前默认为True
        #

    def run(self):
        print(f'客户端:{self.user_name}已经与服务端建立连接.')
        while self.isOn:
            # 从客户端接收数据
            data = self.client_socket.recv(1024).decode('utf-8');
            # 如果客户端点击断开按钮,先给服务器发送断开连接,消息自定义 C-DISCONNECT-S 自定义结束词
            if data == 'C-DISCONNECT-S':
                self.isOn = False
                # 发送一条服务器通知
                # 其他聊天信息显示给所有客户端,包含服务器显示
                self.server.show_info_and_send_client('服务器通知', f'{self.user_name}离开了聊天室',
                                                      time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
            else:
                # 其他聊天信息显示给所有客户端,包含服务器显示
                self.server.show_info_and_send_client(self.user_name, data,
                                                      time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))

        # 关闭socket
        self.client_socket.close()


if __name__ == '__main__':
    # 初始化App()
    app = wx.App()
    # 创建自己的客户端界面对象
    client = LServer()
    client.Show()  # 可以改成LServer().Show()

    # 循环刷新显示
    app.MainLoop()

客户端实现

python"># coding:utf-8
import threading

import wx
from socket import socket, AF_INET, SOCK_STREAM


class LClinet(wx.Frame):
    def __init__(self, clent_name):
        # 调用父类的初始化方法
        # None:没有父级窗口
        # id:表示当前窗口的一个编号
        # title:窗口标题
        # pos:窗体的打开位置
        # size:窗体的大小;单位是像素,400宽,450高
        wx.Frame.__init__(self, None, id=1001, title=clent_name + '的客户端界面', pos=wx.DefaultPosition,
                          size=(400, 600))
        # 创建面板对象
        pl = wx.Panel(self)
        # 在面板中放上盒子
        box = wx.BoxSizer(wx.VERTICAL)  # 垂直方向布局
        # 可伸缩的网格布局
        fgz1 = wx.FlexGridSizer(wx.HSCROLL)  # 水平方向布局
        # 创建连个按钮
        connect_btn = wx.Button(pl, size=(200, 40), label='连接')
        disconnect_btn = wx.Button(pl, size=(200, 40), label='断开')
        # 把两个按钮放到可伸缩的网格布局
        fgz1.Add(connect_btn, 1, wx.TOP | wx.LEFT)
        fgz1.Add(disconnect_btn, 1, wx.TOP | wx.RIGHT)

        # 可伸缩的网格布局添加到box中
        box.Add(fgz1, 1, wx.ALIGN_CENTRE)
        # 只读文本框,用于显示聊天内容
        self.show_text = wx.TextCtrl(pl, size=(400, 210), style=wx.TE_MULTILINE | wx.TE_READONLY)
        box.Add(self.show_text, 1, wx.ALIGN_CENTRE)
        # 创建聊天内容的文本框
        self.chat_text = wx.TextCtrl(pl, size=(400, 210), style=wx.TE_MULTILINE)
        box.Add(self.chat_text, 1, wx.ALIGN_CENTRE)
        # 可伸缩的网格布局
        fgz2 = wx.FlexGridSizer(wx.HSCROLL)  # 水平方向布局
        # 创建连个按钮
        reset_btn = wx.Button(pl, size=(200, 40), label='重置')
        send_btn = wx.Button(pl, size=(200, 40), label='发送')
        # 把两个按钮放到可伸缩的网格布局
        fgz2.Add(reset_btn, 1, wx.TOP | wx.LEFT)
        fgz2.Add(send_btn, 1, wx.TOP | wx.LEFT)
        # 可伸缩的网格布局添加到box中
        box.Add(fgz2, 1, wx.ALIGN_CENTRE)

        # 将盒子放到面板中
        pl.SetSizer(box)

        '''------------------以上代码时客户端界面的绘制---------------------'''
        self.Bind(wx.EVT_BUTTON, self.connect_to_server, connect_btn)
        # 实例属性设置
        self.client_name = clent_name
        self.isConnected = False  # 存储客户端连接服务器的状态,默认False未连接
        self.client_socket = None  # 设置客户端的socket对象为空

        self.Bind(wx.EVT_BUTTON, self.send_to_server, send_btn)
        self.Bind(wx.EVT_BUTTON, self.disconnect_to_server, disconnect_btn)
        self.Bind(wx.EVT_BUTTON, self.reset, reset_btn)

    def reset(self,event):
        # 清空文本框
        self.chat_text.SetValue('')
    def disconnect_to_server(self,event):
        #发送断开的信息
        self.client_socket.send('C-DISCONNECT-S'.encode('utf-8'))
        # 改变连接状态
        self.isConnected=False

    def send_to_server(self, event):
        # 判断连接状态
        if self.isConnected:
            # 从可写文本框中获取输入的内容
            input_data = self.chat_text.GetValue()
            if input_data != '':
                # 向服务器发送数据
                self.client_socket.send(input_data.encode('utf-8'))
                # 发送完数据后清空文本框
                self.chat_text.SetValue('')

    def connect_to_server(self, event):
        print(f'客户端{self.client_name}连接服务器成功')
        # 如果客户端没有连接服务器,则开始连接
        if not self.isConnected:  # 等价与self.isConnected==False
            # TCP编程步骤
            server_host_port = ('127.0.0.1', 8888)
            # 创建socket对象
            self.client_socket = socket(AF_INET, SOCK_STREAM)
            # 发送连接请求
            self.client_socket.connect(server_host_port)
            # 只要连接成功,发送一条数据
            self.client_socket.send(self.client_name.encode('utf-8'))
            # 启动一个线程,客户端的线程与服务器的会话线程进行会话
            client_thread = threading.Thread(target=self.recv_data)
            # 设置线程守护,窗体关闭后,子线程也关闭
            client_thread.daemon = True
            # 修改连接状态
            self.isConnected = True
            # 启动线程
            client_thread.start()

    def recv_data(self):
        # 判断是否是连接状态
        while self.isConnected:
            # 接收服务器的数据
            data = self.client_socket.recv(1024).decode('utf-8')
            # 显示到文本框中
            self.show_text.AppendText('-' * 40 + '\n' + data + '\n')


if __name__ == '__main__':
    # 初始化App()
    app = wx.App()
    name = input('请输入客户端名称:')
    # 创建自己的客户端界面对象
    client = LClinet(name)
    client.Show()  # 可以改成LClinet('LL').Show()

    # 循环刷新显示
    app.MainLoop()

http://www.niftyadmin.cn/n/5744185.html

相关文章

mysql常见的一些配置项

MySQL 有许多配置选项,可以用来调整其行为以满足特定的需求。以下是一些常见的配置选项,除了大小写敏感之外,这些配置选项也经常被调整: 1. 字符集和排序规则 character_set_server: 设置服务器的默认字符集。collation_server:…

基于STM32的贪吃蛇游戏教学

引言 贪吃蛇是一款经典的电脑和手机游戏,它的简单性和趣味性使其成为很多人童年记忆的一部分。在本教程中,我们将创建一个基于STM32的贪吃蛇游戏项目。本项目将使用一个OLED显示屏来展示游戏画面,并使用按键来控制蛇的移动。通过本教程&#…

智能化健身房管理:Spring Boot与Vue的创新解决方案

作者介绍:✌️大厂全栈码农|毕设实战开发,专注于大学生项目实战开发、讲解和毕业答疑辅导。 🍅获取源码联系方式请查看文末🍅 推荐订阅精彩专栏 👇🏻 避免错过下次更新 Springboot项目精选实战案例 更多项目…

笔记--(网络3)、交换机、VLAN

交换机 交换机(Switch)意为“开关”是一种用于电(光)信号转发的网络设备。它可以为接入交换机的任意两个网络节点提供独享的电信号通路。最常见的交换机是以太网交换机。其他常见的还有电话语音交换机、光纤交换机等。 交换机的…

Docker使用相关记录

文章目录 查看本地镜像查看本地容器进入某个容器内部查看 Docker 运行中的日志查看本地镜像 docker images查看本地容器 # 查看本地运行的容器 docker ps # 查看本地所有的容器 docker ps -a进入某个容器内部 docker exec -it <name或id> /bin/bash退出bash, 输入exit…

【物联网技术】ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——UDP数据透传

前言:完成ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——实现UDP数据透传 STA模式,通俗来说就是模块/单片机去连接路由器/热点来通信。 UDP协议,是传输层协议,UDP没有服务器和客户端的说法。 本实验需要注意,wifi模块/单片机与电脑/手机需要连接在同一个…

YoloV9改进策略:上采样改进|CARAFE,轻量级上采样|即插即用|附改进方法+代码

论文介绍 CARAFE模块概述:本文介绍了一种名为CARAFE(Content-Aware ReAssembly of FEatures)的模块,它是一种用于特征上采样的新方法。应用场景:CARAFE模块旨在改进图像处理和计算机视觉任务中的上采样过程,特别适用于目标检测、实例分割、语义分割和图像修复等任务。目标…

参数高效微调

参数高效微调 参数高效微调简介 对于预训练数据涉及较少的垂直领域&#xff0c;大语言模型需要对这些领域及相应的下游任务进行适配。上下文学习和指令微调是进行下游任务适配的有效途径&#xff0c;但它们在效果或效率上存在缺陷。为弥补这些不足&#xff0c;参数高效微调&am…