增加变量选择器;美化帮助窗口和关于窗口;添加反馈通道;添加检查更新功能;版本号更新为V1.0.2
|
@ -1,4 +1,5 @@
|
|||
QT += core gui
|
||||
QT += network
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
|
@ -8,7 +9,8 @@ CONFIG += c++11
|
|||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# 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
|
||||
DEFINES += QT_DEPRECATED_WARNINGS \
|
||||
APP_VERSION=\\\"1.0.2\\\"
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
|
@ -16,17 +18,26 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
|||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
SOURCES += \
|
||||
aboutwindow.cpp \
|
||||
graphwindow.cpp \
|
||||
helpwindow.cpp \
|
||||
listwindow.cpp \
|
||||
main.cpp \
|
||||
mainwindow.cpp
|
||||
|
||||
HEADERS += \
|
||||
aboutwindow.h \
|
||||
graphwindow.h \
|
||||
helpwindow.h \
|
||||
listwindow.h \
|
||||
mainwindow.h \
|
||||
vartype.h
|
||||
|
||||
FORMS += \
|
||||
aboutwindow.ui \
|
||||
graphwindow.ui \
|
||||
helpwindow.ui \
|
||||
listwindow.ui \
|
||||
mainwindow.ui
|
||||
|
||||
RC_ICONS = icon.ico
|
||||
|
|
10
README.md
|
@ -30,13 +30,15 @@
|
|||
|
||||
> 注:变量名不仅可以填入单个变量名,还可以填入合法的C语言表达式(GDB支持即可);复合类型不能修改和绘图,只能实时查看
|
||||
|
||||
1. 编辑`修改变量`列可以修改变量值,双击`图线颜色`列可以选择绘图颜色
|
||||
3. 在选择窗口中可以查看所选Axf文件包含的变量名,也可以一键添加到编辑框或变量列表中
|
||||
|
||||
4. 编辑`修改变量`列可以修改变量值,双击`图线颜色`列可以选择绘图颜色
|
||||
|
||||
![基本操作](imgs/simp-oper.gif)
|
||||
|
||||
4. 单击`变量名`列选中对应的变量,绘图窗口会加粗绘制波形,左下角会显示当前值和查看值(拖动鼠标进行查看)
|
||||
5. 单击`变量名`列选中对应的变量,绘图窗口会加粗绘制波形,左下角会显示当前值和查看值(拖动鼠标进行查看)
|
||||
|
||||
5. 绘图界面说明可以在绘图窗口点击操作说明查看,滚轮配合`Ctrl`、`Shift`、`Alt`可以实现画面的缩放和移动
|
||||
6. 绘图界面说明可以在绘图窗口点击操作说明查看,滚轮配合`Ctrl`、`Shift`、`Alt`可以实现画面的缩放和移动
|
||||
|
||||
![绘图操作](imgs/graph-oper.gif)
|
||||
|
||||
|
@ -76,6 +78,8 @@
|
|||
|
||||
* 将采样频率提升至约1kHz
|
||||
|
||||
* 添加串口模式
|
||||
|
||||
---
|
||||
|
||||
## 运行过程简介
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#include "aboutwindow.h"
|
||||
#include "ui_aboutwindow.h"
|
||||
|
||||
AboutWindow::AboutWindow(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::AboutWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
AboutWindow::~AboutWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AboutWindow::on_btn_back_clicked()
|
||||
{
|
||||
accept();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef ABOUTWINDOW_H
|
||||
#define ABOUTWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class AboutWindow;
|
||||
}
|
||||
|
||||
class AboutWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AboutWindow(QWidget *parent = nullptr);
|
||||
~AboutWindow();
|
||||
|
||||
private slots:
|
||||
void on_btn_back_clicked();
|
||||
|
||||
private:
|
||||
Ui::AboutWindow *ui;
|
||||
};
|
||||
|
||||
#endif // ABOUTWINDOW_H
|
|
@ -0,0 +1,111 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AboutWindow</class>
|
||||
<widget class="QDialog" name="AboutWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<height>279</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="res.qrc">:/icon.ico</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-size:16pt; font-weight:600;">LinkScope</span></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><hr width="300"/><p align="center">版本号:V1.0.2</p><p align="center">更新时间:2022/1/29</p><hr width="300"/><p align="center">Developed by Skythinker</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_back">
|
||||
<property name="text">
|
||||
<string>返回</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -0,0 +1,19 @@
|
|||
#include "helpwindow.h"
|
||||
#include "ui_helpwindow.h"
|
||||
|
||||
HelpWindow::HelpWindow(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::HelpWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
HelpWindow::~HelpWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void HelpWindow::on_btn_back_clicked()
|
||||
{
|
||||
accept();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#ifndef HELPWINDOW_H
|
||||
#define HELPWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class HelpWindow;
|
||||
}
|
||||
|
||||
class HelpWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HelpWindow(QWidget *parent = nullptr);
|
||||
~HelpWindow();
|
||||
|
||||
private slots:
|
||||
void on_btn_back_clicked();
|
||||
|
||||
private:
|
||||
Ui::HelpWindow *ui;
|
||||
};
|
||||
|
||||
#endif // HELPWINDOW_H
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>HelpWindow</class>
|
||||
<widget class="QDialog" name="HelpWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>569</width>
|
||||
<height>504</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>LinkScope - Help</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>523</width>
|
||||
<height>906</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: white;</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-weight:600;">LinkScope简介</span></p><p>本程序使用QT编写,基于OpenOCD和GDB,用于硬件设备的调试,可以实时查看并修改变量值,有波形绘制和数据导出功能 </p><p>程序支持OpenOCD支持的各种调试器及硬件芯片,如STLink、JLink、CMSIS-DAP等以及STM32全系列等</p><hr><p><span style=" font-weight:600;">使用方法</span></p><p>1.在下拉框中选择调试器和芯片类型,选择Axf文件路径,点击连接即可尝试连接芯片 </p><p>2.在表格最后一行变量名处填写变量名可以添加查看变量,选中变量名按Del键可以删除变量 </p><p>3.在选择窗口中可以查看所选Axf文件包含的变量名,可以一键添加到编辑框或变量列表</p><p>4.编辑【修改变量】列可以修改变量值,双击【图线颜色】列可以选择绘图颜色 </p><p>5.单击【变量名】列选中对应的变量,可以在绘图窗口查看历史数据,并会加粗绘制 </p><p>6.绘图界面说明请到绘图窗口点击操作说明 </p><p>7.点击菜单中的保存/导入配置可以将当前配置保存到INI文件或从文件中恢复配置,点击导出数据可以将获取到的采样数据导出到CSV表格文件</p><hr><p><span style=" font-weight:600;">注意事项 </span></p><p>1.修改Axf路径后需要重新连接 </p><p>2.在【变量名】列不仅能填写单个变量名,还可以填入任何合法的C语言表达式 </p><p>3.连接目标前请确认已使用该调试器为目标芯片下载过指定程序 </p><p>4.若程序闪退后发现下一次运行时无法连接目标,请尝试手动结束openocd.exe进程 </p><p>5.连接配置文件位于openocd/share/openocd/scripts下的target和interface中,用户可按照openocd语法编写配置脚本,放入对应目录下后点击“刷新连接配置”菜单项</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_back">
|
||||
<property name="text">
|
||||
<string>返回</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 116 KiB |
|
@ -0,0 +1,271 @@
|
|||
#include "listwindow.h"
|
||||
#include "ui_listwindow.h"
|
||||
|
||||
ListWindow::ListWindow(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ListWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowTitle("LinkScope - Selector");
|
||||
|
||||
treeModel=new QStandardItemModel(ui->tree);
|
||||
ui->tree->setModel(treeModel);
|
||||
|
||||
gdbProcess=new QProcess();
|
||||
|
||||
setGDBState(true);//启动GDB进程
|
||||
}
|
||||
|
||||
ListWindow::~ListWindow()
|
||||
{
|
||||
deleteTempFile();//删除临时文件
|
||||
setGDBState(false);//结束GDB进程
|
||||
delete gdbProcess;
|
||||
delete treeModel;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
//设置GDB进程状态
|
||||
void ListWindow::setGDBState(bool run)
|
||||
{
|
||||
if(run)//启动进程
|
||||
{
|
||||
gdbProcess->setProgram(QCoreApplication::applicationDirPath()+"/gdb/gdb.exe");//设置程序路径
|
||||
gdbProcess->setWorkingDirectory(QCoreApplication::applicationDirPath()+"/gdb");//设置工作路径
|
||||
gdbProcess->setNativeArguments("-q");//设置gdb在安静模式下打开
|
||||
gdbProcess->start();
|
||||
runGDBCmd("set confirm off\r\n");//设置不要手动确认
|
||||
runGDBCmd("set print pretty on\r\n");//设置结构体规范打印
|
||||
}
|
||||
else//结束进程
|
||||
{
|
||||
gdbProcess->kill();
|
||||
}
|
||||
}
|
||||
|
||||
//向GDB进程发送命令并获取标准输出
|
||||
QString ListWindow::runGDBCmd(const QString &input)
|
||||
{
|
||||
gdbProcess->readAllStandardOutput();
|
||||
gdbProcess->write(input.toStdString().c_str());
|
||||
QString res="";
|
||||
do{
|
||||
gdbProcess->waitForReadyRead();
|
||||
res+=gdbProcess->readAllStandardOutput();
|
||||
}while(!res.endsWith("(gdb) "));
|
||||
return res;
|
||||
}
|
||||
|
||||
//使用GDB检查变量名是否为可展开类型
|
||||
bool ListWindow::checkCanExpand(const QString &varFullName)
|
||||
{
|
||||
QString whatis=runGDBCmd(QString("whatis %1\r\n").arg(varFullName));//先使用whatis指令判断数组和函数指针
|
||||
whatis.remove("type = ");
|
||||
whatis.remove("\r\n(gdb) ");
|
||||
if(whatis.contains("["))
|
||||
return true;
|
||||
if(whatis.contains('('))
|
||||
return false;
|
||||
QString ptype=runGDBCmd(QString("ptype %1\r\n").arg(varFullName));//再使用ptype指令判断是否是其他可展开类型
|
||||
ptype.remove("type = ");
|
||||
if(ptype.startsWith("struct")||ptype.startsWith("union"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//从原始变量列表字符串中解析出变量列表
|
||||
QStringList ListWindow::parseVarList(const QString &raw)
|
||||
{
|
||||
QStringList list;
|
||||
QRegExp rootRx("\\s([^ (]*\\(\\*|)([^ )]+)(\\)\\(.*\\)|\\s:\\s\\d+|);");//正则匹配模板
|
||||
rootRx.setMinimal(true);//非贪心匹配
|
||||
int pos=0;
|
||||
while((pos=rootRx.indexIn(raw,pos))!=-1)
|
||||
{
|
||||
QString name=rootRx.cap(2);//获取截取出的变量名部分
|
||||
if(name.contains('*'))//手动剔除'*'
|
||||
name.remove('*');
|
||||
if(name.contains('['))//手动剔除数组长度部分
|
||||
name.remove(QRegExp("\\[\\d+\\]"));
|
||||
list.append(name);
|
||||
pos+=rootRx.matchedLength();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
//删除一个原始变量列表字符串中的嵌套部分,跳过前offset个字符
|
||||
void ListWindow::removeInnerSection(QString &raw,int offset)
|
||||
{
|
||||
for(int startPos=offset;startPos<raw.length();startPos++)//startPos指针向后移动查找'{'
|
||||
{
|
||||
if(raw.at(startPos)=='{')//若找到'{',开始寻找对应的'}'
|
||||
{
|
||||
int innerLayer=1;//当前endPos所在的嵌套层级
|
||||
for(int endPos=startPos+1;endPos<raw.length();endPos++)//endPos指针向后查找对应的'}'
|
||||
{
|
||||
if(raw.at(endPos)=='{')//每遇到一个'{'则嵌套层级+1
|
||||
innerLayer++;
|
||||
else if(raw.at(endPos)=='}')//每遇到一个'}'则嵌套层级-1
|
||||
innerLayer--;
|
||||
|
||||
if(raw.at(endPos)=='}' && innerLayer==0)//若遇到'}'时嵌套层级为0,表示找到了对应的'}'
|
||||
{
|
||||
raw.remove(startPos,endPos-startPos+1);//删除从'{'到'}'的子串
|
||||
startPos--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//计算一个节点的全名(沿parent指针向上回溯)
|
||||
QString ListWindow::getVarFullName(const VarNode &node)
|
||||
{
|
||||
const VarNode *curNode=&node;
|
||||
QString fullName=node.name;
|
||||
while(curNode->parent->parent)//到父节点为根节点为止
|
||||
{
|
||||
if(curNode->name.contains('['))//若为数组元素,父节点为数组名,直接拼接(无需加'.')
|
||||
fullName=curNode->parent->name+fullName;
|
||||
else//非数组类型与父节点拼接时还需加一个'.'
|
||||
fullName=curNode->parent->name+'.'+fullName;
|
||||
curNode=curNode->parent;//指针向上移动一层
|
||||
}
|
||||
return fullName;
|
||||
}
|
||||
|
||||
//解析一个节点的子节点,并更新子节点的可展开状态
|
||||
void ListWindow::parseVarChildren(VarNode &node)
|
||||
{
|
||||
if(node.parent==NULL)//传入的是根节点
|
||||
{
|
||||
QString rawVarList=runGDBCmd("info variables\r\n");//使用info variables指令列出axf中所有变量
|
||||
QStringList varList=parseVarList(rawVarList);//解析出变量列表
|
||||
foreach(QString name,varList)//依次添加子节点并更新可展开状态
|
||||
{
|
||||
node.append(name);
|
||||
node.children.last().canExpand=checkCanExpand(getVarFullName(node.children.last()));
|
||||
}
|
||||
}
|
||||
else//传入的是普通节点
|
||||
{
|
||||
QString fullName=getVarFullName(node);//计算节点全名
|
||||
QString rawVarType=runGDBCmd(QString("whatis %1\r\n").arg(fullName));//使用whatis指令获取类型
|
||||
rawVarType.remove("type = ");
|
||||
rawVarType.remove("\r\n(gdb) ");
|
||||
if(rawVarType.contains("["))//判定是否为数组类型
|
||||
{
|
||||
QRegExp rx("\\[(\\d+)\\]");//使用正则表达式提取出第一维长度len
|
||||
rx.setMinimal(true);
|
||||
rx.indexIn(rawVarType);
|
||||
int len=rx.cap(1).toInt();
|
||||
for(int i=0;i<len;i++)//添加len个子节点
|
||||
{
|
||||
node.append(QString("[%1]").arg(i));
|
||||
if(i==0)//若是第一个元素,计算是否可展开
|
||||
node.children[0].canExpand=checkCanExpand(getVarFullName(node.children[0]));
|
||||
else//若不是第一个元素,直接用前一个元素的可展开状态即可
|
||||
node.children[i].canExpand=node.children[i-1].canExpand;
|
||||
}
|
||||
}
|
||||
else if(!rawVarType.contains('('))//不是数组类型,排除函数类型,判定是否为可展开类型
|
||||
{
|
||||
QString detailRawVarType=runGDBCmd(QString("ptype %1\r\n").arg(fullName));//用ptype指令获取详细类型
|
||||
detailRawVarType.remove("type = ");
|
||||
detailRawVarType.remove("\r\n(gdb) ");
|
||||
if(detailRawVarType.startsWith("struct")||detailRawVarType.startsWith("union"))//判定为可展开类型
|
||||
{
|
||||
removeInnerSection(detailRawVarType,detailRawVarType.indexOf('{')+1);//移除内部直接嵌套的部分
|
||||
QStringList varList=parseVarList(detailRawVarType);//解析出变量列表
|
||||
foreach(QString name,varList)//依次添加子节点及其可展开状态
|
||||
{
|
||||
node.append(name);
|
||||
node.children.last().canExpand=checkCanExpand(getVarFullName(node.children.last()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node.parsed=true;//更新该节点为已解析状态
|
||||
}
|
||||
|
||||
//加载AXF文件
|
||||
void ListWindow::loadNewAxfFile(const QString &path)
|
||||
{
|
||||
updateTempFile(path);//更新AXF文件配置
|
||||
updateTree();//更新树状图
|
||||
}
|
||||
|
||||
//更新临时文件
|
||||
void ListWindow::updateTempFile(const QString &path)
|
||||
{
|
||||
QString tmpFilePath=QCoreApplication::applicationDirPath()+"/gdb/tmp_list";//临时符号文件路径
|
||||
deleteTempFile();//确保删除当前的临时文件
|
||||
QFile::copy(path,tmpFilePath);//将所选符号文件复制为临时文件
|
||||
runGDBCmd("symbol-file tmp_list \r\n");//设置符号文件
|
||||
}
|
||||
|
||||
//删除临时文件
|
||||
void ListWindow::deleteTempFile()
|
||||
{
|
||||
runGDBCmd("symbol-file\r\n");//取消符号文件,解除文件占用
|
||||
QFile::remove(QCoreApplication::applicationDirPath()+"/gdb/tmp_list");//删除复制过来的临时文件
|
||||
}
|
||||
|
||||
//更新整个树状图和节点树
|
||||
void ListWindow::updateTree()
|
||||
{
|
||||
varTree.clear();//从根节点开始递归清理
|
||||
varTree.canExpand=true;//设定根节点可展开
|
||||
|
||||
QStandardItem *rootItem=new QStandardItem("");//创建根节点对应的item
|
||||
rootItem->setData((qlonglong)&varTree);//将item的数据指向根节点
|
||||
rootItem->appendRow(new QStandardItem());//附加一个空item让树状图显示为可展开
|
||||
|
||||
treeModel->clear();//清理树状图数据
|
||||
treeModel->appendRow(rootItem);//添加根节点item
|
||||
}
|
||||
|
||||
//树状图展开槽函数
|
||||
void ListWindow::on_tree_expanded(const QModelIndex &index)
|
||||
{
|
||||
QStandardItem *item=treeModel->itemFromIndex(index);//获取所展开的item
|
||||
VarNode &node=*(VarNode*)(item->data().toULongLong());//获取对应的节点
|
||||
if(!node.parsed)//若节点未解析过,则进行解析
|
||||
{
|
||||
parseVarChildren(node);//解析该节点的子节点,更新各子节点的可展开状态
|
||||
item->removeRows(0,item->rowCount());//先清空当前item下的所有子项
|
||||
for(int i=0;i<node.children.length();i++)//依次创建并添加各子节点的item
|
||||
{
|
||||
VarNode &childNode=node.children[i];
|
||||
QStandardItem *childItem=new QStandardItem();//创建item
|
||||
childItem->setText(childNode.name);//设定item显示变量名
|
||||
childItem->setData((qlonglong)&childNode);//建立item到对应节点的寻址方式
|
||||
if(childNode.canExpand)//若该子节点可展开,则添加一个空item使其显示为可展开状态
|
||||
childItem->appendRow(new QStandardItem());
|
||||
item->appendRow(childItem);//子节点item附加到当前被展开的item下
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//添加到编辑框按钮槽函数
|
||||
void ListWindow::on_btn_add2edit_clicked()
|
||||
{
|
||||
QModelIndex index=ui->tree->currentIndex();//获取当前所选index
|
||||
if(index.row()==-1)
|
||||
return;
|
||||
VarNode &node=*(VarNode*)(treeModel->itemFromIndex(index)->data().toULongLong());//获取所选节点
|
||||
if(node.parent)
|
||||
emit add2Edit(getVarFullName(node));//将变量全名使用信号发出去
|
||||
}
|
||||
|
||||
//添加到列表按钮槽函数(逻辑与添加到编辑框相同,仅发送的信号不同)
|
||||
void ListWindow::on_btn_add2list_clicked()
|
||||
{
|
||||
QModelIndex index=ui->tree->currentIndex();
|
||||
if(index.row()==-1)
|
||||
return;
|
||||
VarNode &node=*(VarNode*)(treeModel->itemFromIndex(index)->data().toULongLong());
|
||||
if(node.parent)
|
||||
emit add2List(getVarFullName(node));
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef LISTWINDOW_H
|
||||
#define LISTWINDOW_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <qprocess.h>
|
||||
#include <qfile.h>
|
||||
#include <QTime>
|
||||
#include <qdebug.h>
|
||||
#include <QStandardItem>
|
||||
#include <qmessagebox.h>
|
||||
|
||||
namespace Ui {
|
||||
class ListWindow;
|
||||
}
|
||||
|
||||
//变量信息结点,用于组成变量树
|
||||
class VarNode{
|
||||
public:
|
||||
QString name;//变量名
|
||||
QList<VarNode> children;//子节点
|
||||
VarNode *parent=NULL;//父节点
|
||||
bool canExpand=false;//可展开状态,即该变量类型是否可被展开
|
||||
bool parsed=false;//是否已经解析出子节点信息
|
||||
VarNode(const QString &_name="",VarNode *_parent=NULL)
|
||||
{
|
||||
name=_name;
|
||||
parent=_parent;
|
||||
}
|
||||
void append(const QString &name)//指定变量名添加一个子节点
|
||||
{
|
||||
VarNode node(name,this);//创建一个子节点并将其父节点设为自己
|
||||
children.append(node);
|
||||
}
|
||||
void clear()//清空数据
|
||||
{
|
||||
children.clear();
|
||||
name="";
|
||||
canExpand=parsed=false;
|
||||
}
|
||||
};
|
||||
|
||||
class ListWindow : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ListWindow(QWidget *parent = nullptr);
|
||||
~ListWindow();
|
||||
void loadNewAxfFile(const QString &path);
|
||||
|
||||
signals:
|
||||
void add2Edit(const QString &name);
|
||||
void add2List(const QString &name);
|
||||
|
||||
private slots:
|
||||
void on_tree_expanded(const QModelIndex &index);
|
||||
void on_btn_add2edit_clicked();
|
||||
void on_btn_add2list_clicked();
|
||||
|
||||
private:
|
||||
Ui::ListWindow *ui;
|
||||
QProcess *gdbProcess;
|
||||
VarNode varTree;
|
||||
QStandardItemModel *treeModel;
|
||||
void setGDBState(bool run);
|
||||
QString runGDBCmd(const QString &input);
|
||||
QStringList parseVarList(const QString &raw);
|
||||
void removeInnerSection(QString &raw,int offset=0);
|
||||
QString getVarFullName(const VarNode &node);
|
||||
bool checkCanExpand(const QString &varFullName);
|
||||
void parseVarChildren(VarNode &node);
|
||||
void updateTempFile(const QString &path);
|
||||
void deleteTempFile();
|
||||
void updateTree();
|
||||
};
|
||||
|
||||
#endif // LISTWINDOW_H
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ListWindow</class>
|
||||
<widget class="QDialog" name="ListWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>680</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="tree">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_add2edit">
|
||||
<property name="text">
|
||||
<string>添加到编辑框</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_add2list">
|
||||
<property name="text">
|
||||
<string>直接添加到列表</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
127
mainwindow.cpp
|
@ -14,6 +14,11 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
graph->setVarList(&varList);
|
||||
graph->show();
|
||||
|
||||
listWindow=new ListWindow();
|
||||
listWindow->show();
|
||||
connect(listWindow,SIGNAL(add2Edit(const QString &)),this,SLOT(slotOnVarAdd2Edit(const QString &)));
|
||||
connect(listWindow,SIGNAL(add2List(const QString &)),this,SLOT(slotOnVarAdd2List(const QString &)));
|
||||
|
||||
stampTimer=new QElapsedTimer();//创建并运行时间戳定时器
|
||||
stampTimer->start();
|
||||
|
||||
|
@ -83,6 +88,7 @@ void MainWindow::closeEvent(QCloseEvent *event)
|
|||
{
|
||||
Q_UNUSED(event);
|
||||
graph->close();
|
||||
listWindow->close();
|
||||
}
|
||||
|
||||
//ocd进程发生错误,自动断开连接
|
||||
|
@ -162,6 +168,20 @@ void MainWindow::slotTableTimerTrig()
|
|||
}
|
||||
}
|
||||
|
||||
//选择器窗口添加到编辑框槽函数
|
||||
void MainWindow::slotOnVarAdd2Edit(const QString &name)
|
||||
{
|
||||
tableModel->disconnect(this,SLOT(slotTableEdit(QModelIndex,QModelIndex)));//断开信号连接,否则触发后会直接添加到列表
|
||||
tableModel->item(tableModel->rowCount()-1,0)->setText(name);
|
||||
connect(tableModel,SIGNAL(dataChanged(QModelIndex,QModelIndex)),this,SLOT(slotTableEdit(QModelIndex,QModelIndex)));//恢复信号连接
|
||||
}
|
||||
|
||||
//选择器窗口添加到列表槽函数
|
||||
void MainWindow::slotOnVarAdd2List(const QString &name)
|
||||
{
|
||||
tableModel->item(tableModel->rowCount()-1,0)->setText(name);
|
||||
}
|
||||
|
||||
//连接按钮点击,触发连接状态切换
|
||||
void MainWindow::on_bt_conn_clicked()
|
||||
{
|
||||
|
@ -326,7 +346,7 @@ void MainWindow::parseGDBRawDisp(QString &raw)
|
|||
}
|
||||
}
|
||||
|
||||
//延时制定毫秒数,进行子任务循环
|
||||
//延时指定毫秒数,进行子任务循环
|
||||
void MainWindow::sleep(uint32_t ms)
|
||||
{
|
||||
QTime untilTime=QTime::currentTime().addMSecs(ms);
|
||||
|
@ -365,6 +385,7 @@ void MainWindow::on_bt_set_axf_clicked()
|
|||
QFileInfo info(fileName);
|
||||
ui->txt_axf_path->setText(info.filePath());
|
||||
axfChosen=true;
|
||||
listWindow->loadNewAxfFile(info.filePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,6 +499,9 @@ void MainWindow::loadFromFile(const QString &filename)
|
|||
}
|
||||
|
||||
redrawTable();//重绘表格
|
||||
|
||||
if(axfChosen)//刷新选择窗口
|
||||
listWindow->loadNewAxfFile(ui->txt_axf_path->text());
|
||||
}
|
||||
|
||||
//导出变量采样数据到指定CSV文件中
|
||||
|
@ -635,39 +659,13 @@ void MainWindow::on_action_export_triggered()
|
|||
//关于菜单栏点击
|
||||
void MainWindow::on_action_about_triggered()
|
||||
{
|
||||
//弹出messagebox显示关于信息
|
||||
QString str="LinkScope 版本号:V1.0.1\n\n"
|
||||
"Developed by Skythinker";
|
||||
QMessageBox box;
|
||||
box.setWindowTitle("关于 LinkScope");
|
||||
box.setText(str);
|
||||
box.exec();
|
||||
AboutWindow().exec();
|
||||
}
|
||||
|
||||
//帮助菜单栏点击
|
||||
void MainWindow::on_action_help_triggered()
|
||||
{
|
||||
//弹出messagebox显示帮助信息
|
||||
QString str="LinkScope简介\n"
|
||||
"本程序使用QT编写,基于OpenOCD和GDB,用于硬件设备的调试,可以实时查看并修改变量值,有波形绘制和数据导出功能\n"
|
||||
"程序支持OpenOCD支持的各种调试器及硬件芯片,如STLink、JLink、CMSIS-DAP等以及STM32全系列等\n\n"
|
||||
"使用方法\n"
|
||||
"1.在下拉框中选择调试器和芯片类型,选择Axf文件路径,点击连接即可尝试连接芯片\n"
|
||||
"2.在表格最后一行变量名处填写变量名可以添加查看变量,选中变量名按Del键可以删除变量\n"
|
||||
"3.编辑【修改变量】列可以修改变量值,双击【图线颜色】列可以选择绘图颜色\n"
|
||||
"4.单击【变量名】列选中对应的变量,可以在绘图窗口查看历史数据,并会加粗绘制\n"
|
||||
"5.绘图界面说明请到绘图窗口点击操作说明\n"
|
||||
"6.点击菜单中的保存/导入配置可以将当前配置保存到INI文件或从文件中恢复配置,点击导出数据可以将获取到的采样数据导出到CSV表格文件\n\n"
|
||||
"注意事项\n"
|
||||
"1.修改Axf路径后需要重新连接\n"
|
||||
"2.在【变量名】列不仅能填写单个变量名,还可以填入任何合法的C语言表达式\n"
|
||||
"3.连接目标前请确认已使用该调试器为目标芯片下载过指定程序\n"
|
||||
"4.若程序闪退后发现下一次运行时无法连接目标,请尝试手动结束openocd.exe进程\n"
|
||||
"5.连接配置文件位于openocd/share/openocd/scripts下的target和interface中,用户可按照openocd语法编写配置脚本,放入对应目录下后点击“刷新连接配置”菜单项\n";
|
||||
QMessageBox box;
|
||||
box.setWindowTitle("LinkScope 帮助");
|
||||
box.setText(str);
|
||||
box.exec();
|
||||
HelpWindow().exec();
|
||||
}
|
||||
|
||||
//显示绘图窗口菜单点击
|
||||
|
@ -696,3 +694,74 @@ void MainWindow::setStylesheet()
|
|||
if(qss.open(QIODevice::ReadOnly))
|
||||
qApp->setStyleSheet(QLatin1String(qss.readAll()));
|
||||
}
|
||||
|
||||
//显示选择窗口菜单点击
|
||||
void MainWindow::on_action_show_selector_triggered()
|
||||
{
|
||||
listWindow->show();
|
||||
listWindow->activateWindow();
|
||||
}
|
||||
|
||||
//反馈菜单点击
|
||||
void MainWindow::on_action_feedback_triggered()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://support.qq.com/product/378753"));//打开反馈页面
|
||||
}
|
||||
|
||||
//检查更新
|
||||
void MainWindow::checkUpdate()
|
||||
{
|
||||
QNetworkAccessManager *manager=new QNetworkAccessManager;
|
||||
if(manager->networkAccessible()!=QNetworkAccessManager::Accessible)
|
||||
manager->setNetworkAccessible(QNetworkAccessManager::Accessible);
|
||||
connect(manager,&QNetworkAccessManager::finished,
|
||||
[=](QNetworkReply *reply){ //解析收到的网络数据
|
||||
if(reply->error()==QNetworkReply::NoError)
|
||||
{
|
||||
QString raw=reply->readAll();
|
||||
QRegExp rx("\"tag_name\":\"v([0-9.]+)\"");//正则匹配原始字符串中的版本号
|
||||
rx.setMinimal(true);
|
||||
if(rx.indexIn(raw)!=-1)
|
||||
{
|
||||
QString tag=rx.cap(1);//截取出版本号字符串
|
||||
if(!tag.isEmpty())
|
||||
{
|
||||
if(tag!=APP_VERSION)//与宏定义中的当前版本号进行比较
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("提示");
|
||||
msgBox.setText("检查到新版本 V"+tag+" ,是否去更新?");
|
||||
msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
|
||||
if(msgBox.exec()==QMessageBox::Yes)
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl("https://gitee.com/skythinker/link-scope/releases"));//打开仓库release页面
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::information(this,"提示","当前已是最新版本");
|
||||
}
|
||||
}
|
||||
}
|
||||
else//正则匹配失败,说明服务器返回的格式有误,无法继续解析
|
||||
{
|
||||
QMessageBox::warning(this,"错误","检查失败,建议点击菜单转到主页查看");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this,"错误","网络错误,请检查网络");
|
||||
}
|
||||
}
|
||||
);
|
||||
QNetworkRequest request;
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
|
||||
request.setUrl(QUrl("https://gitee.com/api/v5/repos/skythinker/link-scope/releases/latest"));//发送get请求到gitee服务器
|
||||
manager->get(request);
|
||||
}
|
||||
|
||||
//检查更新菜单点击
|
||||
void MainWindow::on_action_checkupdate_triggered()
|
||||
{
|
||||
checkUpdate();
|
||||
}
|
||||
|
|
12
mainwindow.h
|
@ -19,6 +19,11 @@
|
|||
#include <qsettings.h>
|
||||
#include <qregexp.h>
|
||||
#include <qdesktopservices.h>
|
||||
#include <listwindow.h>
|
||||
#include <helpwindow.h>
|
||||
#include <aboutwindow.h>
|
||||
#include <qnetworkaccessmanager.h>
|
||||
#include <qnetworkreply.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui { class MainWindow; }
|
||||
|
@ -39,6 +44,8 @@ private slots:
|
|||
void slotTableEdit(QModelIndex topleft, QModelIndex bottomright);
|
||||
void slotWatchTimerTrig();
|
||||
void slotTableTimerTrig();
|
||||
void slotOnVarAdd2Edit(const QString &name);
|
||||
void slotOnVarAdd2List(const QString &name);
|
||||
void on_bt_conn_clicked();
|
||||
void on_bt_set_axf_clicked();
|
||||
void on_bt_reset_clicked();
|
||||
|
@ -52,6 +59,9 @@ private slots:
|
|||
void on_action_show_graph_triggered();
|
||||
void on_action_refresh_conf_triggered();
|
||||
void on_action_homepage_triggered();
|
||||
void on_action_show_selector_triggered();
|
||||
void on_action_feedback_triggered();
|
||||
void on_action_checkupdate_triggered();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
|
@ -64,6 +74,8 @@ private:
|
|||
GraphWindow *graph;//绘图窗口指针
|
||||
bool isWatchProcessing=false;//标记当前是否正在处理变量值查看
|
||||
bool axfChosen=false;//是否已经选择了axf文件
|
||||
ListWindow *listWindow;
|
||||
void checkUpdate();
|
||||
void setStylesheet();
|
||||
void setConnState(bool connect);
|
||||
void setOCDState(bool connect);
|
||||
|
|
|
@ -91,24 +91,23 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>557</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="txt_axf_path">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>请设置符号文件</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -136,8 +135,6 @@
|
|||
<property name="title">
|
||||
<string>菜单</string>
|
||||
</property>
|
||||
<addaction name="action_show_graph"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_refresh_conf"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_save"/>
|
||||
|
@ -151,10 +148,21 @@
|
|||
</property>
|
||||
<addaction name="action_help"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_checkupdate"/>
|
||||
<addaction name="action_homepage"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="action_feedback"/>
|
||||
<addaction name="action_about"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_3">
|
||||
<property name="title">
|
||||
<string>窗口</string>
|
||||
</property>
|
||||
<addaction name="action_show_graph"/>
|
||||
<addaction name="action_show_selector"/>
|
||||
</widget>
|
||||
<addaction name="menu"/>
|
||||
<addaction name="menu_3"/>
|
||||
<addaction name="menu_2"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
|
@ -175,7 +183,7 @@
|
|||
</action>
|
||||
<action name="action_help">
|
||||
<property name="text">
|
||||
<string>帮助</string>
|
||||
<string>使用说明</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_about">
|
||||
|
@ -201,6 +209,21 @@
|
|||
<string>转到主页</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_show_selector">
|
||||
<property name="text">
|
||||
<string>显示选择窗口</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_feedback">
|
||||
<property name="text">
|
||||
<string>反馈</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="action_checkupdate">
|
||||
<property name="text">
|
||||
<string>检查更新</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -153,3 +153,29 @@ QMenu::separator{
|
|||
QTableView{
|
||||
border: 2px solid rgb(85, 170, 255);
|
||||
}
|
||||
|
||||
QTreeView{
|
||||
border: 2px solid rgb(85, 170, 255);
|
||||
}
|
||||
|
||||
QTreeView::branch:has-children:!has-siblings:closed,
|
||||
QTreeView::branch:closed:has-children:has-siblings{
|
||||
image: url(:/qss/qss-img/light-blue-tree-closed.png);
|
||||
}
|
||||
|
||||
QTreeView::branch:open:has-children:!has-siblings,
|
||||
QTreeView::branch:open:has-children:has-siblings{
|
||||
image: url(:/qss/qss-img/light-blue-tree-open.png);
|
||||
}
|
||||
|
||||
QTreeView::branch:has-siblings:!adjoins-item{
|
||||
image: url(:/qss/qss-img/light-blue-tree-vline.png);
|
||||
}
|
||||
|
||||
QTreeView::branch:has-siblings:adjoins-item{
|
||||
image: url(:/qss/qss-img/light-blue-tree-more.png);
|
||||
}
|
||||
|
||||
QTreeView::branch:!has-children:!has-siblings:adjoins-item{
|
||||
image: url(:/qss/qss-img/light-blue-tree-end.png);
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 289 B |
After Width: | Height: | Size: 225 B |
After Width: | Height: | Size: 231 B |
After Width: | Height: | Size: 246 B |
After Width: | Height: | Size: 201 B |
6
res.qrc
|
@ -7,5 +7,11 @@
|
|||
<file>qss/qss-img/light-blue-radiobutton-checked.png</file>
|
||||
<file>qss/qss-img/light-blue-radiobutton-unchecked.png</file>
|
||||
<file>qss/qss-img/light-blue-uparrow.png</file>
|
||||
<file>qss/qss-img/light-blue-tree-open.png</file>
|
||||
<file>qss/qss-img/light-blue-tree-closed.png</file>
|
||||
<file>qss/qss-img/light-blue-tree-end.png</file>
|
||||
<file>qss/qss-img/light-blue-tree-more.png</file>
|
||||
<file>qss/qss-img/light-blue-tree-vline.png</file>
|
||||
<file>icon.ico</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|