《黑马程序员Qt》摘录

文章目录


本文主要目的是补充上一个教程没有提到,不够深入的地方,加深对于Qt的理解,主要参考 师弟推荐的教程

meego:诺基亚曾经还用Qt开发了一个操作系统meego,虽然现在已经基本死掉了,非常小众了。

Qt之跨平台:在各种平台上搭建好环境,一个Qt工程可以直接在各个平台下编译运行,效果是一致的,而基本不需要修改。

Qt Creator的Demo:有空的时候可以学习Qt Creator提供的Demo,感受用法,提高水平。这些Demo水平很高,覆盖了Qt的诸多细节和用法,非常值得学习。

中文路径:Qt的程序路径一定不要有中文,否则很容易出错(之前开发的一个无人机地面站就经常出现这个问题)。

Qt程序基本框架:每个Qt程序,有且只有一个QApplication对象。且注意widget必须要show一下显示出来。
在这里插入图片描述
Qt模块:Qt的类的文档会介绍这个类属于哪个模块,头文件为何,pro文件需要添加什么,继承关系等。
在这里插入图片描述
shadow build:其实有时候使用shadow build模式也有必要,比如我们用git维护项目,那么只需要上传源文件就行了,不必把编译出来的东西也管理,编译出来的东西毕竟太大了。

主窗口没有show:如果我们的主窗口没有调用show函数显示出来,那么程序如何关闭呢?可以通过Qt的关闭按钮,或者系统的任务管理器。
在这里插入图片描述
窗口属性
可以使用setAttribute函数设置窗口属性,比如是否有边框,是否是透明背景等。

父窗口
父窗口的设置方法有两个:
①通过构造函数传入父窗口指针;
②使用setParent函数。

信号和槽
每个控件到底有啥信号和槽可以按F1看文档,这样最全面了。控件没有就追溯控件的父类。

connect的另外一种写法:

connect(发送信号控件指针, &发送信号控件类型::信号函数名不用带括号, 接收信号控件指针, &接收信号控件类型::槽函数名不用带括号);

注意,上述写法如果信号或者槽有重载,那么需要使用强制转换明确使用哪个函数,例如对于一个spinbox,有:

connect(spin, staitc_cast<void (QSpinBox::*)(int)>&QSpinBox::valueChanged,...)

什么可以作为槽函数?在Qt5中,任意成员函数、普通全局函数、静态函数均可。注意信号和槽参数一致。我们自己用成员函数作为槽函数(使用上述方式的时候不用slots关键字也行,但是用SIGNAL和SLOT宏时就必须有slots关键字),然后用上面的语法connect就行。使用slots关键字的语法如:

...
public/private/protected slots:
	槽函数声明;
...

自定义信号的方法:在类声明的地方不使用public等,直接用signals关键字,定义一个函数(只需要声明),不可以有返回值,但是可以有参数,在需要发射信号的地方使用语法:

emit 信号(参数);

.pro.user文件:保留程序的一些路径信息和配置信息,如果移动了项目位置,需要删除它。

Lambda表达式:C++11增加的特性,参考这里
可以用Lambda写简单槽函数,很方便。但是注意不要用[&]然后使用局部变量的值,因为slot触发的时候,局部变量早就释放掉了。
语法

[capture list] (params list) mutable exception-> return type { function body }(params list value)

/*
capture list:捕获外部变量列表
params list:形参列表
mutable指示符:用来说用是否可以修改捕获的变量
exception:异常设定
return type:返回类型
function body:函数体
此外,我们还可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:

序号 格式
1 [capture list] (params list) -> return type {function body}
2 [capture list] (params list) {function body}
3 [capture list] {function body}

格式1声明了const类型的表达式,这种类型的表达式不能修改捕获列表中的值。
格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型: (1):如果function body中存在return语句,则该Lambda表达式的返回类型由return语句的返回类型确定; (2):如果function body中没有return语句,则返回值为void类型。
格式3中省略了参数列表,类似普通函数中的无参函数。

capture list有更多知识点,例如[=]和[&],以及[=, &x],具体见上述参考资料。
*/

qDebug()的使用:具体参考这里
(1) 将字符串当做参数传给qDebug()函数。(这种方法可以不用添加头文件#include<QDebug>,类似于qDebug("%d\n", 10); )
(2) 使用流输出的方法输出多个字符串。(需要添加 #include<QDebug>头文件,类似于qDebug() << 10 << endl;)

Qt内存管理
Qt 即使是局部new出来的对象一般也不用手动释放,这是因为new时我们指定了其父对象指针,所以父窗口销毁的时候就会按照层级的树状结构销毁掉子窗口。

当且仅当一个对象满足如下条件,可以自动释放:
1.该对象是QObject的派生类
2.该对象的父类不为NULL

explicit
explicit在qt中用来修饰构造函数,关键字 explicit 可以禁止“单参数构造函数”被用于自动类型转换。比如我们定义了参数为int a的构造函数,那么初始化该对象时,下面的写法将被禁止:

classname myclass = integer;

参考这里

菜单栏
menuBar()函数可以直接获取菜单控件指针。

状态栏
这种东西的使用,首先创建好状态栏,然后可以往里面放入各种widget,然后我们实际是在widget上显示内容。对于有statusBar的窗口,直接使用statusBar()函数即可获取状态栏指针。注意,里面有个addPermanentWidget函数,是在右侧添加控件的。

中心窗口与浮动窗口
setCentralWidget添加中心窗口 ,而浮动窗口则围绕中心窗口浮动。addDockWidget则添加浮动窗口,浮动窗口根据拖动可以嵌在父窗口内部的某个边上,也可以浮出来。工具栏本身就是可以浮动的。

模态与非模态对话框
show()是非模态的,exec()是模态的。模态对话框会浮在最上面,且不允许再操作其它对话框。

标准对话框和文件对话框
标准:QMessageBox
文件:QFileDialog

自定义控件
我们以QWidget为基类创建自己的控件。然后在ui中,暂时放置一个QWidget占位,布局完毕后,把这个QWidget提升为我们自己定义的控件即可。

样式表
类似于css的语法。可使用ui设计器设置,也可使用函数setStyleSheet。

基本语法:

selector1[, selector2] {attribute1: value;[attribute2: value]}

其中selector值得是自身控件类型或者子控件类型。先设置所有某个类型子控件的样式,然后单独设置某个子控件的样式可以实现某个控件的特殊样式。具体用法还是查看Qt文档比较全面,这里写几个基本的(在设置样式的时候,资源中的文件直接去复制其引用路径最好):

QCheckBox, QComboBox, QSpinBox {
	color: rgb(0, 255, 255);
	background-color: white;
	backgound-image: url(资源中的路径); 	/* 手动复制 */
	font: bold;
}
QPushButton{
	border-width: 4px;
	border-image: url(资源中的路径) 4 4 4 4 stretch stretch; 	/* 自动适应大小,4表示四个边裁掉多少个像素,stretch表示两个方向都延展到原来的大小 */
	min-width: 50px;
	min-height:50px;
}
QPushButton: pressed{ /* 伪状态,只有点下才变化 */
	background-color: yellow;
}

伪状态可选:
在这里插入图片描述
方箱模型:
每个部件都由四个矩形框构成,空白区域(边框外透明的)、边框(border-style)、填充(padding,边框和内容之间的部分)、内容。对于一个平面部件,例如空白、边框、填充都是0像素的,那么四个矩形重合。
在这里插入图片描述

子控件微观的样式也可以修改,比如滚动条的样子:
在这里插入图片描述
setText等函数的样式
可直接采用标记语言,例如

this->setText(QString("<center>hi</center>"));

事件
①介绍:
事件主要包含窗口本身的变化、鼠标、键盘、计时器等,具体查看文档吧。

仔细来看,事件与信号其实并无多大差别,从我们对其需求上来说,都只要能注册事件或信号响应函数,在事件或信号产生时能够被通知到即可。但有一项区别在于,事件处理函数的返回值是有意义的,我们要根据这个返回值来确定是否还要继续事件的处理,比如在QT中,事件处理函数如果返回true,则这个事件处理已完成,QApplication会接着处理下一个事件,而如果返回false,那么事件分派函数会继续向上寻找下一个可以处理该事件的注册方法(父窗口)。信号处理函数的返回值对信号分派器来说是无意义的。

另外还有一个需要我们关注的问题是事件和信号处理时的优先级问题。在QT中,事件因为都是与窗口相关的,所以事件回调时都是从当前窗口开始,一级一级向上派发,直到有一个窗口返回true,截断了事件的处理为止。对于信号的处理则比较简单,默认是没有顺序的,如果需要明确的顺序,可以在信号注册时显示地指明槽的位置,也就是上面我们介绍的那种connect方法。

在QT中,事件使用了一个事件队列来维护,如果事件的处理中又产生了新的事件,那么新的事件会加入到队列尾,直到当前事件处理完毕后, QApplication再去队列头取下一个事件来处理。而信号的处理方式有些不同,信号处理是立即回调的,也就是一个信号产生后,他上面所注册的所有槽都会立即被回调。这样就会产生一个递归调用的问题,比如某个信号处理器中又产生了一个信号,会使得信号的处理像一棵树一样的展开。

当事件发生时,Qt会创建一个事件对象,所有的事件对象都以QEvent为基类,然后Qt把它传给QObject的event()函数,event()函数再调用处理的函数,所有的函数都是虚函数,且应该都是protected的。我们可以覆盖掉虚函数,实现我们自己的功能。

在这里插入图片描述

②一些细节:鼠标移动事件默认是进入点一下才有效,使用setMouseTracking(true)设置即可修改为只要鼠标进入就有效。keyPressEvent只检测键盘按下,不检测大小写,所以组合键还需要单独研究。timerEvent可以直接使用,使用startTimer启动,可以多次启动,那么进入timerEvent时,可以用其参数区分,其参数就是startTimer时候的返回值。

③向父类传递
有时候,事件和信号会发生冲突。比如我们继承了一个QPushButton按钮,我们使用了mousePressEvent处理,同时也对clicked绑定了一个槽函数,如果我们希望槽函数也能工作,那么在mousePressEvent中需要调用QPushButton::mousePressEvent(e)[e是mousePressEvent参数],交给父类处理。可见,信号实际上就是由事件发出去的。

④向父窗口传递
e->ignore和e->accept函数则决定是否把事件继续传递给父窗口。默认是不传的,这个和return true/false是不同作用的,具体参考这里

⑤event函数与事件筛选
可以重载event函数,来屏蔽某些事件,比如:

bool MyWidget::event(QEvent *e)
{
	if (e->type() == QEvent::Timer)
	{
		QTimerEvent *env = static_cast<QTimerEvent *>(e);
		// timerEvent(env);
		return true; // 这个必须有
	}else{
		return QWdiget::event(e);
	}
}

⑥事件过滤器
一次性操作所有子控件的事件。
需要给子控件安装过滤器:ui->label->installEventFilter(this); 然后重写如下函数:

bool eventFilter(QObject *obj, QEvent *e)
{
	if (obj == ui->label)...
	else{return QWidget::eventFilter(obj, e);}
}

Qt绘图:QWidget及派生类支持,因为QWidget同时是QObject和QPaintDevice的派生类 。

关于Qt绘图,可以直接参考这里。知道大概怎么回事儿就行,需要的时候再搜索具体的使用方法。
在这里插入图片描述
Qt绘图核心就是编写paintEvent函数,然后在必要的地方调用update/repaint函数触发绘图事件函数,具体查看paintEvent的文档即可。

void className::paintEvent(QPaintEvent *)
{
	QPainter p(this);
	//或者
	//QPainter p; p.begin(this);{绘图} p.end();
}

Qt绘图就是用QPainter绘制到QPaintDevice上的过程,中间的绘图引擎我们不用管。如果需要在屏幕上绘制图片,需要使用如下几个类配合QPainter。
在这里插入图片描述
注意QImage也是支持透明背景的,下例是把图片保存起来的,当然还可以画到屏幕上:
在这里插入图片描述
pixmap不能修改图片,但是可以转换为image然后再修改:
在这里插入图片描述

不规则窗口实现
啊,实际上我们继承一个QWidget,然后载入一个透明背景的png图片即可呀,看来很容易的事情。

首先,去掉边框:

setWindowFlags(Qt::FramelessWindowHint | windowFlags());

然后,把窗口背景色设置为透明:

setAttribute(Qt::WA_TranslucentBackground);

然后,重写鼠标事件,实现窗口拖动,并想办法实现窗口关闭功能。获取全局坐标的方法:

...mousePressEvent(QMouseEvent *e)
{
	...
	坐标 = e->globalPos() - this->frameGeometry().topLeft();
	...
}

TCP传文件
需要定义一个头,说明文件名称和文件大小,然后把文件按流读取,分成多个包,每次传输一个,最后客户端把数据拼接起来。

这其中可能会遇到粘包问题,具体参看:https://blog.csdn.net/weixin_41047704/article/details/85340311

发布了27 篇原创文章 · 获赞 61 · 访问量 9087

猜你喜欢

转载自blog.csdn.net/u013695457/article/details/104902788