加载中...
qt编程
发表于:2023-06-27 |

(19 封私信 / 82 条消息) 如何优雅的在Windows上使用Qt开发STM32程序呢? - 知乎 (zhihu.com)

基于《学习之路2》学习qt

我们是推荐在栈上创建组件。因为要靠人工管理newdelete的出错概率要远大于在栈上的自动控制。除此之外,在堆上和在栈上创建已经没有任何区别。

看看最开始的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <QApplication>
/**
* @brief 允许在执行时写参数,这种格式是固定写法
* @pragrm argc 参数个数
*@pragrm argv[] 参数
*/
int main(int argc, char *argv[]) {
/*为基于QT Wigets的应用提供主事件循环*/
QApplication a(argc, argv);

/*进入主事件循环,直到调用exit(),返回它的值*/
return QApplication::exec();
}

  • 注:主事件循环是
    1. 处理分发事件
    2. 初始化和终结程序
    3. 设置系统范围和程序范围

如何理解下面代码:

1
Widget::Widget(QWidget *parent) :QWidget(parent)

将parent设置为nullptr将构造一个没有父对象的对象。如果对象是一个QWidget,它将成为一个顶级窗口。

信号和槽(signal and slot)

Qt 5 中,QObject::connect()有五个重载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
*@param sender:发出信号的对象
*@param signal:发出的信号
*@param receiver:接收信号的对象
*@param slot:接收信号后调用的槽函数
*/
connect(sender, signal,receiver, slot);

QMetaObject::Connection connect(const QObject *, const char *,
const QObject *, const char *,
Qt::ConnectionType);

QMetaObject::Connection connect(const QObject *, const QMetaMethod &,
const QObject *, const QMetaMethod &,
Qt::ConnectionType);
//这个函数其实是将 this 指针作为 receiver
QMetaObject::Connection connect(const QObject *, const char *,
const char *,
Qt::ConnectionType) const;

QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
const QObject *, PointerToMemberFunction,
Qt::ConnectionType)
//functor这个类型可以接受 static 函数、全局函数以及 Lambda 表达式。
QMetaObject::Connection connect(const QObject *, PointerToMemberFunction,
Functor);

添加动作(Action)

Qt 将用户与界面进行交互的元素抽象为一种“动作”,使用QAction类表示。

QAction包含了图标、菜单文字、快捷键、状态栏文字、浮动帮助等信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @ 给QAction传入了一个icon图标,一个text文本和this指针
* @param filename 以 : 开始,意味着从资源文件中查找资源
* @param open 文本值前面有一个 &,意味着这将成为一个快捷键。注意看截图中 File 的 F 有一个下划线。
*/
openAction = new QAction(QIcon(":images/doc-open"), tr("&Open..."),this);
//使用了setShortcut()函数,用于说明这个QAction的快捷键.Qt 的QKeySequence为我们定义了很多内置的快捷键,比如我们使用的 Open。
openAction->setShortcuts(QKeySequence::Open);
//setStatusTip()则实现了当用户鼠标滑过这个 action 时,会在主窗口下方的状态栏显示相应的提示。
openAction->setStatusTip(tr("OPen an existing file"));
//点击 槽函数open
connect(openAction,&QAction::triggered,this,&MainWindow::open);

//下面的menuBar()、toolBar()和statusBar()三个是QMainWindow的函数,用于创建并返回菜单栏、工具栏和状态栏。
QMenu *file = menuBar()->addMenu(tr("&File"));
file->addAction(openAction);
QToolBar *toolBar = addToolBar(tr("&File"));
toolBar->addAction(openAction);
statusBar() ;

导入资源文件

原文连接

cmake

1
2
3
4
5
6
# 设置资源文件名称
SET(RCC_FILES images.qrc)
# 添加QRC文件的函数 ,找到包再用函数
qt5_add_resources(QRC ${RCC_FILES})
//添加
add_executable(${PROJECT_NAME} ${QRC})

对象模型(MOC)

用标准 C++ 编译器编译 Qt 源程序之前,Qt 先使用一个叫做 moc(Meta Object Compiler,元对象编译器)的工具,先对 Qt 源代码进行一次预处理。

你应当注意到了,信号函数是不需要编写实现代码的,那怎么可以通过标准 C++ 的编译呢?,这其实就是 moc 进行了处理之后的效果。

Qt 使用 moc,为标准 C++ 增加了一些特性:

  • 信号槽机制,用于解决对象之间的通讯,这个我们已经了解过了,可以认为是 Qt 最明显的特性之一;
  • 可查询,并且可设计的对象属性;
  • 强大的事件机制以及事件过滤器;
  • 基于上下文的字符串翻译机制(国际化),也就是 tr() 函数,我们简单地介绍过;
  • 复杂的定时器实现,用于在事件驱动的 GUI 中嵌入能够精确控制的任务集成;
  • 层次化的可查询的对象树,提供一种自然的方式管理对象关系。
  • 智能指针(QPointer),在对象析构之后自动设为 0,防止野指针;
  • 能够跨越库边界的动态转换机制。

parent 指针

当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针

我们创建的这个QObject对象会自动添加到其父对象的children()列表。当父对象析构的时候,这个列表中的所有对象也会被析构。

注意,这里的父对象并不是继承意义上的父类!

我们也可以自己删除子对象,它们会自动从其父对象列表中删除。

QObject::dumpObjectTree()QObject::dumpObjectInfo()这两个函数进行这方面的调试。

。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。

看看下面代码

1
2
3
4
{
QWidget window;
QPushButton quit("Quit", &window**);
}

作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidgetQObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++ (ISO/IEC 14882:2003)要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。

布局管理(layout)

(13条消息) QT常用的布局方法_SharedNotNew的博客-CSDN博客_qt布局技巧

所谓 GUI 界面,归根结底,就是一堆组件的叠加。我们创建一个窗口,把按钮放上面,把图标放上面,这样就成了一个界面。

我们创建了一个QHBoxLayout对象。显然,这就是一个布局管理器。然后将组件都添加到这个布局管理器,并且把该布局管理器设置为窗口的布局管理器。这些代码看起来都是顺理成章的,应该很容易明白。并且,布局管理器很聪明地做出了正确的行为:保持QSpinBox宽度不变,自动拉伸QSlider的宽度。

1
2
3
4
5
6
7
8
9
10
11
void MainWindow::Layout_set() {

spinBox->setRange(0, 130);
slider->setRange(0, 130);

spinBox->setValue(35);
layout = new QHBoxLayout;
layout->addWidget(spinBox);
layout->addWidget(slider);
this->setLayout(layout);
}
一些布局类 描述
QBoxLayout 水平或垂直排列子控件
QButtonGroup 用于组织按钮控件组的容器
QFormLayout 管理输入控件和其相关的标签
QGraphicsAnchor 表示在QGraphicsAnchorLayout中两个项目之间的锚
QGraphicsAnchorLayout 可以在图形视图中将控件锚定在一起的布局
QGridLayout 网格布局
QGroupBox 带标题的分组框
QHBoxLayout 水平排列控件
QLayout 几何管理器的基类
QLayoutItem QLayout 操作的抽象item
QSizePolicy 描述水平和垂直大小调整策略的布局属性
QSpacerItem 布局中的空间隔
QStackedLayout 堆栈布局,同一时间只有一个控件可见
QStackedWidget 堆栈窗体,同一时间只有一个控件可见
QVBoxLayout 垂直排列控件
  • 布局类有几个关键的成员函数

1
2
3
4
5
6
7
8
void addSpacing(int size); //添加间隔大小
void setContentsMargins(20,20,20,20); //设置外边距
void addStretch(int stretch = 0); //添加控件大小比例
void addSpacerItem(QSpacerItem *spacerItem); //添加弹簧
void addWidget(QWidget *, int stretch = 0, Qt::Alignment alignment = Qt::Alignment());//添加普通控件
void addLayout(QLayout *layout, int stretch = 0);//添加子布局
void addStretch(); //加入 空间间隔 占位符,使两个按钮靠右对齐

信号重载

1
QObject::connect(spinBox, &QSpinBox::valueChanged, slider, &QSlider::setValue);

当我们使用&QSpinBox::valueChanged取函数指针时,编译器不知道应该取哪一个函数(记住前面我们介绍过的,经过 moc 预处理后,signal 也是一个普通的函数。)的地址,因此报错。解决的方法很简单,编译器不是不能确定哪一个函数吗?那么我们就显式指定一个函数。

1
2
3
4
5
6
7
8
9
10
//方法就是,我们创建一个函数指针,这个函数指针参数指定为 int:
void (QSpinBox:: *spinBoxSignal)(int) = &QSpinBox::valueChanged;
//然后我们将这个函数指针作为 signal,与 QSlider 的函数连接:
QObject::connect(spinBox, spinBoxSignal, slider, &QSlider::setValue);

//同上
QObject::connect(&newspaper,
(void (Newspaper:: *)(const QString &, const QDate &))&Newspaper::newPaper,
&reader,
&Reader::receiveNewspaper);

也可以

1
2
3
4
QObject::connect(&newspaper,
static_cast<void (Newspaper:: *)(const QString &, const QDate &)>(&Newspaper::newPaper),
&reader,
&Reader::receiveNewspaper);

下一篇对话框。。。。

第三方库

上一篇:
高中的三观
下一篇:
智能小车