From 996b3d4ed6e1369f9ea6ff0d2331c274d024ee57 Mon Sep 17 00:00:00 2001 From: skythinker Date: Mon, 8 Aug 2022 10:30:49 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=89=E6=8B=A9=E7=AA=97=E5=8F=A3=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=B8=80=E5=B1=82=E6=96=87=E4=BB=B6=E5=90=8D=E8=8A=82?= =?UTF-8?q?=E7=82=B9=EF=BC=9B=E6=B7=BB=E5=8A=A0=E5=8F=B3=E9=94=AE=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=8F=98=E9=87=8F=EF=BC=9B=E6=B7=BB=E5=8A=A0=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E6=97=B6=E6=A3=80=E6=9F=A5openocd=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=EF=BC=9B=E8=B0=83=E6=95=B4=E5=B8=AE=E5=8A=A9=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=EF=BC=9B=E7=89=88=E6=9C=AC=E5=8F=B7=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E8=87=B31.2.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LinkScope.pro | 2 +- LinkScope.pro.user | 2 +- README.md | 4 +-- aboutwindow.ui | 2 +- helpwindow.ui | 51 ++++++++++++++++++++++++++++++-- listwindow.cpp | 72 ++++++++++++++++++++++++++++++++++++++++------ listwindow.h | 2 ++ listwindow.ui | 39 +++++++++++++++++++++---- mainwindow.cpp | 58 +++++++++++++++++++++++++++++++++++++ mainwindow.h | 8 ++++-- mainwindow.ui | 14 ++++++++- 11 files changed, 229 insertions(+), 25 deletions(-) diff --git a/LinkScope.pro b/LinkScope.pro index b30b1dc..532b14b 100644 --- a/LinkScope.pro +++ b/LinkScope.pro @@ -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. diff --git a/LinkScope.pro.user b/LinkScope.pro.user index fa859e8..9c9b16d 100644 --- a/LinkScope.pro.user +++ b/LinkScope.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/README.md b/README.md index 095d04f..64d2af0 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ * 在主窗口表格最后一行变量名(编辑框)处手动填写也可以添加变量 - * 在主窗口中选中变量名按Del键可删除变量 + * 在变量名上右键可删除变量,或在选中变量名后按Del键 * 变量名不仅可以填入单个变量名,还可以填入合法的C语言表达式(GDB支持即可);复合类型不能修改和绘图,只能实时查看 @@ -112,8 +112,6 @@ ## TODO -* 增加对不同文件中同名变量的区分 - * 提升采样频率 * 增加对本地程序的支持 diff --git a/aboutwindow.ui b/aboutwindow.ui index a49613a..6bb06fd 100644 --- a/aboutwindow.ui +++ b/aboutwindow.ui @@ -91,7 +91,7 @@ - <html><head/><body><hr width="300"/><p align="center">版本号:V1.2.1</p><p align="center">更新时间:2022/3/30</p><hr width="300"/><p align="center">Developed by Skythinker</p></body></html> + <html><head/><body><hr width="300"/><p align="center">版本号:V1.2.2</p><p align="center">更新时间:2022/8/7</p><hr width="300"/><p align="center">Developed by Skythinker</p></body></html> diff --git a/helpwindow.ui b/helpwindow.ui index 350afaf..aa1977b 100644 --- a/helpwindow.ui +++ b/helpwindow.ui @@ -32,9 +32,9 @@ 0 - -390 + 0 523 - 1308 + 1283 @@ -53,7 +53,52 @@ - <html><head/><body><p><span style=" font-weight:600;">LinkScope简介</span></p><p>本程序使用QT编写,用于硬件设备的调试,可直接驱动串口或各种调试器(基于OpenOCD支持),有以下几个主要功能</p><p>1.实时查看和修改变量值</p><p>2.变量值波形实时绘制</p><p>3.采样数据导出为CSV表格</p><p>4.格式化日志输出</p><p>调试器连接模式理论上支持OpenOCD允许GDB连接的各种调试器及硬件芯片,如STLink、JLink、CMSIS-DAP等以及STM32全系列等</p><hr/><p><span style=" font-weight:600;">使用方法</span></p><p>1.点击【设置符号文件】选择编译器输出的符号文件,然后添加变量到列表中</p><p>&gt; 加载符号文件后即可在变量选择窗口添加需要查看的变量,可以直接添加到列表或添加到编辑框后进行手动编辑</p><p>&gt; 在主窗口表格最后一行变量名(编辑框)处手动填写也可以添加变量</p><p>&gt; 在主窗口中选中变量名按Del键可删除变量</p><p>&gt; 变量名不仅可以填入单个变量名,还可以填入合法的C语言表达式(GDB支持即可);复合类型不能修改和绘图,只能实时查看</p><p>2.选择连接模式,连接芯片,连接后程序开始循环采样,勾选【监视日志】后即可将下位机输出的日志实时显示出来</p><p>&gt; 调试器模式下,在下拉框中选择调试器和芯片类型,点击连接目标</p><p>&gt; 串口模式下,点击刷新串口加载串口列表,选中所连接的串口,点击连接目标</p><p>&gt; 串口功能和日志功能都需要修改下位机程序,可以在菜单中进入主页查看说明</p><p>3.编辑【修改变量】列可以修改变量值,双击【图线颜色】列可以选择绘图颜色 </p><p>4.单击【变量名】列选中对应的变量,可以在绘图窗口查看历史数据,并会加粗绘制 </p><p>5.绘图界面说明请到绘图窗口点击操作说明 </p><p>6.点击菜单中的保存/导入配置可以将当前配置保存到INI文件或从文件中恢复配置,点击导出数据可以将获取到的采样数据导出到CSV表格文件</p><hr/><p><span style=" font-weight:600;">注意事项 </span></p><p>1.修改符号文件路径后需要重新连接 </p><p>2.连接目标前请确认已使用该调试器为目标芯片下载过指定程序 </p><p>3.若程序闪退后发现下一次运行时无法连接目标,请尝试手动结束openocd.exe进程 </p><p>4.连接配置文件位于openocd/share/openocd/scripts下的target和interface中,用户可按照openocd语法编写配置脚本,放入对应目录下后点击“刷新连接配置”菜单项</p></body></html> + <html> +<head> + <style> + li{margin-top: 10px;} + </style> +</head> +<body> + <p><span style="font-weight:600;">LinkScope简介</span></p> + <p>本程序使用QT编写,用于硬件设备的调试,可直接驱动串口或各种调试器(基于OpenOCD支持),有以下几个主要功能</p> + <ol> + <li>实时查看和修改变量值</li> + <li>变量值波形实时绘制</li> + <li>采样数据导出为CSV表格</li> + <li>格式化日志输出</li> + </ol> + <p>调试器连接模式理论上支持OpenOCD允许GDB连接的各种调试器及硬件芯片,如STLink、JLink、CMSIS-DAP等以及STM32全系列等</p> + <hr /> + <p><span style="font-weight:600;">使用方法</span></p> + <ol> + <li>点击【设置符号文件】选择编译器输出的符号文件,然后添加变量到列表中</li> + <ul> + <li>加载符号文件后即可在变量选择窗口添加需要查看的变量,可以直接添加到列表或添加到编辑框后进行手动编辑</li> + <li>在主窗口表格最后一行变量名(编辑框)处手动填写也可以添加变量</li> + <li>在变量名上右键可删除变量,或在选中变量名后按Del键</li> + <li>变量名不仅可以填入单个变量名,还可以填入合法的C语言表达式(GDB支持即可);复合类型不能修改和绘图,只能实时查看</li> + </ul> + <li>选择连接模式,连接芯片,连接后程序开始循环采样,勾选【监视日志】后即可将下位机输出的日志实时显示出来</li> + <ul> + <li>调试器模式下,在下拉框中选择调试器和芯片类型,点击连接目标</li> + <li>串口模式下,点击刷新串口加载串口列表,选中所连接的串口,点击连接目标</li> + <li>串口功能和日志功能都需要修改下位机程序,可以在菜单中进入主页查看说明</li> + </ul> + <li>编辑【修改变量】列可以修改变量值,双击【图线颜色】列可以选择绘图颜色 </li> + <li>单击【变量名】列选中对应的变量,可以在绘图窗口查看历史数据,并会加粗绘制 </li> + <li>绘图界面说明请到绘图窗口点击操作说明 </li> + <li>点击菜单中的保存/导入配置可以将当前配置保存到INI文件或从文件中恢复配置,点击导出数据可以将获取到的采样数据导出到CSV表格文件</li> + </ol> + <hr /> + <p><span style="font-weight:600;">注意事项 </span></p> + <ul> + <li>修改符号文件路径后需要重新连接 </li> + <li>连接目标前请确认已使用该调试器为目标芯片下载过指定程序 </li> + <li>连接配置文件位于openocd/share/openocd/scripts下的target和interface中,用户可按照openocd语法编写配置脚本,放入对应目录下后点击“刷新连接配置”菜单项</li> + </ul> +</body> +</html> Qt::AutoText diff --git a/listwindow.cpp b/listwindow.cpp index f2fad5a..41396aa 100644 --- a/listwindow.cpp +++ b/listwindow.cpp @@ -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;igetVarListFromRawOutput(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); } diff --git a/listwindow.h b/listwindow.h index a878379..d989570 100644 --- a/listwindow.h +++ b/listwindow.h @@ -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(); }; diff --git a/listwindow.ui b/listwindow.ui index 953e00b..e58e8cd 100644 --- a/listwindow.ui +++ b/listwindow.ui @@ -28,18 +28,47 @@ - - + + + + + + 0 + 0 + + + + 直接添加到列表 + + + + + + + 0 + 0 + + 添加到编辑框 - - + + - 直接添加到列表 + 附带文件名 + + + + + + + false + + + 使用全路径 diff --git a/mainwindow.cpp b/mainwindow.cpp index aea1179..a1770f6 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -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()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(indexget(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() { diff --git a/mainwindow.h b/mainwindow.h index d5345a5..790e109 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -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); diff --git a/mainwindow.ui b/mainwindow.ui index cb52dc2..7120845 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -16,7 +16,11 @@ - + + + Qt::CustomContextMenu + + @@ -332,6 +336,14 @@ 日志窗口 + + + 删除此变量 + + + 删除选中的变量 + +