QT常用控件(一)

1:控件概述

1.1:控件体系的发展

2:QWidget核心属性

2.1:核心属性预览

2.2:enabled

2.2.1:代码1

widget.cpp

2.2.2:代码2

widget.h

widget.cpp

2.3:geometry

2.3.1:代码1

widget.h

widget.cpp

2.3.2:代码2

widget.h

widget.cpp

[2.4:window frame的影响](#2.4:window frame的影响)

代码示例

widget.cpp

2.5:windowTitle

代码示例

widget.cpp

2.6:windowIcon

代码1

widget.cpp

代码2

widget.cpp

2.7:Qt中的qrc机制

2.7.1:使用方式

First

Second

Third

widget.cpp

2.7.2:qrc机制的优缺点

3:QWidget核心属性(2)

3.1:windowOpacity

代码示例

widget.cpp

3.2:cursor

代码1

widget.cpp

代码2

widget.cpp

3.3:font

代码示例

widget.cpp

3.4:toolTip

代码示例

widget.cpp

3.5:focusPolicy

3.6:stylesheet

代码示例1

代码示例2

widget.cpp


1:控件概述

  • Widget 是 Qt 中的核⼼概念. 英文原意是 "小部件", 我们此处也把它翻译为 "控件" . 控件是构成⼀个图形化界⾯的基本要素.
  • 像上述示例中的, 按钮, 列表视图, 树形视图, 单行输入框, 多行输入框, 滚动条, 下拉框等, 都可以称为 "控件"
  • Qt 作为⼀个成熟的 GUI 开发框架, 内置了大量的常⽤控件. 这⼀点在 Qt Designer 中就可以看到端倪.并且 Qt 也提供了 "⾃定义控件" 的能⼒, 可以让程序猿在现有控件不能满⾜需求的时候, 对现有控件做出扩展, 或者⼿搓出新的控件

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

1.1:控件体系的发展

控件是 GUI 开发中的通⽤概念. 不仅仅局限在 Qt 中.
第⼀阶段:

  • 完全没有控件. 此时需要通过⼀些绘图 API 手动的绘制出按钮或者输⼊框等内容, 代码编写繁琐. 例如⽂曲星的 Lava 平台开发.


第⼆阶段:

  • 只包含粗略的控件. 只是提供了按钮, 输⼊框, 单选框, 复选框等最常⽤的控件. 例如 html 的原⽣控件.


第三阶段:

  • 更完整的控件体系, 基本可以覆盖到 GUI 开发中的大部分场景. 例如早期的 MFC, VB, C++ Builder, Qt, Delphi, 后来的 Android SDK, Java FX, 前端的各种 UI 库等.

上图是前端中的 Element-ui 中的控件概览, ⽆论是丰富程度还是颜值, 都比Qt ⾃带的控件更胜⼀筹.

2:QWidget核心属性

  • 在 Qt 中, 使⽤ QWidget 类表示 "控件". 像按钮, 视图, 输入框, 滚动条等具体的控件类, 都是继承自 QWidget.可以说, QWidget 中就包含了 Qt 整个控件体系中, 通⽤的部分.
  • 在 Qt Designer 中, 随便拖⼀个控件过来, 选中该控件, 即可在右下⽅看到 QWidget 中的属性

2.1:核心属性预览

下列表格列出了 QWidget 中的属性及其作⽤.

|---------------|-----------------------------------------------------------------|
| 属性 | 作⽤ |
| enabled | 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤. |
| geometry | 位置和尺⼨. 包含 x, y, width, height 四个部分. 其中坐标是以⽗元素为参考进行设置的. |
| windowTitle | 设置 widget 标题. |
| windowIcon | 设置 widget 图标. |
| windowOpacity | 设置 widget 透明度. |
| cursor | ⿏标悬停时显示的图标形状. 是普通箭头, 还是沙漏, 还是⼗字等形状. 在 Qt Designer 界⾯中可以清楚看到可选项. |
| font | 字体相关属性. 涉及到字体家族, 字体大小, 粗体, 斜体, 下划线等等样式. |
| toolTip | ⿏标悬停在 widget 上会在状态栏中显示的提示信息. |
| toolTipDuring | toolTip 显示的持续时间. |
| statusTip | Widget 状态发⽣改变时显⽰的提⽰信息(⽐如按钮被按下等). |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |

2.2:enabled

|------------|---------------------------------|
| API | 说明 |
| isEnabled | 获取到控件的可用状态. |
| setEnabled | 设置控件是否可使用. true表示可用, false表示禁用. |

  • 所谓 "禁用" 指的是该控件不能接收任何用户的输入事件, 并且外观上往往是灰色的.
  • 如果⼀个 widget 被禁用, 则该 widget 的⼦元素也被禁用.

2.2.1:代码1

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton * MyBtn = new QPushButton(this);
    MyBtn->setText("按钮被禁用");
    MyBtn->setEnabled(false);
}

Widget::~Widget()
{
    delete ui;
}


运⾏程序, 可以看到按钮处于灰⾊状态, ⽆法被点击.

2.2.2:代码2

QObject的objectName属性介绍

  • QObject是QWidget的父类,里面最主要的属性是objectName.
  • 在⼀个 Qt 程序中, objectName 相当于对象的身份标识, 彼此之间不能重复.
  • 在使用Qt Designer 时, 尤其是界面上存在多个 widget 的时候, 可以通过 objectName 获 取到指定的 widget对象.
  • Qt Designer 生成的 ui ⽂件, 本身是 xml 格式的. qmake 会把这个 xml 文件转换成 C++ 的 .h文件(这个⽂件生成在 build 目录中), 构成⼀个 ui_widget 类.
  • 每个 widget 的 objectName 最终就会成为 ui_widget 类的属性名字.
    • 最终这个类的实例, 就是 Ui::Widget *ui , 因此就可以通过形如 ui->pushButton 或
      者 ui->pushButton_2 这样的代码获取到界⾯上的 widget 对象了.
      **当前自动生成的objectname有些小规律,这个名字就是根据控件的类型 + 下划线 + 数字来生成的.**很明显,以数字的方式命名,不是一个好的编程习惯,我们也可以自行修改objectname,在这里由于就两个控件,博主就不修改啦.
widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    qDebug() << "按下按钮";
}


void Widget::on_pushButton_2_clicked()
{
    //获取第一个按钮的可用状态
    bool enable = ui->pushButton->isEnabled();
    if(enable)
    {
        ui->pushButton->setEnabled(false);
    }
    else
    {
        ui->pushButton->setEnabled(true);
    }
}
  • 运⾏程序, 可以看到, 初始情况下, 上⾯的按钮是可用状态.
  • 点击下方按钮, 即可使上方按钮被禁⽤; 再次点击下⽅按钮, 上方按钮就会解除禁用. (禁用状态的按钮为灰⾊, 且不可点击).
  • 在 Qt Designer 中创建按钮的时候, 可以设置按钮的初始状态是 "可⽤" 还是 "禁⽤" .
  • 如果把 enabled 这⼀列的对钩去掉, 则按钮的初始状态就是 "禁⽤" 状态.

2.3:geometry

位置和尺⼨. 其实是四个属性的统称:

  • x:横坐标
  • y:纵坐标
  • width:宽度
  • height:高度
  • x与y表示左上角的位置.
  • 而width和height表示当前控件的位置和尺寸.


但是实际开发中, 我们并不会直接使⽤这⼏个属性, ⽽是通过⼀系列封装的⽅法来获取/修改.
对于 Qt 的坐标系, 不要忘记是⼀个 "左⼿坐标系". 其中坐标系的原点是当前元素的⽗元素的左上⻆.

|---------------------------------------------------------------------|-----------------------------------------------------------------------|
| API | 说明 |
| geometry | 获取到控件的位置和尺寸. 返回结果是⼀个 QRect, 包含了 x, y, width, height. 其中 x, y 是左上⻆的坐标. |
| setGeometry(QRect) setGeometry(int x, int y, int width, int height) | 设置控件的位置和尺寸. 可以直接设置⼀个 QRect, 也可以分四个属性单独设置 |

QRect是一个矩形,QPoint表示一个点,这两个属于是小对象,里面的属性非常少,占用空间也小

在C++中使用上面对象,通常就会按照值传递的方式来传递参数了.

2.3.1:代码1

控制按钮的位置

  • 在界⾯中拖五个按钮.
  • 五个按钮的 objectName 分别为 pushButton_target , pushButton_up ,pushButton_down , pushButton_left , pushButton_right
  • 五个按钮的初始位置和大小都随意.
widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_up_clicked();

    void on_pushButton_left_clicked();

    void on_pushButton_right_clicked();

    void on_pushButton_down_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QRect>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_up_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    rect.setY(rect.y() - 10);
    //设置控件的位置
    ui->pushButton_target->setGeometry(rect);
}


void Widget::on_pushButton_left_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    rect.setX(rect.x() - 10);
    //设置控件的位置
    ui->pushButton_target->setGeometry(rect);
}


void Widget::on_pushButton_right_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    rect.setX(rect.x() + 10);
    //设置控件的位置
    ui->pushButton_target->setGeometry(rect);
}


void Widget::on_pushButton_down_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    rect.setY(rect.y() + 10);
    //设置控件的位置
    ui->pushButton_target->setGeometry(rect);
}

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

  • 上述代码中我们是直接设置的 QRect 中的 x, y . 实际上 QRect 内部是存储了左上和右下两个点的坐标, 再通过这两个点的坐标差值计算长宽. 单纯修改左上坐标就会引起整个矩形的长宽发生改变.
  • 如果需要实现平移的效果(宽度和高度不变,整个按钮的位置都发生改变),那么就不再修改QRect,而是通过QRect基于setGeometry第二个版本的函数重新设置位置即可.
    如果想让整个按钮都移动, 可以改成下列代码:
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QRect>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_up_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    //rect.setY(rect.y() - 10);
    //设置控件的位置
    ui->pushButton_target->setGeometry(rect.x(),rect.y() - 10,rect.width(),rect.height());
}


void Widget::on_pushButton_left_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    //rect.setX(rect.x() - 10);
    //设置控件的位置
    ui->pushButton_target->setGeometry(rect.x() - 10,rect.y(),rect.width(),rect.height());
}


void Widget::on_pushButton_right_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    //rect.setX(rect.x() + 10);
    //设置控件的位置
    ui->pushButton_target->setGeometry(rect.x() + 10,rect.y(),rect.width(),rect.height());
}


void Widget::on_pushButton_down_clicked()
{
    //获取控件的位置
    QRect rect = ui->pushButton_target->geometry();
    //rect.setY(rect.y() + 10);
    //设置控件的位置
     ui->pushButton_target->setGeometry(rect.x(),rect.y() + 10,rect.width(),rect.height());

}

2.3.2:代码2

往界面上拖拽两个按钮和⼀个 Label.
Label 的 objectName 为 pushButton_accept 和 pushButton_reject , label 的
objectName 为 label

widget.h
cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_accept_clicked();

    void on_pushButton_reject_pressed();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
     // 设置随机种子. 使用时间戳作为随机种子.
    srand(time(0));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_accept_clicked()
{
      ui->label->setText("女神女神, 快来嘴儿一个~, mua~");
}


void Widget::on_pushButton_reject_pressed()
{

    // 如果女神点击了这个按钮, 就把这个按钮给挪走.
    // 可以通过生成随机数的方式, 来确定按钮的新的位置.
    
    //获取当前程序窗口的尺寸
    int width = this->geometry().width();
    int height = this->geometry().height();
    // 重新生成按钮的位置.
    /*
    rand()是C标准库的函数,能够生成一个随机的数,这个数字范围很大
    rand() % 100 + 1------>[1,100] 
    */
    int x = rand() % width;
    int y = rand() % height;
    ui->pushButton_reject->move(x,y);
}


运⾏程序, 可以看到, 当点击 "残忍拒绝" 时, 按钮就跑了.

  • 上述代码使用的是 pressed,鼠标按下事件. 如果使用 mouseMoveEvent, 会更狠⼀些, 只要鼠标移动过来, 按钮就跑了.
  • 对应的代码更麻烦⼀些 (需要⾃定义类继承⾃ QPushButton, 重写 mouseMoveEvent ⽅法). 此处暂时不展开.

2.4:window frame的影响

  • 如果 widget 作为⼀个窗口 (带有标题栏, 最小化, 最大化, 关闭按钮), 那么在计算尺寸和坐标的时候就有两种算法. 包含 window frame 和 不包含 window frame.
  • 其中 x(), y(), frameGeometry(), pos(), move() 都是按照包含 window frame的⽅式来计算的
  • 其中geometry(), width(), height(), rect(), size() 则是按照不包含 window frame 的⽅式来计算的.
  • 当然, 如果⼀个不是作为窗⼝的 widget , 上述两类⽅式得到的结果是⼀致的.


相关 API

|-----------------|---------------------------------------------------------------------------------------------------|
| API | 说明 |
| x() | 获取横坐标 计算时包含 window frame |
| y() | 获取纵坐标 计算时包含 window frame |
| pos() | 返回 QPoint 对象, ⾥⾯包含 x(), y(), setX(), setY() 等方法. 计算时包含 window frame |
| frameSize() | 返回 QSize 对象, ⾥⾯包含 width(), height(), setWidth(), setHeight() 等方法.计算时包含 window frame |
| frameGeometry() | 返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取 x, y, width, size. 计算时包含 window frame 对象. |
| width() | 获取宽度 计算时 不包含 window frame |
| height() | 获取⾼度 计算时 不包含 window frame |
| size() | 返回 QSize 对象, ⾥⾯包含 width(), height(), setWidth(), setHeight() 等⽅法.计算时 不包含 window frame |
| rect() | 返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取并设置 x, y, width, size.计算时 不包含window frame 对象. |
| geometry() | 返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取 x, y, width, size. 计算时 不包含windowframe 对象. |
| setGeometry() | 直接设置窗口的位置和尺寸. 可以设置 x, y, width, height, 或者 QRect 对象. 计算时 不包含windowframe 对象. |

代码示例

感受 geometry 和 frameGeometry 的区别.

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QRect>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QRect rect1 = this->geometry();
    QRect rect2 = this->frameGeometry();
    qDebug()<<rect1;
    qDebug()<<rect2;
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    QRect rect1 = this->geometry();
    QRect rect2 = this->frameGeometry();
    qDebug()<<rect1;
    qDebug()<<rect2;
}


执⾏程序, 可以看到, 构造函数中, 打印出的 geometry 和 frameGeometry 是相同的. 但是在点击按钮时, 打印的 geometry 和 frameGeometry 则存在差异.

  • 在构造⽅法中, Widget 刚刚创建出来, 还没有加入到对象树中. 此时也就不具备 Window frame,因此也看不到Window frame的影响.
  • 在按钮的 slot 函数中, 由于用户点击的时候, 对象树已经构造好了, 此时 Widget 已经具备了Window frame, 因此在位置和尺寸上均出现了差异.

如果把上述代码修改成打印 pushButton 的 geometry 和 frameGeometry , 结果就是完全相 同的. 因为 pushButton 并非是⼀个窗口.

2.5:windowTitle

|---------------------------------------|------------|
| API | 说明 |
| windowTitle() | 获取到控件的窗口标题 |
| setWindowTitle(const QString& title) | 设置控件的窗口标题. |

  • 当前的windowsTitle属性,是从属于QWidget.
  • windowTitle属性,只能针对顶层窗口这样的QWidget才有效.
  • 如果是子widget, 这个操作⽆任何效果.

代码示例

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QWidget>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("这是标题");
}

Widget::~Widget()
{
    delete ui;
}

2.6:windowIcon

|------------------------------------|--------------------------|
| API | 说明 |
| windowIcon() | 获取到控件的窗⼝图标. 返回 QIcon 对象. |
| setWindowIcon(const QIcon& icon | 设置控件的窗⼝图标. |

  • windowIcon我们一般使用的很少,主要使用setWindowIcon.
  • 这两api类似于windowTitle,只能针对于顶层窗口使用.

代码1

设置窗⼝图标
先在 D 盘中放⼀个图⽚, 名字为cat.png

修改 widget.cpp

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QIcon>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    QIcon icon("D:/cat.png");
    this->setWindowIcon(icon);
}

Widget::~Widget()
{
    delete ui;
}
  • 之前推荐使用堆来创建对象,主要是因为要确保当前控件的生命周期是足够的,要通过Qt对象树来释放对象.
  • Qicon自身是一个比较小的对象,创建出来之后,就是要设置到某个QWidget里面,QIcon对象本身释放不释放,不影响图标最终的显示,Qicon本身也不支持对象树,无法给他执行父对象.
    运行程序, 可以看到窗口图标已经成为上述图片
  • 实际开发中, 我们⼀般不会在代码中通过绝对路径引入图片. 因为我们⽆法保证程序发布后, 用户的电脑上也有同样的路径
  • 如果使用相对路径, 则需要确保代码中的相对路径写法和图⽚实际所在的路径匹配 (比如代码中写作 "./image/cat.png", 就需要在当前⼯作⽬录中创建 image ⽬录, 并把 cat.png 放进去).
  • 相对路径: 以 . (表⽰当前路径) 或者 以 .. (表⽰当前路径上级路径) 开头的路径. 其中 . 经常也会省略. 相对路径的前提是需要明确 "当前工作目录".
  • 绝对路径: 以盘符(windows)或者以 / (Linux) 开头的路径.
    对于 Qt 程序来说, 当前⼯作⽬录可能是变化的. ⽐如通过 Qt Creator 运⾏的程序, 当前⼯作⽬录是项目的构建目录; 直接双击 exe 运⾏, 工作目录则是 exe 所在目录.所谓构建⽬录, 是和 Qt 项⽬并列的, 专门⽤来放⽣成的临时⽂件和最终 exe 的目录.

代码2

获取当前的⼯作⽬录

在界⾯上创建⼀个⽐较大的 label, 确保能把路径显示完整. objectName 使⽤默认的 label 即可.

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDir>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QString CurrentPath = QDir::currentPath();
    ui->label->setText(CurrentPath);
}

Widget::~Widget()
{
    delete ui;
}

直接在 Qt Creator 中执⾏程序, 可以看到当前⼯作⽬录是项⽬的构建⽬录.

进入上述构建目录, 把⾥⾯的 exe 拷⻉到其他目录中, 比如 D: 中. 再次执行程序, 可以看到当前⼯作目录已经发⽣改变.


要想直接能双击 exe 运⾏, 需要先把 Qt 的路径添加到 path 环境变量中, 否则会提⽰找不到动态库.在最开始搭建开发环境的时候已经操作过, 此处不再赘述.
注意, 上述 构建目录, 是随时可删除的. 比如点击菜单栏中的 "构建" -> "清理项目" , 就会把这个目录中的内容清空掉. 因此如果我们把图片文件放到构建⽬录中, 可能在不小心删除后就丢失了. 我们还是希望能够把图片和源代码放到⼀起, 并且使我们的程序无论到任何位置中都能正确使用图片.
Qt 使用qrc 机制帮我们自动完成了上述工作, 更⽅便的来管理项目依赖的静态资源.

2.7:Qt中的qrc机制

  • qrc文件是⼀种XML格式的资源配置⽂件, 它用XML记录硬盘上的⽂件和对应的随意指定的资 源名称. 应用程序通过资源名称来访问这些资源.
  • 在Qt开发中, 可以通过将资源文件添加到项目中来方便地访问和管理这些资源. 这些资源文件可以位于qrc文件所在目录的同级或其子目录下.
  • 在构建程序的过程中, Qt 会把资源⽂件的⼆进制数据转成 cpp 代码, 编译到 exe 中. 从⽽使依赖的资源变得 "路径无关".
  • 这种资源管理机制并非Qt 独有, 很多开发框架都有类似的机制. 例如 Android 的 Resources和 AssetManager 也是类似的效果.

2.7.1:使用方式

First

在项目中创建一个qrc文件,文件名不要带中文和特殊符号.

Second

先添加一个前缀,前缀的名字改成 / 即可

  • 前缀可以理解成虚拟目录,这个目录在我们的电脑上并没有真实存在,是Qt自己抽象出来的.
  • qrc机制本质就是把图片的二进制数据转换成C++代码(最终就会在代码看到很大的char 数组,里面就是图片的二进制数据).
  • 为了方便Qt代码访问到这个图片,Qt就抽象出了虚拟的目录.
Third

将图片导入到资源文件中.

这个按钮在创建前缀之前是禁用的,创建好前缀后就能够使用了,添加的文件就是添加到前缀下面的,点击添加文件得到的目录就是当前代码所在的目录.

导入图片的时候,需要确保导入的图片在resource.qrc文件的同级目录,或者同级目录的子目录里.因此就可以把需要导入的图片拷贝到项目目录中即可~

看到这个效果,说明导入成功了,创建的前缀叫啥名字,代码中就叫啥名字.并且当代码中需要访问qrc中管理的文件时,就需要在路径上带有:前缀~~

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QIcon>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QIcon icon(":/Cat.png");
    this->setWindowIcon(icon);
}

Widget::~Widget()
{
    delete ui;
}

当Qt项目进行编译的时候,这个cpp文件就被一起编译到了exe中,当exe程序运行的时候,上述图片的数据也就被加载到了内存中.

2.7.2:qrc机制的优缺点

  • 优点: 确保了图片, 字体, 声⾳等资源能够真正做到 "目录无关", ⽆论如何都不会出现资源丢失的情况.

  • 缺点: 不适合管理体积大的资源. 如果资源比较大(比如是几个 MB 的⽂件), 或者资源特别多,生成的最终的 exe 体积就会比较大, 程序运行消耗的内存也会增大, 程序编译的时间也会显著增加.

3:QWidget核心属性(2)

3.1:windowOpacity

|----------------------------|-----------------------------------------------------------------|
| API | 说明 |
| windowOpacity() | 获取到控件的不透明数值. 返回 float, 取值为 0.0 -> 1.0 其中 0.0表示全透明, 1.0表示完全不透明. |
| setWindowOpacity(flo at n) | 设置控件的不透明数值. |

代码示例

在界面上拖放两个按钮, 分别⽤来增加不透明度和减少不透明度.objectName 分别为 pushButton_add 和 pushButton_sub.

  • 编写 wdiget.cpp, 编写两个按钮的 slot 函数.
  • 点击 pushButton_sub 会减少不透明度, 也就是窗⼝越来越透明.
  • 点击 pushButton_add 会增加不透明度, 窗口会逐渐恢复
widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_add_clicked()
{
    float Opacity = this->windowOpacity();
    //0.0表示完全透明,1.0表示完全不透明
    if(Opacity >= 1.0)
        return;
    qDebug()<< Opacity;
    Opacity += 0.1;
    this->setWindowOpacity(Opacity);
}


void Widget::on_pushButton_sub_clicked()
{
    float Opacity = this->windowOpacity();
    //0.0表示完全透明,1.0表示完全不透明
    if(Opacity <= 0.0)
        return;
    qDebug()<< Opacity;
    Opacity -= 0.1;
    this->setWindowOpacity(Opacity);
}


执行程序, 可以看到, 点击了几下 - 之后, 就可以透过窗口看到后⾯的蓝色背景了. 点击 + 又会逐渐恢复.同时控制台中也可以看到 opacity 数值的变化.

注意, C++ 中 float 类型遵守 IEEE 754 标准, 因此在进⾏运算的时候会有⼀定的精度误差. 因此 1 - 0.1 的数值并非是 0.9.

3.2:cursor

|---------------------------------------------------------------|------------------------------------------------------------------------|
| API | 说明 |
| cursor() | 获取到当前 widget 的 cursor 属性, 返回 QCursor 对象.当⿏标悬停在该 widget 上时, 就会显示出对应的形状. |
| setCursor(const QCursor& cursor) | 设置该 widget 光标的形状. 仅在⿏标停留在该widget 上时⽣效. |
| QGuiApplication::setOverrideCursor(co nst QCursor& cursor) | 设置全局光标的形状. 对整个程序中的所有 widget 都会生效.覆盖.上面的setCursor 设置的内容. |

QGuiApplication::setOverrideCursor(const QCursor& cursor)设置全局光标指的是系统内的全局光标,而不是系统级的全局.
在 Qt Designer 中设置按钮的光标

在界面中创建⼀个按钮.

直接在右侧属性编辑区修改 cursor 属性为 "等待"


运⾏程序, ⿏标悬停到按钮上, 即可看到光标的变化
截图无法截到鼠标光标, uu们自行运行验证

代码1

widget.cpp
cpp 复制代码
#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->move(100,100);
    MyButton->setText("这是一个按钮");

    //设置按钮的Cursor
    MyButton->setCursor(QCursor(Qt::OpenHandCursor));
}

Widget::~Widget()
{
    delete ui;
}

系统内置的光标形状如下

cpp 复制代码
 enum CursorShape {
    ArrowCursor,
    UpArrowCursor,
    CrossCursor,
    WaitCursor,
    IBeamCursor,
    SizeVerCursor,
    SizeHorCursor,
    SizeBDiagCursor,
    SizeFDiagCursor,
    SizeAllCursor,
    BlankCursor,
    SplitVCursor,
    SplitHCursor,
    PointingHandCursor,
    ForbiddenCursor,
    WhatsThisCursor,
    BusyCursor,
    OpenHandCursor,
    ClosedHandCursor,
    DragCopyCursor,
    DragMoveCursor,
    DragLinkCursor,
    LastCursor = DragLinkCursor,
    BitmapCursor = 24,
    CustomCursor = 25
 }

代码2

⾃定义鼠标光标
Qt ⾃带的光标形状有限. 我们也可以自己找个图片, 做成⿏标的光标. 比如我们有请猫猫⽼铁

创建 qrc 资源⽂件, 添加前缀 / , 并加入Cat .png

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPixmap>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPixmap pixmap(":/Cat.png");
    //通过这个函数对图片进行播放,注意播放不是修改图片对象本身,而是返回一个新的图片对象副本
    pixmap = pixmap.scaled(70,70);
    // 创建 QCursor 对象, 并指定 "热点" 为 (2, 2) 坐标位置.
    // 所谓 "热点" 就是⿏标点击时⽣效的位置.
    QCursor cursor(pixmap,2,2);
    this->setCursor(cursor);
}

Widget::~Widget()
{
    delete ui;
}

3.3:font

|-----------------------------|--------------------------------|
| API | 说明 |
| font() | 获取当前 widget 的字体信息. 返回 QFont对象. |
| setFont(const QFont& font) | 设置当前 widget 的字体信息. |

关于 QFont.

|-----------|-------------------------------------------------------------|
| 属性 | 说明 |
| family | 字体家族. 比如 "楷体", "宋体", "微软雅⿊" 等. |
| pointSize | 字体大小. |
| weight | 字体粗细. 以数值⽅式表示粗细程度取值范围为 [0, 99], 数值越⼤, 越粗. |
| bold | 是否加粗. 设置为 true, 相当于 weight 为 75. 设置为 false 相当于 weight 为 50. |
| italic | 是否倾斜. |
| underline | 是否带有下划线. |
| strikeOut | 是否带有删除线. |

在 Qt Designer 中设置字体属性

在界面上创建⼀个 label;


在右侧的属性编辑区, 设置该 label 的 font 相关属性

执行程序, 观察效果

代码示例

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->label->setText("Hello QT");
    //创建字体对象
    QFont font;
    //设置字体家族
    font.setFamily("仿宋");
    //设置字体大小
    font.setPointSize(20);
    //设置字体加粗
    font.setBold(true);
    //设置字体倾斜
    font.setItalic(true);
    //设置下划线
    font.setUnderline(true);
    //设置删除线
    font.setStrikeOut(true);
    //设置字体对象到Label上
    ui->label->setFont(font);
}

Widget::~Widget()
{
    delete ui;
}

3.4:toolTip

|------------------|---------------------------------------------|
| API | 说明 |
| setToolTip | 设置 toolTip. ⿏标悬停在该 widget 上时会有提示说明. |
| setToolTipDuring | 设置 toolTip 提⽰的时间. 单位 ms. 时间到后 toolTip ⾃动消失. |

代码示例

toolTip 只是给用户看的. 在代码中⼀般不需要获取到 toolTip.

在界⾯上拖放两个按钮. objectName 设置为 pushButton_yes 和 pushButton_no

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->pushButton_yes->setToolTip("这是YES按钮");
    ui->pushButton_yes->setToolTipDuration(3000);

    ui->pushButton_no->setToolTip("这是NO按钮");
    ui->pushButton_no->setToolTipDuration(3000);
}

Widget::~Widget()
{
    delete ui;
}

3.5:focusPolicy

设置控件获取到焦点的策略. 比如某个控件能否用鼠标选中或者能否通过 tab 键选中.

  • 所谓 "焦点" , 指的就是能选中这个元素. 接下来的操作 (比如键盘操作), 就都是针对该焦点元素进行的了. 这个对于输入框, 单选框, 复选框等控件非常有用的.
  • 这个事情就和 war3 或者 sc2 中, 先选中单位, 再下达命令是⼀样的.

|-----------------------------------------|---------------------------------------------|
| API | 说明 |
| focusPolicy() | 获取该widget 的 focusPolicy, 返回 Qt::FocusPolicy |
| setFocusPolicy(Qt::FocusPolicy policy | 设置 widget 的 focusPolicy |

Qt::FocusPolicy 是⼀个枚举类型. 取值如下.

  • Qt::NoFocus :控件不会接收键盘焦点.
  • Qt::TabFocus :控件可以通过Tab键接收焦点.
  • Qt::ClickFocus :控件在鼠标点击时接收焦点.
  • Qt::StrongFocus :控件可以通过Tab键和⿏标点击接收焦点 (默认值)
  • Qt::WheelFocus : 类似于 Qt::StrongFocus , 同时控件也通过鼠标滚轮获取到焦点 (新增的选项, ⼀般很少使用.

在界⾯上创建四个单行输入框 (Line Edit)

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

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

修改第⼆个输入框的 focusPolicy 为 Qt::NoFocus , 则第⼆个输⼊框不会被 tab / 鼠标左键
选中.


此时这个输入框也就⽆法输⼊内容了.

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

3.6:stylesheet

通过 CSS 设置 widget 的样式.

  • CSS (Cascading Style Sheets 层叠样式表) 本⾝属于网页前端技术. 主要就是用来描述界⾯的样式.
  • 所谓 "样式", 包括不限于大小, 位置, 颜⾊, 间距, 字体, 背景, 边框等. 我们平时看到的丰富多彩的网页, 就都会用到大量的 CSS.
  • Qt 虽然是做 GUI 开发, 但实际上和网页前端 有很多异曲同⼯之处. 因此 Qt 也引入了对于 CSS的支持.
    CSS 中可以设置的样式属性非常多. 基于这些属性 Qt 只能支持其中⼀部分, 称为 QSS (Qt Style Sheet) 具体的⽀持情况可以参考 Qt ⽂档中 "Qt Style Sheets Reference" 章节.

代码示例1

在界面上创建 label

编辑右侧的 styleSheet 属性, 设置样式

  • 此处的语法格式同 CSS, 使用键值对的⽅式设置样式. 其中键和值之间使⽤ : 分割. 键值对之间使用; 分割.
  • 另外, Qt Designer 只能对样式的基本格式进行校验, 不能检测出哪些样式不被 Qt 支持. 比如 text-align: center 这样的⽂本居中操作, 就无法支持.

编辑完成样式之后, 可以看到在 Qt Designer 中能够实时预览出效果

代码示例2

在界面上创建⼀个多行输入框 (Text Edit) 和两个按钮. objectName 分别为 pushButton_light 和 pushButton_dark

编写按钮的 slot 函数.
#333 是深⾊, 但是没那么黑.
#fff 是纯白色.
#000 是纯黑色.

widget.cpp
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_light_clicked()
{
    this->setStyleSheet("background-color: #f3f3f3");
    ui->textEdit->setStyleSheet("background-color:#fff;color:#000;");
    ui->pushButton_light->setStyleSheet("color:#000");
    ui->pushButton_dark->setStyleSheet("color:#000");
}


void Widget::on_pushButton_dark_clicked()
{
    this->setStyleSheet("background-color: #333");
    ui->textEdit->setStyleSheet("background-color:#333;color:#fff;");
    ui->pushButton_light->setStyleSheet("color:#fff");
    ui->pushButton_dark->setStyleSheet("color:#fff");
}

计算机中使⽤ "像素" 表示屏幕上的⼀个基本单位(也就是⼀个发亮的光点).
每个光点都使⽤三个字节表示颜色, 分别是 R (red), G (green), B (blue) ⼀个字节表示 (取值范围是 0-255, 或者 0x00-0xFF).
混合三种不同颜⾊的数值⽐例, 就能搭配出千千万万的颜⾊出来.

  • rgb(255, 0, 0) 或者 #FF0000 或者 #F00 表示纯红⾊.
  • rgb(0, 255, 0) 或者 #00FF00 或者 #0F0 表示纯绿⾊.
  • rgb(0, 0, 255) 或者 #0000FF 或者 #00F 表示纯蓝⾊.
  • rgb(255, 255, 255) 或者 #FFFFFF 或者 #FFFb表示纯白色.
  • rgb(0, 0, 0) 或者 #000000 或者 #000 表示纯⿊⾊.

当然, 上述规则只是针对⼀般的程序而言是这么设定的. 实际的显示器, 可能有 8bit ⾊深或者
10bit色深等, 实际情况会更加复杂
运行程序, 点击 "日间模式", 就是浅⾊背景, 深⾊文字; 点击 "夜间模式", 就是深⾊背景, 浅色文字.

相关推荐
爱尔兰极光4 小时前
Python--常量和变量
开发语言·python
极地星光4 小时前
Asio网络编程入门:从零构建同步客户端与服务器
服务器·网络
Evan芙4 小时前
php多版本编译安装
开发语言·php
柯南二号4 小时前
【后端】【Java】《Spring Boot 统一接口耗时统计实践:基于 HandlerInterceptor 的工程级方案》
java·开发语言·数据库
黑客思维者4 小时前
Python modbus-tk在配电物联网边缘网关的应用
开发语言·python·物联网
Cigaretter74 小时前
Day 30 类的定义与方法
开发语言·python
Xの哲學4 小时前
Linux ALSA音频架构: 从内核驱动到应用开发的全面解析
linux·服务器·算法·架构·边缘计算
初心_20244 小时前
11. 嵌入式Linux防火墙nftables的使用
linux·运维·服务器
郝学胜-神的一滴4 小时前
Separate Buffer、InterleavedBuffer 策略与 OpenGL VAO 深度解析
开发语言·c++·程序人生·算法·游戏程序·图形渲染