目录
一、前言
1.1 项目地址
1.2 功能简介
程序集成了代码编辑、编译烧录、拖拽式图形化编程、串口通信、OpenOCD调试器、输入/输出频谱图、日志打印输出等核心功能。
二、开发环境
- Qt 6.3.0
- Windows 10 x64
- MinGW 11.20_64
基于Qt6框架开发,支持跨平台。
三、设计思路
本文的布局大部分通过代码实现,而不是.ui文件,更便于进行控件的动态加载与响应式布局。
Modular-SDA将各模块(代码编辑、拖拽式编程等)作为单独的QWidget来实现,同时将其布局在不同的“页”中,通过页的切换来实现不同模块间的交互。
为了让不同模块在来回切换中保存其状态,Qt提供的QStackedWidget控件恰满足了这一要求。不同于移动端APP的滑动Swipe,在PC端的应用程序中,不同页之间的切换往往是通过侧边栏、顶部(底部或侧边)标签卡等形式进行交互的,因此我们需要QStackedWidget的标签选项卡的支持。虽然QStackedWidget本身具备标签卡,但并不便于设计成侧边栏按钮的形式,且其封装的信号与槽函数有限,很难扩展成侧边栏。因而这里我们自定义一个SideBar控件,模仿Qt Creator的左侧侧边栏形式,将侧边栏按钮的点击信号与自定义槽函数(调用QStackedWidget的SetCurrentWidget()函数)相连接,以触发QStackedWidget的切换页面的操作。
对于编译
、烧录
、连接设备
等简单操作,将其作为单独的QAction,利用QToolBar实现即可。
而设计页
的模块拖拽,可以用QGraphicsScene来实现一个类似画布的页面,再将电路模块作为QGraphicsPolygonItem,将其加入QGraphicScene即可。
最后,波形图的绘制采用QCustomplot第三方库实现。
四、模块介绍
4.1 主控件 CentralWidget
将侧边栏SideBar与主控件QSplitter装入一个水平布局,并将该布局作为MainWindow中CentralWidget的布局。其中,QSplitter分割器垂直分割QStackedWidget和LogWindow子控件。
4.2 页面切换 QStackedWidget
利用QStackedWidget容器将代码编辑页
、设计页
、图表页
、通信设置页
等不同控件QWidget装入,同时自定义为不同页定义各自的槽函数(亦可之实现一个槽函数,通过侧边栏Action列表的index来切换QStackedWidget),以接收侧边栏的点击信号。
- 通过
setCurrentWidget(QWidget*)
实现页面切换:
// 为每个侧边栏Action定义单独的槽函数
void MainWindow::action_edit_clicked()
{
qDebug() << "current page:" << qStackedWidget->currentIndex();
qStackedWidget->setCurrentWidget(codePage);
}
// 绑定侧边栏 “编辑Action”与对应的QStackedWidget页切换槽函数
QObject::connect(sidebar_edit, &QAction::triggered, this, &MainWindow::action_edit_clicked);
- (或)通过
setCurrentIndex(int)
实现页面切换:
// 为所有侧边栏Action定义一个的槽函数
// 另外得自定义侧边栏Action信号,加入index参数
void MainWindow::sidebar_action_clicked(int index)
{
qDebug() << "current page:" << qStackedWidget->currentIndex();
qStackedWidget->setCurrentIndex(index);
}
侧边栏具体实现代码见仓库 Modular-SDA/src/custom/sidebar.cpp。
4.3 代码编辑模块 CodeEditor
代码编辑页主要控件是一个自定义的继承自QPlainTextEdit的类CodeEditor,定义如下:
class LineNumberArea;
class CodeEditor : public QPlainTextEdit
{
Q_OBJECT
public:
CodeEditor(QWidget *parent = 0);
void setMode(editorMode mode); //设置编辑模式
void lineNumberAreaPaintEvent(QPaintEvent *event); // 自定义的行号绘制事件
int lineNumberAreaWidth(); // 自定义的行号宽度处理函数
protected:
void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; // 重写QPlainTextEdit的resize事件
private slots:
void updateLineNumberAreaWidth(int newBlockCount); // 更新行号区域宽度槽函数
void highlightCurrentLine(); // 高亮当前行槽函数
void updateLineNumberArea(const QRect &, int); // 更新行号区域槽函数
private:
QWidget *lineNumberArea; // 行号区域QWidget
};
/* 自定义的行号区域控件 */
class LineNumberArea : public QWidget
{
public:
LineNumberArea(CodeEditor *editor) : QWidget(editor) {
codeEditor = editor;
}
QSize sizeHint() const Q_DECL_OVERRIDE {
return QSize(codeEditor->lineNumberAreaWidth(), 0);
}
protected:
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE {
codeEditor->lineNumberAreaPaintEvent(event);
}
private:
CodeEditor *codeEditor;
};
除了基本的代码编辑功能外,为了能对代码关键字、标识符等进行高亮,需再定义一个继承自QSyntaxHighlighter类的自定义类MyHighLighter,其定义如下:
class MyHighLighter : public QSyntaxHighlighter
{
Q_OBJECT
public:
MyHighLighter(QTextDocument* parent = 0);
protected:
void highlightBlock(const QString& text) Q_DECL_OVERRIDE;
private:
// 封装高亮规则
struct HighlightingRule
{
QRegExp pattern; //高亮规则的正则表达式
QTextCharFormat format; // 高亮字符格式
};
QVector<HighlightingRule> highlightingRules;
QRegExp commentStartExpression; // 注释开始的规则
QRegExp commentEndExpression; // 注释结束的规则
/* 各种字符串高亮格式(C语言标识符与关键字) */
QTextCharFormat keywordFormat; // 关键字
QTextCharFormat classFormat; // 类名
QTextCharFormat singleLineCommentFormat; // 单行注释
QTextCharFormat multiLineCommentFormat; // 多行注释
QTextCharFormat quotationFormat; // 引用
QTextCharFormat functionFormat; // 函数
};
具体实现代码见 Modular-SDA/src/syntax/SyntaxHighlighter.cpp。
4.4 模块设计DesignerPage
画板的核心是参照官方例程Diagram Scene Example设计而成,定义如下:
#ifndef DESIGNERPAGE_H
#define DESIGNERPAGE_H
#include <QWidget>
#include <QListWidget>
#include <QLabel>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QGraphicsView>
#include <QToolBox>
#include <QButtonGroup>
#include <QSplitter>
#include <QThread>
#include <QDoubleSpinBox>
#include <QToolButton>
#include <QStackedWidget>
#include "custom/commonHeaders.h"
#include "Diagram/diagramscene.h"
#include "Diagram/diagramitem.h"
namespace Ui
{
class DesignerPage;
}
class DesignerPage : public QWidget
{
Q_OBJECT
public:
explicit DesignerPage(QGraphicsView* view, DiagramScene* scene, QMenu* itemMenu, QWidget* parent = nullptr);
~DesignerPage();
QWidget* getInstance();
void unCheckButtonGroupItem(int index);
void unCheckButtonGroupTextItem();
void addAttributesBox(QWidget* widget);
void updateAttributesBox(QWidget* widget);
void resetAttributesBox();
private slots:
void buttonGroupClicked(QAbstractButton* button);
private:
Ui::DesignerPage* ui;
// variables
QGraphicsView* view = nullptr;
DiagramScene* scene = nullptr;
QMenu* itemMenu;
QFrame* toolBoxes;
QStackedWidget* attributesBox;
QWidget* designBoard;
QWidget* noModuleSelected;
QButtonGroup* buttonGroup_1;
QLabel* toolBoxName_1;
QToolBox* toolBox_1;
QSpinBox* qSpinBox_pointCnt;
QSpinBox* qSpinBox_SampleFreq;
// functions
void init();
void createToolbars();
void initToolBox();
void initAttributesBox();
// QWidget* createCellWidget(QButtonGroup*, const QString&, DiagramItem::DiagramType);
QWidget* createCellWidget(QButtonGroup*, const QString&, DiagramItem::ModuleType);
};
#endif // DESIGNERPAGE_H
具体实现代码见 Modular-SDA/src/Pages/designerpage.cpp。
其中,算法模块属性等功能已实现,算法模块的代码生成未实现。
4.5 波形模块AnalyzerPage
绘图控件基于QCustomplot进行修改,加入了自定义的XTracer追踪指针位置,以显示横纵轴参考线及数值,波形图使用频谱图QCustomplot::SpectrumPlot即可。
用于追踪光标位置的XTracer
类定义如下:
#ifndef MYTRACER_H
#define MYTRACER_H
#include <QObject>
#include "qcustomplot.h"
class XTracer : public QObject
{
Q_OBJECT
public:
enum TracerType
{
XAxisTracer,//依附在x轴上显示x值
YAxisTracer,//依附在y轴上显示y值
DataTracer//在图中显示x,y值
};
explicit XTracer(QCustomPlot* _plot, TracerType _type, QObject* parent = Q_NULLPTR);
~XTracer();
void setPen(const QPen& pen);
void setBrush(const QBrush& brush);
void setText(const QString& text);
void setLabelPen(const QPen& pen);
void updatePosition(double xValue, double yValue);
void setVisible(bool m_visible);
protected:
bool m_visible;//是否可见
TracerType m_type;//类型
QCustomPlot* m_plot;//图表
QCPItemTracer* m_tracer;//跟踪的点
QCPItemText* m_label;//显示的数值
QCPItemLine* m_arrow;//箭头
};
class XTraceLine : public QObject
{
public:
enum LineType
{
VerticalLine,//垂直线
HorizonLine, //水平线
Both//同时显示水平和垂直线
};
explicit XTraceLine(QCustomPlot* _plot, LineType _type = VerticalLine, QObject* parent = Q_NULLPTR);
~XTraceLine();
void initLine();
void updatePosition(double xValue, double yValue);
void setVisible(bool vis)
{
if(m_lineV)
{
m_lineV->setVisible(vis);
}
if(m_lineH)
{
m_lineH->setVisible(vis);
}
}
protected:
bool m_visible;//是否可见
LineType m_type;//类型
QCustomPlot* m_plot;//图表
QCPItemStraightLine* m_lineV; //垂直线
QCPItemStraightLine* m_lineH; //水平线
};
#endif // MYTRACER_H
具体实现代码参见Modular-SDA/src/lib/XTracer.cpp。
五、结语
由于个人时间问题,该项目暂时告一段落,后续应该不会再着手。
该项目目前还有以下主要功能需完善:
- 代码编辑页的文件保存功能
- 模块设计页的代码生成功能
项目地址见文章开头,欢迎各位fork后进行拓展。