QtGUI模块功能详细说明,图标和光标(七)

目录

一.窗口和屏幕管理

二. 绘图和渲染

三. 图像处理

四. 字体和文本

五. 事件和输入处理

六. OpenGL 和硬件加速

七. 颜色和外观

八. 图标和光标

1、QIcon: 图标管理

1.1、QIcon 简介

1.2、图标的来源与创建

1.3、多分辨率与 DPI 支持

1.4、图标的状态管理

2、QCursor: 鼠标光标样式和自定义形状

2.1、QCursor 简介

2.2、预定义光标样式

2.3、自定义光标形状

2.4、光标的设置与管理

3、QIconEngine: 自定义图标渲染引擎

3.1、QIconEngine 简介

3.2、QIconEngine 的工作原理

3.3、实现自定义图标引擎

3.4、将自定义引擎与 QIcon 关联使用

九. 平台和渲染后端

十. 国际化(GUI 相关)


一. 窗口和屏幕管理

请跳转章节,此处不再重复:QtGUI模块功能详细说明,窗口和屏幕管理(一)

二. 绘图和渲染

 请跳转章节,此处不再重复:QtGUI模块功能详细说明,图形绘制与渲染(二)

三. 图像处理

 请跳转章节,此处不再重复:QtGUI模块功能详细说明,图像处理(三)

四. 字体和文本

 请跳转章节,此处不再重复:QtGUI模块功能详细说明, 字体和文本渲染(四)

五. 事件和输入处理

 请跳转章节,此处不再重复:QtGUI模块功能详细说明,事件与输入处理(五)

六. OpenGL 和硬件加速

支持 OpenGL 和 Vulkan 渲染,适用于高性能图形。暂不说明。

七. 颜色和外观

 请跳转章节,此处不再重复:QtGUI模块功能详细说明,颜色和外观(六)

八. 图标和光标

1、QIcon: 图标管理

1.1、QIcon 简介

QIcon 是一个跨平台的图标管理工具,能够加载和显示图像(例如 PNG、JPG、SVG 等格式)作为应用程序中的视觉元素。QIcon 不直接存储图像数据,而是通过引用图像文件或资源来管理图标的显示。

  • 多模式支持:QIcon 支持多种模式(如 QIcon::Normal、QIcon::Disabled、QIcon::Active、QIcon::Selected),允许为不同交互状态指定不同图像。

  • 多状态支持:支持 On 和 Off 状态,常用于切换控件(如复选框或按钮)。

  • 多种构造方式:可以通过文件路径、资源文件、QPixmap 或 QImage 创建 QIcon。

  • 自动缩放:根据控件大小或高 DPI 设置,QIcon 能自动调整图标尺寸。

1.1.1、QIcon 在 Qt 部件中的应用

按钮(QPushButton、QToolButton 等)

  • QPushButton:通过 setIcon() 方法为按钮设置图标。

    QPushButton *button = new QPushButton;
    QIcon icon(":/images/save.png"); // 从资源文件加载图标
    button->setIcon(icon);
    button->setIconSize(QSize(24, 24)); // 设置图标大小
  • QToolButton:工具按钮通常只显示图标,不显示文本,QIcon 是其核心视觉元素。

    QToolButton *toolButton = new QToolButton;
    toolButton->setIcon(QIcon(":/images/open.png"));
  • 状态支持:QIcon 可以为按钮的不同状态(如禁用或按下)设置不同图标:

    QIcon icon;
    icon.addFile(":/images/save_normal.png", QSize(), QIcon::Normal);
    icon.addFile(":/images/save_disabled.png", QSize(), QIcon::Disabled);
    button->setIcon(icon);

动作(QAction)

QAction 是 Qt 中用于表示菜单项、工具栏按钮等抽象动作的类,QIcon 为其提供视觉标识。

  • 菜单项:在 QMenu 中,QAction 使用 QIcon 显示图标。

    QAction *openAction = new QAction(QIcon(":/images/open.png"), "Open", this);
    QMenu *fileMenu = menuBar()->addMenu("File");
    fileMenu->addAction(openAction);
  • 工具栏:在 QToolBar 中,QAction 的图标由 QIcon 提供,点击工具栏按钮会触发相应的动作。

    QToolBar *toolbar = addToolBar("Main Toolbar");
    toolbar->addAction(openAction);

窗口图标(QMainWindow、QDialog 等)

QIcon 可用于设置窗口的标题栏图标或任务栏图标,增强应用程序的品牌识别:

  • 设置窗口图标:

    QMainWindow *window = new QMainWindow;
    window->setWindowIcon(QIcon(":/images/app_icon.png"));
  • 系统托盘图标:在 QSystemTrayIcon 中,QIcon 用于显示托盘图标。

    QSystemTrayIcon *trayIcon = new QSystemTrayIcon(QIcon(":/images/tray_icon.png"), this);
    trayIcon->show();

其他部件

  • QListWidget、QTreeWidget 等:QIcon 用于为列表项或树节点设置图标。

    QListWidgetItem *item = new QListWidgetItem(QIcon(":/images/folder.png"), "Folder");
    listWidget->addItem(item);
  • QTabBar/QTabWidget:为选项卡设置图标。

    QTabWidget *tabWidget = new QTabWidget;
    tabWidget->addTab(new QWidget, QIcon(":/images/tab.png"), "Tab 1");
  • QLabel:虽然 QLabel 通常显示文本或图像,但可以通过 QIcon 转换为 QPixmap 显示图标。

    QLabel *label = new QLabel;
    label->setPixmap(QIcon(":/images/icon.png").pixmap(32, 32));

1.2、图标的来源与创建

1.2.1、从文件加载(PNG, JPG, SVG 等格式)

QIcon 可以通过文件路径直接加载图像文件,支持多种常见格式,包括 PNG、JPG、SVG、ICO 等。

  • 使用 QIcon 的构造函数或 addFile() 方法加载图像文件。

  • 可以为不同状态(如 Normal、Disabled)或尺寸指定不同的图像文件。

// 简单加载单个图像文件
QIcon icon("path/to/icon.png"); // PNG 文件
QPushButton *button = new QPushButton;
button->setIcon(icon);

// 为不同状态加载不同图像
QIcon multiStateIcon;
multiStateIcon.addFile("path/to/normal.png", QSize(), QIcon::Normal);   // 正常状态
multiStateIcon.addFile("path/to/disabled.png", QSize(), QIcon::Disabled); // 禁用状态
button->setIcon(multiStateIcon);

// 加载 SVG 文件(支持矢量缩放)
QIcon svgIcon("path/to/icon.svg");
button->setIcon(svgIcon);

1.2.2、从 QPixmap 或 QImage 创建

 QIcon 可以通过 QPixmap 或 QImage 对象创建,适合需要动态生成图标或从内存中加载图像的场景。QPixmap 适用于与显示设备相关的像素数据,而 QImage 更适合图像处理。

  • 使用 QIcon 的构造函数直接从 QPixmap 或 QImage 创建。

  • 也可以通过 addPixmap() 方法为不同状态或模式添加图像。

// 从 QPixmap 创建
QPixmap pixmap(32, 32);
pixmap.fill(Qt::red); // 创建一个红色矩形
QIcon icon(pixmap);
QPushButton *button = new QPushButton;
button->setIcon(icon);

// 从 QImage 创建
QImage image(32, 32, QImage::Format_ARGB32);
image.fill(Qt::blue); // 创建一个蓝色矩形
QIcon imageIcon(QPixmap::fromImage(image));
button->setIcon(imageIcon);

// 为不同状态添加不同 QPixmap
QIcon multiIcon;
multiIcon.addPixmap(QPixmap("path/to/normal.png"), QIcon::Normal);
multiIcon.addPixmap(QPixmap("path/to/disabled.png"), QIcon::Disabled);
button->setIcon(multiIcon);

注意事项:

  • 性能:QPixmap 与显示设备相关,适合直接渲染;QImage 更适合图像操作(如缩放、滤镜)。

  • 格式转换:QImage 需通过 QPixmap::fromImage() 转换为 QPixmap 再创建 QIcon。

  • 内存管理:QIcon 内部会对 QPixmap 或 QImage 进行深拷贝,确保原始对象的生命周期不会影响图标。

1.2.3、从 Qt 资源系统 (.qrc) 加载图标

Qt 资源系统允许将图像文件嵌入应用程序的可执行文件中,通过 .qrc 文件管理资源。QIcon 可以直接从资源路径加载图标,路径以 :/ 开头。这种方式适合打包应用程序,确保图标文件不会丢失。

  • 创建 .qrc 文件(如 resources.qrc),列出资源文件。

  • 使用 QIcon 构造函数或 addFile() 加载资源路径。

创建 .qrc 文件:可直接使用QtCreator 添加资源文件界面操作实现创建

<!DOCTYPE RCC>
<RCC version="1.0">
    <qresource>
        <file>images/icon.png</file>
        <file>images/icon_disabled.png</file>
    </qresource>
</RCC>

编译资源:

在 Qt 项目文件(.pro)中添加:使用 qmake 或 CMake 编译,资源将被嵌入可执行文件。

RESOURCES += resources.qrc
// 加载单个资源图标
QIcon icon(":/images/icon.png");
QPushButton *button = new QPushButton;
button->setIcon(icon);

// 为不同状态加载资源图标
QIcon multiIcon;
multiIcon.addFile(":/images/icon.png", QSize(), QIcon::Normal);
multiIcon.addFile(":/images/icon_disabled.png", QSize(), QIcon::Disabled);
button->setIcon(multiIcon);

1.2.4、QIcon 的拷贝和赋值语义

QIcon 是一个轻量级类,采用隐式共享(copy-on-write)机制,类似于 QString 和 QPixmap。这意味着拷贝和赋值操作非常高效,多个 QIcon 对象可以共享相同的底层数据,直到其中一个对象被修改。

  • 拷贝构造:创建 QIcon 的副本时,共享相同的图像数据。

  • 赋值操作:将一个 QIcon 赋值给另一个时,共享数据,引用计数增加。

  • 修改操作:当修改 QIcon(如调用 addFile() 或 addPixmap())时,触发深拷贝,创建独立的数据副本。

// 拷贝构造
QIcon icon1(":/images/icon.png");
QIcon icon2(icon1); // 共享数据,高效
QPushButton *button1 = new QPushButton;
button1->setIcon(icon2); // 使用拷贝的图标

// 赋值操作
QIcon icon3;
icon3 = icon1; // 共享数据
icon3.addFile(":/images/new_icon.png"); // 触发深拷贝,icon1 不受影响

// 验证共享状态
qDebug() << icon1.isDetached(); // false(共享数据)
qDebug() << icon3.isDetached(); // true(深拷贝后独立)

1.3、多分辨率与 DPI 支持

1.3.1、为不同 DPI 提供多个尺寸的图像

为了支持高 DPI 显示,开发者可以为 QIcon 提供多个分辨率的图像版本(例如 1x、2x、3x),以适应不同的设备像素比。Qt 会根据屏幕的 DPR 和控件的需求尺寸选择最合适的图像。

  • 使用 QIcon::addFile() 或 QIcon::addPixmap() 方法,为同一图标的不同分辨率指定图像。

  • 通常通过文件名后缀(如 @2x、@3x)或显式指定尺寸来区分不同分辨率的图像。

  • 资源文件或文件系统中的图像可以按分辨率组织。

// 为不同分辨率提供图像(通过文件)
QIcon icon;
icon.addFile(":/images/icon.png", QSize(16, 16)); // 1x,标准分辨率
icon.addFile(":/images/icon@2x.png", QSize(32, 32)); // 2x,高分辨率
icon.addFile(":/images/icon@3x.png", QSize(48, 48)); // 3x,超高分辨率
QPushButton *button = new QPushButton;
button->setIcon(icon);
button->setIconSize(QSize(16, 16)); // 控件期望的逻辑尺寸

使用 QPixmap: 如果图标是动态生成的,可以通过 QPixmap 指定不同分辨率:

QIcon icon;
QPixmap pixmap1x(16, 16);
pixmap1x.fill(Qt::red);
QPixmap pixmap2x(32, 32);
pixmap2x.fill(Qt::red);
icon.addPixmap(pixmap1x, QIcon::Normal, QIcon::Off); // 1x
icon.addPixmap(pixmap2x, QIcon::Normal, QIcon::Off); // 2x
QPushButton *button = new QPushButton;
button->setIcon(icon);
button->setIconSize(QSize(16, 16));

1.3.2、Qt 自动选择最佳尺寸图标的机制

Qt 的 QIcon 引擎会根据以下因素自动选择最合适的图标版本:

  • 设备像素比(DPR):由 QScreen::devicePixelRatio() 或 QWindow::devicePixelRatio() 提供。

  • 控件期望的图标尺寸:由 setIconSize() 或控件的默认尺寸(如按钮的 iconSize 属性)指定。

  • 图标状态和模式:如 Normal、Disabled、Active 等。

  • 可用图像的分辨率:QIcon 中注册的图像尺寸。

选择机制:

  • 计算物理尺寸:

    • Qt 将控件期望的逻辑尺寸(QSize)乘以设备的 DPR,得到物理像素尺寸。

    • 例如:控件设置 iconSize 为 16x16 像素,DPR 为 2.0,则 Qt 会寻找接近 32x32 像素的图像。

  • 匹配最近尺寸:

    • Qt 从 QIcon 中注册的图像列表中选择与目标物理尺寸最接近的图像。

    • 如果没有精确匹配,Qt 会选择尺寸稍大的图像并缩放到目标尺寸(以避免模糊)。

  • 优先级:

    • 优先选择与当前状态(如 Normal、Disabled)和模式匹配的图像。

    • 如果没有特定状态的图像,Qt 会回退到默认状态(通常是 Normal)。

  • 缩放处理:

    • 如果选择的图像尺寸与目标尺寸不匹配,Qt 会使用高质量缩放算法(如平滑缩放)调整图像。

    • 对于 SVG 图像,Qt 会直接渲染到目标尺寸,无需额外图像版本。

// 创建一个支持多分辨率的 QIcon
QIcon icon;
icon.addFile(":/images/icon.png", QSize(16, 16)); // 1x
icon.addFile(":/images/icon@2x.png", QSize(32, 32)); // 2x

// 设置按钮图标
QPushButton *button = new QPushButton;
button->setIcon(icon);
button->setIconSize(QSize(16, 16)); // 逻辑尺寸

// 假设设备 DPR = 2.0,Qt 将选择 32x32 的图像(icon@2x.png)

验证 DPR 和选择:

开发者可以通过以下代码检查设备的 DPR 和实际使用的图标尺寸:

qDebug() << "Device Pixel Ratio:" << button->window()->windowHandle()->devicePixelRatio();
QPixmap actualPixmap = icon.pixmap(button->iconSize(), QIcon::Normal);
qDebug() << "Selected Pixmap Size:" << actualPixmap.size();

1.4、图标的状态管理

QIcon 支持为不同的交互状态和控件状态提供不同的图像,从而增强用户界面的动态性和直观性。

1.4.1、QIcon::Mode:图标模式

QIcon::Mode 枚举定义了图标的视觉模式,用于表示控件在不同交互状态下的外观。Qt 会根据控件的状态(如启用、禁用、悬停等)自动选择对应的模式。

  • QIcon::Normal:默认模式,表示控件处于正常、启用状态。

  • QIcon::Disabled:禁用模式,表示控件不可交互(例如按钮被禁用)。

  • QIcon::Active:激活模式,通常在,

  • QIcon::Selected:选中模式,表示控件被选中(如在菜单或列表中选中某项)。

使用场景:

  • 按钮在禁用时显示灰色图标(Disabled)。

  • 工具栏按钮在悬停或点击时高亮显示(Active)。

  • 菜单项选中时显示不同图标(Selected).

QIcon icon;
icon.addFile(":/images/button_normal.png", QSize(), QIcon::Normal);   // 正常状态
icon.addFile(":/images/button_disabled.png", QSize(), QIcon::Disabled); // 禁用状态
icon.addFile(":/images/button_active.png", QSize(), QIcon::Active);   // 激活状态
QPushButton *button = new QPushButton;
button->setIcon(icon);
button->setEnabled(false); // 禁用按钮,将显示 disabled 图标

1.4.2、QIcon::State:图标状态

QIcon::State 枚举定义了图标的开关状态,主要用于表示可切换控件(切换按钮)的两种状态。

  • QIcon::On:表示控件处于“开启”状态(如复选框被选中)。

  • QIcon::Off:表示控件处于“关闭”状态(如复选框未选中)。

#include <QApplication>
#include <QToolButton>
#include <QIcon>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QIcon icon;
    icon.addFile(":/images/checkbox_on.png", QSize(), QIcon::Normal, QIcon::On);   // 选中状态
    icon.addFile(":/images/checkbox_off.png", QSize(), QIcon::Normal, QIcon::Off); // 未选中状态

    QToolButton *toggleButton = new QToolButton;
    toggleButton->setCheckable(true); // 启用切换状态
    toggleButton->setIcon(icon);
    toggleButton->setIconSize(QSize(16, 16));
    toggleButton->setText("Toggle");
    toggleButton->show();

    return app.exec();
}

2、QCursor: 鼠标光标样式和自定义形状

2.1、QCursor 简介

QCursor 用于表示和管理应用程序中的鼠标光标(cursor)。它提供了对光标形状、位置以及行为的控制,允许开发者自定义光标的外观和功能,以适应不同的用户交互场景。

  • 设置光标形状:QCursor 允许开发者设置不同的光标形状,例如箭头(Qt::ArrowCursor)、十字(Qt::CrossCursor)、等待(Qt::WaitCursor)等。

  • 自定义光标:开发者可以通过加载图像(如位图或图标)创建自定义光标形状,使用 QCursor(QPixmap) 构造函数加载自定义图像。

  • 控制光标位置:QCursor 提供方法(如 QCursor::setPos() 和 QCursor::pos())来设置或获取鼠标光标的屏幕位置,常用于需要程序化控制光标移动的场景,如游戏或绘图应用。

  • 动态光标管理:支持在特定控件或窗口区域内动态切换光标。通过 QWidget::setCursor() 设置某个控件的光标形状,或者通过 QApplication::setOverrideCursor() 全局覆盖光标。

2.1.1、关键方法与属性:

  • QCursor(Qt::CursorShape shape):基于预定义形状创建光标。

  • QCursor(const QPixmap &pixmap, int hotX = -1, int hotY = -1):基于自定义图像创建光标,指定热点位置。

  • QCursor::pos():获取当前光标在屏幕上的位置。

  • QCursor::setPos(int x, int y):将光标移动到指定屏幕坐标。

  • QWidget::setCursor(const QCursor &cursor):为特定控件设置光标。

  • QApplication::setOverrideCursor(const QCursor &cursor):临时覆盖全局光标。

  • QApplication::restoreOverrideCursor():恢复被覆盖的光标。

#include <QApplication>
#include <QWidget>
#include <QCursor>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QWidget window;
    
    // 设置窗口的光标为手形
    window.setCursor(QCursor(Qt::PointingHandCursor));
    
    // 全局设置等待光标
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    
    window.show();
    return app.exec();
}

2.2、预定义光标样式

2.2.1、Qt::CursorShape 枚举的各种标准光标样式

Qt::CursorShape 是 Qt 框架中定义的一个枚举类型,位于 Qt 命名空间内,用于指定预定义的鼠标光标形状。

  • Qt::ArrowCursor:

    • 描述:标准的箭头光标,通常为一个向左上方倾斜的箭头。

    • 用途:默认光标,用于表示普通选择或导航状态,适用于大多数非特定交互场景(如窗口背景、普通控件)。

    • 典型场景:桌面应用程序的主窗口、文件浏览器中的非交互区域。

  • Qt::PointingHandCursor:

    • 描述:手形光标,显示为一个指向的手指,通常表示可点击的元素。

    • 用途:提示用户可以点击某个控件或区域,例如超链接、按钮或可交互的图标。

    • 典型场景:Web 浏览器中的超链接、GUI 中的按钮。

  • Qt::IBeamCursor:

    • 描述:I 形光标(或称文本光标),形似一个垂直的“I”或光标线。

    • 用途:表示用户可以选择或编辑文本,常见于文本输入区域。

    • 典型场景:文本编辑器、输入框(如 QLineEdit、QTextEdit)。

  • Qt::WaitCursor:

    • 描述:等待光标,通常显示为沙漏(Windows)或旋转的圆圈(macOS/Linux)。

    • 用途:提示用户程序正在执行耗时操作,暂时无法响应用户输入。

    • 典型场景:文件加载、数据处理、网络请求期间。

  • Qt::BusyCursor:

    • 描述:忙碌光标,通常与 Qt::WaitCursor 类似,但可能带有额外的箭头,表示程序忙碌但仍允许部分交互。

    • 用途:用于程序忙碌但仍可接受某些输入的场景。

    • 典型场景:后台任务运行时,允许用户继续操作其他控件。

  • Qt::CrossCursor:

    • 描述:十字光标,显示为一个十字形。

    • 用途:表示精确选择或定位,常见于绘图或设计工具中。

    • 典型场景:图像编辑软件中的选择工具、CAD 软件中的定位操作。

  • Qt::OpenHandCursor:

    • 描述:张开的手形光标,表示可以抓取或拖动。

    • 用途:提示用户可以拖动对象或平移视图。

    • 典型场景:地图应用程序中的平移操作、可拖动的界面元素。

  • Qt::ClosedHandCursor:

    • 描述:握紧的手形光标,表示正在拖动或抓取。

    • 用途:表示拖动操作正在进行,通常与 Qt::OpenHandCursor 配合使用。

    • 典型场景:拖动窗口、调整图形元素时。

  • Qt::SizeVerCursor:

    • 描述:垂直双向箭头光标,表示可以上下调整大小。

    • 用途:提示用户可以调整控件或窗口的垂直尺寸。

    • 典型场景:调整窗口高度、表格行高。

  • Qt::SizeHorCursor:

    • 描述:水平双向箭头光标,表示可以左右调整大小。

    • 用途:提示用户可以调整控件或窗口的水平尺寸。

    • 典型场景:调整窗口宽度、表格列宽。

  • Qt::SizeFDiagCursor:

    • 描述:对角线双向箭头光标(从左上到右下),表示可以沿对角线调整大小。

    • 用途:提示用户可以同时调整宽度和高度。

    • 典型场景:调整窗口的右下角大小。

  • Qt::SizeBDiagCursor:

    • 描述:对角线双向箭头光标(从右上到左下),表示可以沿另一对角线调整大小。

    • 用途:与 Qt::SizeFDiagCursor 类似,用于调整窗口或控件大小。

    • 典型场景:调整窗口的左下角或右上角。

  • Qt::UpArrowCursor:

    • 描述:向上箭头光标。

    • 用途:较少使用,通常用于特定场景,如表示向上选择或导航。

    • 典型场景:自定义工具栏或导航控件。

  • Qt::BlankCursor:

    • 描述:空光标,即隐藏光标。

    • 用途:用于完全隐藏光标,常见于全屏游戏或触摸屏应用。

    • 典型场景:第一人称射击游戏、视频播放器。

  • Qt::DragMoveCursor:

    • 描述:表示移动操作的光标,通常为一个箭头加四向箭头图标。

    • 用途:提示用户正在执行拖放操作中的移动。

    • 典型场景:文件拖放到新文件夹、拖动界面元素。

  • Qt::DragCopyCursor:

    • 描述:表示复制操作的光标,通常为一个箭头加加号图标。

    • 用途:提示用户正在执行拖放操作中的复制。

    • 典型场景:文件复制到新位置。

  • Qt::ForbiddenCursor:

    • 描述:禁止光标,通常为一个圆形加斜杠。

    • 用途:提示用户当前操作不被允许。

    • 典型场景:尝试将文件拖放到不可写入的区域。

2.2.2、如何设置标准光标到部件或应用程序

Qt 提供了灵活的方式来设置标准光标样式,可以针对特定部件(QWidget 及其子类)或整个应用程序进行设置。

为特定部件设置光标

通过 QWidget::setCursor() 方法,可以为特定的控件或窗口设置光标样式。当鼠标进入该控件区域时,光标会自动切换为指定的形状。

#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建主窗口
    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);

    // 创建按钮并设置不同的光标样式
    QPushButton *button1 = new QPushButton("Click Me");
    button1->setCursor(QCursor(Qt::PointingHandCursor)); // 手形光标

    QPushButton *button2 = new QPushButton("Edit Text");
    button2->setCursor(QCursor(Qt::IBeamCursor)); // 文本光标

    // 添加按钮到布局
    layout->addWidget(button1);
    layout->addWidget(button2);

    window.setLayout(layout);
    window.show();

    return app.exec();
}

为整个应用程序设置全局光标

通过 QApplication::setOverrideCursor() 方法,可以临时覆盖整个应用程序的光标样式。这种方法适合在程序执行特定任务(如耗时操作)时统一显示光标。

#include <QApplication>
#include <QPushButton>
#include <QTimer>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建主窗口和按钮
    QWidget window;
    QPushButton *button = new QPushButton("Start Long Task", &window);

    // 按钮点击时模拟耗时操作,显示等待光标
    QObject::connect(button, &QPushButton::clicked, [&]() {
        // 设置全局等待光标
        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

        // 模拟耗时任务(3秒后恢复光标)
        QTimer::singleShot(3000, []() {
            QApplication::restoreOverrideCursor();
        });
    });

    window.show();
    return app.exec();
}

动态切换光标(结合事件)

在某些场景下,需要根据用户交互动态切换光标样式,例如鼠标悬停或拖放操作。可以通过重写控件的事件处理函数(如 enterEvent、leaveEvent)或使用信号槽机制实现。

#include <QApplication>
#include <QWidget>
#include <QEvent>

class CustomWidget : public QWidget {
protected:
    void enterEvent(QEvent *event) override {
        setCursor(QCursor(Qt::PointingHandCursor)); // 鼠标进入时设置为手形光标
        QWidget::enterEvent(event);
    }

    void leaveEvent(QEvent *event) override {
        unsetCursor(); // 鼠标离开时恢复默认光标
        QWidget::leaveEvent(event);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    CustomWidget widget;
    widget.resize(200, 200);
    widget.show();

    return app.exec();
}

2.3、自定义光标形状

2.3.1、使用 QPixmap 和 QBitmap 定义光标图像和掩码

可以通过 QPixmap 创建光标图像,并结合 QBitmap 定义光标的掩码(mask)来控制光标的透明区域和形状。

  • 创建光标图像 (QPixmap):使用 QPixmap 加载或绘制光标图像。图像可以是 PNG、BMP 等格式,通常建议大小为 32x32 像素或更小,以符合系统光标大小限制。

  • 创建光标掩码 (QBitmap):掩码是一个单色位图,用于指定光标的哪些部分是透明的(黑色表示透明,白色表示不透明)。

  • 创建自定义光标 (QCursor):使用 QPixmap 和 QBitmap 创建 QCursor 对象。

  • 应用光标:将光标应用到窗口或控件上:

  • 注:如果 QPixmap 包含透明通道,掩码可以省略,直接使用 QCursor cursor(pixmap);。

#include <QApplication>
#include <QWidget>
#include <QEvent>
#include <QBitmap>
#include <QPixmap>
#include <QPainter>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    QWidget window;
    window.resize(300,400);


    QPixmap pixmap(":/images/save.png");

    //从 QPixmap 生成掩码
    QBitmap mask = pixmap.createMaskFromColor(Qt::transparent);

//    //手动绘制掩码:
//    QBitmap mask(pixmap.size());
//    QPainter painter(&mask);
//    mask.fill(Qt::color0); // 黑色(透明)
//    painter.setBrush(Qt::color1); // 白色(不透明)
//    painter.drawEllipse(0, 0, 16, 16); // 绘制圆形不透明区域

    QCursor customCursor(pixmap, mask);// 创建自定义光标 (QCursor)

    window.setCursor(customCursor); // 设置到当前窗口

    window.show();

    return app.exec();
}

2.3.2、设置光标热点(Hotspot)

光标热点(Hotspot)是光标图像中表示点击位置的点(x, y 坐标)。例如,箭头光标的热点通常在其尖端,十字光标的热点在其中心。

指定热点坐标:

在创建 QCursor 时,通过参数指定热点的 x 和 y 坐标(相对于光标图像的左上角)。

QPixmap pixmap(":/cursor.png");
QCursor customCursor(pixmap, 16, 16); // 热点位于图像中心(假设图像为 32x32)

动态计算热点:

如果热点位置需要根据图像内容动态计算,可以分析图像的透明区域或关键点。例如,检测图像的最左侧不透明像素作为热点。

int hotX = pixmap.width() / 2;
int hotY = pixmap.height() / 2;
QCursor customCursor(pixmap, hotX, hotY);

注意:热点坐标必须在图像范围内(0 ≤ x < pixmap.width(), 0 ≤ y < pixmap.height()),否则可能导致光标行为异常。

2.3.3、动态或动画光标(通过定时器或事件循环更新)

动态或动画光标通过定时器或事件循环定期更新光标图像,实现光标的动态效果,例如旋转、闪烁或帧动画。

准备多帧图像:

创建一组 QPixmap 图像,用于表示动画的每一帧。例如,旋转光标可能包含 8 个不同角度的图像。

QList<QPixmap> frames;
frames << QPixmap(":/frame1.png") << QPixmap(":/frame2.png") << QPixmap(":/frame3.png");

使用定时器切换帧:

使用 QTimer 定期触发帧切换,更新光标。

class MyWidget : public QWidget {
public:
    MyWidget(QWidget *parent = nullptr) : QWidget(parent), currentFrame(0) {
        frames << QPixmap(":/frame1.png") << QPixmap(":/frame2.png") << QPixmap(":/frame3.png");
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &MyWidget::updateCursor);
        timer->start(100); // 每 100ms 更新一次
    }

private:
    void updateCursor() {
        currentFrame = (currentFrame + 1) % frames.size(); // 循环切换帧
        QCursor cursor(frames[currentFrame]);
        setCursor(cursor);
    }

    QList<QPixmap> frames;
    int currentFrame;
};

2.4、光标的设置与管理

2.4.1、部件级别的光标设置 (QWidget::setCursor(), unsetCursor())

件级别的光标设置允许为特定的 QWidget(如窗口、按钮或自定义控件)设置自定义光标或系统预定义光标。QWidget 类提供了 setCursor() 和 unsetCursor() 方法来管理光标。

设置光标 (QWidget::setCursor()):

//设置系统箭头光标:
QWidget *widget = new QWidget;
widget->setCursor(Qt::ArrowCursor);

//设置自定义光标(基于 QPixmap):
QPixmap pixmap(":/cursor.png");
QCursor customCursor(pixmap, 16, 16); // 热点坐标 (16, 16)
widget->setCursor(customCursor);

恢复默认光标 (QWidget::unsetCursor()):

调用 unsetCursor() 移除部件的自定义光标,恢复到父部件或应用程序的默认光标(通常是 Qt::ArrowCursor)。

widget->unsetCursor();

2.4.2、应用程序级别的全局光标设置 (QApplication::setOverrideCursor())

应用程序级别的光标设置允许临时覆盖整个应用程序的所有光标,通常用于表示全局状态(如等待或繁忙状态)。QApplication 类提供了 setOverrideCursor() 和 restoreOverrideCursor() 方法来实现这一功能。

设置全局光标 (QApplication::setOverrideCursor()):

//设置等待光标:
QApplication::setOverrideCursor(Qt::WaitCursor);


//自定义光标
QPixmap pixmap(":/busy_cursor.png");
QCursor customCursor(pixmap, 16, 16);
QApplication::setOverrideCursor(customCursor);

恢复先前光标 (QApplication::restoreOverrideCursor()):

调用 restoreOverrideCursor() 移除当前全局光标,恢复之前的全局光标或部件级光标。

QApplication::restoreOverrideCursor();

光标栈管理:

setOverrideCursor() 支持光标栈,多次调用会将光标压栈,restoreOverrideCursor() 会弹出栈顶光标。

QApplication::setOverrideCursor(Qt::WaitCursor); // 压栈:等待光标
QApplication::setOverrideCursor(Qt::CrossCursor); // 压栈:十字光标
QApplication::restoreOverrideCursor(); // 弹出:恢复到等待光标
QApplication::restoreOverrideCursor(); // 弹出:恢复到默认光标

3、QIconEngine: 自定义图标渲染引擎

3.1、QIconEngine 简介

QIconEngine 是 Qt 中用于渲染 QIcon 对象的底层接口。QIcon 是一个高层次类,用于表示可在不同上下文(如按钮、工具栏、菜单等)中显示的图标,而 QIconEngine 负责实际的图标渲染逻辑。每个 QIcon 实例都关联一个 QIconEngine(或其子类),由 Qt 内部管理。

核心功能

  • 图标渲染:根据请求的大小、模式(如正常、禁用、选中)和状态(如激活、悬停)生成图标图像。

  • 多分辨率支持:提供不同尺寸或像素密度的图标,适配高 DPI 显示。

  • 动态生成:支持运行时生成图标,而非依赖静态图像文件。

内置的 QIconEngine 

  • QPixmapIconEngine:基于 QPixmap 的静态图像渲染。

  • QImageIconEngine:基于 QImage 的渲染。

  • QSvgIconEngine:用于 SVG 矢量图标的渲染。

3.2、QIconEngine 的工作原理

QIconEngine 是一个抽象基类,开发者需要实现其核心虚函数以定义图标的渲染逻辑。

3.2.1、抽象基类及其核心虚函数

QIconEngine 定义在 <QIconEngine> 头文件中。

virtual void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0

  • painter:用于绘制的画笔。

  • rect:目标矩形区域,定义图标的绘制大小和位置。

  • mode:图标模式(QIcon::Normal、QIcon::Disabled、 QIcon::Active、QIcon::Selected)。

  • state:图标状态(QIcon::On 或 QIcon::Off,例如复选框的选中/未选中状态)。

virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)

  • 返回指定大小、模式和状态的图标图像(QPixmap)。

  • 默认实现通过调用 paint() 渲染到 QPixmap,通常无需重写。

  • 参数同 paint(),但以 QSize 指定尺寸。

virtual QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)

  • 返回图标的实际大小,可能受限于图标的固有尺寸。

  • 默认实现返回请求的 size,可重写以支持特定约束。

virtual QIconEngine *clone() const:

  • 创建引擎的副本,用于 QIcon 的复制操作。

  • 必须实现,通常返回一个新的引擎实例。

virtual QString key() const:

  • 返回引擎的唯一标识符,用于缓存或比较。

  • 默认实现返回类名,可重写以提供更具体的值。

virtual void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state):

  • 添加静态 QPixmap 作为图标内容(可选)。

  • 常用于混合静态和动态渲染。

virtual void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state):

  • 从文件加载图标(可选)。

  • 常用于支持文件路径的图标加载。

virtual QStringList availableSizes(QIcon::Mode mode, QIcon::State state) const:

  • 返回支持的图标尺寸(可选)。

  • 用于多分辨率图标支持。

3.2.2、paint() 方法的实现(核心渲染逻辑)

paint() 是 QIconEngine 的核心方法,负责实际的图标绘制。

初始化画笔:配置 QPainter 的属性(如抗锯齿、画刷、笔)以确保高质量渲染。

处理模式和状态:根据 state 切换图标内容(例如开关的开/关状态)。根据 mode 调整渲染效果:

  • QIcon::Normal:标准渲染。

  • QIcon::Disabled:降低透明度或使用灰度。

  • QIcon::Active:高亮或加粗。

  • QIcon::Selected:更改颜色或背景。

绘制图标:使用 QPainter 绘制形状、路径或图像,适配目标 rect。

适配大小:确保绘制内容填充 rect,必要时缩放或裁剪。

3.2.3、pixmap() 方法的实现(获取缓存图像)

pixmap() 方法用于生成指定大小、模式和状态的 QPixmap,通常由 Qt 内部调用以缓存渲染结果。默认实现通过调用 paint() 完成渲染,开发者可以选择重写以优化性能。

默认实现

默认的 pixmap() 方法创建一个 QPixmap,调用 paint() 绘制内容:

QPixmap QIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) {
    QPixmap pixmap(size);
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);
    paint(&painter, QRect(QPoint(0, 0), size), mode, state);
    return pixmap;
}

自定义实现

如果需要优化(如缓存特定尺寸的图像),可以重写 pixmap():使用 QCache 或 QMap 存储已渲染的 QPixmap,避免重复渲染。

3.2.4、示例代码

展示动态圆形图标的渲染,支持不同模式和状态,并使用缓存优化 pixmap():

#include <QApplication>
#include <QPushButton>
#include <QIconEngine>
#include <QPainter>
#include <QCache>

class CircleIconEngine : public QIconEngine {
public:
    CircleIconEngine() = default;
    QIconEngine *clone() const override { return new CircleIconEngine(); }

    void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override {
        painter->setRenderHint(QPainter::Antialiasing, true);

        // 根据模式和状态调整颜色
        QColor color = (mode == QIcon::Disabled) ? Qt::gray :
                       (mode == QIcon::Selected) ? Qt::blue :
                       (mode == QIcon::Active) ? Qt::green : Qt::red;
        if (mode == QIcon::Disabled) {
            painter->setOpacity(0.5);
        }

        // 根据状态调整形状
        if (state == QIcon::On) {
            painter->setBrush(color);
            painter->drawEllipse(rect); // 实心圆
        } else {
            painter->setPen(QPen(color, 2));
            painter->setBrush(Qt::NoBrush);
            painter->drawEllipse(rect); // 空心圆
        }
    }

    QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override {
        QString cacheKey = QString("%1_%2_%3_%4")
            .arg(size.width()).arg(size.height()).arg(mode).arg(state);
        
        if (cache.contains(cacheKey)) {
            return *cache.object(cacheKey);
        }

        QPixmap pixmap(size);
        pixmap.fill(Qt::transparent);
        QPainter painter(&pixmap);
        paint(&painter, QRect(QPoint(0, 0), size), mode, state);
        cache.insert(cacheKey, new QPixmap(pixmap));
        return pixmap;
    }

private:
    static QCache<QString, QPixmap> cache;
};

QCache<QString, QPixmap> CircleIconEngine::cache(100);

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建 QIcon 使用自定义引擎
    QIcon icon(new CircleIconEngine());

    // 创建按钮并设置图标
    QPushButton button;
    button.setIcon(icon);
    button.setIconSize(QSize(64, 64));
    button.setText("Dynamic Icon");
    button.setCheckable(true); // 支持 On/Off 状态
    button.resize(200, 100);
    button.show();

    return app.exec();
}

3.3、实现自定义图标引擎

3.3.1、子类化 QIconEngine

要创建自定义图标引擎,需要继承 QIconEngine 并实现其核心虚函数,尤其是 paint() 和 clone()。

  • 继承 QIconEngine:创建一个新类,继承自 QIconEngine

  • 实现核心虚函数:

    • paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state):定义图标的绘制逻辑。

    • clone() const:返回引擎的副本,用于 QIcon 复制。

    • 可选实现 pixmap()、actualSize() 等函数以优化性能或支持特定功能。

  • 添加自定义逻辑:根据需求添加成员变量或构造函数,用于存储图标的参数(如形状、颜色)。

#include <QIconEngine>
#include <QPainter>

class CustomIconEngine : public QIconEngine {
public:
    CustomIconEngine() = default;

    QIconEngine *clone() const override {
        return new CustomIconEngine(*this);
    }

    void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override {
        painter->setRenderHint(QPainter::Antialiasing, true);
        painter->setBrush(Qt::red);
        painter->drawEllipse(rect); // 示例:绘制红色圆形
    }
};

3.3.2、集成第三方 SVG 渲染库或字体图标库

假设使用 Font Awesome 字体,需先将字体文件(如 fa-solid-900.ttf)加载到应用中。确保字体文件已经拷贝到执行目录下:

#include <QApplication>
#include <QPushButton>
#include <QIconEngine>
#include <QPainter>
#include <QCache>

#include <QPainter>
#include <QFontDatabase>
#include <QFont>

class FontIconEngine : public QIconEngine {
public:
    FontIconEngine(const QString &iconCode, const QColor &color, qreal size)
        : iconCode_(iconCode), color_(color), size_(size) {
        // 加载字体
        QFontDatabase db;
        int fontId = db.addApplicationFont("fa-solid-900.ttf");
        fontFamily_ = db.applicationFontFamilies(fontId).first();
    }

    QIconEngine *clone() const override {
        return new FontIconEngine(iconCode_, color_, size_);
    }

    void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override {
        painter->setRenderHint(QPainter::Antialiasing, true);

        // 设置字体
        QFont font(fontFamily_);
        font.setPixelSize(qMin(rect.width(), rect.height()) * 0.8);
        painter->setFont(font);

        // 根据模式调整颜色
        QColor renderColor = color_;
        if (mode == QIcon::Disabled) {
            renderColor = renderColor.lighter(150);
            painter->setOpacity(0.5);
        } else if (mode == QIcon::Selected) {
            renderColor = Qt::blue;
        }
        painter->setPen(renderColor);

        // 居中绘制图标
        painter->drawText(rect, Qt::AlignCenter, iconCode_);
    }

private:
    QString iconCode_; // 字体图标的 Unicode 码点(如 "\uf015" 表示家图标)
    QColor color_;
    qreal size_;
    QString fontFamily_;
};


int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    // 创建 Font Awesome 家图标,
    QIcon homeIcon(new FontIconEngine(QString(QChar(0xf015)), Qt::black, 32));

    // 创建按钮并设置图标
    QPushButton button;
    button.setIcon(homeIcon);
    button.setIconSize(QSize(64, 64));
    button.setText("Home Icon");
    button.resize(200, 100);
    button.show();

    return app.exec();
}


效果:

3.4、将自定义引擎与 QIcon 关联使用

创建引擎实例:实例化自定义的 QIconEngine 子类。

DynamicIconEngine *engine = new DynamicIconEngine(DynamicIconEngine::Circle, Qt::red);

创建 QIcon:使用 QIcon(QIconEngine *engine) 构造函数,将引擎传递给 QIcon。

QIcon icon(engine);

应用到控件:将 QIcon 设置到按钮、工具栏或其他支持图标的控件上。

QPushButton button;
button.setIcon(icon);
button.setIconSize(QSize(64, 64));

九. 平台和渲染后端

提供平台特定集成和渲染后端支持。

  • QPlatformIntegration: 平台特定的窗口系统集成(Windows、X11、Wayland 等)。

  • QRasterPaintEngine: 软件光栅化渲染引擎。

  • QPlatformSurface: 平台特定的渲染表面。

  • QPlatformTheme: 平台主题(如按钮样式、对话框风格)。

  • QPlatformGraphicsBuffer: 平台特定的图形缓冲区。

  • QPlatformSharedGraphicsCache: 共享图形缓存,加速渲染。

十. 国际化(GUI 相关)

支持 GUI 相关的字符编码和区域设置。

  • QTextCodec(部分):字符编码支持(仅限 GUI 文本显示)。

  • QLocale(部分):区域设置(仅限 GUI 格式,如日期、数字显示)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旭唐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值