选择窗口添加一层文件名节点;添加右键删除变量;添加启动时检查openocd进程;调整帮助信息样式;版本号更新至1.2.2

This commit is contained in:
skythinker 2022-08-08 10:30:49 +08:00
parent 033393b905
commit 996b3d4ed6
11 changed files with 229 additions and 25 deletions

View File

@ -11,7 +11,7 @@ CONFIG += c++11
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS \
APP_VERSION=\\\"1.2.1\\\"
APP_VERSION=\\\"1.2.2\\\"
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.0, 2022-03-30T17:03:19. -->
<!-- Written by QtCreator 4.11.0, 2022-08-08T00:56:42. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@ -44,7 +44,7 @@
* 在主窗口表格最后一行变量名(编辑框)处手动填写也可以添加变量
* 在主窗口中选中变量名按Del键可删除变量
* 在变量名上右键可删除变量或在选中变量名后按Del键
* 变量名不仅可以填入单个变量名还可以填入合法的C语言表达式GDB支持即可复合类型不能修改和绘图只能实时查看
@ -112,8 +112,6 @@
## TODO
* 增加对不同文件中同名变量的区分
* 提升采样频率
* 增加对本地程序的支持

View File

@ -91,7 +91,7 @@
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;hr width=&quot;300&quot;/&gt;&lt;p align=&quot;center&quot;&gt;版本号V1.2.1&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;更新时间2022/3/30&lt;/p&gt;&lt;hr width=&quot;300&quot;/&gt;&lt;p align=&quot;center&quot;&gt;Developed by Skythinker&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;hr width=&quot;300&quot;/&gt;&lt;p align=&quot;center&quot;&gt;版本号V1.2.2&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;更新时间2022/8/7&lt;/p&gt;&lt;hr width=&quot;300&quot;/&gt;&lt;p align=&quot;center&quot;&gt;Developed by Skythinker&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

View File

@ -32,9 +32,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>-390</y>
<y>0</y>
<width>523</width>
<height>1308</height>
<height>1283</height>
</rect>
</property>
<property name="autoFillBackground">
@ -53,7 +53,52 @@
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;LinkScope简介&lt;/span&gt;&lt;/p&gt;&lt;p&gt;本程序使用QT编写用于硬件设备的调试可直接驱动串口或各种调试器基于OpenOCD支持有以下几个主要功能&lt;/p&gt;&lt;p&gt;1.实时查看和修改变量值&lt;/p&gt;&lt;p&gt;2.变量值波形实时绘制&lt;/p&gt;&lt;p&gt;3.采样数据导出为CSV表格&lt;/p&gt;&lt;p&gt;4.格式化日志输出&lt;/p&gt;&lt;p&gt;调试器连接模式理论上支持OpenOCD允许GDB连接的各种调试器及硬件芯片如STLink、JLink、CMSIS-DAP等以及STM32全系列等&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;使用方法&lt;/span&gt;&lt;/p&gt;&lt;p&gt;1.点击【设置符号文件】选择编译器输出的符号文件,然后添加变量到列表中&lt;/p&gt;&lt;p&gt;&amp;gt; 加载符号文件后即可在变量选择窗口添加需要查看的变量,可以直接添加到列表或添加到编辑框后进行手动编辑&lt;/p&gt;&lt;p&gt;&amp;gt; 在主窗口表格最后一行变量名(编辑框)处手动填写也可以添加变量&lt;/p&gt;&lt;p&gt;&amp;gt; 在主窗口中选中变量名按Del键可删除变量&lt;/p&gt;&lt;p&gt;&amp;gt; 变量名不仅可以填入单个变量名还可以填入合法的C语言表达式GDB支持即可复合类型不能修改和绘图只能实时查看&lt;/p&gt;&lt;p&gt;2.选择连接模式,连接芯片,连接后程序开始循环采样,勾选【监视日志】后即可将下位机输出的日志实时显示出来&lt;/p&gt;&lt;p&gt;&amp;gt; 调试器模式下,在下拉框中选择调试器和芯片类型,点击连接目标&lt;/p&gt;&lt;p&gt;&amp;gt; 串口模式下,点击刷新串口加载串口列表,选中所连接的串口,点击连接目标&lt;/p&gt;&lt;p&gt;&amp;gt; 串口功能和日志功能都需要修改下位机程序,可以在菜单中进入主页查看说明&lt;/p&gt;&lt;p&gt;3.编辑【修改变量】列可以修改变量值,双击【图线颜色】列可以选择绘图颜色 &lt;/p&gt;&lt;p&gt;4.单击【变量名】列选中对应的变量,可以在绘图窗口查看历史数据,并会加粗绘制 &lt;/p&gt;&lt;p&gt;5.绘图界面说明请到绘图窗口点击操作说明 &lt;/p&gt;&lt;p&gt;6.点击菜单中的保存/导入配置可以将当前配置保存到INI文件或从文件中恢复配置点击导出数据可以将获取到的采样数据导出到CSV表格文件&lt;/p&gt;&lt;hr/&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;注意事项 &lt;/span&gt;&lt;/p&gt;&lt;p&gt;1.修改符号文件路径后需要重新连接 &lt;/p&gt;&lt;p&gt;2.连接目标前请确认已使用该调试器为目标芯片下载过指定程序 &lt;/p&gt;&lt;p&gt;3.若程序闪退后发现下一次运行时无法连接目标请尝试手动结束openocd.exe进程 &lt;/p&gt;&lt;p&gt;4.连接配置文件位于openocd/share/openocd/scripts下的target和interface中用户可按照openocd语法编写配置脚本放入对应目录下后点击“刷新连接配置”菜单项&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;
&lt;head&gt;
&lt;style&gt;
li{margin-top: 10px;}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;&lt;span style=&quot;font-weight:600;&quot;&gt;LinkScope简介&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;本程序使用QT编写用于硬件设备的调试可直接驱动串口或各种调试器基于OpenOCD支持有以下几个主要功能&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;实时查看和修改变量值&lt;/li&gt;
&lt;li&gt;变量值波形实时绘制&lt;/li&gt;
&lt;li&gt;采样数据导出为CSV表格&lt;/li&gt;
&lt;li&gt;格式化日志输出&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;调试器连接模式理论上支持OpenOCD允许GDB连接的各种调试器及硬件芯片如STLink、JLink、CMSIS-DAP等以及STM32全系列等&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;span style=&quot;font-weight:600;&quot;&gt;使用方法&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;点击【设置符号文件】选择编译器输出的符号文件,然后添加变量到列表中&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;加载符号文件后即可在变量选择窗口添加需要查看的变量,可以直接添加到列表或添加到编辑框后进行手动编辑&lt;/li&gt;
&lt;li&gt;在主窗口表格最后一行变量名(编辑框)处手动填写也可以添加变量&lt;/li&gt;
&lt;li&gt;在变量名上右键可删除变量或在选中变量名后按Del键&lt;/li&gt;
&lt;li&gt;变量名不仅可以填入单个变量名还可以填入合法的C语言表达式GDB支持即可复合类型不能修改和绘图只能实时查看&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;选择连接模式,连接芯片,连接后程序开始循环采样,勾选【监视日志】后即可将下位机输出的日志实时显示出来&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;调试器模式下,在下拉框中选择调试器和芯片类型,点击连接目标&lt;/li&gt;
&lt;li&gt;串口模式下,点击刷新串口加载串口列表,选中所连接的串口,点击连接目标&lt;/li&gt;
&lt;li&gt;串口功能和日志功能都需要修改下位机程序,可以在菜单中进入主页查看说明&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;编辑【修改变量】列可以修改变量值,双击【图线颜色】列可以选择绘图颜色 &lt;/li&gt;
&lt;li&gt;单击【变量名】列选中对应的变量,可以在绘图窗口查看历史数据,并会加粗绘制 &lt;/li&gt;
&lt;li&gt;绘图界面说明请到绘图窗口点击操作说明 &lt;/li&gt;
&lt;li&gt;点击菜单中的保存/导入配置可以将当前配置保存到INI文件或从文件中恢复配置点击导出数据可以将获取到的采样数据导出到CSV表格文件&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;span style=&quot;font-weight:600;&quot;&gt;注意事项 &lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修改符号文件路径后需要重新连接 &lt;/li&gt;
&lt;li&gt;连接目标前请确认已使用该调试器为目标芯片下载过指定程序 &lt;/li&gt;
&lt;li&gt;连接配置文件位于openocd/share/openocd/scripts下的target和interface中用户可按照openocd语法编写配置脚本放入对应目录下后点击“刷新连接配置”菜单项&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>

View File

@ -30,7 +30,7 @@ QString ListWindow::getVarFullName(const VarNode &node)
{
const VarNode *curNode=&node;
QString fullName=node.name;
while(curNode->parent->parent)//到父节点为根节点为止
while(curNode->parent->parent->parent)//到父节点为文件节点为止
{
if(curNode->name.contains('['))//若为数组元素,父节点为数组名,直接拼接(无需加'.'
fullName=curNode->parent->name+fullName;
@ -41,14 +41,51 @@ QString ListWindow::getVarFullName(const VarNode &node)
return fullName;
}
//计算一个节点所在的文件名
QString ListWindow::getVarFileName(const VarNode &node,bool isFull)
{
const VarNode *curNode=&node;
while(curNode->parent->parent)//到父节点为根节点为止
curNode=curNode->parent;
QString filename=curNode->name;
if(isFull)
return filename;//返回全路径
else
return filename.split("/").last().split("\\").last();//返回文件名部分
}
//解析一个节点的子节点,并更新子节点的可展开状态
void ListWindow::parseVarChildren(VarNode &node)
{
static QString rawVarInfo="";//展开根节点时保存"info variables"结果,展开文件名时直接从中解析,提高效率
if(node.parent==NULL)//传入的是根节点
{
QString rawVarList=gdb->runCmd("info variables\r\n");//使用info variables指令列出axf中所有变量
gdb->removeInnerSection(rawVarList,0);//移除内部直接嵌套的部分
QStringList varList=gdb->getVarListFromRawOutput(rawVarList);//解析出变量列表
rawVarInfo=gdb->runCmd("info variables\r\n");//使用info variables指令列出axf中所有变量
gdb->removeInnerSection(rawVarInfo,0);//移除内部直接嵌套的部分
QRegExp fileRx("\\nFile\\s(.*):\\r\\n");//正则匹配获取所有文件名并添加为节点
fileRx.setMinimal(true);
int pos=0;
while((pos=fileRx.indexIn(rawVarInfo,pos))!=-1)
{
node.append(fileRx.cap(1));
node.children.last().expandable=true;
pos+=fileRx.matchedLength();
}
}
else if(node.parent->parent==NULL)//传入的是文件名节点
{
QString regName="";
for(int i=0;i<node.name.length();i++)//将文件名每个字符转换为16进制格式用于正则匹配
regName+=QString("\\x%1").arg(node.name.at(i).unicode(),0,16);
int startPos=QRegExp(QString("\\nFile\\s%1:\\r\\n").arg(regName)).indexIn(rawVarInfo);//找到文件名所在位置
int endPos=QRegExp("\\nFile\\s(.*):\\r\\n").indexIn(rawVarInfo,startPos+1);//找到下一个文件名所在位置
if(endPos==-1)
endPos=rawVarInfo.length()-1;
QStringList varList=gdb->getVarListFromRawOutput(rawVarInfo.mid(startPos,endPos-startPos));//截取两个位置之间的子串解析变量列表
foreach(QString name,varList)//依次添加子节点并更新可展开状态
{
node.append(name);
@ -146,8 +183,15 @@ void ListWindow::on_btn_add2edit_clicked()
if(index.row()==-1)
return;
VarNode &node=*(VarNode*)(treeModel->itemFromIndex(index)->data().toULongLong());//获取所选节点
if(node.parent)
emit add2Edit(getVarFullName(node));//将变量全名使用信号发出去
if(node.parent && node.parent->parent)
{
QString varName=getVarFullName(node);
if(ui->cb_use_path->isChecked())
emit add2Edit("\'"+getVarFileName(node,ui->cb_full_path->isChecked())+"\'::"+varName);
else
emit add2Edit(varName);
}
}
//添加到列表按钮槽函数(逻辑与添加到编辑框相同,仅发送的信号不同)
@ -157,6 +201,18 @@ void ListWindow::on_btn_add2list_clicked()
if(index.row()==-1)
return;
VarNode &node=*(VarNode*)(treeModel->itemFromIndex(index)->data().toULongLong());
if(node.parent)
emit add2List(getVarFullName(node));
if(node.parent && node.parent->parent)
{
QString varName=getVarFullName(node);
if(ui->cb_use_path->isChecked())
emit add2List("\'"+getVarFileName(node,ui->cb_full_path->isChecked())+"\'::"+varName);
else
emit add2List(varName);
}
}
//附带文件名选框槽函数
void ListWindow::on_cb_use_path_toggled(bool checked)
{
ui->cb_full_path->setEnabled(checked);
}

View File

@ -57,6 +57,7 @@ private slots:
void on_tree_expanded(const QModelIndex &index);
void on_btn_add2edit_clicked();
void on_btn_add2list_clicked();
void on_cb_use_path_toggled(bool checked);
private:
Ui::ListWindow *ui;
@ -65,6 +66,7 @@ private:
VarNode varTree;
QStandardItemModel *treeModel;
QString getVarFullName(const VarNode &node);
QString getVarFileName(const VarNode &node,bool isFull);
void parseVarChildren(VarNode &node);
void updateTree();
};

View File

@ -28,18 +28,47 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="btn_add2list">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>直接添加到列表</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="btn_add2edit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>添加到编辑框</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_add2list">
<item row="0" column="1">
<widget class="QCheckBox" name="cb_use_path">
<property name="text">
<string>直接添加到列表</string>
<string>附带文件名</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="cb_full_path">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>使用全路径</string>
</property>
</widget>
</item>

View File

@ -51,6 +51,9 @@ MainWindow::MainWindow(QWidget *parent)
tableModel=new QStandardItemModel(this);//创建并初始化表格
initTable();
tablePopMenu=new QMenu(ui->tb_var);//创建表格右键菜单
tablePopMenu->addAction(ui->action_del_var);
openocd=new OpenOCD();//创建OpenOCD对象并连接错误处理槽
connect(openocd,&OpenOCD::onErrorOccur,this,&MainWindow::slotOnConnErrorOccur,Qt::QueuedConnection);
@ -64,6 +67,8 @@ MainWindow::MainWindow(QWidget *parent)
loadConfFileList();//从openocd文件夹中读取配置文件列表
loadGlobalConf();//加载软件全局配置
loadFromFile("autosave.ini");//加载自动保存的工程配置
QTimer::singleShot(100,this,&MainWindow::checkOpenocdProcess);//窗口加载完成后检查是否有正在运行的openocd进程
}
MainWindow::~MainWindow()
@ -80,6 +85,7 @@ MainWindow::~MainWindow()
gdb->stop();//结束gdb进程
delete ui;
delete tableModel;
delete tablePopMenu;
delete openocd;
delete gdb;
delete stampTimer;
@ -602,6 +608,16 @@ void MainWindow::on_tb_var_clicked(const QModelIndex &index)
}
}
//表格右键菜单槽函数
void MainWindow::on_tb_var_customContextMenuRequested(const QPoint &pos)
{
QModelIndex index=ui->tb_var->indexAt(pos);//获取所点击的单元格索引
if(index.column()==0 && index.row()<varList.size())
{
tablePopMenu->exec(QCursor::pos());//弹出右键菜单
}
}
//保存配置菜单点击
void MainWindow::on_action_save_triggered()
{
@ -715,6 +731,23 @@ void MainWindow::on_action_feedback_triggered()
QDesktopServices::openUrl(QUrl("https://support.qq.com/product/378753"));//打开反馈页面
}
//删除选中变量槽函数
void MainWindow::on_action_del_var_triggered()
{
if(ui->tb_var->currentIndex().column()==0)//确定已选中某个变量的变量名
{
int index=ui->tb_var->currentIndex().row();//获取所选变量的下标
if(index<varList.size())
{
varList.removeAt(index);//删除变量
redrawTable();
if(connected)//若正在连接状态则向gdb发送新的变量列表
updateGDBList();
}
}
}
//检查更新
void MainWindow::checkUpdate()
{
@ -767,6 +800,31 @@ void MainWindow::checkUpdate()
manager->get(request);
}
//检查后台是否存在openocd进程存在的话询问用户是否关闭
void MainWindow::checkOpenocdProcess()
{
QProcess listProcess(0);//使用tasklist查找openocd进程
listProcess.setProgram("tasklist");
listProcess.setNativeArguments("/fi \"imagename eq openocd.exe\"");
listProcess.start();
listProcess.waitForFinished();
if(listProcess.readAllStandardOutput().contains("openocd.exe"))//后台存在openocd进程
{
QMessageBox msgBox;
msgBox.setWindowTitle("提示");
msgBox.setText("发现正在运行的OpenOCD进程可能是由于上次不正常退出导致的是否强制结束若您没有其他正在调试的程序建议结束");
msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
if(msgBox.exec()==QMessageBox::Yes)
{
QProcess killProcess(0);//用taskkill强行结束进程
killProcess.setProgram("taskkill");
killProcess.setNativeArguments("/F /IM openocd.exe");
killProcess.start();
killProcess.waitForFinished();
}
}
}
//检查更新菜单点击
void MainWindow::on_action_checkupdate_triggered()
{

View File

@ -56,6 +56,7 @@ private slots:
void on_bt_reset_clicked();
void on_tb_var_doubleClicked(const QModelIndex &index);
void on_tb_var_clicked(const QModelIndex &index);
void on_tb_var_customContextMenuRequested(const QPoint &pos);
void on_action_save_triggered();
void on_action_load_triggered();
void on_action_export_triggered();
@ -65,6 +66,7 @@ private slots:
void on_action_homepage_triggered();
void on_action_feedback_triggered();
void on_action_checkupdate_triggered();
void on_action_del_var_triggered();
void on_rb_openocd_toggled(bool checked);
void on_rb_serialocd_toggled(bool checked);
void on_bt_refresh_serial_clicked();
@ -84,9 +86,11 @@ private:
GraphWindow *graph;//绘图窗口指针
bool isWatchProcessing=false;//标记当前是否正在处理变量值查看
bool axfChosen=false;//是否已经选择了axf文件
ListWindow *listWindow;
LogWindow *logWindow;
ListWindow *listWindow;//选择窗口指针
LogWindow *logWindow;//日志窗口指针
QMenu *tablePopMenu;//右键点击表格时弹出的菜单
void checkUpdate();
void checkOpenocdProcess();
void setStylesheet();
void setConnState(bool connect);
void sleep(uint32_t ms);

View File

@ -16,7 +16,11 @@
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTableView" name="tb_var"/>
<widget class="QTableView" name="tb_var">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
@ -332,6 +336,14 @@
<string>日志窗口</string>
</property>
</action>
<action name="action_del_var">
<property name="text">
<string>删除此变量</string>
</property>
<property name="toolTip">
<string>删除选中的变量</string>
</property>
</action>
</widget>
<resources/>
<connections/>