目录
3.6、QNativeGestureEvent: 平台特定手势
3.7、 QInputMethod, QInputMethodEvent:输入法支持
一. 窗口和屏幕管理
提供跨平台窗口创建、管理以及屏幕信息访问功能。
请跳转章节,此处不再重复:QtGUI模块功能详细说明,窗口和屏幕管理(一)
二. 绘图和渲染
2D 绘图是指在二维平面(通常以像素为单位)上绘制图形、图像和文本的过程。
请跳转章节,此处不再重复:QtGUI模块功能详细说明,图形绘制与渲染(二)
三. 图像处理
Qt 的图像处理功能核心类包括 QImage、QPixmap、QBitmap 和 QPainter。
请跳转章节,此处不再重复:QtGUI模块功能详细说明,图像处理(三)
四. 字体和文本
提供字体管理和低级文本渲染功能。
请跳转章节,此处不再重复:QtGUI模块功能详细说明, 字体和文本渲染(四)
五. 事件和输入处理
1、GUI 事件驱动编程基础
图形用户界面(GUI)是现代应用程序与用户交互的核心方式,其背后的事件驱动编程范式是实现动态、响应式用户体验的基础。
1.1、GUI 应用程序的事件驱动模型概述
事件驱动编程是 GUI 应用程序开发的核心范式,其设计理念是让程序根据用户或系统的输入(称为“事件”)动态响应。
1.1.1、事件的概念:
-
事件是用户或系统触发的动作,例如鼠标点击、键盘输入、窗口调整大小或定时器到期。
-
事件通常由操作系统的输入设备或其他外部源(如网络)生成,并通过 GUI 框架传递到应用程序。
1.1.2、事件驱动模型的工作原理:
-
事件循环(Event Loop):GUI 应用程序运行一个主事件循环,持续监听和处理事件。事件循环从事件队列中获取事件,并分派给相应的处理程序。
-
事件队列(Event Queue):事件按发生顺序存储在队列中,等待处理。这种机制确保事件按序处理,避免竞争条件。
-
事件处理器(Event Handler):每个事件都关联一个处理器(或回调函数),负责执行特定逻辑。例如,点击按钮可能触发一个显示对话框的函数。
1.1.3、典型应用场景:
-
用户交互:处理按钮点击、文本输入等。
-
系统通知:响应窗口关闭、屏幕分辨率变化等。
-
异步操作:处理定时器、网络请求等非阻塞任务。
1.2、Qt 事件处理机制的特点与优势
1.2.1、特点:
-
事件对象(QEvent):Qt 使用 QEvent 类及其子类(如 QMouseEvent、QKeyEvent)封装事件信息,包含事件类型、触发时间、位置等详细信息。
-
事件分派机制:Qt 的事件循环(由 QApplication 或 QCoreApplication 管理)负责将事件分派到目标对象(通常是 QObject 派生类,如窗口或控件)。
-
信号与槽(Signals and Slots):Qt 提供了一种高级的事件处理机制,通过信号(事件触发)和槽(处理函数)的松耦合连接,简化了事件处理逻辑的编写。
-
事件过滤器(Event Filters):Qt 允许开发者在事件到达目标对象之前拦截和处理事件,提供更高的灵活性。
-
层次化事件处理:事件可以被父控件或子控件捕获,允许分层处理复杂交互。
1.2.2、优势:
-
跨平台一致性:Qt 的事件处理机制屏蔽了底层操作系统差异,确保在 Windows、macOS 和 Linux 上行为一致。
-
高效性:Qt 的事件循环和分派机制经过优化,适合高性能 GUI 应用。
-
灵活性:支持从低级事件处理(如重写 event() 函数)到高级信号与槽机制,满足不同开发需求。
-
可扩展性:开发者可以自定义事件类型,扩展 Qt 的事件系统以支持特定需求。
-
调试友好:Qt 提供工具(如 Qt Creator)帮助开发者跟踪和调试事件流。
2、Qt 事件处理核心概念
Qt 的事件处理系统是其 GUI 框架的核心,提供了高效、灵活的方式来响应用户交互和系统通知。
2.1、事件循环 (Event Loop)
事件循环是 Qt 应用程序运行的“心脏”,负责监听、调度和分派事件。
2.1.1、作用原理与事件调度
作用:事件循环持续运行,监听来自用户(鼠标、键盘等)、系统(窗口变化、定时器等)或其他源(如网络)的事件,并将事件分派到目标对象。
事件调度流程:
-
操作系统或输入设备生成原始事件(如鼠标点击)。
-
Qt 将原始事件转换为 QEvent 对象并放入事件队列。
-
事件循环从队列中取出事件,按照优先级和顺序分派给目标 QObject(通常是控件或窗口)。
-
目标对象的事件处理器(如虚函数或信号槽)处理事件。
事件队列:Qt 使用先进先出(FIFO)队列存储事件,但某些高优先级事件(如窗口重绘)可能被优先处理。
异步性:事件循环是非阻塞的,允许应用程序在等待事件时保持响应。
2.1.2、QCoreApplication::exec() 与事件处理流程
exec() 方法:
-
QCoreApplication::exec()(或其子类 QApplication::exec())启动主事件循环。
-
它是 Qt 应用程序运行的入口,通常在 main() 函数中调用。
事件处理流程:
-
exec() 初始化事件循环并进入阻塞状态,等待事件。
-
当事件到达,事件循环调用 QEventDispatcher 将事件分派到目标对象。
-
目标对象的 event() 方法或特定虚函数处理事件。
-
处理完成后,事件循环继续监听下一事件。
退出机制:调用 QCoreApplication::quit() 或关闭主窗口可终止事件循环,返回控制权到 main()。
2.2、事件对象 (QEvent 及其子类)
QEvent 是 Qt 事件系统的核心类,封装了事件的类型和相关数据。
2.2.1、QEvent 基类属性
-
type():返回事件的类型(如 QEvent::MouseButtonPress、QEvent::KeyPress)
-
accept():标记事件已被处理,阻止其进一步传播
-
ignore():标记事件未被处理,允许其继续传播到父对象或其他处理器
-
spontaneous():返回布尔值,指示事件是否由系统生成(true)或由程序手动发送(false)
2.2.2、事件的创建、发送与生命周期
创建:
-
Qt 自动将操作系统事件转换为 QEvent 子类(如 QMouseEvent、QKeyEvent)。
-
开发者可手动创建事件:
QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, QPointF(100, 100), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
发送:
-
同步发送:QCoreApplication::sendEvent(receiver, event) 立即处理事件。
-
异步发送:QCoreApplication::postEvent(receiver, event) 将事件放入队列,稍后处理。
QCoreApplication::postEvent(widget, new QMouseEvent(...));
生命周期:
-
事件创建(由系统或程序)。
-
事件进入队列(异步)或直接分派(同步)。
-
目标对象的 event() 方法或特定虚函数处理事件。
-
事件销毁(通常由 Qt 自动管理,开发者无需手动删除)。
2.3、事件处理方式
Qt 提供了多种事件处理方式,适用于不同场景。
2.3.1、重载特定虚函数
特定虚函数(如 mousePressEvent()、keyPressEvent())是处理特定事件类型的便捷方式。
实现方式:在 QWidget 或其子类中重载虚函数。
class MyWidget : public QWidget {
protected:
void mousePressEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
qDebug() << "Left button pressed at" << event->pos();
}
event->accept();
}
};
2.3.2、重载 QObject::event() 方法
event() 是所有事件的分发入口,适合处理自定义或非标准事件。
实现方式:重载 QObject::event(),手动检查事件类型并处理。
bool MyWidget::event(QEvent *event) {
if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << "Mouse pressed at" << mouseEvent->pos();
return true; // 事件已处理
}
return QWidget::event(event); // 转发给基类
}
通用事件分发入口:
-
event() 是 Qt 事件系统的核心,所有事件首先到达这里。
-
子类可拦截任何事件类型,包括自定义事件。
注意事项:
-
必须手动检查事件类型,代码可能较复杂。
-
需调用基类 event() 处理未拦截的事件,避免破坏默认行为。
-
返回 true 表示事件已处理,false 表示继续传播。
2.3.3、事件过滤器 (Event Filter)
事件过滤器允许在事件到达目标对象之前拦截和处理。
原理与实现:使用 installEventFilter() 安装过滤器,eventFilter() 处理事件。
class MyWidget : public QWidget {
public:
MyWidget() {
childWidget->installEventFilter(this);
}
protected:
bool eventFilter(QObject *obj, QEvent *event) override {
if (obj == childWidget && event->type() == QEvent::MouseButtonPress) {
qDebug() << "Child widget clicked";
return true; // 阻止事件到达子控件
}
return QWidget::eventFilter(obj, event);
}
private:
QWidget *childWidget;
};
应用场景:
-
全局拦截:监控应用程序中所有控件的事件。
-
父对象处理子对象事件:集中管理子控件的行为。
-
动态行为:无需修改子控件代码即可改变其事件处理逻辑。
2.3.4、信号与槽 (Signals & Slots) 与事件的关系
信号与槽是 Qt 提供的高级事件处理机制,简化了事件处理逻辑。
作为事件处理的高级抽象:
-
信号(Signal):表示事件发生(如按钮点击发出 clicked() 信号)。
-
槽(Slot):处理信号的函数,与信号连接。
connect(button, &QPushButton::clicked, this, &MyWidget::handleButtonClick);
内部事件到信号的转换机制:
Qt 内部将某些事件(如 QMouseEvent)转换为信号(如 clicked())。
-
事件到达控件(如按钮)的 event() 或虚函数。
-
控件识别事件并触发相应信号。
-
信号通过 QObject::connect() 调用槽函数。
与事件的区别:
-
事件是低级机制,直接与操作系统交互。
-
信号与槽是高级抽象,基于事件但更易用。
2.4、事件传播机制
Qt 的事件传播机制决定了事件如何在对象之间传递。
2.4.1、事件的发送方向与接收顺序
发送方向:
-
事件首先发送到目标对象(通常是焦点控件或鼠标所在控件)。
-
如果目标对象未处理(ignore()),事件可能传播到其父对象。
接收顺序:
-
事件过滤器(若安装)优先于目标对象的 event() 方法。
-
对于控件层次,子控件优先于父控件。
2.4.2、event->accept() 和 event->ignore() 对传播链的影响
-
accept():标记事件已处理,终止传播。
-
ignore():标记事件未处理,允许传播到父对象或下一个处理器。
2.4.3、父子控件间的事件传递(冒泡与穿透概念)
-
冒泡(Event Bubbling):如果子控件未处理事件,事件“冒泡”到父控件。
-
穿透(Event Propagation):某些事件(如鼠标事件)可能穿透到下层控件。
-
实现细节:
-
Qt 通过控件层次结构(QWidget 的 parent-child 关系)管理事件传递。
-
开发者可通过 ignore() 或事件过滤器控制传播路径。
-
2.5、焦点管理:键盘事件的关键前提
焦点管理决定了哪个控件接收键盘事件,是键盘交互的基础。
setFocusPolicy():定义控件的焦点获取方式。
-
Qt::NoFocus:不接受焦点。
-
Qt::TabFocus:通过 Tab 键获取焦点。
-
Qt::ClickFocus:通过鼠标点击获取焦点。
-
Qt::StrongFocus:支持 Tab 和点击。
button->setFocusPolicy(Qt::StrongFocus);
setFocus():主动设置焦点到指定控件。
textEdit->setFocus(); // 使文本框获得焦点
3、GUI 交互事件
Qt 的事件系统通过 QEvent 及其子类处理用户交互和系统通知。
3.1、QMouseEvent: 鼠标点击、移动事件
QMouseEvent 表示鼠标相关的交互事件,如点击、双击、移动等。
事件类型:
-
MouseButtonPress 鼠标按下
-
MouseButtonRelease 鼠标抬起
-
MouseButtonDblClick 鼠标双击
-
MouseMove 鼠标移动
位置信息:
-
pos():返回鼠标位置(相对于控件,整数坐标,QPoint)。
-
localPos():返回鼠标位置(相对于控件,浮点坐标,QPointF)。
-
globalPos():返回全局屏幕坐标(QPoint)。
按钮状态:
-
button():触发事件的按钮(如 Qt::LeftButton、Qt::RightButton)。
-
buttons():当前按下的按钮组合(可多按钮同时按下)。
修饰键:modifiers() 返回按下的修饰键(如 Qt::ShiftModifier)。
控制事件传播:accept()/ignore()
启用鼠标跟踪(setMouseTracking(true))以接收非按下状态的 mouseMoveEvent。
#include <QWidget>
#include <QMouseEvent>
#include <QDebug>
class MyWidget : public QWidget {
protected:
void mousePressEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
qDebug() << "Left click at" << event->pos();
event->accept();
} else if (event->button() == Qt::RightButton) {
qDebug() << "Right click at" << event->pos();
event->ignore(); // 允许父控件处理
}
}
void mouseMoveEvent(QMouseEvent *event) override {
if (event->buttons() & Qt::LeftButton) {
qDebug() << "Dragging at" << event->pos();
}
}
};
3.2、QHoverEvent: 鼠标悬停事件
QHoverEvent 表示鼠标指针在控件上悬停时的移动事件(不涉及按键)。
事件类型:
-
HoverEnter 悬停进入
-
HoverLeave 悬停离开
-
HoverMove 悬停移动
位置信息:
-
pos():当前鼠标位置(相对于控件,QPoint)。
-
oldPos():前一次悬停位置(QPoint)。
修饰键:modifiers()。
使用场景:
-
实现工具提示(tooltip)或悬停高亮效果。
-
动态更新 UI 元素(如悬停时改变按钮颜色)。
注意事项:
-
必须设置 Qt::WA_Hover 属性(setAttribute(Qt::WA_Hover))以启用悬停事件。
-
悬停事件仅在鼠标未按下时触发。
#include <QWidget>
#include <QHoverEvent>
#include <QDebug>
class MyWidget : public QWidget {
public:
MyWidget() {
setAttribute(Qt::WA_Hover); // 启用悬停事件
}
protected:
bool event(QEvent *event) override {
if (event->type() == QEvent::HoverMove) {
QHoverEvent *hoverEvent = static_cast<QHoverEvent*>(event);
qDebug() << "Hover at" << hoverEvent->pos();
return true;
}
return QWidget::event(event);
}
};
3.3、QWheelEvent: 鼠标滚轮事件
QWheelEvent 表示鼠标滚轮滚动事件。
事件类型:QEvent::Wheel。
滚动量:
-
angleDelta():返回滚轮旋转角度(QPoint,x/y 表示水平/垂直滚动,单位为 1/8 度)。
-
pixelDelta():返回像素级滚动距离(若可用)。
位置信息:
-
pos()(控件坐标)
-
globalPos()(屏幕坐标)。
修饰键:modifiers()。
方向:inverted() 检查滚轮方向是否反转(某些设备可能反转)。
注意事项:
-
通常 angleDelta().y() 用于垂直滚动,angleDelta().x() 用于水平滚动。
-
滚动量可能因设备(鼠标、触控板)而异,需归一化处理。
-
某些平台可能提供 pixelDelta(),优先使用以提高精度。
#include <QWidget>
#include <QWheelEvent>
#include <QDebug>
class MyWidget : public QWidget {
protected:
void wheelEvent(QWheelEvent *event) override {
int delta = event->angleDelta().y();
if (delta > 0) {
qDebug() << "Wheel scrolled up";
} else if (delta < 0) {
qDebug() << "Wheel scrolled down";
}
event->accept();
}
};
3.4、QKeyEvent: 键盘输入事件
QKeyEvent 表示键盘按下、释放或自动重复事件。
事件类型:QEvent::KeyPress、KeyRelease。
按键信息:
-
key():返回按键的虚拟键码(如 Qt::Key_Enter)。
-
text():返回按键的文本(对于可打印字符)。
-
isAutoRepeat():检查是否为自动重复按键。
修饰键:modifiers()(如 Qt::ControlModifier)。
计数:count() 表示按键重复次数。
注意事项:
-
仅焦点控件接收键盘事件(需设置焦点,参考 setFocusPolicy())。
-
text() 仅对可打印字符有效,特殊键需用 key()。
#include <QWidget>
#include <QKeyEvent>
#include <QDebug>
class MyWidget : public QWidget {
protected:
void keyPressEvent(QKeyEvent *event) override {
if (event->key() == Qt::Key_Escape) {
qDebug() << "Escape pressed";
event->accept();
} else if (event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_C) {
qDebug() << "Ctrl+C pressed";
event->accept();
} else {
qDebug() << "Key pressed:" << event->text();
event->ignore();
}
}
};
3.5、QTouchEvent: 触摸输入事件
QTouchEvent 表示触摸屏或触控板的多点触控事件。
事件类型:
-
TouchBegin 触摸开始,压下
-
TouchUpdate 触摸更新
-
TouchEnd 触摸结束,抬起
-
TouchCancel 触摸关闭
触摸点:
-
touchPoints():返回 QTouchEvent::TouchPoint 列表,包含每个触摸点的状态、位置等。
-
TouchPoint 属性:
-
id():触摸点唯一标识。
-
pos():控件坐标。
-
screenPos():屏幕坐标。
-
state():触摸状态(如 Qt::TouchPointPressed、Moved)。
-
设备:device() 返回触摸设备(如触摸屏)。
使用场景:
-
实现多点触控(如捏合缩放、双指旋转)。
-
移动设备的手势交互。
注意事项:
-
需设置 Qt::WA_AcceptTouchEvents 启用触控。
-
处理多点触控时,需跟踪 id() 以区分不同触摸点。
-
某些设备可能触发 TouchCancel,需妥善处理。
#include <QWidget>
#include <QTouchEvent>
#include <QDebug>
class MyWidget : public QWidget {
public:
MyWidget() {
setAttribute(Qt::WA_AcceptTouchEvents); // 启用触控
}
protected:
bool event(QEvent *event) override {
if (event->type() == QEvent::TouchUpdate) {
QTouchEvent *touchEvent = static_cast<QTouchEvent*>(event);
for (const QTouchEvent::TouchPoint &point : touchEvent->touchPoints()) {
qDebug() << "Touch point" << point.id() << "at" << point.pos();
}
return true;
}
return QWidget::event(event);
}
};
3.6、QNativeGestureEvent: 平台特定手势
QNativeGestureEvent 表示特定于平台的复杂手势(如 macOS 的多指手势)。
事件类型:QEvent::NativeGesture。
手势类型:gestureType() 返回手势类型(如 Qt::ZoomNativeGesture、RotateNativeGesture)。
幅度:value():手势的幅度(如缩放比例)。
位置:pos():手势发生位置。
设备:device() 返回触发设备。
#include <QWidget>
#include <QNativeGestureEvent>
#include <QDebug>
class MyWidget : public QWidget {
protected:
bool event(QEvent *event) override {
if (event->type() == QEvent::NativeGesture) {
QNativeGestureEvent *gesture = static_cast<QNativeGestureEvent*>(event);
if (gesture->gestureType() == Qt::ZoomNativeGesture) {
qDebug() << "Zoom gesture, value:" << gesture->value();
return true;
}
}
return QWidget::event(event);
}
};
3.7、 QInputMethod, QInputMethodEvent:输入法支持
QInputMethod:
-
Qt 提供的类,用于管理输入法系统的状态和行为,例如虚拟键盘的显示、语言切换或输入法候选词的管理。
-
它是全局单例,通过 QGuiApplication::inputMethod() 访问。
-
主要负责与底层输入法系统交互,提供应用程序与输入法之间的接口。
QInputMethodEvent:
-
Qt 事件类,表示输入法相关的事件,例如用户通过输入法输入文本、预编辑候选词或提交最终文本。
-
事件类型包括 QEvent::InputMethod(输入法事件)和 QEvent::InputMethodQuery(输入法查询)。
3.7.1、关键属性和方法
QInputMethod
-
状态查询:
-
isVisible():返回输入法是否可见(如虚拟键盘是否显示)。
-
locale():返回输入法的语言环境(如 QLocale("zh_CN") 表示中文)。
-
inputDirection():返回文本输入方向(如 Qt::LeftToRight 或 Qt::RightToLeft)。
-
keyboardRectangle():返回虚拟键盘的屏幕区域(QRectF)。
-
-
控制方法:
-
show():显示输入法(如弹出虚拟键盘)。
-
hide():隐藏输入法。
-
reset():重置输入法状态(清空预编辑文本)。
-
commit():强制提交当前预编辑文本。
-
setInputItemTransform(const QTransform &transform):设置输入控件的坐标变换。
-
-
信号:
-
visibleChanged():输入法可见性变化。
-
keyboardRectangleChanged():虚拟键盘区域变化。
-
localeChanged():语言环境变化。
-
QInputMethodEvent
-
事件类型:
-
QEvent::InputMethod:输入法生成文本或更新预编辑状态。
-
QEvent::InputMethodQuery:输入法查询控件属性(如光标位置)。
-
-
关键方法:
-
preeditString():返回预编辑文本(输入法候选词,如拼音输入时的临时文本)。
-
commitString():返回提交的最终文本(用户确认后的文本)。
-
replacementStart():返回替换文本的起始位置(相对于光标)。
-
replacementLength():返回替换文本的长度。
-
attributes():返回输入法特定属性(如高亮、选中范围)。
-
-
属性类型(QInputMethodEvent::Attribute):
-
TextFormat:格式化预编辑文本(如加下划线)。
-
Cursor:光标位置。
-
Selection:选中文本范围。
-
3.7.2、实施原理
QInputMethod 与操作系统的输入法框架交互:
-
Mobile:依赖 iOS/Android 的输入法 API。
-
Linux:使用 XIM、IBus、Fcitx 或 Wayland 输入协议。
-
macOS:使用 NSTextInputContext。
-
Windows:使用 IMM(Input Method Manager)或 TSF(Text Services Framework)。
事件流程:
-
输入法生成文本或状态变化,系统通知 Qt。
-
Qt 创建 QInputMethodEvent,通过事件循环分派到焦点控件。
-
控件处理事件,更新文本或 UI(如显示预编辑文本)。
查询机制:
-
输入法通过 QEvent::InputMethodQuery 查询控件状态(如光标位置、文本格式)。
-
控件通过 inputMethodQuery() 响应查询。
预编辑与提交:
-
预编辑文本(preeditString)是输入法暂存的候选词,显示为临时状态(如加下划线)。
-
提交文本(commitString)是用户确认后的最终输入。
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QInputMethod>
#include <QInputMethodEvent>
#include <QDebug>
class InputWidget : public QWidget {
public:
InputWidget(QWidget *parent = nullptr) : QWidget(parent) {
// 启用输入法支持
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_InputMethodEnabled);
// UI 布局
QVBoxLayout *layout = new QVBoxLayout(this);
inputLabel = new QLabel("Input: ", this);
preeditLabel = new QLabel("Preedit: ", this);
showKeyboardButton = new QPushButton("Show Virtual Keyboard", this);
hideKeyboardButton = new QPushButton("Hide Virtual Keyboard", this);
layout->addWidget(inputLabel);
layout->addWidget(preeditLabel);
layout->addWidget(showKeyboardButton);
layout->addWidget(hideKeyboardButton);
// 获取输入法
inputMethod = QGuiApplication::inputMethod();
// 连接按钮信号
connect(showKeyboardButton, &QPushButton::clicked, this, &InputWidget::showKeyboard);
connect(hideKeyboardButton, &QPushButton::clicked, this, &InputWidget::hideKeyboard);
}
protected:
void inputMethodEvent(QInputMethodEvent *event) override {
// 处理预编辑文本
if (!event->preeditString().isEmpty()) {
preeditLabel->setText("Preedit: " + event->preeditString());
qDebug() << "Preedit:" << event->preeditString();
} else {
preeditLabel->setText("Preedit: ");
}
// 处理提交文本
if (!event->commitString().isEmpty()) {
currentText += event->commitString();
inputLabel->setText("Input: " + currentText);
qDebug() << "Commit:" << event->commitString();
}
// 处理替换
if (event->replacementLength() > 0) {
currentText.remove(event->replacementStart(), event->replacementLength());
inputLabel->setText("Input: " + currentText);
}
// 处理属性(如高亮)
for (const QInputMethodEvent::Attribute &attr : event->attributes()) {
if (attr.type == QInputMethodEvent::TextFormat) {
qDebug() << "Text format attribute at" << attr.start << "length" << attr.length;
}
}
event->accept();
}
QVariant inputMethodQuery(Qt::InputMethodQuery query) const override {
switch (query) {
case Qt::ImCursorPosition:
return currentText.length(); // 光标在文本末尾
case Qt::ImFont:
return font(); // 返回控件字体
case Qt::ImCursorRectangle:
return QRect(10, 10, 10, 10); // 简化的光标矩形
default:
return QVariant();
}
}
private:
void showKeyboard() {
inputMethod->show();
setFocus(); // 确保控件获得焦点
qDebug() << "Virtual keyboard shown";
}
void hideKeyboard() {
inputMethod->hide();
qDebug() << "Virtual keyboard hidden";
}
QInputMethod *inputMethod;
QLabel *inputLabel;
QLabel *preeditLabel;
QPushButton *showKeyboardButton;
QPushButton *hideKeyboardButton;
QString currentText; // 存储输入的文本
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
InputWidget widget;
widget.resize(300, 200);
widget.show();
return app.exec();
}
3.8、QDrag, QDropEvent: 拖放操作
-
QDrag:Qt 提供的类,用于发起拖放操作,负责将数据从源控件拖动到目标控件。它封装了拖放的数据和视觉效果(如拖动图标)。
-
QDropEvent:Qt 事件类,表示拖放操作的目标接收到拖放数据时触发的事件。它包含拖放数据、位置和建议的操作(如复制或移动)。
相关事件类型:
-
QEvent::DragEnter:拖动进入目标控件。
-
QEvent::DragMove:拖动在目标控件内移动。
-
QEvent::DragLeave:拖动离开目标控件。
-
QEvent::Drop:数据被放置到目标控件。
3.8.1、关键属性和方法
QDrag
-
构造:QDrag(QObject *dragSource),指定拖放的源对象(通常是控件)。
-
数据设置:setMimeData(QMimeData *data):设置拖放数据(如文本、图像、URL)。
-
视觉效果:
-
setPixmap(const QPixmap &pixmap):设置拖动时的图标。
-
setHotSpot(const QPoint &hotspot):设置图标的热点(相对于图标的偏移)。
-
-
执行拖放:
-
exec(Qt::DropActions supportedActions = Qt::CopyAction, Qt::DropAction defaultDropAction = Qt::IgnoreAction):
-
启动拖放操作,返回最终执行的操作(如 Qt::CopyAction)。
-
supportedActions:支持的操作(如 Qt::CopyAction、Qt::MoveAction)。
-
-
-
setDragCursor(const QPixmap &pixmap, Qt::DropAction action):为特定操作设置自定义光标。
QDropEvent
-
事件类型:QEvent::Drop(数据放置)、DragEnter(进入)、DragMove(移动)、DragLeave(离开)。
-
数据访问:mimeData() const:返回拖放的 QMimeData(包含文本、图像等)。
-
位置:pos():拖放位置(控件坐标,QPoint)。posF():拖放位置(控件坐标,QPointF,更高精度)。
-
操作控制:
-
proposedAction():建议的操作(如 Qt::CopyAction)。
-
acceptProposedAction():接受建议操作。
-
dropAction():实际执行的操作。
-
setDropAction(Qt::DropAction action):设置执行的操作。
-
-
事件控制:accept()/ignore():控制事件是否继续传播。
QMimeData 是拖放数据的核心类,支持多种格式:
-
setText()、text():文本数据。
-
setHtml()、html():HTML 数据。
-
setUrls()、urls():文件或 URL 列表。
-
setData(const QString &mimeType, const QByteArray &data):自定义 MIME 类型。
-
hasFormat(const QString &mimeType):检查是否支持某 MIME 类型。
3.8.2、实现原理
拖放流程:
-
发起:用户在源控件触发拖动(如鼠标按下并移动),QDrag 创建并设置 QMimeData。
-
传输:Qt 与系统拖放机制交互(如 Windows 的 OLE、macOS 的 NSPasteboard、Linux 的 X11 拖放协议)。
-
接收:目标控件接收 DragEnter、DragMove 和 Drop 事件,验证并处理数据。
事件驱动:拖放操作通过 Qt 的事件循环分派,QDrag 和 QDropEvent 分别处理源和目标的行为。
MIME 数据:拖放数据以 MIME 格式存储,允许目标应用程序选择合适的格式。
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QLabel>
#include <QDrag>
#include <QDropEvent>
#include <QMimeData>
#include <QDebug>
class DragWidget : public QLabel {
public:
DragWidget(const QString &text, QWidget *parent = nullptr) : QLabel(text, parent) {
setStyleSheet("background-color: lightblue; padding: 10px;");
setFixedSize(100, 50);
}
protected:
void mousePressEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
QDrag *drag = new QDrag(this);
QMimeData *mimeData = new QMimeData;
mimeData->setText(text());
drag->setMimeData(mimeData);
drag->setPixmap(grab());
drag->setHotSpot(QPoint(10, 10));
Qt::DropAction action = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
qDebug() << "Drag result:" << action;
}
}
};
class DropWidget : public QWidget {
public:
DropWidget(QWidget *parent = nullptr) : QWidget(parent) {
setAcceptDrops(true);
setStyleSheet("background-color: lightgreen; border: 2px dashed black;");
setMinimumSize(200, 200);
QVBoxLayout *layout = new QVBoxLayout(this);
label = new QLabel("Drop here", this);
label->setAlignment(Qt::AlignCenter);
layout->addWidget(label);
}
protected:
void dragEnterEvent(QDragEnterEvent *event) override {
if (event->mimeData()->hasText() || event->mimeData()->hasUrls()) {
event->acceptProposedAction();
} else {
event->ignore();
}
}
void dragMoveEvent(QDragMoveEvent *event) override {
event->acceptProposedAction();
}
void dropEvent(QDropEvent *event) override {
const QMimeData *mimeData = event->mimeData(); // Corrected line
if (mimeData->hasText()) {
label->setText(mimeData->text());
qDebug() << "Dropped text:" << mimeData->text();
} else if (mimeData->hasUrls()) {
QStringList paths;
for (const QUrl &url : mimeData->urls()) {
paths << url.toLocalFile();
}
label->setText(paths.join("\n"));
qDebug() << "Dropped files:" << paths;
}
event->acceptProposedAction();
}
private:
QLabel *label;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget window;
QVBoxLayout *layout = new QVBoxLayout(&window);
DragWidget *dragWidget = new DragWidget("Drag Me!");
DropWidget *dropWidget = new DropWidget;
layout->addWidget(dragWidget);
layout->addWidget(dropWidget);
window.setWindowTitle("Drag and Drop Example");
window.resize(300, 400);
window.show();
return app.exec();
}
3.9、QClipboard: 剪贴板访问
QClipboard 是 Qt 提供的类,用于访问和操作系统剪贴板,允许应用程序读取或写入剪贴板中的数据(如文本、图像或自定义格式)。它支持跨应用程序的数据共享,是实现复制、粘贴功能的核心组件。
-
访问方式:通过 QApplication::clipboard() 获取全局剪贴板实例。
-
数据类型:支持文本、图像、HTML、自定义 MIME 类型等多种数据格式。
3.9.1、关键属性和方法
基本数据操作:
-
setText(const QString &text, Mode mode = Clipboard):设置剪贴板的文本内容。
-
text(Mode mode = Clipboard) const:获取剪贴板的文本内容。
-
setImage(const QImage &image, Mode mode = Clipboard):设置剪贴板的图像。
-
image(Mode mode = Clipboard) const:获取剪贴板的图像。
-
setPixmap(const QPixmap &pixmap, Mode mode = Clipboard):设置剪贴板的像素图。
-
pixmap(Mode mode = Clipboard) const:获取剪贴板的像素图。
自定义数据:
-
setMimeData(QMimeData *data, Mode mode = Clipboard):设置自定义 MIME 类型的数据(如 HTML、文件 URL)。
-
mimeData(Mode mode = Clipboard) const:获取剪贴板的 MIME 数据。
模式(Mode):
-
QClipboard::Clipboard:标准剪贴板(默认,用于跨应用复制/粘贴)。
-
QClipboard::Selection:鼠标选择剪贴板(仅在 X11 系统支持,如 Linux)。
-
QClipboard::FindBuffer:搜索缓冲区(特定于某些平台)。
-
示例:clipboard->setText("Selected text", QClipboard::Selection);
信号:
-
dataChanged():当剪贴板内容变化时触发。
-
selectionChanged():当 X11 选择剪贴板变化时触发。
-
findBufferChanged():当搜索缓冲区变化时触发。
其他方法:
-
clear(Mode mode = Clipboard):清空剪贴板。
-
ownsClipboard() const:检查当前应用程序是否拥有剪贴板。
-
supportsSelection() const:检查是否支持选择剪贴板(主要针对 X11)。
3.9.2、实现原理
底层机制:
-
QClipboard 通过 Qt 的平台抽象层与操作系统的剪贴板 API 交互:
-
Windows:使用 Clipboard API(如 SetClipboardData)。
-
macOS:使用 NSPasteboard。
-
Linux:使用 X11 的 CLIPBOARD 和 PRIMARY 选择(或 Wayland 的剪贴板协议)。
-
数据存储:
-
剪贴板数据以 MIME 格式存储,支持多种类型(如 text/plain、image/png)。
-
QMimeData 是 Qt 用于封装剪贴板数据的核心类,允许同时存储多种格式。
事件驱动:
-
剪贴板变化由系统通知,Qt 将其转换为 dataChanged() 信号。
-
应用程序通过事件循环处理剪贴板操作。
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QLabel>
#include <QClipboard>
#include <QMimeData>
#include <QDebug>
#include <QPainter>
class ClipboardWidget : public QWidget {
public:
ClipboardWidget(QWidget *parent = nullptr) : QWidget(parent) {
// 初始化 UI
QVBoxLayout *layout = new QVBoxLayout(this);
textEdit = new QTextEdit(this);
copyTextButton = new QPushButton("Copy Text", this);
pasteTextButton = new QPushButton("Paste Text", this);
copyImageButton = new QPushButton("Copy Image", this);
imageLabel = new QLabel("No Image", this);
imageLabel->setMinimumSize(200, 200);
layout->addWidget(textEdit);
layout->addWidget(copyTextButton);
layout->addWidget(pasteTextButton);
layout->addWidget(copyImageButton);
layout->addWidget(imageLabel);
// 获取剪贴板
clipboard = QApplication::clipboard();
// 连接按钮信号
connect(copyTextButton, &QPushButton::clicked, this, &ClipboardWidget::copyText);
connect(pasteTextButton, &QPushButton::clicked, this, &ClipboardWidget::pasteText);
connect(copyImageButton, &QPushButton::clicked, this, &ClipboardWidget::copyImage);
// 监控剪贴板变化
connect(clipboard, &QClipboard::dataChanged, this, &ClipboardWidget::onClipboardChanged);
}
private:
void copyText() {
QString text = textEdit->toPlainText();
if (!text.isEmpty()) {
clipboard->setText(text);
qDebug() << "Copied text:" << text;
}
}
void pasteText() {
QString text = clipboard->text();
if (!text.isEmpty()) {
textEdit->setText(text);
qDebug() << "Pasted text:" << text;
}
}
void copyImage() {
// 创建示例图像
QImage image(200, 200, QImage::Format_RGB32);
image.fill(Qt::blue);
QPainter painter(&image);
painter.setPen(Qt::white);
painter.drawText(50, 100, "Sample Image");
clipboard->setImage(image);
imageLabel->setPixmap(QPixmap::fromImage(image));
qDebug() << "Copied image";
}
void onClipboardChanged() {
// 检查剪贴板内容
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
qDebug() << "Clipboard text changed:" << clipboard->text();
}
if (mimeData->hasImage()) {
QImage image = clipboard->image();
imageLabel->setPixmap(QPixmap::fromImage(image));
qDebug() << "Clipboard image changed";
}
}
QClipboard *clipboard;
QTextEdit *textEdit;
QPushButton *copyTextButton;
QPushButton *pasteTextButton;
QPushButton *copyImageButton;
QLabel *imageLabel;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ClipboardWidget widget;
widget.show();
return app.exec();
}
3.10、QTabletEvent: 数位板输入事件
QTabletEvent 是 Qt 事件系统中的一种事件,用于处理数位板(如 Wacom、Huion 等图形输入板)或支持压感的手写板设备的输入。它支持高级输入特性,如压力、倾斜角度和笔类型,适用于需要精确控制的绘图或手写应用。
事件类型:
-
QEvent::TabletPress:笔接触数位板。
-
QEvent::TabletMove:笔在数位板上移动(无论是否接触)。
-
QEvent::TabletRelease:笔离开数位板。
-
所属类:QTabletEvent 继承自 QInputEvent。
触发时机:当用户使用数位板设备(如压感笔或橡皮擦)与应用程序交互时触发。
3.10.1、关键属性和方法
位置信息:
-
pos():返回控件坐标系中的整数位置(QPoint)。
-
posF():返回控件坐标系中的浮点位置(QPointF,更高精度)。
-
globalPos():返回屏幕坐标系中的整数位置(QPoint)。
-
globalPosF():返回屏幕坐标系中的浮点位置(QPointF)。
压力:pressure():返回笔的压力值(范围 [0.0, 1.0],0 表示无压力,1 表示最大压力)。
倾斜:
-
xTilt():笔在 X 轴的倾斜角度(范围 [-60, 60] 度,相对于垂直方向)。
-
yTilt():笔在 Y 轴的倾斜角度(范围 [-60, 60] 度)。
设备和笔类型:
-
device():返回设备类型(如 QTabletEvent::Pen、Eraser、Airbrush)。
-
pointerType():返回笔类型(如 QTabletEvent::Pen、Eraser、Cursor)。
唯一标识:uniqueId():返回设备的唯一标识,用于区分多个数位板设备。
Z 轴位置:z():返回笔的 Z 轴位置(仅部分设备支持,如悬浮高度)。
修饰键:modifiers():返回按下的修饰键(如 Qt::ShiftModifier)。
事件控制:accept()/ignore():控制事件是否继续传播。
3.10.2、实现原理
-
触发机制:
-
数位板设备通过驱动程序向操作系统发送输入数据(如位置、压力、倾斜)。
-
Qt 的平台抽象层(Windows Ink、X11、macOS 等)将这些数据转换为 QTabletEvent。
-
事件通过 Qt 的事件循环分派到焦点控件或目标窗口。
-
-
与 QMouseEvent 的关系:
-
当数位板事件触发时,Qt 通常也会生成对应的 QMouseEvent(如 TabletPress 伴随 MouseButtonPress)。
-
QTabletEvent 优先级高于 QMouseEvent,开发者可通过 accept() 阻止鼠标事件传播。
-
#include <QWidget>
#include <QTabletEvent>
#include <QPainter>
#include <QDebug>
class TabletWidget : public QWidget {
public:
TabletWidget(QWidget *parent = nullptr) : QWidget(parent) {
setMinimumSize(400, 300);
// 初始化画布
canvas = QImage(size(), QImage::Format_ARGB32);
canvas.fill(Qt::white);
lastPoint = QPointF();
}
protected:
void tabletEvent(QTabletEvent *event) override {
switch (event->type()) {
case QEvent::TabletPress: {
lastPoint = event->posF();
drawLine(event);
event->accept();
break;
}
case QEvent::TabletMove: {
drawLine(event);
event->accept();
break;
}
case QEvent::TabletRelease: {
lastPoint = QPointF(); // 重置起点
event->accept();
break;
}
default:
break;
}
update(); // 触发重绘
}
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
painter.drawImage(0, 0, canvas); // 绘制画布
}
void resizeEvent(QResizeEvent *event) override {
// 调整画布大小
QImage newCanvas(size(), QImage::Format_ARGB32);
newCanvas.fill(Qt::white);
QPainter painter(&newCanvas);
painter.drawImage(0, 0, canvas);
canvas = newCanvas;
QWidget::resizeEvent(event);
}
private:
void drawLine(QTabletEvent *event) {
QPainter painter(&canvas);
painter.setRenderHint(QPainter::Antialiasing);
// 根据设备类型选择笔刷
if (event->pointerType() == QTabletEvent::Eraser) {
painter.setPen(Qt::NoPen);
painter.setBrush(Qt::white);
painter.drawEllipse(event->posF(), 10, 10); // 橡皮擦
} else {
// 压感笔刷:粗细随压力变化
qreal pressure = event->pressure();
int penWidth = pressure * 10; // 最大粗细 10 像素
painter.setPen(QPen(Qt::black, penWidth, Qt::SolidLine, Qt::RoundCap));
painter.drawLine(lastPoint, event->posF());
}
lastPoint = event->posF();
qDebug() << "Tablet event at" << event->posF() << "Pressure:" << event->pressure();
}
QImage canvas; // 画布
QPointF lastPoint; // 上次绘制点
};
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
TabletWidget widget;
widget.show();
return app.exec();
}
3.11、QExposeEvent: 窗口暴露事件
QExposeEvent 是 Qt 事件系统中的一种事件,表示窗口或控件的一部分(或全部)暴露出来,需要重绘。它通常由窗口系统触发,通知应用程序窗口区域变为可见或需要更新。
-
事件类型:QEvent::Expose。
-
所属类:QExposeEvent 继承自 QEvent。
触发时机:当窗口从隐藏变为可见、被调整大小、被移动、被其他窗口遮挡后重新暴露,或窗口内容因其他原因需要重绘时触发。
3.11.1、关键属性和方法
-
region():
-
返回类型:QRegion。
-
描述:表示需要重绘的窗口区域,可能包含多个矩形区域。
-
-
accept()/ignore():
-
accept():标记事件已处理,通常在自定义处理后调用。
-
ignore():允许事件继续传播(很少使用,因为暴露事件通常由 Qt 自动处理)。
-
3.11.2、实现原理
与 QPaintEvent 的关系:
-
QExposeEvent 标记需要重绘的区域。
-
Qt 随后触发 QPaintEvent,调用 paintEvent() 执行实际绘制。
-
开发者通常在 paintEvent() 中处理绘制逻辑,而 exposeEvent() 用于预处理或优化。
避免重载 exposeEvent(除非必要):
-
Qt 通常自动处理 QExposeEvent,直接在 paintEvent() 中处理绘制逻辑即可。
-
仅在需要优化重绘或调试暴露区域时重载 exposeEvent()。
#include <QWidget>
#include <QExposeEvent>
#include <QPainter>
#include <QDebug>
class ExposeWidget : public QWidget {
public:
ExposeWidget(QWidget *parent = nullptr) : QWidget(parent) {
setMinimumSize(400, 300);
}
protected:
void exposeEvent(QExposeEvent *event) {
// 打印暴露区域
QRegion exposedRegion = event->region();
qDebug() << "Exposed region:" << exposedRegion.rects();
// 标记需要重绘
update(exposedRegion); // 触发 paintEvent,仅重绘暴露区域
event->accept();
}
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制背景
painter.fillRect(rect(), Qt::white);
// 绘制暴露区域的边界(仅用于演示)
for (const QRect &exposedRect : event->region().rects()) {
painter.setPen(Qt::red);
painter.drawRect(exposedRect.adjusted(1, 1, -1, -1));
}
// 绘制示例内容(一个蓝色圆)
painter.setBrush(Qt::blue);
painter.drawEllipse(rect().center(), 50, 50);
qDebug() << "Painting region:" << event->region().rects();
}
};
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
ExposeWidget widget;
widget.show();
return app.exec();
}
六. OpenGL 和硬件加速
支持 OpenGL 和 Vulkan 渲染,适用于高性能图形。
-
QOpenGLContext: OpenGL 上下文管理。
-
QOpenGLFunctions, QOpenGLExtraFunctions: OpenGL API 封装。
-
QOpenGLFramebufferObject: 帧缓冲对象,用于离屏渲染。
-
QOpenGLShader, QOpenGLShaderProgram: 着色器支持。
-
QOpenGLTexture: 纹理管理。
-
QOpenGLBuffer: 顶点和索引缓冲区。
-
QOpenGLVertexArrayObject: 顶点数组对象。
-
QOpenGLTimerQuery, QOpenGLTimeMonitor: OpenGL 性能监控。
-
QAbstractOpenGLFunctions (Qt 6): 抽象化的 OpenGL 函数接口。
-
QVulkanInstance, QVulkanWindow (Qt 6): Vulkan 渲染支持。
七. 颜色和外观
管理颜色和外观设置。
-
QColor: 颜色表示(支持 RGB、HSV、CMYK)。
-
QPalette: 颜色方案管理(前景、背景等)。
-
QColorSpace (Qt 6): 颜色空间管理(支持 ICC 配置文件)。
-
QColormap: 颜色映射(主要用于旧平台)。
八. 图标和光标
支持图标和鼠标光标管理。
-
QIcon: 图标管理,支持多分辨率和状态。
-
QCursor: 鼠标光标样式和自定义形状。
-
QIconEngine: 自定义图标渲染引擎。
九. 平台和渲染后端
提供平台特定集成和渲染后端支持。
-
QPlatformIntegration: 平台特定的窗口系统集成(Windows、X11、Wayland 等)。
-
QRasterPaintEngine: 软件光栅化渲染引擎。
-
QPlatformSurface: 平台特定的渲染表面。
-
QPlatformTheme: 平台主题(如按钮样式、对话框风格)。
-
QPlatformGraphicsBuffer: 平台特定的图形缓冲区。
-
QPlatformSharedGraphicsCache: 共享图形缓存,加速渲染。
十. 国际化(GUI 相关)
支持 GUI 相关的字符编码和区域设置。
-
QTextCodec(部分):字符编码支持(仅限 GUI 文本显示)。
-
QLocale(部分):区域设置(仅限 GUI 格式,如日期、数字显示)。