QT:快速了解基本概念
QT Creator 设计模式
QT Creator 是 QT 自带的 IDE,初次接触建议直接使用 QT Creator 而不是折腾 VS,可以节省很多时间。
首先了解一下 QT 的主要功能 —— GUI 设计,这也是 QT 相较于 C++ 额外增加的一部分。
- Central Widget (中央部件)
在这里可以显示窗口的布局,选中控件以后可以在右侧进行属性的编辑 - 控件盒子
所有的控件都位于这里 - 工具栏
这里的工具分为两部分:- 调整编辑模式
- 编辑控件
- 编辑信号/槽
- 编辑伙伴(Buddy)
- 编辑 Tab 顺序
- 调整布局
- 调整编辑模式
- 对象查看器
展示当前使用的所有的控件。
在左侧“对象”一栏中,我们可以修改空间的对象名,这里的对象名就是运行在程序中的变量名。
也可以在这里选中控件并在下方编辑。 - 属性编辑器
可以对选中控件的属性进行设置。
在这里可以看到当前组件的继承关系。 - 控件的右键窗口
可以快速地进行一些设置(如更改文本、大小等)。
最重要的是“转到槽”可以绑定信号与槽。 - Action 编辑器 和 信号与槽编辑器
- 添加 Action (可以先不关注这个概念)
- 添加信号与槽(作用和
connect
函数类似)
简单来说,我们创建一个可视化窗口的过程就是:
- 创建控件
- 从左侧控件盒子拖入中央部件
- 使用代码创建(详见下文)
- 为控件设置合适的属性(样式、布局)
- 通过鼠标拖动。
- 在右侧属性编辑器中进行设置。
- 代码控制控件属性(详见下文)
- 链接信号与槽(详见下文)
- 右键菜单 -> 转到槽
- 使用
connect
链接信号与槽
QT 程序的基本框架
QT 是 C++ 一个库,因此使用 QT 就必须将对应的库 include
到程序中。
一个运行的程序是不会轻易结束的,因此最后的 return
不能简单的返回 0
,这里使用 app.exec()
( app
是用户自定义的对象名)作为返回值,此时窗口会一直显示在屏幕上。
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
return app.exec();
}
创建与设置控件
通常有两种创建控件的方式:
- 使用代码创建
- 使用“设计”进行创建
使用代码创建控件
先前使用过的几种语言也有类似的创建可视化的对象的方式,无一例外都是使用了面向对象的方式。
首先简单回顾一下其他语言的实现方式:
Java swing
:
使用 swing
的过程并不是很复杂,最简单的例子就是创建一个 JOptionPane.showMessageDialog
对象。
参考示例:
programming-week13 – Task 82
如果想要创建一个窗口,则更复杂一点:
- 程序中声明需要的对象
- 通过对象的函数设置属性值来控制显示的样式效果
- 如果需要布局,需要
setLayout
(创建不同的Layout
对象)
参考示例:
Advanced Programming Project – Poker
这个过程是与 QT 的代码编辑过程十分类似的。
Python pyplot
:
pyplot
是 Python 的一个绘图库。
面向对象的方式很清晰的能读明白每个子图是如何绘制的以及对应的属性是什么:
def merge():
image = Image.open("Blog/paste/paste-rightPaste.png")
plt.subplot(231),plt.imshow(image,'gray'), plt.title('rightPaste')
plt.axis('off')
image = Image.open("Blog/paste/paste-noBox.png")
plt.subplot(232),plt.imshow(image,'gray'), plt.title('noBox')
plt.axis('off')
image = Image.open("Blog/paste/paste-noMask.png")
plt.subplot(233),plt.imshow(image,'gray'), plt.title('noMask')
plt.axis('off')
image = Image.open("Blog/paste/paste-swapOrder.png")
plt.subplot(234),plt.imshow(image,'gray'), plt.title('swapOrder')
plt.axis('off')
image = Image.open("Blog/paste/paste-saveWithNoConvert.png")
plt.subplot(235),plt.imshow(image,'gray'), plt.title('saveWithNoConvert')
plt.axis('off')
image = Image.open("Blog/paste/paste-noConvert.png")
plt.subplot(236),plt.imshow(image,'gray'), plt.title('noConvert')
plt.axis('off')
plt.savefig("Blog/paste/paste-all.png",dpi = 400)
参考示例:
使用 Python 批量添加图片水印 – 测试代码 – merge函数
JavaScript canvas
:
canvas
的绘制过程实际上也是对 canvas
对象进行的各种操作,通过不同的成员函数进行控制。
参考示例:
通过HTML+JavaScript绘制冰墩墩
QT 中使用代码创建控件与 Java 的 swing
是极为相似的:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 创建控件
QLabel *infoLabel = new QLabel;
QLabel *openLabel = new QLabel;
QLineEdit *cmdLineEdit = new QLineEdit;
QPushButton *commitButton = new QPushButton;
QPushButton *cancelButton = new QPushButton;
QPushButton *browseButton = new QPushButton;
// 设置属性
infoLabel->setText("input amd:");
openLabel->setText("open");
commitButton->setText("commit");
cancelButton->setText("cancel");
browseButton->setText("browse");
// 设置布局
QHBoxLayout *cmdLayout = new QHBoxLayout;
cmdLayout->addwidget(openLabel);
cmdLayout->addwidget(cmdLineEdit);
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addwidget(commitButton);
buttonLayout->addwidget(cancelButton);
buttonLayout->addwidget(browseButton);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addwidget(infoLabel);
mainLayout->addLayout(cmdLayout);
mainLayout->addLayout(buttonLayout);
// 创建窗口并启动
Qwidget w;
w.setLayout(mainLayout);
w.show();
return app.exec();
}
使用“设计”创建控件
简单来说就是从左侧选择合适的控件拖过去就好了,此处不在展开。
信号与槽(Signal and Slot)
信号与槽(函数)是 QT 中非常重要的一个概念,控件对于事件的响应都是通过信号与槽来实现的。
有些类似 Linux 中的 signal
,都是设置需要对哪种信号进行相应,然后需要调用什么函数进行相应。
有三种比较简单的方式设置信号与槽:
- 右键 -> 转到槽,不一样的槽函数响应不一样的信号
- 自己编写槽函数,使用
connect
函数绑定(自己绑定事件与函数) - 不编写槽函数,使用 lambda 表达式替换掉
connect
中的第四个参数
需要注意的是:
- 所有的槽函数需要在
.h
的文件中(如mainwindow.h
)的访问控制符后面加上关键字slots
,如:
private slots:
// 此处的函数是槽函数
void on_pushButton_clicked();
- 启动新的进程使用
QProcess
ui
指向的是整个窗口,可以通过它访问不同的对象(控件),然后访问其成员
connect
函数的使用
connect
通常有四种参数,分别表示:谁发出信号、发出什么信号、谁处理信号、怎么处理信号。
其中的“谁”都是表示一个对象。
他的作用是:将使用代码调用函数,更改为了通过用户触发事件、发出信号从而调用的方式。
普通的 connect
调用:
对于第二个参数和第四个参数,我们需要用到两个 QT 的宏,宏的作用是进行字符串化(const char *
)。
SIGNAL()
中的参数可以通过查阅文档获取,传入不同的值表示响应不同的信号
SLOT()
中的参数是一个函数名,表示接收到这个信号后调用哪个函数处理。
connect(ui->eastButton,SIGNAL(clicked()),this,SLOT(on_east_btn_clicked()))
ui->eastButton
表示eastButton
(这就是上面“对象查看器”中“对象”一列的值)要发出信号。SIGNAL(clicked())
表示要处理的信号是clicked()
(表示点击)。this
表示当前类(窗口)来处理信号。SLOT(on_east_btn_clicked())
表示当前对象的on_east_btn_clicked()
函数来处理信号,这个函数声明的时候必须有slot
修饰。
简言之,这个 connect
的作用是:
当 eastButton
控件发出点击的信号(clicked
)后,当前的类(窗口)将会调用自己的 on_east_btn_clicked()
函数。
使用指针的 connect
调用:
第二种方式是将宏替换为函数指针:
connect(ui->cancelButten, &QPushButton::clicked, this, &Widget::on_cancelButten_clicked);
这种方式的缺点是遇到重载的函数会发生错误,需要再加以区分。
使用 lambda 表达式的 connect
调用:
connect(ui->browseButten, &QPushButton::clicked,[this]()
{
QMessageBox::infomation(this,"text1","text2");
})
总结
QT 是一个第三方的库,主要作用是为我们的 C++ 程序创建图形化用户界面。
因此学习 QT 可以理解为:
- 如何使用 QT 封装的各种类;
- 如何使用 QT 进行界面的设计。
除了 QWidget
的这种形式,QT 还提供了 QML
用以进行图形用户界面的设计,这个将在之后进行学习介绍。