C++ Qt入门
1. Qt基本文件组成
1. .pro项目文件
Qt版本不同,内容略有差异。如5.14版本,记录了模块、编译器版本、编译文件、输出路径等信息。
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
27
28
29
30
31#4.x版本的Qt的gui与widgets是合并的,在5.x中分开了,模块化编程要载入。
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
#编译器版本
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# 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
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
#编译的头文件、cpp文件
SOURCES += \
main.cpp \
widget.cpp
HEADERS += \
widget.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
2. 主程序文件main.cpp
1 |
|
3. 头文件widget.h
1 |
|
4. cpp文件widget.cpp
之所以继承使用widget而不直接使用QWidget,是为了让用户在widget.h与widget.cpp中添加自己的组件和功能。
1
2
3
4
5
6
7
8
9
//子类Widget类外实现有参构造,且调用了链表传参子类parent构造时同时向父类传入参数
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
}
Widget::~Widget()
{
}
2. Qt Assitant使用
得益于C++强大的可复用性,Qt编程中有大量的类不可能完全记忆,Assitant可以帮助查找有关类的信息。 例如需要查找某个窗口属性,索引搜索
QWidget
首先是目录索引: 查找思路:公有函数->公有的槽函数->父类的函数
其次是类的信息
例如设置窗口标题,大小等: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//widget.cpp
//在构造函数中设置
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//设置标题
this->setWindowTitle("Hello QT");
//获取窗口标题
QString s=this->windowTitle();
//输出标题
qDebug()<<s;
// //设置窗口尺寸
// this->resize(600,400);
//设置固定大小
this->setFixedSize(500,400);
}
3. 按钮控件
1. 按钮控件的类
1 | QPushButton Class |
2. 添加一个按钮
1 | QPushButton button; |
然而这个代码没有输出按钮,这是因为按钮控件需要绑定一个父对象,决定了它在哪个窗口进行显示,这种嵌套的形式称对象树。标准写法是:
1
2
3
4//创建一个按钮对象
QPushButton *button=new QPushButton;
//设置父对象
button->setParent(this);//在父类构造函数中
3. 对象树的空间释放问题:
- 父对象释放空间时,也会释放所有子对象列表的空间。
- Qt按钮控件一般选择在堆区开辟空间,原因是:如果在栈区开辟空间,如:
此时父对象P0被释放空间时,按钮也会被释放,程序结束时,按钮空间再次被释放,引起多次释放。而如果定义在静态区,意味着该按钮在程序结束前始终未被释放,在多开的场合引起大量的资源浪费。
1
2
3
4
5int main{
Parent P0;
QPushButton button;
button.setparent(&P0);
}
4. 设置按钮属性
打开Assistant,开始查找:设置文本属性函数在QPushButton的父类QAbstractButton中
1
void setText(const QString &text);
1
void setFixedSize(int w,int h);
1
void move(int x, int y);
1 | //widget.cpp |
显示一个简单的按钮及其空间分布。
4. 信号与槽机制
信号槽Qt框架重要的机制之一,当某个事件发生后(例如按钮被点击了一下),它就会发出某个信号(signal),这种信号是一种广播,没有目的性的,当某个对象对这个信号感兴趣,它就会使用连接(connect)函数,把这个信号和自身的一个函数(称为槽,也即slot)绑定;每次信号发出时,该槽函数会自动被回调。实际上类似一种观察者模式:当感兴趣的事件发生以后,某个操作会自动被触发。
connect函数,查找connect: 1
2
3
4
5
6
7
8
9connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
功能:接收者接收指定信号的处理操作
参数:
sender:信号的发起者(如按钮)
signal:指定信号(如点击),可以是系统的,也可以自定义
receiver:信号的接收者(如窗口)
method:槽函数,处理方法(对象的槽函数,如窗口关闭)
type:默认参数
1. 系统槽函数的调用
调用窗口退出槽函数,实现窗口关闭 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44//widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//设置标题
this->setWindowTitle("Hello QT");
//设置窗口尺寸
this->resize(600,400);
//创建登录、注册、退出按钮
QPushButton *buttonReg=new QPushButton;
QPushButton *buttonLog=new QPushButton;
QPushButton *buttonExit=new QPushButton;
buttonReg->setParent(this);
buttonLog->setParent(this);
buttonExit->setParent(this);
buttonReg->setFixedSize(100,50);
buttonLog->setFixedSize(100,50);
buttonExit->setFixedSize(100,50);
buttonReg->setText("Register");
buttonLog->setText("Login");
buttonExit->setText("Exit");
buttonReg->move(100,150);
buttonLog->move(250,150);
buttonExit->move(400,150);
//信号:点击 槽函数:窗口关闭 发起者:退出按钮 接收者:窗口
//4.x版本写法
//connect(buttonExit,SIGNAL(clicked()),this,SLOT(close()));
//Qt5.0版本以上,推荐
connect(buttonExit,&QPushButton::clicked,this, &QWidget::close);
}
Widget::~Widget()
{}
2.自定义槽函数
1. 全局函数定义槽函数
1 | //widget.cpp中 |
2. 成员函数定义槽函数
1 | //widget.cpp |
3. 使用lambda表达式实现槽函数
这是最简单简洁的方法,把槽函数和connect写在一起,只能在本窗口(类)中使用,甚至可以省略参数this指针。
1
2//构造函数中
connect(buttonLog,&QPushButton::clicked,[=](){qDebug()<<"Login success!";});
5.窗体切换
在cpp中选择Add new添加一个新窗口类,继承QWidget: 这个操作相当于新建了一个窗口,我们可以在新窗口上添加按钮等元素,现在我们希望两个窗口页面通过按钮来实现来回切换。此前我们要先理解一个新的知识,定义自己的信号。
1. 自定义信号实现窗体切换
从本窗口点击按钮,切换到新的页面,这个只需要在本窗口包含新窗口头文件,创建对象,使用各自显示、隐藏槽函数即可。但是切换后如何回到原来窗口呢?关键是把新窗口的操作,以信号的方式广播出去,因此这里要了解自定义信号的定义和传递。首先在新窗口头文件定义一个新信号
1
2
3
4
5
6
7
8
9
10
11
12
13
14//newwidget.h
class newwidget : public QWidget
{
Q_OBJECT
public:
explicit newwidget(QWidget *parent = nullptr);
signals:
void lspgsignal();//定义自己的信号
};1
2
3
4//newwidget.cpp构造函数中
connect(buttonlspage,&QPushButton::clicked,[=](){
emit this->lspgsignal(); //发出当前类的信号
});1
2
3
4
5//widget.cpp
connect(newwindow,&newwidget::lspgsignal,[=](){
newwindow->hide();
this->show();
});
2. 信号的参数传递
从上面的例子我们直观看到信号这种广播机制为不同窗口切换带来便利,此外,这种广播还可以用于不同窗口间值的传递。
1
2
3
4
5
6
7
8
9
10
signals:
//newwidget.h
void testpassval(int num);//定义信号
//newwidget.cpp
connect(buttonlspage,&QPushButton::clicked,[=](){
emit this->testpassval(100); //发出当前类的信号,参数是要发的值
});1
2
3
4
5
6//widget.cpp捕获信号和值,打印出来
connect(newwindow,&newwidget::testpassval,[=](int num){
newwindow->hide();
this->show();
qDebug()<<num;
});
6. 信号与槽函数的有关说明
- 发送者、接收者都必须是QObject的子类,槽函数是全局函数、Lambda表达式等无需接收者时除外。
- 信号和槽函数返回值都是void;
- 信号只需要声明,不需要实现;槽函数需要声明,也需要实现;
- 槽函数当使用成员函数实现时,会受到public、private、protected的影响。
- emit用于发送信号,connect()函数用于连接信号和槽,任何成员函数、全局函数、static函数、Lambda表达式都可以作为槽函数。
- 信号与槽一般参数一致,特殊情况是槽参数可以比信号的参数少,但是顺序要遵循(就像默认参数设置那块)。
- 一个信号可以连接到多个槽,但是槽函数被调用的顺序是不确定的;多个信号可以连接到一个槽,信号发出即可自动调用。
- 信号和信号可以连接,例如一个信号被发出,即发出第二个信号,和信号槽机制类似;
- 信号和槽可以被取消链接,关键字disconnect
7. 行编辑器QLineEdit
实现类似计算器输入框的功能,为下一节的计算器编程做铺垫。尝试利用按钮+行编辑器实现文本装载和清空功能。槽函数我们使用了QLineEdit的清空函数,为了更改字体,字体装载我们自定义了一个槽函数来定义字体样式、大小、粗细等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//widget.h
class Widget : public QWidget
{
Q_OBJECT //宏定义,不要去除,否则无法识别槽函数编译器报错
public:
Widget(QWidget *parent = nullptr);
~Widget();
QLineEdit *qledit;
public slots:
void setLineText();
};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
27
28
29
30
31
32
33
34
35
36
37
38//widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
this->setFixedSize(600,400);
this->qledit=new QLineEdit(this);
this->qledit->setFixedSize(600,100);
this->qledit->move(0,50);
QPushButton *button_clear=new QPushButton(this);
QPushButton *button_equi=new QPushButton(this);
button_clear->setText("Clear");
button_equi->setText("equipment");
button_clear->setFixedSize(100,50);
button_equi->setFixedSize(100,50);
button_clear->move(100,200);
button_equi->move(300,200);
connect(button_clear,&QPushButton::clicked,this->qledit,&QLineEdit::clear);
//自定义槽函数
connect(button_equi,&QPushButton::clicked,this,&Widget::setLineText);
}
Widget::~Widget()
{
}
void Widget::setLineText(){
this->qledit->setText("Hello QT");
//QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false)
this->qledit->setFont(QFont("Microsoft YaHei",28,QFont::Bold));//字体样式、大小、粗细、斜体(false不使用)
}
8. 代码实战:实现一个简单计算器
我们将新建一个工程,实现一个简单布局的计算器以及正整数加减乘除等操作。
9. QmainWindow
新建任务class选择QmainWindow,勿选择Qwidget
菜单栏、菜单、菜单项
1 |
|
实现效果:
状态栏
状态栏和菜单栏属性有差异,状态栏可放入Qwidget类及其子类控件,例如前面所述的按钮、行编辑器等,QAction和Qwidget同继承自QObject,因此状态栏不能放入QAction对象。
1 | ///////相应头文件/////// |
实现效果:
中心部件与铆接部件
铆接部件是《QMainWindow》定义的一种部件,指的是窗口上停靠的浮动框部件,可以使用函数进行停靠位置、窗体属性等设置,往往配合中心部件一起使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//设置停靠窗体可停靠的区域
setAllowedAreas()
//Qt::LeftDockWidgetArea //可在主窗口的左侧停靠
//Qt::RightDockWidgetArea //可在主窗口的右侧停靠
//Qt::TopDockWidgetArea //可在主窗口的上侧停靠
//Qt::BottomDockWidgetArea //可在停靠主窗口的底部停靠
//Qt::AllDockWidgetAreas //可在主窗口的任意部分停靠
//Qt::NoDockWidgetArea //只停靠在插入处
//设置停靠窗体的特性
setFeatures(DockWidgetFeatures features)
//QDockWidget::DockWidgetClosable //停靠窗体可以关闭
//QDockWidget::DockWidgetFloatable //停靠窗体可浮动
//QDockWidget::DockWidgetMovable //停靠窗体可移动
//QDockWidget::AllDockWidgetFeatures //此参数表示拥有停靠窗体的全部属性
//QDockWidget::NoDockWidgetFeatures //不可移动,不可关闭,不可浮动
中心部件不是一种独立的类,任何QWidget类控件都可以作为中心部件,例如按钮、行编辑器等,比较常使用的是文本编辑器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23////////////头文件///////////////////////
//中心部件
//铆接部件
////////////////////////////////////
//以下添加在MainWindow
//文本编辑器作中心部件
QTextEdit * mainwindow = new QTextEdit("Edit Here...",this);
setCentralWidget(mainwindow); //设置为中心部件
//铆接部件
QDockWidget *dockwindows = new QDockWidget("选项卡",this);
this->addDockWidget(Qt::RightDockWidgetArea,dockwindows); //默认停靠位置
dockwindows->setFixedSize(500,100);
dockwindows->setAllowedAreas(Qt::LeftDockWidgetArea); //允许停靠位置
//属性配置
dockwindows->setFeatures(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable);
实现效果: