pythonGUI之三: wxpython实现tcp客户端调试工具
安装 wxPython
使用 pip 命令安装是最简单的方法,可以在命令提示符(Windows)或终端(macOS/Linux)中输入以下命令:
pip install -U wxPython
该命令会自动从 Python Package Index (PyPI) 下载并安装与你的 Python 版本兼容的最新版本的 wxPython。
可以指定镜像源
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple wxPython
这个命令会从清华大学的镜像源下载并安装 wxPython,通常能显著提高下载速度。
如果你希望将清华大学的镜像源设置为默认源,可以使用以下命令:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
这样,后续使用 pip 安装任何包时都会默认使用清华大学的镜像源
tcp客户端调试工具源码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TCP Client 网络调试助手(wxPython 极简版)
2025-07-15
"""
import wx
import wx.lib.scrolledpanel as scrolled # 导入 ScrolledPanel
import socket
import threading
import binascii
import time
BUFFER = 4096
class TcpClientFrame(wx.Frame):
def __init__(self):
super().__init__(None, title='TCP Client 调试助手V1.0', size=(1200, 800), style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.sock = None
self.alive = False
self.build_ui()
self.Center()
# ---------------- UI ----------------
def build_ui(self):
panel = wx.Panel(self)
main_sizer = wx.BoxSizer(wx.HORIZONTAL) # 主布局为水平布局
# 左侧:接收区和发送区
left_sizer = wx.BoxSizer(wx.VERTICAL)
# 连接区
conn_box = wx.StaticBoxSizer(wx.StaticBox(panel, label='连接'), wx.HORIZONTAL)
conn_box.Add(wx.StaticText(panel, label='Host:'), 0, wx.ALL | wx.CENTER, 5)
self.host_ctrl = wx.TextCtrl(panel, value='127.0.0.1', size=(100, -1))
conn_box.Add(self.host_ctrl, 0, wx.ALL, 5)
conn_box.Add(wx.StaticText(panel, label='Port:'), 0, wx.ALL | wx.CENTER, 5)
self.port_ctrl = wx.TextCtrl(panel, value='5002', size=(60, -1))
conn_box.Add(self.port_ctrl, 0, wx.ALL, 5)
self.conn_btn = wx.Button(panel, label='连接')
self.conn_btn.Bind(wx.EVT_BUTTON, self.on_connect)
conn_box.Add(self.conn_btn, 0, wx.ALL, 5)
left_sizer.Add(conn_box, 0, wx.EXPAND | wx.ALL, 5)
# 接收区
recv_box = wx.StaticBoxSizer(wx.StaticBox(panel, label='接收'), wx.VERTICAL)
self.recv_ctrl = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH2)
recv_box.Add(self.recv_ctrl, 1, wx.ALL | wx.EXPAND, 5)
# 创建一个水平布局用于放置 HEX 显示复选框、清空按钮、启用TCL复选框和保存参数按钮
recv_options_box = wx.BoxSizer(wx.HORIZONTAL)
# HEX 显示复选框
self.hex_recv = wx.CheckBox(panel, label='HEX 显示')
recv_options_box.Add(self.hex_recv, 0, wx.ALL | wx.CENTER, 5)
# 清空接收区按钮
self.clear_btn = wx.Button(panel, label='清空接收区')
self.clear_btn.Bind(wx.EVT_BUTTON, lambda e: self.recv_ctrl.Clear())
recv_options_box.Add(self.clear_btn, 0, wx.ALL | wx.CENTER, 5)
# 创建一个水平布局用于放置启用TCL复选框和保存参数按钮
right_options_box = wx.BoxSizer(wx.HORIZONTAL)
# 启用TCL复选框
self.enable_tcl_checkbox = wx.CheckBox(panel, label='启用TCL')
right_options_box.Add(self.enable_tcl_checkbox, 0, wx.ALL | wx.CENTER, 5)
# 保存参数按钮
self.save_params_btn = wx.Button(panel, label='保存参数')
self.save_params_btn.Bind(wx.EVT_BUTTON, self.on_save_params)
right_options_box.Add(self.save_params_btn, 0, wx.ALL | wx.CENTER, 5)
# 将右侧行布局添加到选项布局
recv_options_box.AddStretchSpacer(1) # 添加可伸展的空白区域,将右侧行布局推到右边
recv_options_box.Add(right_options_box, 0, wx.ALL, 5)
# 将选项布局添加到接收区
recv_box.Add(recv_options_box, 0, wx.ALL | wx.EXPAND, 5)
left_sizer.Add(recv_box, 1, wx.EXPAND | wx.ALL, 5)
# 发送区
send_box = wx.StaticBoxSizer(wx.StaticBox(panel, label='发送'), wx.HORIZONTAL)
self.send_ctrl = wx.TextCtrl(panel, style=wx.TE_PROCESS_ENTER)
self.send_ctrl.Bind(wx.EVT_TEXT_ENTER, self.on_send)
send_box.Add(self.send_ctrl, 1, wx.ALL | wx.EXPAND, 5)
self.hex_send = wx.CheckBox(panel, label='HEX')
send_box.Add(self.hex_send, 0, wx.ALL | wx.CENTER, 5)
self.send_btn = wx.Button(panel, label='发送')
self.send_btn.Bind(wx.EVT_BUTTON, self.on_send)
send_box.Add(self.send_btn, 0, wx.ALL, 5)
left_sizer.Add(send_box, 0, wx.EXPAND | wx.ALL, 5)
# 右侧:快捷发送区
right_sizer = wx.BoxSizer(wx.VERTICAL)
# 快捷发送区标题
quick_send_notebook = wx.Notebook(panel)
# 初始化快捷发送面板
self.quick_send_panels = [[] for _ in range(8)] # 创建8个空列表,每个列表对应一个标签页
for tab_idx in range(8):
tab_panel = scrolled.ScrolledPanel(quick_send_notebook, style=wx.VSCROLL | wx.BORDER_DOUBLE)
tab_panel.SetupScrolling() # 设置滚动功能
quick_send_sizer = wx.BoxSizer(wx.VERTICAL)
for row_idx in range(30):
quick_send_panel = wx.Panel(tab_panel)
quick_send_sizer.Add(quick_send_panel, 0, wx.EXPAND | wx.ALL, 5)
quick_send_layout = wx.BoxSizer(wx.HORIZONTAL)
# 创建一个静态文本作为标签
tcl_label = wx.StaticText(quick_send_panel, label=f"{row_idx + 1}")
quick_send_layout.Add(tcl_label, 0, wx.ALL | wx.CENTER, 5)
# 创建 CheckBox
delay_checkbox = wx.CheckBox(quick_send_panel, label="")
quick_send_layout.Add(delay_checkbox, 0, wx.ALL | wx.CENTER, 5)
# 发送数据输入框
text_ctrl = wx.TextCtrl(quick_send_panel)
quick_send_layout.Add(text_ctrl, 1, wx.ALL | wx.EXPAND, 5)
# 发送按钮
send_btn = wx.Button(quick_send_panel, label='发送')
send_btn.Bind(wx.EVT_BUTTON, lambda event, tab=tab_idx, row=row_idx, ctrl=text_ctrl, delay=delay_checkbox: self.on_quick_send(event, tab, row, ctrl, delay))
quick_send_layout.Add(send_btn, 0, wx.ALL, 5)
# 延时输入框
delay_ctrl = wx.TextCtrl(quick_send_panel, value='0', size=(40, -1))
quick_send_layout.Add(delay_ctrl, 0, wx.ALL, 5)
quick_send_panel.SetSizer(quick_send_layout)
self.quick_send_panels[tab_idx].append((text_ctrl, send_btn, delay_ctrl, delay_checkbox)) # 将控件存储到对应的标签页列表中
tab_panel.SetSizer(quick_send_sizer)
quick_send_notebook.AddPage(tab_panel, f"指令区域{tab_idx + 1}")
right_sizer.Add(quick_send_notebook, 1, wx.EXPAND | wx.ALL, 5)
# 将左侧和右侧布局加入主布局
main_sizer.Add(left_sizer, 60, wx.EXPAND | wx.ALL, 5)
main_sizer.Add(right_sizer, 40, wx.EXPAND | wx.ALL, 5)
panel.SetSizer(main_sizer)
# 冻结窗口
self.Freeze()
panel.Layout() # 强制布局更新
self.Thaw() # 解冻窗口
# ---------------- 事件 ----------------
def on_connect(self, event):
if self.sock: # 当前已连接 -> 断开
self.close_conn()
return
host = self.host_ctrl.GetValue().strip()
port = self.port_ctrl.GetValue().strip()
if not host or not port:
wx.MessageBox('请输入地址和端口', '提示', wx.OK | wx.ICON_WARNING)
return
try:
port = int(port)
except ValueError:
wx.MessageBox('端口必须为数字', '提示', wx.OK | wx.ICON_WARNING)
return
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.settimeout(2)
try:
self.sock.connect((host, port))
self.alive = True
self.conn_btn.SetLabel('断开')
self.recv_ctrl.AppendText(f'[+] 已连接 {host}:{port}\n')
threading.Thread(target=self.recv_thread, daemon=True).start()
except Exception as e:
wx.MessageBox(f'连接失败: {e}', '错误', wx.OK | wx.ICON_ERROR)
self.sock = None
def on_send(self, event):
if not self.sock:
wx.MessageBox('请先连接服务器', '提示', wx.OK | wx.ICON_WARNING)
return
text = self.send_ctrl.GetValue()
if not text:
return
try:
if self.enable_tcl_checkbox.IsChecked():
text = text #需要加入TCL引擎, #TODO
if self.hex_send.IsChecked():
data = bytes.fromhex(text.replace(' ', ''))
else:
data = text.encode('utf-8', errors='ignore')
self.sock.sendall(data)
self.send_ctrl.Clear()
except Exception as e:
self.recv_ctrl.AppendText(f'[!] 发送失败: {e}\n')
self.close_conn()
def on_quick_send(self, event, tab_idx, row_idx, text_ctrl, delay_checkbox):
if not self.sock:
wx.MessageBox('请先连接服务器', '提示', wx.OK | wx.ICON_WARNING)
return
text = text_ctrl.GetValue()
if not text:
return
delay_ctrl = self.quick_send_panels[tab_idx][row_idx][2] # 通过 tab_idx 和 row_idx 访问延时输入框
delay = int(delay_ctrl.GetValue()) if delay_ctrl.GetValue().isdigit() else 0
try:
if self.enable_tcl_checkbox.IsChecked():
text = text # 需要加入TCL引擎, #TODO
if self.hex_send.IsChecked():
data = bytes.fromhex(text.replace(' ', ''))
else:
data = text.encode('utf-8', errors='ignore')
if delay_checkbox.IsChecked():
# 延时后发送
if delay > 0:
time.sleep(delay / 1000.0) # 延时单位为毫秒
self.sock.sendall(b'cmd:' + data)
self.recv_ctrl.AppendText(f'-->>: cmd:{text}\n')
else:
self.sock.sendall(data)
self.recv_ctrl.AppendText(f'-->: {text}\n')
except Exception as e:
self.recv_ctrl.AppendText(f'[!] 快捷发送失败: {e}\n')
self.close_conn()
def on_save_params(self, event):
wx.MessageBox('功能待完成', '提示', wx.OK | wx.ICON_INFORMATION)
# ---------------- 线程 ----------------
def recv_thread(self):
while self.alive :
try:
data = self.sock.recv(BUFFER)
if not data:
break
if self.hex_recv.IsChecked():
text = binascii.hexlify(data, ' ').decode('utf-8') + '\n'
else:
text = data.decode('utf-8', errors='ignore')
wx.CallAfter(self.recv_ctrl.AppendText, f"<--{text}")
except socket.timeout:
continue
except Exception as e:
if self.alive:
wx.CallAfter(self.recv_ctrl.AppendText, f'[!] 接收异常: {e}\n')
break
wx.CallAfter(self.close_conn)
# ---------------- 关闭 ----------------
def close_conn(self):
self.alive = False
if self.sock:
try:
self.sock.close()
self.conn_btn.SetLabel('连接')
self.recv_ctrl.AppendText('[-] 连接已断开\n')
except:
pass
self.sock = None
class App(wx.App):
def OnInit(self):
TcpClientFrame().Show()
return True
if __name__ == '__main__':
App().MainLoop()
运行效果


浙公网安备 33010602011771号