pythonGUI之10: dearpygui实现tcp客户端调试工具

Dear PyGui 是一个用于构建图形用户界面(GUI)的 Python 开源库,具有以下特点和优势:
核心特点

  • 即时模式 GUI:与传统的保留模式 GUI 框架不同,Dear PyGui 采用即时模式,界面元素在每一帧都会被重新绘制。
  • 简单易用:API 设计简洁直观,学习曲线平缓,开发者可以快速上手。
  • 高性能:基于 C++ 编写,使用 OpenGL 进行图形渲染,支持 GPU 加速,适合处理图形密集型任务。
  • 跨平台支持:支持 Windows、Linux 和 macOS 等主流操作系统。
  • 丰富的 GUI 组件:包含按钮、文本框、滑块、菜单、表格、绘图、节点编辑器等高级组件。
  • 无依赖:安装后无需额外依赖,即可使用。

1.安装包

 pip install dearpygui

Collecting dearpygui
Downloading https://pypi.tuna.tsinghua.edu.cn/packages/26/2f/6cf64bfc46d0aac54273be8182a8ac185cea9028a323b01d43a27c5ee33e/dearpygui-2.1.0-cp39-cp39-win_amd64.whl (1.8 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 1.7 MB/s eta 0:00:00
Installing collected packages: dearpygui
Successfully installed dearpygui-2.1.0

2.TCP客户端源码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TCP Client 网络调试助手(Dear PyGui 版)
2025-07-16
"""

import dearpygui.dearpygui as dpg
import socket
import threading
import binascii
import time

BUFFER = 4096

class TCPClient:
    def __init__(self):
        self.sock = None
        self.alive = False
        self.connected = False  # 添加连接状态标志
        self.hex_recv = False
        self.hex_send = False
        self.enable_tcl = False
        self.create_gui()

    def recv_thread(self):
        while self.alive:
            try:
                data = self.sock.recv(BUFFER)
                if not data:
                    break
                if self.hex_recv:
                    text = binascii.hexlify(data).decode('utf-8') + '\n'
                else:
                    text = data.decode('utf-8', errors='ignore')
                dpg.set_value("recv_text", dpg.get_value("recv_text") + f"<-- {text}\n")
            except socket.timeout:
                continue
            except Exception as e:
                if self.alive:
                    dpg.set_value("recv_text", dpg.get_value("recv_text") + f'[!] 接收异常: {e}\n')
                    break
        dpg.set_value("recv_text", dpg.get_value("recv_text") + '[-] 连接已断开\n')
        self.close_conn()

    def on_connect(self):
        if self.connected:
            self.close_conn()
            return

        host = dpg.get_value("host_input")
        port = dpg.get_value("port_input")
        if not host or not port:
            dpg.set_value("recv_text", dpg.get_value("recv_text") + "请输入地址和端口\n")
            return
        try:
            port = int(port)
        except ValueError:
            dpg.set_value("recv_text", dpg.get_value("recv_text") + "端口必须为数字\n")
            return

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(5)  # 设置超时时间为5秒
        try:
            self.sock.connect((host, port))
            self.alive = True
            self.connected = True  # 设置连接状态为 True
            dpg.set_item_label("connect_button", "断开")
            dpg.set_value("recv_text", dpg.get_value("recv_text") + f"[+] 已连接 {host}:{port}\n")
            threading.Thread(target=self.recv_thread, daemon=True).start()
        except socket.timeout:
            dpg.set_value("recv_text", dpg.get_value("recv_text") + "连接超时\n")
            self.sock.close()
            self.sock = None
        except Exception as e:
            dpg.set_value("recv_text", dpg.get_value("recv_text") + f"连接失败: {e}\n")
            self.sock.close()
            self.sock = None

    def on_send(self):
        if not self.connected:  # 检查是否已连接
            dpg.set_value("recv_text", dpg.get_value("recv_text") + "请先连接服务器\n")
            return
        text = dpg.get_value("send_input")
        if not text:
            return
        try:
            if self.enable_tcl:
                text = text  # 需要加入TCL引擎,#TODO
            if self.hex_send:
                data = bytes.fromhex(text.replace(' ', ''))
            else:
                data = text.encode('utf-8', errors='ignore')
            self.sock.sendall(data)
            dpg.set_value("send_input", "")
        except Exception as e:
            dpg.set_value("recv_text", dpg.get_value("recv_text") + f"[!] 发送失败: {e}\n")
            self.close_conn()

    def on_quick_send(self, sender, app_data, user_data):
        if not self.connected:  # 检查是否已连接
            dpg.set_value("recv_text", dpg.get_value("recv_text") + "请先连接服务器\n")
            return
        text = dpg.get_value(user_data["text_ctrl"])
        if not text:
            return
        delay = int(dpg.get_value(user_data["delay_ctrl"])) if dpg.get_value(user_data["delay_ctrl"]).isdigit() else 0

        try:
            if self.enable_tcl:
                text = text  # 需要加入TCL引擎,#TODO
            if self.hex_send:
                data = bytes.fromhex(text.replace(' ', ''))
            else:
                data = text.encode('utf-8', errors='ignore')
            if dpg.get_value(user_data["delay_checkbox"]):
                if delay > 0:
                    time.sleep(delay / 1000.0)  # 延时单位为毫秒
                self.sock.sendall(b'cmd:' + data)
                dpg.set_value("recv_text", dpg.get_value("recv_text") + f"-->>: cmd:{text}\n")
            else:
                self.sock.sendall(data)
                dpg.set_value("recv_text", dpg.get_value("recv_text") + f"-->: {text}\n")
        except Exception as e:
            dpg.set_value("recv_text", dpg.get_value("recv_text") + f"[!] 快捷发送失败: {e}\n")
            self.close_conn()

    def close_conn(self):
        self.alive = False
        if self.sock:
            try:
                self.sock.close()
                dpg.set_item_label("connect_button", "连接")
                dpg.set_value("recv_text", dpg.get_value("recv_text") + "[-] 连接已断开\n")
            except:
                pass
            self.sock = None
        self.connected = False  # 设置连接状态为 False

    def clear_recv_text(self):
        dpg.set_value("recv_text", "")

    def create_gui(self):
        try:
            with dpg.font_registry():
                with dpg.font(r"C:\Windows\Fonts\simsun.ttc", 18) as default_font:
                    dpg.add_font_range_hint(dpg.mvFontRangeHint_Default)
                    dpg.add_font_range_hint(dpg.mvFontRangeHint_Chinese_Full)
        except Exception as e:
            print(f"字体加载失败: {e}")

        with dpg.window(label="TCP Client 调试助手 V1.0", width=1200, height=800):
            # 左侧:接收区和发送区
            with dpg.group(horizontal=True):
                # 左侧接收区和发送区
                with dpg.child_window(width=720, height=-1, border=True) as left_panel:
                    # 连接区
                    with dpg.group(horizontal=True):
                        dpg.add_text("Host:")
                        dpg.add_input_text(tag="host_input", default_value="127.0.0.1", width=100)
                        dpg.add_text("Port:")
                        dpg.add_input_text(tag="port_input", default_value="5002", width=100)
                        dpg.add_button(tag="connect_button", label="连接", callback=self.on_connect)

                    # 接收区
                    dpg.add_text("接收")
                    dpg.add_input_text(tag="recv_text", multiline=True, readonly=True, height=560, width=700)
                    with dpg.group(horizontal=True):
                        dpg.add_checkbox(label="HEX 显示", tag="hex_recv_checkbox", callback=lambda sender: setattr(self, 'hex_recv', dpg.get_value(sender)))
                        dpg.add_button(label="清空接收区", callback=self.clear_recv_text)
                        dpg.add_checkbox(label="启用TCL", tag="enable_tcl_checkbox", callback=lambda sender: setattr(self, 'enable_tcl', dpg.get_value(sender)))

                    # 发送区
                    dpg.add_text("发送")
                    with dpg.group(horizontal=True):
                        dpg.add_input_text(tag="send_input", width=580, on_enter=True, callback=self.on_send)
                        dpg.add_checkbox(label="HEX", tag="hex_send_checkbox", callback=lambda sender: setattr(self, 'hex_send', dpg.get_value(sender)))
                        dpg.add_button(label="发送", callback=self.on_send)

                # 右侧:快捷发送区
                with dpg.child_window(width=480, height=-1, border=True) as right_panel:
                    with dpg.tab_bar():
                        for tab_idx in range(8):
                            with dpg.tab(label=f"指令区域{tab_idx + 1}"):
                                for row_idx in range(30):
                                    with dpg.group(horizontal=True):
                                        dpg.add_text(f"{row_idx + 1:02d}")
                                        text_ctrl = dpg.add_input_text(width=250, tag=f"text_ctrl_{tab_idx}_{row_idx}")
                                        delay_checkbox = dpg.add_checkbox(label="", tag=f"delay_checkbox_{tab_idx}_{row_idx}")
                                        delay_ctrl = dpg.add_input_text(default_value="0", width=60, tag=f"delay_ctrl_{tab_idx}_{row_idx}")
                                        dpg.add_button(label="发送", callback=self.on_quick_send, user_data={
                                            "text_ctrl": f"text_ctrl_{tab_idx}_{row_idx}",
                                            "delay_checkbox": f"delay_checkbox_{tab_idx}_{row_idx}",
                                            "delay_ctrl": f"delay_ctrl_{tab_idx}_{row_idx}"
                                        })

        # 应用字体
        dpg.bind_font(default_font)

def main():
    dpg.create_context()
    client = TCPClient()
    dpg.create_viewport(title="TCP Client 调试助手 V1.0", width=1200, height=800)
    dpg.setup_dearpygui()
    dpg.show_viewport()
    dpg.start_dearpygui()
    dpg.destroy_context()

if __name__ == "__main__":
    main()

3.运行效果
image

posted @ 2025-07-24 13:11  cupid8505  阅读(37)  评论(0)    收藏  举报