文章目录
简介:上篇文章:Qt 通过控件按钮实现hello world + 命名规范(7)。
通过按钮方式创建hello world程序
中涉及到connect函数以及信号和槽部分知识
,这篇文章主要是对这部分的知识做一个相关补充,再介绍Qt坐标系的相关知识。欢迎各位看官好友浏览,下篇文章见!
Qt坐标系
平面直角坐标系(笛卡尔坐标系)分为两种坐标系,一种是数学上的坐标系(右手坐标系),一种是计算机中的坐标系(左手坐标系)
坐标系的原点(0,0)就是屏幕的左上角或窗口的左上角。
给Qt的某个控件设置位置的时候就需要指定坐标,对于这个控件来说,坐标系原点就是相对于父窗口控件的(0,0)
。比如下方QPushButton
的父元素/父控件/父窗口就是QWidget
,而QWidget
没有父元素(NULL),就相当于父元素就是整个显示器桌面(电脑屏幕)
下面红色所框的范围就是代码中创建的
Widget
的范围,此时默认情况下按钮就是在(0,0)位置处(左上角)。可以使用move函数来设置该按钮的位置
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* myButton = new QPushButton(this);
myButton->setText("按钮");
myButton->move(400,500);
this->move(100,200);
}
Widget::~Widget()
{
delete ui;
}
move(400,500)
;坐标背后的单位是像素。显示器它本质上就是由一大堆可以发光的小亮点或小灯泡构成的,如果你使用手机对着屏幕去拍照,把对焦放到最大,是有可能看到像素的。而且在电脑的设置中我们能看到:显示器分辨率 1920 X 1080
。这表示的是,水平方向上有1920个像素(亮点),垂直方向上有1080个像素,那显示器亮点数量越多,它的画面就越好,那显示器的价格也随之越贵。分辨率1920 X 1080 :1080p
,分辨率2560 X 1440 :2k
,分辨率3840 X 2160 :4k
,其中2k与4k分为数字电影与消费级,上述介绍的是消费级中的2k与4k。下面的图片是我用手机拍电脑屏幕聚焦放到最大的结果
信号和槽
在Linux中,信号Sigal是系统内部的通知机制,也是进程间通信的方式。而Qt中的信号和Linux中的信号虽然不是一样的概念,但是确实有相似之处。
在Qt中谈到信号时,肯定涉及到下面的三个要素
信号源
:由哪个控件发出的信号信号的类型
:用户进行不同的操作,就可能触发不同的信号。比如点击按钮的时候就会触发点击信号,在输入框中移动光标,就会触发移动光标的信号信号的处理方式
:注册信号处理函数(槽(slot)函数
),在信号被触发的时候自动调用执行。在Qt中可以使用connect这样的函数,把一个信号和槽关联起来,后续只要信号触发了,Qt就会自动的执行槽函数,而所谓的槽函数
本质上也是一种回调函数
关于这个回调函数,
在C语言进阶中的指针进阶用到了函数指针
去实现转移表,降低代码的圈复杂度
,qsort实现回调函数效果。在C++
STL中用到的仿函数和lambda表达式。在Linux中
有信号处理函数,线程的入口函数和epoll基于回调的机制
举个形象的例子来帮助理解
:
- 信号源:你女朋友
- 信号的类型:今天面色不善
- 信号的处理方式:在脑子里面快速搜索各种可能,最终确定出最可能的一个原因是:每个月总有那么几天身体不舒服。OK自动帮忙去煮一杯红糖水
在上述操作过程中,都是提前把不同的信号的处理方式都准备好了,再去触发信号。比如,如何煮一杯红糖水,这个我方法我已经会了,将这个方法与面色不善这个信号给关联起来,一触发信号就能开煮,而不是在网上搜索如何煮红糖水。要特别注意,在Qt中一定是先关联信号和槽,然后再触发这个信号,这里的顺序不能颠倒,如果先触发这个信号,都没有关联起来,那咋知道如何去处理这个信号呢?
connect函数
connect函数的介绍
connect这个函数和Linux TCP socket中建立连接的函数并没有任何关系,只是名字恰巧一样罢了。connect函数是QObject提供的静态成员函数,又因为Qt中提供的这些控件类本身是存在一定的继承关系的,而QObject类是其它Qt内置类的
祖宗
,所以connect使用没有任何限制,想到哪个类中使用就使用
,下面是其中一部分类继承关系图。在Java中也存在类似的设定,Java所有的类都是继承自Object类
connect函数具体的使用方式
这里的相关用法也可以去参考这篇内容中的
通过按钮方式创建hello world程序
:Qt 通过控件按钮实现hello world + 命名规范(7)。
// 在Assistant中输入connect
connect(const QObject *sender,
const char *signal,
const QObject *receiver,
const char *method,
Qt::ConnectionType type = Qt::AutoConnection)
const QObject *sender
:描述了当前信号是哪个控件发出来的const char *signal
:信号的类型const QObject *receiver
:哪个控件对象负责处理信号const char *method
:这个对象要怎么处理(输入要处理信号的对象所提供的成员函数
)Qt::ConnectionType type = Qt::AutoConnection
:暂时先不考虑,也很少使用这个
一个简单的例子
界面上包含一个按钮,用户点击按钮,则关闭窗口
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* myButton = new QPushButton(this);
myButton->setText("按钮");
myButton->move(200,200);
connect(myButton, &QPushButton::clicked, this, &Widget::close);
}
Widget::~Widget()
{
delete ui;
}
(myButton, &QPushButton::clicked)
;connect要求这两参数必须是匹配的,myButton的类型如果是QPushButton*
,那第二个参数的信号必须是QPushButton内置的信号(或者是它父类的信号
),不能是一个其他的类,比如QLineEdit的信号。所谓的信号也就是Qt中对象内部提供的一些成员函数
(this, &Widget::close)
;这里close是QWidget内置的槽函数,Widget继承自QWidget,也就继承了父类的槽函数,而close槽函数功能已经是人家内部实现好了,无需我们去关心,具体的作用就是关闭当前的窗口/控件
解析click与clicked
;从单词的层次看,一个是一般现在形式,一个是过去分词形式。(这个参数是信号类型
)那clicked它的意思就是:鼠标点完了后,这就是要触发的点击信号
。而click它是一个槽函数,它的作用就是在调用的时候相当于点击了一下按钮
两个问题
咋知道的QPushButton有一个clicked信号
如果想知道Qt里到底提供了哪些内置的信号和槽可以让我们直接使用呢?
那唯一的方式就是:多看文档
。其次在翻阅文档的时候,如果在当前类中没有找到对应的线索,可以看看这个类的父类
QPushButton它的父类是
QAbstractButton
,单词abstract的意思是抽象的,摘要。Qt中会提供好几种按钮,然后将这些按钮存在的一些共性内容给提取出来,放到QAbstractButton
类里面
当我们要去查
clicked信号
时,会发现QPushButton并没有这个信号函数,那就去它的父类找,当查阅到文档中的信号时,最重点就是关注信号的发送时机(也就是用户进行了啥样的操作,就能产生这个信号
)
官方文档找不到相关线索怎么办
&QPushButton::clicked
与&widget::close
这两是一个函数指针
,那在connect函数具体的使用方式中提到
,它的形参不是要求传入的是一个 const char*
,这char* 和 函数指针能是同一个东西吗?虽然同为指针,不过很明显这两压根不是同一个东西,char* 的类型就叫做char*
,void clicked(bool checked = fasle);
该函数的函数指针类型是void(*)();
而且它虽与bool(*)();
同为函数指针,但它们的类型也是不一致的,那在C++中是不允许你使用两个不同的指针类型相互赋值的(因为函数传参本质上就是赋值)。那在文档中也找不出线索了,好像没辙了,这时就要进入到库中看看这个connect函数到底是怎么写的
鼠标放在该类名或函数上,按住ctrl键+鼠标左键进去,按住alt + 左方向键可以返回
文档中的这个函数声明是以前旧版本的Qt的connect函数的声明。以前板本中,传参的写法和现在其实也是有区别的。之前给信号参数传参,要搭配一个
SIGNAL宏
,给槽参数传参,要搭配一个SLOT宏
才能将传入的函数指针转成 char*。connect(mybutton, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close))
可能因为一些原因,官方文档没有及时更新,那我们就去库中看看。从Qt 5开始,对上述写法做出了简化,就不再需要写SIGNAL 和SLOT 宏了,直接给connect 提供了重载版本。在重载版本中,第二个参数和第四个参数成了泛型参数,允许咱们传入任意类型的函数指针了。(
C++泛型编程
)其次使用Qt封装的类型萃取器,此时 connect 函数就带有了一定的 参数检查功能,如果你传入的第一个参数和第二个参数不匹配,或者第三个参数和第四个参数不匹配(不匹配,2,4参数的函数指针,不是1,3参数的成员函数)此时代码编译出错