- 作者:
- 分类:项目&制作->开源项目
- 阅读:20399
- 点赞:146
- 版权:CC BY-SA 4.0
- 创建:2019-10-14
- 更新:2023-12-07
原文链接(持续更新):https://neucrack.com/p/186
好看实用跨平台带图形界面无广告串口调试助手 网络调试助手 终端工具 COMTool !
所有代码均开源!喜欢请点个 star 哦~: Github
跨平台(windows,linux,macOS,Raspberry Pi)串口调试助手/网络调试助手/终端工具/波形实时显示工具,完全开源! 可以直接下载使用,也可以自己根据需求二次开发,可以直接添加插件,省去了自己去调试数据收发和打包的麻烦,基于此很快就能编写出一个能够稳定实用的 GUI 程序, 编写插件十分简单,只需要实现几个函数即可。
Windows | Linux | Raspberry Pi | macOS |
---|---|---|---|
白色主题 | 黑色主题 | 协议插件 | TCP/UDP | 终端 | 图表绘制 |
---|---|---|---|---|---|
特性
- 跨平台 (Windows, Linux, macOS, Raspberry Pi)(使用 python 编写,只要你的平台支持 python)
- 可靠,界面不会卡死
- 多语言支持
- 白色主题和黑色主题切换
- 多种字符编码格式支持,比如
ASII,GBK(Chinese),UTF-8,UTF-16
等 - 自动保存设置(退出保存)
- 多种连接方式支持,同时支持编写连接插件
- 串口 支持
- 串口自动检测,支持记住上次使用的串口号
- 串口断线自动重连
- 波特率(随意设置)、校验、停止位、流控等设置支持
-
rts
和dtr
手动控制
- TCP/UDP 支持,包括客户端和服务端模式支持
- SSH 客户端支持
- 串口 支持
- 插件支持(插件开发请看docs/plugins_zh.md)),内置插件如下:
- 调试插件,基本收发数据调试
- 基础收发功能(字符(ASCII) 和 十六进制(HEX))
- 收发计数
- 清空接收缓冲区支持
- 自动换行
- 定时发送
- 发送记录保存和再次选中发送
- 自定义常用发送内容,一键发送
- 两种常用换行符CR LF(\r\n) 和 LF(\n) 支持
- 快捷键比如 Ctrl+Enter 发送数据
- 转义字符支持,比如
\r \n \t \x
等 - 收发记录,以及添加时间戳和记录到文件功能
- 发送文件
-
unix
终端风格颜色支持,比如\x1b[33;42mhello\x1b[0mhello2
- 协议插件,可自定义收发协议
- 自定义协议编解码支持
- 自定义快捷键发送
- 终端插件, 基本终端功能
- 图表插件
- 支持动态添加图表控件,添加你需要的控件
- 实时显示折线图,支持自定义协议头(支持转移符)
- 自定义按钮来发送数据,支持自定义快捷键
- 调试插件,基本收发数据调试
下载
- github release 页面
- 百度云 提取码: 7ip6
源码简单介绍
使用了Python3
编写, 界面使用PyQt5
编写,国际化(多语言)使用 babel
处理,Main.py
中包含了主要逻辑,布局使用了qss
(一种类似css
的语法,支持一些css2
语法,实际用起来还是有点诡异= =)。
这个项目是自己刚学 python
的时候拿来练手的,2017年做了串口助手的功能并发布, 后面断断续续一直在维护,支持了更多功能, 以及支持了插件
源码运行的方法以及插件开发方法在项目首页的 README
文件中有详细说明,仔细阅读即可
如果你打算修改源码或者写插件,请一定要注意:
- 变量风格和
PyQt
的一样了,变量函数小驼峰,类大驼峰 - 所有更新和获取 UI 控件的操作都需要在主线程里面完成,比如初始化,或者槽里面更新
- 如果你有代码需要长时间占用 CPU, 比如 sleep, 或者等待什么或者消耗时长长的操作比如串口发送大文件这样的操作,请开一个新的线程进行处理,不要在主线程或者槽里面处理
- 如果你在你的新线程里面要获取 UI 控件的值, 请给 UI 控件绑定变量, 代码里面是绑定到了
self.config
的值,当UI 控件改变,去改变这个变量,其它线程就能直接获取这个变量,由于 Python 的多线程并不是真正的多线程, 线程锁也不需要 - 如果你在你的新线程要去改变控件,请新建一个信号,信号绑定一个槽,新的线程里面需要改变控件时发送信号,在槽函数里面去改变 UI 控件
画曲线图说明
只需要打开 图标
插件页面,双击左边plot
,连接串口或者 TCP等。
通过串口或者 TCP 等发送数据,格式为$[line name],[x],[y]<,checksum>\n
, checksum
是可选的,比如:
$roll,1.0,2.0\n
, 表示在曲线roll
上坐标1.0, 2.0
处画一个点。$pitch,1.0,2.0,179\n
,表示在曲线ptch
上坐标1.0, 2.0
处画一个点,这里计算了和校验值(即,checksum
前面所有字节的和对 256 取余)
以上例子不断改变x, y
的值发送,最终会出现两条曲线
C 代码示例:
int plot_pack_ascii(uint8_t *buff, int buff_len, const char *name, float x, float y)
{
snprintf(buff, buff_len, "$%s,%f,%f", name, x, y);
// add checksum
int sum = 0;
for (int i = 0; i < strlen(buff); i++)
{
sum += buff[i];
}
snprintf(buff + strlen(buff), buff_len - strlen(buff), ",%d\n", sum & 0xFF);
return strlen(buff);
}
uint8_t buff[64];
double x = 1.0, y = 2.0;
int len = plot_pack_ascii(buff, sizeof(buff), "data1", x, y);
send_bytes(buff, len);
另外还支持二进制协议,更多请看软件帮助
里面的文档。
插件开发
参考文档: https://github.com/Neutree/COMTool/blob/master/docs/plugins_zh.md
参考例程: myplugin.py 和 myplugin2
比如 myplugin.py
如下(实际以源码中的文件内容为准,可能有小幅度改动,但是改动不会很大)
定义一个 Plugin
类,名字固定, 集成自Plugin_Base
类,实现几个必要的方法即可,以下对代码加了中文解释说明:
'''
@brief This is an example plugin, can receive and send data
@author
@date
@license LGPL-3.0
'''
# 导入画界面需要用到的库(PyQt5)
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QTextEdit, QPushButton, QLineEdit
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QFont, QTextCursor
from PyQt5 import uic
try:
# 导入需要使用到的类,直接使用了相对路径,优先从当前路径下找,这样在用源码调试时就会调用这个了
from plugins.base import Plugin_Base
from conn import ConnectionStatus
from i18n import _
except ImportError:
# 如果上面的相对路径找不到再去 python 库里面找 COMTool 包调用
from COMTool.plugins.base import Plugin_Base
from COMTool.i18n import _
from COMTool.conn import ConnectionStatus
# 定义一个固定名字为 Plugin 的类, 继承子 Plugin_Base
class Plugin(Plugin_Base):
# 定义插件的唯一 id
id = "myplugin"
# 定义插件的名称,这里用了英文,如果插件需要支持国际化,请参考 myplugin2 示例
name = "my plugin"
# 用于通知 UI 线程更新 UI 的信号,写 pyqt 程序时需要非常注意所有 UI 更新只能在 UI 线程里,否则界面可能会被阻塞而表现为卡死
updateSignal = pyqtSignal(str, str)
def onConnChanged(self, status:ConnectionStatus, msg:str):
# 连接状态改变回调函数
print("-- connection changed: {}, msg: {}".format(status, msg))
def onWidgetMain(self, parent):
'''
生成主界面, 需要返回一个 QWidget 对象,在这个 QWidget 对象中可以画任何你想实现的界面
包括在这里加载 ui 文件都是可以的
此处样例顶一个两个输入框,一个用来显示接收到的数据,一个用来输入需要发送的信息,
然后定义一个按钮,按钮用来发送消息
'''
# 可以从 ui 文件加载,UI 文件可以用 QT creator 画界面后生成
# self.widget = uic.loadUi(os.path.join(os.path.abspath(os.path.dirname(__file__)), "my_widget.ui"))
# 定义一个主控件
self.widget = QWidget()
# 定义布局
layout = QVBoxLayout()
# 定义接收框
self.receiveArea = QTextEdit("")
font = QFont('Menlo,Consolas,Bitstream Vera Sans Mono,Courier New,monospace, Microsoft YaHei', 10)
self.receiveArea.setFont(font)
self.receiveArea.setLineWrapMode(QTextEdit.NoWrap)
# 定义发送框
self.input = QTextEdit()
self.input.setAcceptRichText(False)
# 定义发送按钮,同样的,这里的文字可以国际化,参考 myplugin2 的做法
self.button = QPushButton("Send")
# 添加控件到布局中
layout.addWidget(self.receiveArea)
layout.addWidget(self.input)
layout.addWidget(self.button)
self.widget.setLayout(layout)
# 添加事件绑定 ,按钮按下触发函数,触发的函数会在 UI 线程中运行
self.button.clicked.connect(self.buttonSend)
# 添加 UI 更新事件回调函数绑定
self.updateSignal.connect(self.updateUI)
# 返回主控件
return self.widget
def buttonSend(self):
'''
按钮绑定的事件函数, 运行在 UI 线程
'''
# 获取数据
data = self.input.toPlainText()
# 没有数据,提示需要输入,同样这里的文字也需要国际化,参照 myplugin2
if not data:
# to pop up a warning window
self.hintSignal.emit("error", "Error", "Input data first please")
return
# 将字符串编码为字节,编码方式使用了全局编码设定,即界面上的下拉框选项
dataBytes = data.encode(self.configGlobal["encoding"])
# 调用发送,这个函数已经自动被绑定到了对应的连接上
self.send(dataBytes)
def updateUI(self, dataType, data):
'''
更新 UI, 运行在 UI 线程
'''
if dataType == "receive":
# 将光标移动到最底并插入数据
self.receiveArea.moveCursor(QTextCursor.End)
self.receiveArea.insertPlainText(data)
def onReceived(self, data : bytes):
'''
接收到数据的回调函数, 注意此函数不在 UI 线程,是一个独立的接收线程,
所以如果有时间消耗长的代码需要执行可以放到这里执行,执行完毕需要显示内容再提交给 UI 线程
'''
# 调用基类的接收函数
super().onReceived(data)
# 解码数据,这里就是将字节数据解码为字符串
dataStr = data.decode(self.configGlobal["encoding"])
# 将内容提交给 UI 线程进行显示
# 注意不能直接在这里调用 seld.receiveBox, 否则可能会出现程序退出或者界面无响应等问题
# 通过我们定义的 UI 更新信号将需要更新的内容传达给 UI 线程即可
self.updateSignal.emit("receive", dataStr)
编写完毕后,通过界面的加载插件按钮即可加载插件;
另外如果想把插件集成到软件中,在 这里 添加一下插件的类就好了;
也可以将插件打包成 python
包,包名需要comtool_plugin_xxx
格式, 用 pip
安装到系统,comtool
(仅限源码安装) 就会自动扫描到