当前位置: 首页 > news >正文

【Qt】常用控件1——QWidget

目录

一. QWidget的enable属性

1.1.示例1:使⽤代码创建⼀个禁⽤状态的按钮

1.2.示例2:通过按钮2切换按钮1的禁⽤状态

1.3.示例3:通过可视化操作来实现

二. QWidget的geometry属性

2.1.示例1:拉伸和平移

2.2.示例2:⼀个表⽩程序

2.3.WindowFrame的影响

三.QWidget的windowTitle属性

四.QWidget的windowIcon属性

4.1.基本介绍

4.2.qrc机制

4.2.1.示例1

4.2.2.示例2

五.QWidget的windowOpacity属性

5.1.示例1

5.2.精度问题

六. QWidget的cursor属性

6.1.示例1——使用.ui文件进行设置

6.2.示例2——使用代码进行修改

6.3.示例3——自定义光标

七.QWidget的font属性

7.1.示例1——.ui文件里面进行属性编辑

7.2.示例2——使用代码来修改

八.QWidget的toolTip属性

九.QWidget的focusPolicy属性

9.1.焦点

9.2.相关API

9.3.示例1——使用.ui文件进行修改

9.4.示例2——使用代码进行修改

十.QWidget的styleSheet属性

10.1.示例1——通过.ui文件进行修改

10.2.示例2——实现切换夜间模式

10.3.关于计算机中的颜色表示

10.3.1.RGB颜色模型示例

10.3.2.十六进制颜色值示例


什么是Widget

Widget 是Qt中的核⼼概念。

英⽂原义是"⼩部件",我们此处也把它翻译为"控件"。

控件是构成⼀个图形化界⾯的基本要素.

像,按钮,列表视图,树形视图,单⾏输⼊框,多⾏输⼊框,滚动条,下拉框等,都可以称为"控 件".

Qt 作为⼀个成熟的GUI开发框架,内置了⼤量的常⽤控件.这⼀点在QtDesigner中就可以看到端倪. 并且Qt也提供了"⾃定义控件"的能⼒,可以让程序猿在现有控件不能满⾜需求的时候,对现有控件做 出扩展,或者⼿搓出新的控件.

这里就是我们Qt Designer的内置好的控件。

综上,学习Qt,其中⼀个很重要的任务就是熟悉并掌握Qt内置的常⽤控件. 这些控件对于我们快速开发出符合需求的界⾯,是⾄关重要的.

QWidget

在Qt类库中,所有界面组件类的直接或间接父类都是QWidget。这意味着所有界面组件类都能直接使用QWidget的相关函数和属性。

可以说,QWidget中就包含了Qt整个控件体系中,通⽤的部分.

所有从QWidget继承而来的界面组件被称为widget组件,它们是构成GUI应用程序的窗口界面 的基本元素。

所以我们需要好好学习QWidget。

我们创建一个新项目

进去之后看到下面这个

右边黄色的就是我们QWidget的属性。我们也可以进行设置。

至于这些属性是什么意思,我们暂且不讲。

这些属性既可以通过QtDesigner会直接修改,也可以通过代码的⽅式修改. 这些属性的具体含义,在QtAssistant中均有详细介绍.

在QtAssistant中搜索QWidget,即可找到对应的⽂档说明.(或者在QtCreator代码中,选中 QWidget, 按F1也可)

一. QWidget的enable属性

enabled 属性控制着一个控件是否能够与用户进行交互。这是一个非常基础且重要的状态管理功能。

API 详解

bool isEnabled() const
  • 功能:这是一个 Getter 方法。它用于查询控件当前的可用状态。

  • 返回值:返回一个布尔值 (bool)。

    • true:表示控件当前处于 可用 (Enabled) 状态。

    • false:表示控件当前处于 禁用 (Disabled) 状态。

  • 用法:当你需要根据控件的状态来决定程序逻辑时,就需要调用这个函数。例如,在执行某个操作前检查按钮是否可用。

void setEnabled(bool enable)
  • 功能:这是一个 Setter 方法。它用于设置控件的可用状态。

  • 参数

    • enable:一个布尔值参数。

      • true:将控件设置为 可用 状态。

      • false:将控件设置为 禁用 状态。

  • 用法:当你需要动态地开启或关闭一个控件(及其子控件)的交互功能时,调用此方法。

2. “禁用”状态的深层含义

a) 功能上的禁用:无法接收输入事件

当一个控件被禁用时,它实际上被从交互逻辑中“剥离”了。

  • 鼠标事件:点击、双击、悬停、释放等事件都不会被禁用控件接收。

  • 键盘事件:Tab 键焦点切换会跳过被禁用的控件;键盘输入也不会送达给它们。

  • 其他事件:如拖放操作等,同样不会发生在禁用控件上。

效果就是:用户看到这个控件,试图去操作它,但不会有任何反应。 这对于防止用户在错误的时间或条件下执行某些操作至关重要(例如,在未填写必填表单时禁用“提交”按钮)。

b) 视觉上的禁用:灰色外观(🎨)

Qt 自动为禁用状态提供了视觉反馈,这通常被称为 “灰色显示” (grayed-out)

  • 实现机制:Qt 的样式系统在绘制禁用状态的控件时,会使用不同的调色板颜色(通常是灰色和浅色)来渲染文本和基础控件。这使得它看起来“变灰了”、“褪色了”或“不活跃”。

3. 父子关系传播:禁用状态的级联效应

这是 enabled 属性一个非常强大且重要的特性,它直接源于 Qt 的对象模型。

  • 规则如果一个父控件被禁用,那么它的所有子控件也会自动变为禁用状态,无论它们自身的 enabled 属性是如何设置的。

  • 内在逻辑:子控件是父控件内容的一部分。如果父控件本身不可用,那么其内部的子内容自然也不应该可用。这符合用户的直觉。

  • 实际例子

    1. 你有一个 QGroupBox(分组框),里面包含了几个 QRadioButton(单选按钮)和 QLineEdit(输入框)。

    2. 你调用 groupBox->setEnabled(false)

    3. 此时,整个分组框变灰,内部的单选按钮无法被选中,输入框也无法接收文本输入

    4. 即使你尝试单独调用 lineEdit->setEnabled(true),也无法覆盖其父控件 QGroupBox 的禁用状态。子控件的 isEnabled() 会返回 false,并且视觉上也是禁用的。

  • 如何判断真正的状态

    • isEnabled():返回的是控件的有效状态 (effective enabled state)。它综合考虑了控件自身的设置和其父控件的状态。如果任何一级父控件是禁用的,它就会返回 false

    • isEnabledTo(const QWidget *parent):这是一个更底层的方法,用于检查该控件相对于某个特定父控件是否可用。在大多数情况下,使用 isEnabled() 就足够了。

1.1.示例1:使⽤代码创建⼀个禁⽤状态的按钮

我们创建一个新项目

我们让按钮1失效,按钮2正常,我们运行一下,

这个按钮1其实是灰色了。代表按钮1失效。

但是我们还是没有见到它的功能是否失效,我们需要修改我们的代码

我们接着运行一下

点击按钮1

没有任何变化啊,我们点击按钮2

怎么样!!按钮1是真的没有效了吧。

1.2.示例2:通过按钮2切换按钮1的禁⽤状态

接下来我们将提供按钮2来切换按钮1的启用和禁用状态

我们运行看看

点击按钮1

发现是失效的,我们点击按钮2

发现按钮1的颜色变了,这个时候我们点击按钮1

我们发现按钮1的内容变了,这个时候我们再次点击按钮1

按钮1内容又变了,这个时候我们点击按钮2

按钮1的颜色变灰色了,我们点击按钮1

至此,我们完成了按钮2对按钮1的有效性的控制。

1.3.示例3:通过可视化操作来实现

我们创建一个新项目,点击下面这个

拖2个下面这个控件出去

其中我们得知道

按钮1的objectname是

按钮2的objectname是

QObject的objectName 属性介绍:

QObject 是 Qt 框架中所有对象的基类,也是 QWidget 及其所有派生类的父类。其最核心的属性之一就是 objectName,该属性在 Qt 应用程序中扮演着至关重要的角色。

在一个 Qt 程序中,objectName 是对象的身份标识,彼此之间不能重复.

在使用 Qt Designer 进行界面设计时,尤其是在界面中包含多个 widget 的情况下,必须通过为每个 widget 设置唯一的 objectName,从而在代码中准确获取到指定的控件对象。

Qt Designer 所生成的 UI 文件实质上是 XML 格式的文件,qmake 在构建过程中会将该 XML 文件转换为 C++ 头文件(该文件通常生成在构建目录中),并形成一个与界面对应的类(如 Ui::Widget)。

在这个过程中,每个 widget 在 Designer 中所设置的 objectName 最终会成为ui_widget类的成员变量名称。

因此,在实际代码中,我们可以通过该类的实例(通常命名为 ui,类型为 Ui::Widget *)直接访问这些界面控件,例如使用 ui->pushButton 或 ui->pushButton_2 等方式获取按钮对象。

多说无益,我们直接看例子

回到我们上面的项目,我们直接点击运行

我们点击叉,跟着下面走

我们打开看看

怎么样?是不是和我们上面说的一模一样。

每个 widget 在 Designer 中所设置的 objectName 最终会成为ui_widget类的成员变量名称。

而且这些成员变量都是public的!!!

那么我们怎么调用这些成员变量呢?

我们注意到这个ui_widget.h末尾还有下面这么一句

也就是说在Ui::Widget继承了我们这里的Ui_Widget类。子类会继承父类的所以public成员变量。

所以我们只需要创建一个Ui::Widget对象就可以使用这个Ui_Widget类的public成员变量。

而我们看这个widget.h

刚好自动帮我们创建好了这么一个Ui::Widget对象,现在我们在这个Widget对象里面就可以使用this->ui->pushButton,this->ui->pushButton2来分别访问按钮1和按钮2了。

回到项目

 接着我们跟着下面做

点击OK跳转到下面这里

把这个函数修改成下面这样子

接着我们回到这里

跳转到下面这里,同样我们修改一下代码

我们点击运行

点击按钮1,按钮1内容改变

点击按钮1,按钮1内容改变

点击按钮2,致使按钮1失效,颜色变灰

点击按钮1,什么变化都没有

点击按钮2,按钮1变回有效,颜色也恢复如初

 点击按钮1,按钮1内容改变

点击按钮1,按钮1内容改变

至此,完成。

注意:

在QtDesigner中创建按钮的时候,可以设置按钮的初始状态是"可⽤"还是"禁⽤".

如果把enabled这⼀列的对钩去掉,则按钮的初始状态就是"禁⽤"状态

二. QWidget的geometry属性

geometry在英文的意思是几何。

 这个其实是四个属性的统称:

  • x横坐标
  • y纵坐标
  • width 宽度
  • height ⾼度

但是实际开发中,我们并不会直接使⽤这⼏个属性,⽽是通过⼀系列封装的⽅法来获取/修改。

对于Qt的坐标系,不要忘记是⼀个"左⼿坐标系",其中坐标系的原点是当前元素的⽗元素的左上⻆.

API

QRect geometry() const

作用:获取控件的位置和尺寸

返回:QRect 对象,QRect 包含控件的几何信息

  • x: 控件左上角相对于父widget的横坐标

  • y: 控件左上角相对于父widget的纵坐标

  • width: 控件的宽度

  • height: 控件的高度

void setGeometry(const QRect &rect)

作用:通过 QRect 对象设置控件的位置和尺寸

参数

  • rect: QRect 类型参数,包含x坐标、y坐标、宽度和高度信息

void setGeometry(int x, int y, int width, int height)

作用:通过单独参数设置控件的位置和尺寸

参数

  • x: 控件左上角相对于父widget的横坐标

  • y: 控件左上角相对于父widget的纵坐标

  • width: 控件的宽度

  • height: 控件的高度

注意:两种 setGeometry 形式功能等效,仅参数传递方式不同。

QRect 类的相关接口

这个QRect 类, 确实提供了一系列的函数来获取和设置这些几何信息。

int x() const
  • 作用:返回矩形左上角的 X 坐标

  • 说明:这是矩形相对于父坐标系(通常是父窗口或父控件)的水平起始位置。

int y() const
  • 作用:返回矩形左上角的 Y 坐标

  • 说明:这是矩形相对于父坐标系的垂直起始位置。

int width() const
  • 作用:返回矩形的宽度

  • 说明:返回值是水平方向占据的像素大小。

int height() const
  • 作用:返回矩形的高度

  • 说明:返回值是垂直方向占据的像素大小。

void setX(int x)
  • 作用单独设置矩形左上角的 X 坐标

  • 行为:调用此函数只改变矩形的位置(水平移动),不会改变矩形的宽度。

void setY(int y)
  • 作用单独设置矩形左上角的 Y 坐标

  • 行为:调用此函数只改变矩形的位置(垂直移动),不会改变矩形的高度。

  • 示例

void setWidth(int width)
  • 作用单独设置矩形的宽度

  • 行为:调用此函数只改变矩形的宽度。矩形的左上角位置(X, Y)保持不变,右边界会随之移动。

void setHeight(int height)
  • 作用单独设置矩形的高度

  • 行为:调用此函数只改变矩形的高度。矩形的左上角位置(X, Y)保持不变,下边界会随之移动。

2.1.示例1:拉伸和平移

我们创建一个新项目

修改一下名字

把它的objectname修改成下面这个

接着我们在搞一下几个按钮

下面4个按钮的objectname分别修改成下面这样子

接下来我们要实现的功能就是点击下面这4个按钮

从而来修改下面这个tagget的位置

我们给下面这4个按钮都添加上槽函数

我们先搞这个up按钮

点击OK跳转到下面这个界面,我们把代码写一下

我们运行一下

点击一下这个up按钮

QRect(290,50 101x51) 表示:

  • x 坐标:290(距离父容器左侧 290 像素)

  • y 坐标:50(距离父容器顶部 50 像素)

  • 宽度:101 像素

  • 高度:51 像素

好了,见过了QRect,就该干正事了。我们修改一下代码

我们运行一下

我们连续点击5次up,我们发现这个target按钮长高了

我们再次连续点击5次left,发现这个按钮变胖了

我们再次连续点击5次right,发现这个按钮又变瘦了

我们再连续点5次down,发现这个按钮回到原来的地方了

发现了没有?

当前代码实际执行的效果,是在调整左上角那个点的位置。

左上角位置改变的同时,整个target按钮的高度和宽度也同时发生了改变~~

也就是在做一个拉伸

运⾏程序,可以看到,按下下⽅的四个按钮,就会控制target的左上⻆的位置。对应的按钮整个尺⼨也会 发⽣改变.

上述代码中我们是直接设置的QRect中的x,y。

实际上QRect内部是存储了左上和右下两个点的坐标,再通过这两个点的坐标差值计算⻓宽. 单纯修改左上坐标就会引起整个矩形的⻓宽发⽣改变


 

如果想要让这个按钮能够平移的话(宽度高度不变,整个按钮的位置都发生改变),我们应该怎么做呢?其实很简单

我们点击运行

连续点击5次up

我们发现形状没有发送变化了。

连续点击5次left

连续点5次down

连续点5次right

怎么样?是不是完美实现了平移。

事实上,如果说只是想要实现平移这么一个效果,我们也可以使用move

这个的效果和上面那个是一模一样的

2.2.示例2:⼀个表⽩程序

接下来我们将写一个表白程序。

我们先创建一个新项目

拖一个出来,并将其中的内容进行修改一下

它的objectname是下面这个

接着,

我们拖两个出来,并修改成下面这样子

接下来我们来修改这两个按钮的objectname

接下来我们就来给这两个按钮来配置槽函数

我们点击一下OK

接着我们来配置拒绝按钮的槽函数

跳转到下面这里,我们也将里面的代码进行修改一下

我们运行看看

点击残忍拒绝

再点击一下

我们点击一下欣然同意

但是这个机制其实是不好的,我们是点击了这个残忍拒绝。

  • 点击是包含按下+松开的
  • 按下+松开对应clicked信号
  • 按下对应pressed信号
  • 松开对应released信号

其实我们可以完全在按下,但是还没有松开的时候让按钮移动走,这就需要pressed信号,而不是clicked信号了

改成下面这样子

我们运行看看

我只是按下,并没有松开,它就走了

这就是pressed信号。

事实上,我们也可以使用下面这个来实现同样的效果

有的人可能不满足于此,觉得还需要我按下去它才会走。

如果我们需要鼠标移动到这个拒绝按钮,他就会离开,但是那个是关于事件的,我们现在实现不了。

2.3.WindowFrame的影响

我们好好讲讲这个 Qt 里的 WindowFrame(窗口框架)到底是什么东西。

你可以把它想象成一扇现实中窗户的 窗框

  • 窗框 (Window Frame):就是窗户周围那圈固定的结构。它包括:

    • 标题栏:最上面那条,写着窗户名字(比如“文档 - 记事本”),有最小化、最大化、关闭按钮。

    • 边框:四周那圈,让你可以用鼠标拖拽来改变窗户大小。

    • 菜单栏:虽然菜单栏的内容是你的应用提供的,但它通常被绘制在系统提供的窗框区域内。

  • 窗户玻璃 (Client Area):这就是窗框中间那块透明的玻璃,你可以透过它看到屋里的内容。在Qt里,这块“玻璃”就是你的应用程序界面本身,比如你放的一个按钮、一个文本框、你自己画的图形等等。

关键点: 在绝大多数情况下,这个“窗框”是由你的操作系统(Windows, macOS, Linux的桌面环境)负责绘制和管理的,而不是由你的Qt应用程序直接绘制。当你创建一个最普通的窗口时,Qt会向操作系统申请:“嘿,给我一个标准的窗口!”,操作系统就会把这个现成的“窗框”套在你的应用内容外面。

我们创建一个新项目

直接运行,我们发现得到的弹窗是下面这样子的

我们看着这个,千万不要以为它是下面这样子的

它的真实样子其实是下面这样子的

除了最里面那个大的矩形之外,其他的部分就是我们说的WindowFrame。

那么问题来了

 我们Qt中有很多计算坐标的函数,它们都是以A点为原点还是以B点为原点呢?

其实是都有的,

在计算尺⼨和坐标的时候就有两种算法.

  1. 包含windowframe:以A点为原点
  2. 不包含windowframe:以B点为原点

至此,计算尺寸和坐标的函数就分成两大派

  • 其中frameGeometry(),x(),y(), pos(), move() 都是按照包含window frame的⽅式来计算的.
  • 其中geometry(),setGeometry(),width(), height(), rect(), size() 则是按照不包含window frame的⽅式来计算的.

API 名称返回值/参数类型包含 Window Frame?说明与用途
位置相关 (Position)
x()int获取窗口左上角相对于父窗口或屏幕的横坐标(包含边框)。
y()int获取窗口左上角相对于父窗口或屏幕的纵坐标(包含边框)。
pos()QPoint获取一个包含 x() 和 y() 的 QPoint 对象,代表窗口左上角的位置(包含边框)。方便同时处理坐标。
尺寸相关 (Size)
width()int获取窗口客户区(Client Area) 的宽度(不包含边框)。
height()int获取窗口客户区(Client Area) 的高度(不包含边框)。
size()QSize获取一个包含 width() 和 height() 的 QSize 对象,代表客户区的尺寸(不包含边框)。
frameSize()QSize获取一个包含窗口整体宽度和高度的 QSize 对象(包含边框)。
组合(位置 + 尺寸)
geometry()QRect获取一个 QRect 对象,描述窗口的客户区。其 x() 和 y() 相对于父窗口,且不包含边框。常用于布局管理。
rect()QRect获取一个 QRect 对象,描述窗口客户区内部的矩形。其左上角坐标永远是 (0, 0)width() 和 height() 与 size() 相同。用于绘制和内部坐标计算。
frameGeometry()QRect获取一个 QRect 对象,描述窗口的整体框架(包含边框)。其 x() 和 y() 是窗口在屏幕上的绝对位置。
设置函数
setGeometry()(接受 QRect 或 x, y, w, h)直接设置窗口客户区的位置和大小。其坐标和尺寸的计算规则与 geometry() 一致,不包含边框。这是移动和调整窗口最常用的方法。

我们创建一个新项目

直接运行

嗯?怎么两个是一样的。

这个是因为当前代码是在Widget的构造函数里面,也就是说这个Widget对象还在构造,还没有被加入到window frame中。

因此,此时还看不到区别。

我们点击运行

点击按钮

这是啥意思呢?

让我们把它们放在一起看:

  • 矩形 AQRect(1069, 240, 800, 600)

    • 它的左上角位于屏幕的 (1069, 240) 位置。

    • 它是一个宽 800 像素,高 600 像素的矩形。

    • 它的右下角坐标是 (1069+800, 240+600) = (1869, 840)

  • 矩形 BQRect(1068, 209, 802, 632)

    • 它的左上角位于屏幕的 (1068, 209) 位置。

    • 它是一个宽 802 像素,高 632 像素的矩形。

    • 它的右下角坐标是 (1068+802, 209+632) = (1870, 841)

比较 A 和 B,可以看出:

  • 矩形 B 的左上角比矩形 A 的向左移动了 1 个像素(1068 vs 1069)。

  • 矩形 B 的左上角比矩形 A 的向上移动了 31 个像素(209 vs 240)。

所以,大家需要好好注意一下,这个坐标尺寸的函数,到底是以哪一个作为原点的。

三.QWidget的windowTitle属性

什么是windowTitle,我们直接创建一个新的项目,直接运行

这个Widget就是我们的windowTitle.

API 名称说明与用途
QString windowTitle()获取当前控件(通常是窗口)的标题栏文本。如果未设置过标题,则返回一个空的 QString
void setWindowTitle(const QString &title)设置控件(窗口)的标题栏文本。该文本将显示在窗口的标题栏、任务栏图标和任务管理器中。

我们直接创建一个新项目

运行

很好啊,完全符合我们的预期

注意事项

注意!上述设置操作针对不同的widget可能会有不同的⾏为.

如果是顶层widget(独⽴窗⼝),这个操作才会有效.

如果是⼦widget,这个操作⽆任何效果.


我们直接创建一个新的项目

我们发现这个button->setWindowTitle("通过按钮设置窗口标题");完全是没有用的。

但是这里又没有进行报错,这其实是不太科学的。

四.QWidget的windowIcon属性

4.1.基本介绍

API说明
windowIcon()获取到控件的窗口图标,返回 QIcon 对象。
setWindowIcon(const QIcon& icon)设置控件的窗口图标。

QIcon 就表示一个图标。

这俩 api 类似于 windowTitle 只能针对顶层窗口使用。

我们先准备一个图片,就是下面这个

注意这个图片的路径是:"D:\rc.png"。注意这个路径不要带上中文。

接着我们创建一个新的项目

这个其实是有问题的!!!

在 C++ 的字符串中,反斜杠 \ 是一个特殊字符,用于表示转义序列。它告诉编译器将其后面的字符组合在一起解释为特殊含义,而不是字面意义上的字符。

例如:

  • \n 表示换行符(Newline)

  • \t 表示制表符(Tab)

  • \" 表示双引号(而不是字符串结束符)

  • \\ 表示一个实际的反斜杠字符

在我的代码 QIcon icon("D:\rc.png"); 中:

  • 编译器会将 \r 解释为回车符(Carriage Return),而不是字符 \ 后跟字符 r

  • 因此,实际传递给 QIcon 构造函数的路径变成了 "D:<回车符>c.png",这显然不是一个有效的文件路径。

有几种正确的方法来处理 Windows 路径中的反斜杠:

  • 1. 使用双反斜杠 \\(最常用)

这是最传统和广泛兼容的方法。每个单反斜杠用两个反斜杠表示

QIcon icon("D:\\rc.png");
// 或者对于更长的路径
QIcon icon("C:\\Users\\YourName\\Pictures\\image.png");

原理:第一个反斜杠"转义"第二个反斜杠,告诉编译器将其视为字面意义上的反斜杠字符。

  • 2. 使用正斜杠 /(推荐)

Qt 和大多数文件操作 API 都支持使用正斜杠作为路径分隔符,即使在 Windows 系统上:

QIcon icon("D:/rc.png");
// 或者对于更长的路径
QIcon icon("C:/Users/YourName/Pictures/image.png");

优点

  • 代码更清晰易读

  • 跨平台兼容性更好(Linux/macOS 本来就使用正斜杠)

  • 不需要担心转义序列问题

  • 3. 使用原始字符串字面量 (C++11 及以上)

对于复杂的路径,可以使用原始字符串字面量,它不会处理任何转义序列:

QIcon icon(R"(D:\rc.png)");
// 或者对于更长的路径
QIcon icon(R"(C:\Users\YourName\Pictures\image.png)");

语法R"(...)" 其中的所有字符都会按字面意义处理。


我们来修改一下我们的代码

运行一下

除了这里,我们发现我们的任务栏也变了

怎么样,是不是还是很好的。

4.2.qrc机制

但是我们注意到一个问题

在Qt程序开发中,使用绝对路径加载图片资源(例如 QIcon icon("d:/rose.jpg");)是一种不推荐的做法,主要原因在于其可移植性较差。

我们写的程序,可是需要发布到别人的电脑里的。

开发者电脑上的路径结构(如D盘下的某个位置)与最终用户电脑的环境很可能不一致——用户的电脑可能根本没有D盘,或者文件被存放在完全不同的目录中。

这会导致程序在发布后无法正确找到资源文件,进而引发运行错误。

为了解决这个问题,我们通常采用相对路径的方式引用资源。相对路径以某个给定的基准目录为起点,使用 . 或 .. 表示当前目录或上级目录。例如:

  • 若基准目录是 D:/,则相对路径 ./rose.jpg 表示在 D:/ 下直接查找 rose.jpg

  • 而 ./image/rose.jpg 则表示在 D:/ 下先进入 image 子目录,再在该子目录中寻找 rose.jpg

然而,相对路径仍无法彻底解决资源部署的可靠性问题,因为用户仍可能意外删除或移动资源文件。为此,Qt提供了一种更为可靠的资源管理机制——qrc(Qt Resource Collection)机制

qrc机制通过以下方式从根本上解决了资源路径和存在性问题:

  1. 确保资源路径存在:资源在编译时被嵌入到最终的可执行文件中,因此不再依赖于用户机器上的具体目录结构。

  2. 防止资源被意外修改或删除:资源文件被直接编译到二进制程序中,用户无法直接删除或移动它们。

该机制的使用方法是:

  • 创建一个额外的XML格式文件(后缀名为 .qrc),在其中声明需要使用的资源文件及其在项目中的虚拟路径。

  • Qt在编译项目时,会根据qrc文件的描述,自动提取所有注册资源的二进制数据,并将其转换为C++代码,最终将这些资源数据静态链接到生成的可执行文件(如exe)中。

  • 资源文件全部都被添加到可执行文件里面了。这样子就不存在说数据丢失或者数据损坏了。

需要注意的是,qrc机制虽然强大,但也有其局限性:它不适合嵌入过大的资源文件(例如几个GB的视频文件)。因为将所有资源编译到程序中会导致可执行文件体积显著增大,进而影响程序的启动速度和内存占用。对于这类大型资源,通常建议在程序安装或运行时单独下载或动态加载。

4.2.1.示例1

我们回到上面那个项目

首先我们需要创建⼀个QtResourceFile(qrc⽂件),

⽂件名随意起(注意不要带中⽂),此处叫做 resource.qrc 

直接点完成,跳转到下面这里

现在我们需要创建一个前缀,

所谓的"前缀"可以理解成 虚拟的目录.

这个目录没有在你的电脑上真实存在,是 Qt 自己抽象出来的.

qrc 机制本质上就是把,图片 的二进制数据,转成 C++ 代码.(最终就会在代码中看到很大的 char 数组,里面就是图片的二进制数据)

为了方便 Qt 代码中访问到这个图片,Qt 就自己抽象出了 虚拟的目录~~

此处我们前缀设置成 / 即可.

所谓的前缀,可以理解成"⽬录".这个前缀决定了后续我们如何在代码中访问资源

接下来我们需要将我们的资源导入进来

一打开其实是处于当前代码所在位置

然后我们就去寻找我们的资源,导入进去

点击之后,发现出现了下面这个问题

导入图片的时候,需要确保你导入的图片必须在 resource.qrc 文件的同级目录, 或者同级目录中的子目录里~~

因此, 就可以把 我们这个图片 拷贝到当前项目目录中即可~~

现在我们再次点击

点击打开即可

现在就导入成功了。

导入成功之后,我们就可以在代码中使用我们设定好的虚拟目录来访问这个资源文件了

当代码中需要访问 qrc 中管理的文件时, 就需要在路径上带有 : 前缀~~

然后:之后就是我们的虚拟路径

现在我们点击运行

也是可以正常运行的。

我们不是说使用了qrc机制,在构建程序的过程中,Qt会把资源⽂件的⼆进制数据转成cpp代码,编译到exe中。从⽽使依赖的资源变得"路径⽆关"。

那么这个cpp代码呢?

我们去看看

qrc 中导入的图片资源,就会被转成这个 qrc resource.cpp 这个 c++ 代码

我们也可以进去看看

看起来很像二进制的格式啊。

这样子就验证了在构建程序的过程中,Qt会把资源⽂件的⼆进制数据转成cpp代码,编译到exe中。

现在我们的图片就融入到exe文件了。就不怕找不到这个图片了。

4.2.2.示例2

首先我们先准备两张图片

这两张图片都叫rc.png

接下来我们创建一个新的项目

同样的我们创建一个qrc文件

点击下一步

这里的前缀设置为了/new/prefix1

接下来我们接着创建前缀

现在我们能看到

现在我们分别对这两个前缀里面添加图片

注意图片要提取拷贝到我们qrc文件所在目录或者它的子目录里面

现在我们的资源都添加好了,现在我们来写一下代码

怎么样?有没有对qrc机制了解的更多啊!!!

上述qrc这⼀套资源管理⽅案,优点和缺点都很明显.

  • 优点:确保了图⽚,字体,声⾳等资源能够真正做到"⽬录⽆关",⽆论如何都不会出现资源丢失 的情况.
  • 缺点:不适合管理体积⼤的资源.如果资源⽐较⼤(⽐如是⼏个MB的⽂件),或者资源特别多, ⽣成的最终的exe体积就会⽐较⼤,程序运⾏消耗的内存也会增⼤,程序编译的时间也会显著 增加.

五.QWidget的windowOpacity属性

这个其实是控制widget的透明度。

windowOpacity 是一个 qreal 类型(即 double)的属性,取值范围从 0.0 到 1.0

  • 0.0: 表示完全透明(窗口不可见,但依然存在)。

  • 1.0: 表示完全不透明(这是所有窗口的默认值)。

  • 0.0 到 1.0 之间: 表示半透明。例如,0.5 表示 50% 不透明,即 50% 透明。

相关API

float windowOpacity()

参数: 无

返回值: float - 范围 0.0(完全透明)到 1.0(完全不透明)

功能: 获取控件的当前不透明度。


 void setWindowOpacity(float level)

参数:

  • levelfloat 类型。要设置的不透明度值,范围 0.0(完全透明)到 1.0(完全不透明)。

返回值: 无 (void)

功能: 设置控件的不透明度。

5.1.示例1

我们先创建一个新项目

把他们的objectname修改一下

现在分别给他们配置槽函数

同样的,我们给sub按钮也添加槽函数

我们运行一下

我们现在点击3次sub按钮

我们现在点击1add按钮

怎么样?这个透明度是不是一直都在变化

5.2.精度问题

但是我们仔细看看

嗯?这个不透明度的变化怎么都不是按0.1变化的。

C++计算内部小数的内部逻辑

电脑的CPU非常快,但它底层在处理小数时,其实是个“只会用分数(而且是分母是2的幂次方,比如1/2, 1/4, 1/8...)来表示数字的强迫症”。

现在,你想让它表示一个数:0.1

你问它:“你有 1/10 吗?”
它看了看自己手里的“分数砖块”(1/2, 1/4, 1/8, 1/16, 1/32, 1/64...),发现没有一块正好是 1/10。

于是它只好试着用现有的砖块拼凑出一个最接近 0.1 的数:
1/16 + 1/32 + 1/256 + 1/512 + ...
它拼命地往上加更小的碎块,但无论如何都拼不出一个正好是 0.1 的数,只能得到一个非常、非常接近 0.1 的近似值

float 和 double 类型就像是给这个强迫症两个不同大小的“工具箱”。

  • float 工具箱小,能放的“分数砖块”少,拼出来的数字精度低一点。

  • double 工具箱大得多,能放的砖块更多,拼出来的数字就更接近真实值,精度更高。

但无论如何,它们都无法精确地表示 0.1。你让电脑存的 0.1,其实存的是那个“近似值”。

这会带来什么后果?

当你进行运算时,电脑实际上是在用这些“近似值”做计算。

所以:
1.0 - 0.1
电脑并不是计算 1.0 - 0.1,而是计算:
1.0 - (那个非常接近0.1的近似值)
得到的结果,自然也是一个非常接近 0.9 的近似值,而不是完美的 0.9。

最经典的例子就是:
0.1 + 0.2 == 0.3 // 在很多语言里,这条式子会返回 false

为什么呢?
因为电脑用“近似值的0.1”加上“近似值的0.2”,得到了一个“近似值的0.30000000000000004”,而这个结果不等于“近似值的0.3”。

这就像你用一把刻度非常精细但不完全准的尺子去量东西,每次测量都有极其微小的误差,但几次测量加起来,这个误差就可能变得肉眼可见了。

写代码时该怎么办?(重点!)

  1. 永远不要直接用 == 比较两个浮点数!
    这是最重要的原则。不要写 if (a == b),因为它们可能只是“近似相等”,而非绝对相等。

  2. 正确的比较方法:看它们是否“足够近”
    我们可以判断两个数的差值是否在一个非常小的误差范围内(这个范围通常叫做 epsilon)。

    float a = 0.1f + 0.2f;
    float b = 0.3f;// 错误的比较方式:
    if (a == b) { ... } // 可能会失败!// 正确的比较方式:
    float epsilon = 1e-6f; // 定义一个误差范围,比如 0.000001
    if (fabs(a - b) < epsilon) { // 如果a和b的差值的绝对值小于误差范围// 我们就认为它们“相等”
    }
  3. 在对精度要求极高的场合,避免使用浮点数!
    比如在处理金钱时,绝对不要用 float 或 double!因为微小的误差累积起来会变成巨大的错误。
    最好的做法是:把所有钱都以“分”为单位,用整数(intlong long)类型来存储和计算。 比如 12.34元,在程序里就存为 1234(分)。这样就完全避免了小数精度问题。

六. QWidget的cursor属性

在日常使用电脑的过程中,我们遇到的鼠标光标的形状,大概就是下面这些

这个cursor属性属性的作用: 控制当鼠标悬停在该控件(及其未覆盖的子区域)上时,鼠标光标的形状。

相关API

QCursor cursor() const

这个方法用于获取当前 widget 设置的光标对象。

意思就是获取一下如果⿏标悬停到当前 widget时,光标会是什么样子。

  • 作用: 这是一个“只读”操作。它返回一个 QCursor 对象,该对象描述了当前 widget 的光标形状、位置等信息。

  • 何时使用:当你需要知道当前 widget 的光标是什么,或者想基于当前光标做一些操作(例如临时改变后又恢复)时,会用到它。


void setCursor(const QCursor &cursor)

这个方法用于为当前 widget 设置光标形状。

意思就是设置一下如果⿏标悬停到当前 widget时,光标会是什么样子。

  • 作用: 这是一个“写入”操作。它设置 widget 的光标属性。当鼠标移动到这个 widget 的区域时,光标就会变成你设置的形状。一旦鼠标移出这个 widget,光标就会恢复为之前的形状(通常是箭头)。

  • 参数const QCursor &cursor - 一个 QCursor 对象的引用,表示你想要设置的光标。你可以传入一个预定义的光标形状,也可以传入自定义的光标图片。

  • 特点:

    • 局部性: 只影响这一个 widget。

    • 自动恢复: 鼠标移出 widget 后,效果自动消失。


static void QGuiApplication::setOverrideCursor(const QCursor &cursor)

这是一个静态函数,用于设置整个应用程序的全局光标。

  • 作用: 强制覆盖整个应用程序中所有 widget 的光标设置。无论鼠标移动到哪个窗口、哪个 widget 上,显示的光标都是你设置的这一个。

  • 参数const QCursor &cursor - 要设置的全局光标对象,创建方式同上。

  • 特点:

    • 全局性: 影响整个应用程序。

    • 强制性: 它会覆盖所有 widget 的 setCursor() 设置。

    • 非自动恢复: 你必须手动调用 restoreOverrideCursor() 来恢复之前的状态。如果不恢复,这个效果会一直持续。

  • 何时使用:通常用于表示应用程序正处于一个短暂的、阻塞性的状态,比如:

    • 执行一个耗时操作(如文件读写、大量计算)时,将光标设置为 Qt::WaitCursor(沙漏/旋转圆圈),告诉用户程序正忙。

    • 在绘图软件中,全局设置为画笔、橡皮擦等特定工具的光标。

  • 重要注意事项:

    • setOverrideCursor() 和 restoreOverrideCursor() 必须成对使用,类似于一个堆栈(Stack)。如果你调用了两次 setOverrideCursor(),就必须调用两次 restoreOverrideCursor() 才能恢复到最初的光标。

    • 通常在耗时操作开始前设置,在操作结束后立即恢复。

6.1.示例1——使用.ui文件进行设置

我们创建一个新项目

把它修改成下面这个

此外我们选中这个大的QWidget控件

我们直接运行一下我们的程序

还是很不一样的吧。

我们这个使用.ui文件来设置的话,还是很快的啊。

6.2.示例2——使用代码进行修改

我们创建一个新项目

 

它的objectname是

怎么样。

事实上,Qt内置了很多的光标样式,我们去看看

光标移动到上面那里,然后我们ctrl+鼠标左键进入下面这里

这些都是Qt内置的光标的形状。

6.3.示例3——自定义光标

Qt ⾃带的光标形状有限。

Qt运行我们自定义的图片来做成光标。

我们也可以⾃⼰找个图⽚,做成⿏标的光标。就比如说下面这个

它的路径是"D:\mouse.png",注意不能有中文

我们创建一个新项目

我们创建一个qrc文件

 

⽂件名随意起(注意不要带中⽂),此处叫做 resource.qrc 

点击完成即可

注意这里的图片资源要提前放到我们qrc文件所在目录或者它的子目录里面

点击打开

添加成功

接下来我们来编写代码

看起来很好对不对,但是事实上是存在一些问题的。

  • 问题1:这个鼠标怎么这么大
  • 问题2:这个鼠标真的准吗

问题1:这个鼠标怎么这么大

其实我们可以对我们的图片进行缩放

我们运行一下

是不是比刚才小了啊!!

问题2:这个鼠标真的准吗

事实上,其实是不准的,这都是QCursor cursor(pixmap);问题。

QCursor cursor(pixmap); 就相当于:

你只告诉了电脑:“嘿,这是我的新鼠标图片”,但没有告诉它“打击点”在哪里。

因为电脑必须知道一个精确的点来代表位置,所以它就自作主张地帮你选了一个默认的“打击点”。

这个默认的“打击点”就是图片的左上角,也就是坐标 (0, 0) 的位置。

  • 这会带来什么后果?

后果就是:“指哪儿不打哪儿”,非常难用!

  • 你的眼睛和手:配合着光标图片的“形状”来移动。比如你想点击一个按钮,你会用箭头光标的尖尖去对准它。

  • 但电脑:却用图片的左上角来计算位置。

结果就是你感觉鼠标漂移了,总是点不准。你必须用图片的左上角(可能是一片空白的地方)去对准你想点的目标,这完全违背了你的直觉。

注意我们这个鼠标可是一个方形的图片

就会存在这么一个误差,那么我们应该怎么做呢?其实很简单,

QCursor提供了另外一种构造函数,专门用于指定打击点。

想象你手里拿着一把锤子

现在,你想用这把锤子去钉墙上的一个钉子。

问题来了: 计算机怎么知道你是用锤子的哪个点去砸中钉子的?

是锤子把儿的顶端?还是锤子头的中心?还是锤子头的最前面?

QCursor cursor(pixmap, 10, 10); 这行代码,就是在告诉计算机:

“嘿!我真正的‘打击点’在这里!”

(10, 10) 就是那个“打击点”的位置。它表示:

  • 从图片最左边往右 10 个像素

  • 从图片最顶上往下 10 个像素

的那个点。


如果没有这个设置会怎样?

如果你不告诉电脑“打击点”在哪,电脑就会偷懒,默认认为你的“打击点”是这张图片的左上角(也就是 (0, 0) 的位置)。

这就好比:
你用锤子去钉钉子,必须用锤子的木质把手顶端去对准钉子,才能砸中。这太奇怪了!你眼睛看着的是锤子头,但电脑却用把手顶端的位置来计算。

这个设置有什么用?

设置了正确的“打击点”后,一切都变得合理了。

  • 对于箭头光标,你应该把“打击点”设置在箭头的尖尖上。

  • 对于手指光标,你应该把“打击点”设置在手指的指尖上。

  • 对于一个十字准心,你应该把“打击点”设置在十字的中心

这样,你用眼睛看着光标图片的尖尖去对准屏幕上的按钮,点击时,电脑用同一个点去计算,就百分百精准了。

总结一句话:

QCursor cursor(pixmap, 10, 10); 就是在定义你鼠标真正的“指尖”在哪里,让电脑知道你想到底是点哪个位置。

这样讲,明白了吗?

当然,你不一定非要指定(10,10)为打击点,这完全取决于你的图片的尖尖在哪里。

七.QWidget的font属性

这个其实是设置字体的属性

相关API

方法名 (Method)说明 (Description)
font()获取当前widget的字体信息,返回QFont对象。
setFont(const QFont& font)设置当前widget的字体信息,接收一个QFont对象作为参数。

关于QFont

属性名 (Property)说明 (Description)
family字体家族名称,用于指定文本显示的字体类型,例如“微软雅黑”、“宋体”、“Arial”。
pointSize字体大小,单位是磅 (Point),数值越大,字体显示越大。
weight字体粗细程度。以数值表示,取值范围通常为[0,99](或100的倍数),数值越大,字体越粗。常规约为50,加粗约为75。
bold是否加粗。设置为 true 相当于将 weight 设置为75(加粗),设置为 false 相当于将 weight 设置为50(常规)。
italic是否倾斜。设置为 true 时,字体样式显示为斜体。
underline是否带有下划线。设置为 true 时,文本底部会添加一条下划线。
strikeOut是否带有删除线。设置为 true 时,文本中间会添加一条删除线。

QFont 包含了你在上一个表格中看到的所有属性,并提供了方法来获取和设置它们。

属性QFont 中的方法(示例)说明
familysetFamily("Microsoft YaHei")family()设置/获取字体家族。
pointSizesetPointSize(12)pointSize()设置/获取以磅 (Point) 为单位的大小。这是一个物理尺寸,与设备无关。
pixelSizesetPixelSize(16)pixelSize()设置/获取以像素 (Pixel) 为单位的大小。与设备相关,更直接控制屏幕显示高度。
weightsetWeight(QFont::Bold)weight()设置/获取粗细程度。Qt 提供了枚举值,如 QFont::NormalQFont::Bold
boldsetBold(true)bold()设置/获取是否加粗(weight的快捷方式)。
italicsetItalic(true)italic()设置/获取是否倾斜。
underlinesetUnderline(true)underline()设置/获取是否带下划线。
strikeOutsetStrikeOut(true)strikeOut()设置/获取是否带删除线。

7.1.示例1——.ui文件里面进行属性编辑

拖一个出来

现在修改一下

现在我们在右边就可以设置这个font属性

我们修改成下面这样子

我们发现

可以看到这个Qt designer会实时显示我们设置完的这个字体。

我们运行一下

7.2.示例2——使用代码来修改

我们创建一个新的项目

我们运行一下看看

接下来我们就来修改一下我们的代码

还是很简单的吧!!

八.QWidget的toolTip属性

ToolTip,中文常翻译为“工具提示”或“悬浮提示”,是图形用户界面(GUI)中一种非常常见的元素。

它指的是当用户将鼠标光标悬停在一个控件(如按钮、图标、输入框等)上一小段时间后,在旁边弹出的一个简短的文字说明框。

它的主要作用是:

  1. 解释功能:说明该控件的作用或功能。

  2. 提供额外信息:显示某些状态的额外信息,例如文件路径、缩略图预览、数据详情等。

  3. 指导用户:在不干扰主界面布局的情况下,为用户提供即时帮助。

我们看个例子

我们把鼠标光标悬停在这个确定按钮,不一会它就弹出了一个简短的文字说明框。

  • 相关API
方法名称说明
setToolTip(text)设置当鼠标悬停在控件上时显示的提示文本。文本内容由参数text指定。
setToolTipDuring(msec)设置提示文本显示的持续时间(单位:毫秒)。此方法需在设置提示文本后调用,用于覆盖系统默认的显示时长。

示例

我们创建一个新的项目

拖两个按钮出来,并且修改成下面这样子

它们的objectname分别是pushButton_yes和pushButton_no。

其实我们可以在这个qt designer这里进行设置

但是我们就不演示了,大家自己去琢磨一下

我们运行一下,

把鼠标光标移动到yes按钮上

3秒后消失

把鼠标光标移动到no按钮上

7秒后消失。

九.QWidget的focusPolicy属性

9.1.焦点

在一个图形用户界面中,同一时间可能有多个可以接收键盘输入的控件(如输入框、按钮、列表等)。

就像下面这样子

如果我们直接在键盘上面打几个字,那么这几个字会被写入哪一个输入框呢????

这个其实我们不清楚,但是我们清楚

界面上有一个输入框,此时必须要选中这个输入框,接下来键盘按键才会输入到输入框中。

如果选中的是别的控件,或者别的窗口,此时键盘的输入就不会进入到这个输入框中。

注意:

  • “选中” 这个词,在界面交互里就是 “获得焦点”

  • 当输入框“获得焦点”时,它才是键盘输入的接收者。

  • 当“别的控件”获得焦点时,键盘输入自然就归那个控件了(比如按钮获得焦点时,按空格或回车会触发点击)。

  • 当“别的窗口”被激活时,整个当前应用程序的窗口都可能失去焦点,键盘输入会发送到那个新窗口中去。


再举一个简单的例子来说明一下焦点

想象一下一个教室的场景:

  • 老师:就像键盘,是信息的发出者。

  • 学生们:就像屏幕上的各种控件(输入框、按钮等)。

  • “焦点”:就像老师用手指着的那个学生。全班只有一个学生会被老师指着

现在,我们来还原您描述的场景:

  1. 初始状态:老师(键盘)站在讲台上,还没有指定要谁回答问题。此时,她说话(敲键盘)全班都能听见,但可能没有特定目标(比如系统快捷键)。

  2. 选中输入框:老师说:“小明(输入框),请你来回答接下来的问题。” 然后老师就用手指向了小明。

    • 这一刻,小明获得了“焦点”

  3. 键盘输入:现在,老师开始念题目(敲击键盘字母)。因为她指着的是小明,所以所有的答案(输入的文本)都会由小明来接收和写下。其他同学(比如旁边的按钮、列表)虽然也在听,但他们知道现在不是自己答题的时候,所以不会行动。

  4. 切换焦点:老师又说:“小红(按钮),你觉得答案对吗?” 然后老师把手指从小明转向了小红。

    • “焦点”从小明转移到了小红

  5. 再次输入:老师现在念的题目(敲击键盘),就是针对小红的了。比如老师念“对还是错?”,小红就可以通过按回车键来表示“对”。而之前的小明(输入框)就不再接收输入文字了。

  • 为什么需要“焦点”?

计算机屏幕充满了可交互的元素:多个输入框、按钮、复选框、下拉菜单等等。如果没有“焦点”机制,键盘输入就会变得混乱无比,系统根本无法知道你想把文字输入到哪一个框里。

焦点解决了一个核心问题:在众多候选控件中,确定唯一一个当前接收键盘输入的对象。

9.2.相关API

获取焦点的方式

Qt::FocusPolicy 定义了以下几种行为,你可以把它们看作是控件获取焦点的“资格”或“方式”。

1. Qt::NoFocus

行为:控件完全不会接收键盘焦点。无论用户如何操作(Tab 键、点击、滚轮),焦点都不会落到这个控件上。

2. Qt::TabFocus

行为:控件只能通过用户按下 Tab 键 来获得焦点。用鼠标点击它不会使其获得焦点。

3. Qt::ClickFocus

行为:控件只能通过用户用鼠标点击它来获得焦点。使用 Tab 键切换焦点时会跳过它。

4. Qt::StrongFocus(默认值)

行为:这是最常用的策略。控件既可以通过 Tab 键,也可以通过鼠标点击来获得焦点。它是 TabFocus 和 ClickFocus 的组合。

5. Qt::WheelFocus

行为:在 StrongFocus 的基础上,增加了一种获取焦点的方式:当鼠标滚轮在控件上滚动时,控件也会获得焦点。这是对 StrongFocus 的扩展,而不是一个独立的类型。

我们这个基本不会使用,因为它的行为可能出乎用户意料:用户可能只是想滚动页面内容,却不小心把焦点转移到了另一个控件上,这可能会打断当前的操作流。

相关API

方法描述
Qt::FocusPolicy focusPolicy()获取该widget的焦点策略
void setFocusPolicy(Qt::FocusPolicy policy)设置widget的焦点策略

焦点策略就是我们上面举的那5个

  • 1. Qt::NoFocus
  • 2. Qt::TabFocus
  • 3. Qt::ClickFocus
  • 4. Qt::StrongFocus(默认值)
  • 5. Qt::WheelFocus

9.3.示例1——使用.ui文件进行修改

我们创建一个新项目

拖4个输入框出来

修改四个输⼊框的 focusPolicy 属性为Qt::StrongFocus (默认取值,⼀般不⽤额外修改)

此时运⾏程序,可以看到,使⽤⿏标单击,就可以移动光标所在输⼊框。从⽽接下来的输⼊就是针对这个获取焦点的输⼊框展开的了。

当前是第一个输入框获得了焦点,现在我们鼠标点击哪一个输入框,哪一个输入框就会获得焦点

当然,我们现在按下Tab键,也能实现焦点从第1个->第2个->第3个->第4个->第1个……这样子循环式的切换焦点

也就是说鼠标/Tab能切换的焦点如下图所示


 现在我们 修改第⼆个输⼊框的选中. focusPolicy 为Qt::NoFocus ,

则第⼆个输⼊框不会被tab/⿏标左键,此时这个输⼊框也就⽆法输⼊内容了。

鼠标/Tab选中情况


接下来我们修改第⼆个输⼊框focusPolicy 为Qt::TabFocus 

则现在第2个输入框只能通过tab选中,⽆法通过⿏标选中.

鼠标能选中的输入框

Tab能选中的输入框


修改第⼆个输⼊框focusPolicy 为Qt::ClickFocus ,则只能通过⿏标选中,⽆法通过tab选中。

 鼠标能选中的输入框

Tab能选中的输入框

9.4.示例2——使用代码进行修改

当然,我们也是可以通过代码来实现上面那个场景的。

我们创建一个新项目

可以看到,默认就是StrongFocus。

鼠标/Tab能切换的焦点如下图所示


 现在我们 修改第⼆个输⼊框的选中. focusPolicy 为Qt::NoFocus ,

 则第⼆个输⼊框不会被tab/⿏标左键,此时这个输⼊框也就⽆法输⼊内容了。

鼠标/Tab选中情况


 修改第⼆个输⼊框focusPolicy 为Qt::ClickFocus ,则只能通过⿏标选中,⽆法通过tab选中。

 鼠标能选中的输入框

Tab能选中的输入框


 接下来我们修改第⼆个输⼊框focusPolicy 为Qt::TabFocus 

则现在第2个输入框只能通过tab选中,⽆法通过⿏标选中.

鼠标能选中的输入框

Tab能选中的输入框

十.QWidget的styleSheet属性

其实这个本质是通过CSS设置widget的样式.

styleSheet的意思是样式表


你可以把 CSS(层叠样式表) 想象成给界面穿衣服、化妆的工具。

它最初是为网页设计的,用来告诉浏览器:“这个标题要用蓝色、那个按钮要圆角、那段文字要放大...” 这样,网页就能变得丰富多彩、非常好看。

Qt(就是制作我们现在这个窗口的程序框架) 觉得这个主意棒极了!心想:“我的软件界面也不能太丑啊!”

于是,Qt 就 借鉴了 CSS 的idea,自己做了一套类似的规则,叫做 QSS(Qt Style Sheet

  • 怎么用呢? 很简单!几乎每个窗口部件(比如按钮、标签、输入框)都有一个叫 styleSheet 的属性,就像它的“穿衣指南”。我们直接在里面写样式规则就行了。

  • 效果怎么样? 虽然 QSS 没有网页 CSS 那么强大(有些特别炫酷的效果做不到),但绝对够用!用它来改变颜色、字体、大小、间距、背景、边框等等,都是小菜一碟,足以让我们的软件界面焕然一新。

简单来说:

CSS 是给网页美容的,QSS 是给 Qt 软件界面美容的。它们是一对很像的“亲兄弟”,学会了基本思路,两边都能用上。

10.1.示例1——通过.ui文件进行修改

我们创建一个新项目

这个时候我们在右边可以看到

这个时候,我们可以点击这一栏,就会弹出一个弹窗

或者我们通过下面这个方式,也能

也可以弹出一个弹窗

召唤出这个弹窗之后,我们把下面这段代码粘贴进去即可

font-family: '微软雅黑';
font-size: 30px;
font-style: italic;
text-decoration: underline;
color: green;

和 CSS 类似. QSS 设置的样式也是 键值对 的格式,键和值之间使用 :分隔,键值对和键值对之间通过;分割。

点击确定之后,我们的Qt Designer也是实时显示了这个字体的样式

我们运行一下,和预览的效果是一样的。

如果说想要了解更多QSS的样式,可以直接去Qt文档里面搜索Qt Style Sheet。

10.2.示例2——实现切换夜间模式

我们创建一个新项目

在界⾯上创建⼀个多⾏输⼊框(TextEdit)和两个按钮(pushButton)

它们的objectname分别如下

  • 多⾏输⼊框:textEdit
  • 日间模式:pushButton_sun
  • 夜间模式:pushButton_moon

接下来来给按钮配置槽函数

跳转到下面,并修改代码

接着也是一样的,我们给这个夜间模式配置一下槽函数

我们点击运行

往里面输入很多文字

点击夜间模式

我们点击日间模式

怎么样??是不是实现了日间/夜间模式的切换。

但是我们仔细观看一下,这个日间模式和我们程序刚启动时的演示很不一样啊。我们的日间模式太白了。

10.3.关于计算机中的颜色表示

在生活中,我们能用“中国红”、“天蓝色”这样的词语来描述颜色。但在计算机的世界里,它需要一种更精确、可量化的方式来“理解”和“显示”颜色。

1. 命名的颜色 (Named Colors)

为了方便,CSS(网页样式)和QSS(Qt样式表)等语言预定义了一系列颜色名称,比如直接使用 white(白色)、black(黑色)、red(红色)、green(绿色)、blue(蓝色)、yellow(黄色)等。这就像给常用的颜色起了个小名,简单直观。

但就像口红色号有无数种一样,命名颜色只是冰山一角,我们能定义的颜色远不止这些。计算机能够显示的颜色数量极其庞大,理论上可达 1600多万种

2. RGB 颜色模型:色彩的基石

计算机屏幕是如何创造出这亿万种色彩的呢?答案就是 RGB 颜色模型

  • R 代表 Red(红色)

  • G 代表 Green(绿色)

  • B 代表 Blue(蓝色)

这三种颜色被称为“光的三原色”。通过将它们以不同的亮度比例进行叠加混合,就能模拟出人眼所能感知的绝大多数颜色。这就像是调色,红、绿、蓝就是你的三罐基础颜料。

你可以把你的电脑屏幕想象成由无数个微小的“灯泡”组成的矩阵,每个“灯泡”就是一个像素。而每个像素,其实又是由一个红色、一个绿色和一个蓝色的微型子像素(sub-pixel)构成的。

3. 如何量化颜色?—— 字节与数值

计算机是数字的世界,颜色也需要用数字来表示。通常,每个颜色通道(R, G, B)的亮度等级用一个字节(Byte)来存储。

  • 一个字节有 8 个比特(bit),其表示的范围是 0 到 255(用十六进制表示就是 0x00 到 0xFF)。

  • 0 表示这个颜色成分完全不发光(最暗)。

  • 255 表示这个颜色成分亮度达到最亮(拉满)。

通过调节这三个通道的数值,我们就可以得到任何想要的颜色。

举个例子:

  • rgb(255, 0, 0):红色拉满(255),绿色和蓝色不发光(0)。所以这是最纯的红色

  • rgb(0, 255, 0):这是最纯的绿色

  • rgb(0, 0, 255):这是最纯的蓝色

  • rgb(255, 255, 255):红、绿、蓝全部拉满,三色光混合就成了白色

  • rgb(0, 0, 0):所有颜色都不发光,就是黑色

  • rgb(255, 0, 255):红色拉满,蓝色拉满,绿色不发光。红蓝混合,得到的就是洋红色(Magenta)

4. 十六进制表示法

除了 rgb(r, g, b) 的格式,在CSS和QSS中更常见的是十六进制表示法,格式为 #RRGGBB

  • 它同样代表红、绿、蓝三个通道。

  • #FF0000:前两位 FF(十进制255)是红色,中间00是绿色,后两位00是蓝色。所以这是红色

  • #00FF00绿色

  • #0000FF蓝色

  • #FFFFFF白色

  • #000000黑色

  • #FF00FF:红色FF + 蓝色FF = 洋红色。这和你例子中的 rgb(255, 0, 255) 是完全相同的颜色!

5.十六进制颜色值的缩写规则

在CSS或QSS中,当用一个十六进制数表示颜色时,标准的格式是6位,即 #RRGGBB

但为了书写简洁,如果 RRGGBB 这三部分的每两位都各自相同(即 #RRGGBB样式),则可以采用3位的缩写格式 #RGB

这个缩写的规则是:将一位数字或字母复制一次,扩展回两位。

让我们来拆解您提到的例子,看看它们是如何从3位扩展为6位的:

  1. #F00

    • F 代表红色的分量 RRF 扩展为 FF

    • 0 代表绿色的分量 GG0 扩展为 00

    • 0 代表蓝色的分量 BB0 扩展为 00

    • 所以,#F00 = #FF0000 → 这是最纯的红色

  2. #0F0

    • 0 代表红色的分量 RR0 扩展为 00

    • F 代表绿色的分量 GGF 扩展为 FF

    • 0 代表蓝色的分量 BB0 扩展为 00

    • 所以,#0F0 = #00FF00 → 这是最纯的绿色

  3. #6FA (这个例子尤其好,因为它包含了数字和字母)

    • 6 代表红色的分量 RR6 扩展为 66

    • F 代表绿色的分量 GGF 扩展为 FF

    • A 代表蓝色的分量 BBA 扩展为 AA

    • 所以,#6FA = #66FFAA → 这是一个比较明亮的青绿色

注意:大小写不敏感#ffcc00 和 #FFCC00 以及 #fc0 和 #FC0 都是等价的。但通常为了易读,大写更为常见。


10.3.1.RGB颜色模型示例

我们把代码修改成下面这样子

运行一下

我们输入一些文本

点击夜间模式

点击日间模式

10.3.2.十六进制颜色值示例

我们可以修改一下我们的代码,采用上面那个十六进制颜色值的方法来设置

#333

这是一个非常经典的深灰色。

  • 扩展过程

    • 第一位 3 代表红色分量 RR,扩展为 33

    • 第二位 3 代表绿色分量 GG,扩展为 33

    • 第三位 3 代表蓝色分量 BB,扩展为 33

    • 所以,#333 的完整形式是 #333333

  • 数值含义

    • 在十六进制中,33 是一个很小的值(十进制为 51)。

    • 这意味着红、绿、蓝三个光源都只以非常低的亮度(51/255 ≈ 20% 的亮度)在发光。

    • 因为三个通道的亮度完全一致且都很低,混合出来的就是一种非常深的灰色,非常接近黑色,但又比纯黑(#000)要亮一些。

#fff

这是纯白色。

  • 扩展过程

    • 第一位 f 代表红色分量 RR,扩展为 ff

    • 第二位 f 代表绿色分量 GG,扩展为 ff

    • 第三位 f 代表蓝色分量 BB,扩展为 ff

    • 所以,#fff 的完整形式是 #ffffff

  • 数值含义

    • 在十六进制中,f 是最大值,代表十进制中的 255。

    • ff 意味着红色、绿色、蓝色三个光源都以100%的最大亮度在发光。

    • 在光学中,所有颜色的光叠加在一起就会产生白色。因此,#fff 就是最纯、最亮的白色。

运行一下

我们输入一些文本

点击夜间模式

点击日间模式

怎么样,还行吗?

http://www.dtcms.com/a/394040.html

相关文章:

  • 9.21关于大模型推理未来的思考
  • 如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘uvicorn’ 问题
  • 变分自编码器(VAE):生成模型的另一条技术路线
  • 【LVS入门宝典】LVS NAT模式实战指南:ip_forward、iptables与SNAT、DNAT规则配置详解
  • 【Android】BottomSheet的三种使用
  • Spring MVC 九大组件源码深度剖析(八):RequestToViewNameTranslator - 视图名转换的奥秘
  • 在Linux环境下安装和卸载DMETL5数据迁移工具
  • 《计算》第五六章读书笔记
  • daily notes[47]
  • 模电基础:放大电路的分析方法---图解法
  • Windows10系统Web UI自动化测试学习系列1--介绍(序章-万事开头难)
  • 安装vllm的艰苦过程
  • 探索 Event 框架实战指南:微服务系统中的事件驱动通信:
  • FPGA超高速接口GTP_GTY_GTX使用说明
  • Blender常用第三方插件总结
  • Kurt-Blender零基础教程:第2章:建模篇——第3节:陈列/父子级/蒙皮/置换修改器与小狐狸角色建模
  • npm启动项目报错“无法加载文件……”
  • 从 0 到 1 精通 Nacos:服务发现与配置中心的实战指南
  • 基于DrissionPage的趣易百影院数据采集实战指南
  • github十大开源FPGA项目
  • R语言 csv新增一列 dplyr操作
  • IDEA创建Module子项目后,只有一个普通的文件夹
  • 支持向量机深度解析:从数学原理到工程实践的完整指南
  • 2025华为杯研究生数学建模竞赛B题及求解思路
  • 三星CIS全球产能布局解析:本土根基、海外扩张与策略雄心
  • js集装箱号校验算法
  • 【机器学习】最优传输(OT)和 KL散度的区别
  • 推荐一个随机生成图片的网站: Lorem Picsum
  • APE自动化提示词工程
  • 探究某黄鱼x-sign生成算法——终极篇