文章目录
一、自定义控件的封装
在使用Qt的ui文件搭建界面的时候,工具栏栏中只为我们提供了标准的窗口控件,如果我们想使用自定义控件怎么办?需要我们自己去封装
1.SpinBox 和 HorizontalSlider 结合使用
1.1 目标效果
SpinBox 和 HorizontalSlider 结合使用,我们希望左边的部件数值发生变化,右边的部件也能一起发生移动;右边的部件移动,左边部件的数值也发生变化。
2. 封装步骤
2.1 在项目中添加新Qt设计师界面类文件
(1)
(2)
(3)添加好了新文件后,就会看到:
2.2 进入设计控件的UI界面
(1)在 SmallWideget.ui 中将要封装的部件拖拽到本文件窗口,然后做好水平布局,接着裁剪大小。
SmallWideget.ui文件和对应的窗口是给我们自定义控件使用的,要和项目的 widget.ui 文件分清。相当于就是一个蛋糕店,有做蛋糕的厨房(smallwidget.ui),有展示蛋糕的展厅(widget.ui)。smallwidget.ui 是做控件给 widget.ui 使用的。为了方便说明,在下面的文章中,我会将smallwidget.ui 称为做控件界面,widget.ui 称为使用控件界面。
2.3 使用控件界面使用自定义控件
如果控件被制作出来了,还有一个问题,在其他的界面如何使用?
(1)要知道制作的控件是属于哪个类,现在制作的控件是属于Qwidget 类。
(2)在使用自定义控件的界面给自定义控件,预留空间。使用和自定义控件属于同一个类的控件去占空间。
自定义控件是属于 QWiget 类的,那么在使用控件的界面中就用 Widget 这个控件去占位置:如果自定义的控件做好了,就直接放到预留的空间那里就行。
注意要属于同一个类的,才能帮占位置。就有点像 上课一样,叫本班同学帮占位置。
(3)还要将占位置的控件提升为自定义控件的类,然后点击添加:(这个就相当于你说明这个位置是帮谁占的。)
还有一个要注意的地方,这里使用的是全局包含。
(4)运行使用控件界面,就会出现自定义的控件:(就相当于,占座的人过来上课了)
2.4 在制作控件的文件中完善控件的功能
(1)完成 SpinBox 数值变化,水平滑块移动。SpinBox 和水平滑块是两个对象,也是要使用 connect 函数联系起来。
信号发出者是 SpinBox ,那么要知道 SpinBox 发出什么信号才能够满足需求。SpinBox 的主要作用是值发生变化,查看帮助文档 SpinBox 中也存在这个函数:
(2)当值发生变化,就会发出信号,这个函数有重载,所以需要用到函数指针。
(3)右侧滚动,左侧数值发生变化:
二、常用的鼠标事件
1.捕获鼠标进入或者离开Label区域
(1)新建 class 文件,要新建一个MyLabel 类,在Label 类的基础上增加新的功能。
(2)修改继承的类和包含的头文件,Mylabel 类继承 QLabel 类:
(3)在UI界面,放置一个Label部件:
(4)将Label 提升为新建的 MyLabel 类:
点击添加以后,就点击提升,提升为我们自定义的 MyLabel 类,这样就拥有了 MyLabel 类的功能:
(5)textLabel 就从 QLabel 变成了 MyLabel:这个将相当于我们自己封装了一个Label。
(6)将默认的 TextLabel 删掉,然后设置边框:
(7)在mylabel.h 文件中写捕捉鼠标事件的声明,就是为MyLabel 类增加新的功能,就是捕获鼠标离开或者进去 Label 区域的事件。这个功能是 QLabel 类没有的,是我们自己增加的。
(8)在 mylabel.cpp 文件中写函数定义:
(9)测试效果:其实就是在原有的 Label 类的基础上增加了新的功能,就相当于继承了 QLabel (父类),mylabel 是子类,有自己新特性。
2.捕获鼠标的其他事件
(1)在头文件中声明,也是在 Mylabel 中增加功能:
(2)在 mylabel.cpp 文件中实现:
(3)测试结果
(4)获取鼠标在 label 区域内操作那个时刻的坐标。比如说在哪个位置按下了鼠标。要注意这里的字符串拼接的方式,和占位符是怎么写的:占位符不需要写数据类型,而是写第几个参数:
(5)修改需求,点击鼠标左键才打印,右键不打印:
3.在原有类上增加定时器事件
3.1 在 Label 中让数字每隔1秒就进行 ++ 操作
(1)在 UI 界面中添加一个 Label 部件,并且去掉默认文字,还有增加边框:Label_2
(2)需求:在 Label_2 中增加定时器,让一个数字每隔1S就进行++操作,并且在Label_2 显示。
(3)在 widget.h 文件中增加定时器事件声明:
(4)在 widget.cpp 文件中对定时器事件定义:
1)这里要注意,每隔一秒就是调用一次这个函数,那么 num 要定义为 static,不然就会被重置.
2)要注意在 label_2 中显示的是字符串,num 是int型,要进行类型转换,int 转 string 的方法
(5)设置好定时器,要启动定时器:
在构造函数中启动定时器,参数是触发的时间间隔,就是间隔多长时间调用一次:
void Widget::timerEvent(QTimerEvent *)
(6)效果:
4.处理多个定时器
(1)需求又改了,要求在同一个界面(窗口)有两个 label区域,一个区域是每隔一秒数字++,另一窗口是每隔两秒++。
(2)再添加一个 label_3:
(3)问题来了,虽然有两个定时器,但是定时器事件只有一个,也就是两个不同的定时器调用的是同一个函数。怎么解决问题?解决问题就在于,间隔1秒的定时器在调用定时器事件的时候,只执行每隔1秒的操作;间隔两秒的定时器,只执行两秒对应的操作。
(4)利用定时器的id,和 if 来解决问题:
1)定时器的返回值是 int 型的定时器id,先声明两个定时器id:
2)定时器调用定时器事件的时候,会将定时器对象指针传进去:这样就可以判断是哪个定时器在调用,然后执行对应的代码:
三、定时器类
1.创建定时器对象
推荐使用定时器类,因为定时器比较多时,直接创建定时器对象就行,不用在定时器事件中写很多的定时器id判断:
2.停止定时器
四、事件分发器
事件(event)是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。
事件分发器:先这样简单地理解,在Qt中,用户的不同操作会发出不同的事件。发出的事件并不是直接发到对应的处理函数,而是要经过一个中间的事件分发器,事件会暂时放在它这里,然后由它发给对应的函数。这些函数是Qt 已经写好了的,事件处理函数。
1.事件分发器的 bool 返回值
如果返回的是 true 代表用户自己处理,不向下分发(不发给Qt 中已经写好了的事件处理函数),也就是说事件分发器是可以拦截事件的。
1.1 拦截鼠标点击事件
在Qt中,点击鼠标会发出一个事件,然后经过事件分发器,接着发到Qt提供的鼠标点击的处理函数 mousePressEvent()。
(1)那么现在我要在事件分发器将鼠标点击这个事件拦截下来。
(2)原本我们利用Qt提供的鼠标点击处理函数写的处理函数:
(3)现在我们要执行程序,然后点击鼠标,看是哪个函数去处理的。如果被事件分发器拦截了,那么就不会调用 mousePressEvent 去处理鼠标点击事件。查看效果:
没有调用 mousePressEvent 去处理鼠标点击事件,事件在事件分发器那里被拦截了,然后处理了。没有分发到Qt提供的事件处理函数。通常我们不会去拦截事件,因为拦截了之后,自己去处理比较复杂,容易出错。
五、事件过滤器
在应用程序中产生的事件,在发给事件分发器之前,还可以对事件进行拦截,使用事件过滤器:
1.使用过滤器的步骤
(1)给控件安装过滤器:
(2)重写过滤器事件:
注意参数是什么,obj 参数表示的是控件。也就是说有可能会有多个控件安装有过滤器,并且来调用过滤器事件。那么要清楚是哪个控件在调用过滤器事件:
(3)运行程序,过滤器拦截了事件,事件分发器和Qt提供的事件处理函数都不会收到事件:
六、绘图事件
简单点说就是可以对 Qt 的窗口进行画画。
(1)在绘画之前要先了解绘画事件,这个事件不需要手动去调用,构件运行程序的时候会自动调用。
(2)然后创建一个画家,还要指定画家往哪里画画,现在我们指定画家往创建的窗口画画。注意画家的构造函数的一个参数,绘画设备,就是画家往哪里画画。
(3)画一条直线,这两个参数是两个点,两个点决定一条直线:
(4)查看效果:
1.手动调用绘画事件
可以利用画家,直接调用准备好的资源进行绘画。
1.1 画资源图片
就是将图片画在创建的窗口上:
1.2 在窗口增加按钮,让图片向右移动
其实就是点击按钮,重新画一次图片。将按钮和绘画联系起来,点击按钮,就会调用 update() ,更新坐标。
七、绘图设备
绘图设备是指继承QPainterDevice的子类。
1.QPixmap 的使用
(1)使用步骤:
(2)查看磁盘中的画纸,并打开:
(3)可以改变纸张(pix)的颜色:
查看效果:
2.QImage 做绘图设备
QImage专门为图像的像素级访问做了优化。
2.1 修改某块区域的像素点
3.QPicture
这是一个可以记录和重现QPainter命令的绘图设备。 QPicture将QPainter的命令序列化到一个IO设备,保存为一个平*立的文件格式。
比如说我画了一个圆,然后把步骤都记录下来,接着保存下来。
文件后缀名是没有要求的:
如果下一次我还想画同样的圆,那么把步骤文件加载过来就可以了。
八、QFile 文件读写
1.读写文件操作
下面通过一个案例来了解读文件的操作,需求:点击按钮弹出文件对话框,然后选择文件。选择了一个文件之后,文件路径显示在单行框,文件的内容显示在下方的文本框。
1.1 点击按钮打开文件对话框
(1)要使用 connect 函数,将按钮和打开文件对话框联系起来。因为没有现成的信号处理函数,也就是打开文件对话框函数,需要我们自己写。
(2)其实就是点击了按钮,然后调用一个函数去创建一个文件对话框。
(3)要注意文件对话框的返回值是文件的路径,类型是QString。
1.2 将文件的路径显示到 LineEdit
(1)要判断文件的路径是否为空,文件路径不为空,才进行操作。
1.3 以只读方式打开文件
1.4 一次全部读取文件的内容
读取文件内容的方式有一次全部读取。使用 readAll() 函数,这个函数的返回值是 QByteArry 。所以在读取内容之前要声明一个 QByteArry 类型的变量,以便接收读取出来的全部文件内容。
1.5 文字编码格式问题
在 Qt 中默认的编码方式是 “uft-8”,如果要打开的文件不是 uft-8 格式的就会显示乱码。所以要注意修改编码格式:
1.6 一次读取一行文件内容
注意文件的结束时怎么写的,readLine() 的返回值是什么:
1.7 追加的方式写文件
九、文件信息类
通过文件信息类,可以获取文件的信息,比如文件大小,文件路径,文件创建时间,文件后缀等等。
1.常用函数
注意打印的文件大小是字节
2.日期数据类型
日期也是一种数据类型,不同数据类型需要进行数据类型之间的转换。