pythonGUI之九: kivy实现tcp客户端调试工具
Kivy 是一个开源的 Python 库,用于开发多点触控应用程序。它支持多种平台,包括 Windows、macOS、Linux、iOS 和 Android。Kivy 的主要特点是其简单易用的 API 和强大的多点触控支持,这使得它非常适合开发需要复杂交互的用户界面。
Kivy 的主要特点
- 多平台支持:
Kivy 可以在多种操作系统上运行,包括 Windows、macOS、Linux、iOS 和 Android。
它提供了一致的 API,使得开发者可以轻松地在不同平台上部署应用程序。
多点触控支持:
Kivy 提供了强大的多点触控支持,可以处理多个触摸点的输入。
这使得它非常适合开发需要复杂手势识别的应用程序,如游戏、教育工具等。
简单易用的 API:
Kivy 的 API 设计简洁明了,易于上手。
它提供了丰富的控件(如按钮、文本输入框、标签等)和布局管理器(如 BoxLayout、GridLayout、AnchorLayout 等),方便开发者快速构建用户界面。
自定义控件:
Kivy 允许开发者通过 Python 和 Kivy 的语言(KV 语言)轻松创建自定义控件。
KV 语言是一种专门用于定义用户界面布局的声明式语言,使得界面设计更加直观和灵活。
图形和动画支持:
Kivy 提供了强大的图形和动画支持,可以用来创建复杂的视觉效果。
它支持 OpenGL ES 2.0,可以高效地渲染图形和动画。
社区和文档:
Kivy 拥有一个活跃的社区,提供了丰富的文档、教程和示例代码。
社区的支持使得开发者可以更容易地解决问题并学习新技能。
1.安装包
pip install kivy -i https://pypi.tuna.tsinghua.edu.cn/simple
2.TCP客户端源码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
TCP Client 网络调试助手(Kivy 版)
2025-07-15
"""
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.checkbox import CheckBox
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem
from kivy.uix.scrollview import ScrollView
from kivy.uix.gridlayout import GridLayout
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.core.text import LabelBase
from os.path import join, dirname
import socket
import threading
import binascii
import time
# 设置窗口大小
Window.size = (1200, 800)
BUFFER = 4096
class TcpClientApp(App):
def build(self):
self.title = "TCP客户端调试工具V1.0" # 设置窗口标题
self.sock = None
self.alive = False
self.layout = TcpClientLayout()
return self.layout
class TcpClientLayout(BoxLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.orientation = 'horizontal'
self.quick_send_panels = [[] for _ in range(8)] # 定义 quick_send_panels
self.sock = None # 定义 sock 属性
self.alive = False # 定义 alive 属性
self.left_panel = BoxLayout(orientation='vertical', size_hint=(0.6, 1))
self.right_panel = BoxLayout(orientation='vertical', size_hint=(0.4, 1))
self.build_left_panel()
self.build_right_panel()
self.add_widget(self.left_panel)
self.add_widget(self.right_panel)
def build_left_panel(self):
# 连接区
conn_box = BoxLayout(orientation='horizontal', size_hint=(1, None), height=30)
conn_box.add_widget(Label(text='Host:', size_hint=(None, 1), width=60, font_name='SimHei'))
self.host_ctrl = TextInput(text='127.0.0.1', multiline=False, size_hint=(0.3, 1), font_name='SimHei')
conn_box.add_widget(self.host_ctrl)
conn_box.add_widget(Label(text='Port:', size_hint=(None, 1), width=60, font_name='SimHei'))
self.port_ctrl = TextInput(text='5002', multiline=False, size_hint=(0.2, 1), font_name='SimHei')
conn_box.add_widget(self.port_ctrl)
self.conn_btn = Button(text='连接', size_hint=(0.2, 1), font_name='SimHei')
self.conn_btn.bind(on_press=self.on_connect)
conn_box.add_widget(self.conn_btn)
self.left_panel.add_widget(conn_box)
# 接收区
recv_box = BoxLayout(orientation='vertical', size_hint=(1, 0.7))
self.recv_ctrl = TextInput(readonly=True, multiline=True, size_hint=(1, 1), font_name='SimHei')
recv_box.add_widget(self.recv_ctrl)
recv_options_box = BoxLayout(orientation='horizontal', size_hint=(1, None), height=30)
self.hex_recv = CheckBox(size_hint=(None, 1), width=80)
recv_options_box.add_widget(self.hex_recv)
recv_options_box.add_widget(Label(text='HEX 显示', size_hint=(None, 1), width=80, font_name='SimHei', halign='left'))
self.clear_btn = Button(text='清空接收区', size_hint=(0.2, 1), font_name='SimHei')
self.clear_btn.bind(on_press=self.clear_recv_ctrl)
recv_options_box.add_widget(self.clear_btn)
self.enable_tcl_checkbox = CheckBox(size_hint=(None, 1), width=80)
recv_options_box.add_widget(self.enable_tcl_checkbox)
recv_options_box.add_widget(Label(text='启用TCL', size_hint=(None, 1), width=80, font_name='SimHei', halign='left'))
self.save_params_btn = Button(text='保存参数', size_hint=(0.2, 1), font_name='SimHei')
self.save_params_btn.bind(on_press=self.on_save_params)
recv_options_box.add_widget(self.save_params_btn)
recv_box.add_widget(recv_options_box)
self.left_panel.add_widget(recv_box)
# 发送区
send_box = BoxLayout(orientation='horizontal', size_hint=(1, None), height=30)
self.send_ctrl = TextInput(multiline=False, size_hint=(0.7, 1), font_name='SimHei')
send_box.add_widget(self.send_ctrl)
self.hex_send = CheckBox(size_hint=(None, 1), width=100)
send_box.add_widget(self.hex_send)
send_box.add_widget(Label(text='HEX', size_hint=(None, 1), width=30, font_name='SimHei'))
self.send_btn = Button(text='发送', size_hint=(0.2, 1), font_name='SimHei')
self.send_btn.bind(on_press=self.on_send)
send_box.add_widget(self.send_btn)
self.left_panel.add_widget(send_box)
def build_right_panel(self):
# 设置 TabbedPanel 的高度
self.quick_send_notebook = TabbedPanel(do_default_tab=False, size_hint=(1, 1))
for tab_idx in range(8):
# 创建 TabbedPanelItem 并设置固定高度
tab_item = TabbedPanelItem(text=f'指令区域{tab_idx + 1}', font_name='SimHei', size_hint_y=None, height=30)
# 创建 GridLayout 并设置固定高度
tab_panel = GridLayout(cols=1, spacing=5, size_hint_y=None, height=30)
tab_panel.bind(minimum_height=tab_panel.setter('height'))
# 创建 ScrollView 并设置固定高度
scroll_view = ScrollView(size_hint=(1, 1)) # 确保 ScrollView 的高度与 TabbedPanelItem 一致
scroll_view.add_widget(tab_panel)
# 将 ScrollView 添加到 TabbedPanelItem
tab_item.add_widget(scroll_view)
# 将 TabbedPanelItem 添加到 TabbedPanel
self.quick_send_notebook.add_widget(tab_item)
# 添加指令行
for row_idx in range(30):
quick_send_layout = BoxLayout(orientation='horizontal', size_hint_y=None, height=30)
tcl_label = Label(text=str(row_idx + 1), size_hint_x=None, width=30, font_name='SimHei')
quick_send_layout.add_widget(tcl_label)
delay_checkbox = CheckBox(size_hint_x=None, width=30)
quick_send_layout.add_widget(delay_checkbox)
text_ctrl = TextInput(multiline=False, size_hint_x=0.6, font_name='SimHei')
quick_send_layout.add_widget(text_ctrl)
send_btn = Button(text='发送', size_hint_x=0.2, font_name='SimHei')
send_btn.bind(on_press=lambda instance, tab=tab_idx, row=row_idx, ctrl=text_ctrl,
delay=delay_checkbox: self.on_quick_send(instance, tab, row, ctrl, delay))
quick_send_layout.add_widget(send_btn)
delay_ctrl = TextInput(text='0', multiline=False, size_hint_x=None, width=30, font_name='SimHei')
quick_send_layout.add_widget(delay_ctrl)
tab_panel.add_widget(quick_send_layout)
self.quick_send_panels[tab_idx].append((text_ctrl, send_btn, delay_ctrl, delay_checkbox))
self.right_panel.add_widget(self.quick_send_notebook)
def on_connect(self, instance):
if self.sock:
self.close_conn()
return
host = self.host_ctrl.text.strip()
port = self.port_ctrl.text.strip()
if not host or not port:
self.show_message('请输入地址和端口')
return
try:
port = int(port)
except ValueError:
self.show_message('端口必须为数字')
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.text = '断开'
self.recv_ctrl.text += f'[+] 已连接 {host}:{port}\n'
threading.Thread(target=self.recv_thread, daemon=True).start()
except Exception as e:
self.show_message(f'连接失败: {e}')
self.sock = None
def on_send(self, instance):
if not self.sock:
self.show_message('请先连接服务器')
return
text = self.send_ctrl.text
if not text:
return
try:
if self.enable_tcl_checkbox.active:
text = text # 需要加入TCL引擎,#TODO
if self.hex_send.active:
data = bytes.fromhex(text.replace(' ', ''))
else:
data = text.encode('utf-8', errors='ignore')
self.sock.sendall(data)
self.send_ctrl.text = ''
except Exception as e:
self.recv_ctrl.text += f'[!] 发送失败: {e}\n'
self.close_conn()
def on_quick_send(self, instance, tab_idx, row_idx, text_ctrl, delay_checkbox):
if not self.sock:
self.show_message('请先连接服务器')
return
text = text_ctrl.text
if not text:
return
delay_ctrl = self.quick_send_panels[tab_idx][row_idx][2]
delay = int(delay_ctrl.text) if delay_ctrl.text.isdigit() else 0
try:
if self.enable_tcl_checkbox.active:
text = text # 需要加入TCL引擎,#TODO
if self.hex_send.active:
data = bytes.fromhex(text.replace(' ', ''))
else:
data = text.encode('utf-8', errors='ignore')
if delay_checkbox.active:
if delay > 0:
time.sleep(delay / 1000.0)
self.sock.sendall(b'cmd:' + data)
self.recv_ctrl.text += f'-->>: cmd:{text}\n'
else:
self.sock.sendall(data)
self.recv_ctrl.text += f'-->: {text}\n'
except Exception as e:
self.recv_ctrl.text += f'[!] 快捷发送失败: {e}\n'
self.close_conn()
def on_save_params(self, instance):
self.show_message('功能待完成')
def clear_recv_ctrl(self, instance):
self.recv_ctrl.text = ''
def show_message(self, message):
from kivy.uix.popup import Popup
from kivy.uix.label import Label
popup = Popup(title='提示', content=Label(text=message, font_name='SimHei'), size_hint=(None, None),
size=(400, 200))
popup.open()
def recv_thread(self):
while self.alive:
try:
data = self.sock.recv(BUFFER)
if not data:
break
if self.hex_recv.active:
text = binascii.hexlify(data, ' ').decode('utf-8') + '\n'
else:
text = data.decode('utf-8', errors='replace') # 使用 'replace' 替代 'ignore'
Clock.schedule_once(
lambda dt, text=text: setattr(self.recv_ctrl, 'text', self.recv_ctrl.text + f"<--{text}"))
except socket.timeout:
continue
except Exception as e:
if self.alive:
Clock.schedule_once(
lambda dt, e=e: setattr(self.recv_ctrl, 'text', self.recv_ctrl.text + f'[!] 接收异常: {e}\n'))
break
Clock.schedule_once(lambda dt: self.close_conn())
def close_conn(self):
self.alive = False
if self.sock:
try:
self.sock.close()
self.conn_btn.text = '连接'
self.recv_ctrl.text += '[-] 连接已断开\n'
except:
pass
self.sock = None
if __name__ == '__main__':
TcpClientApp().run()
3.运行效果


浙公网安备 33010602011771号