第十章 Qt定时器类QTimer
QTimer是Qt中用于重复或延迟执行函数的类,基于事件循环触发定时器事件。
关键构造函数
// 创建一个定时器,并设置时间间隔
QTimer(int interval, QObject *parent = nullptr);
// 创建一个定时器,时间间隔可通过setInterval()设置
QTimer(QObject *parent = nullptr)
常用函数
//启动或重启定时器,可指定时间间隔
void start(int msec = 0); //毫秒
//停止定时器
void stop();
//设置定时器的时间间隔
void setInterval(int msec);
//获取当前时间间隔。
int interval() const;
//设置定时器是否只触发一次。
void setSingleShot(bool singleShot);
//判断定时器是否只触发一次。
bool isSingleShot() const;
//检查定时器是否处于活动状态。
bool isActive() const;
//设置定时器的精度。
void setTimerType(Qt::TimerType atype);
/*
Qt::PreciseTimer:高精确度定时器,尽量准时触发,但可能受系统负载影响。
Qt::CoarseTimer:低精度定时器,减少CPU占用,可能延迟触发。
Qt::VeryCoarseTimer:极低精度定时器,最小化资源占用,适合不要求精确时间的任务。
*/
//获取当前定时器的精度。
Qt::TimerType timerType() const;
//返回定时器标识符
int timerId() const;//每个QTimer对象都有一个唯一的标识符
信号
//定时器超时时发射的信号。
void timeout();
静态函数
//在指定时间后发射一次信号到指定的接收对象和槽函数
void singleShot(int msec, // 延迟的毫秒数
const QObject *receiver, // 接收信号的对象
PointerToMemberFunction method); // 槽函数地址
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &MyClass::onTimeout);
timer->start(1000); // 每隔1秒触发一次
第十一章 绘制事件
当应用程序收到绘制事件,会调用QWidget::paintEvent()来绘制窗口。
您应该在QWidget子类中重写QWidget::paintEvent()方法,并在其中使用QPainter进行绘图。
有两种方法可请求重绘:
update():添加重绘事件到队列中,可合并重复调用,不闪烁,可指定区域。
repaint():立即产生绘制事件,一般不推荐,只用于特效,可指定区域。
处理绘制事件需重写paintEvent(),并使用QPainter对象绘制。示例代码:
class MyWidget : public QWidget {
protected:
void paintEvent(QPaintEvent *event) override {
QPainter painter(this);
// 绘制代码
}
};
11.1 基本绘制
QPainter 类用于2D绘制操作,其构造函数接受一个 QPaintDevice 对象指针。
QPaintDevice 是2D画布的抽象基类,多个类如 QWidget、QImage、QPixmap 等都继承自它,因此都可作为 QPainter 的绘制目标。
QWidget *widget = new QWidget();
QPainter painter(widget);
// 使用 painter 进行绘制操作
11.2 2D绘图
QPainter 的绘图函数总结如下:
函数 | 功能 | 函数 | 功能 |
---|---|---|---|
drawArc() | 弧 | drawPixmap() | QPixmap表示的图像 |
drawChord() | 弦 | drawPoint() | 点 |
drawConvexPolygon() | 凸多边形 | drawPoints() | 多个点 |
drawEllipse() | 椭圆 | drawPolygon() | 多边形 |
drawImage() | QImage表示的图像 | drawPolyline() | 多折线 |
drawLine() | 线 | drawRect() | 矩形 |
drawLines() | 多条线 | drawRects() | 多个矩形 |
drawPath() | 路径 | drawRoundRect() | 圆角矩形 |
drawPicture() | 按QPainter指令绘制 | drawText() | 文字 |
drawPie() | 扇形 | drawTiledPixmap() | 平铺图像 |
11.3 画笔
画笔属性含线型、线宽、颜色等,
可在构造函数或通过
setStyle(),setWidth(), setBrush(),setCapStyle(), setJoinStyle()等函数设定。
Qt提供6种标准风格(Qt::PenStyle),并支持自定义风格(Qt::CustomDashLine),通过QPen的 setDashPattern()设定。
QPen pen;
pen.setStyle(Qt::DashLine); // 设置线型为虚线
pen.setWidth(2); // 设置线宽
pen.setColor(Qt::red); // 设置颜色
pen.setDashPattern(QVector<qreal>{5, 5}); // 自定义虚线模式
QPainter painter(widget);
painter.setPen(pen); // 应用画笔属性
// 使用painter进行绘制
端点风格(Cap Style)
端点风格决定线宽大于1的线的端点样式,
Qt::PenCapStyle | 说明 |
---|---|
Qt::SquareCap | 线条顶点方形且绘制区域包含端点,延伸半个线宽 |
Qt::FlatCap | 线条顶点方形但绘制区域不包含端点 |
Qt::RoundCap | 线条顶点圆形且绘制区域包含端点 |
QPen pen;
pen.setWidth(2); // 设置线宽
pen.setCapStyle(Qt::RoundCap); // 设置端点风格为圆形
QPainter painter(widget);
painter.setPen(pen); // 应用画笔属性
// 使用painter进行绘制
连接风格(join style)
连接风格定义线宽≥1的线如何连接,
Qt::PenStyle | 说明 |
---|---|
Qt::BevelJoin | 方形顶端相汇 |
Qt::MiterJoin | 尖顶连接 |
Qt::RoundJoin | 圆弧形连接 |
QPen pen;
pen.setWidth(2); // 设置线宽
pen.setJoinStyle(Qt::RoundJoin); // 设置连接风格为圆弧形
QPainter painter(widget);
painter.setPen(pen); // 应用画笔属性
// 使用painter进行绘制
11.4 画刷
在Qt中,图形使用模式画刷QBrush进行填充,它包含填充颜色和风格(填充模式)。
颜色由QColor类表示,支持RGB、HSV、CMYK颜色模型,并包含透明度(alpha)控制。
RGB:面向硬件,由红、绿、蓝三基色混合而成。
HSV/HSL:更符合人对颜色的感觉,由色调、饱和度、亮度组成,常用于颜色选择器。
CMYK:由青、洋红、黄、黑四基色组成,主要用于打印等拷贝设备。
QColor的构造函数:
QColor(int r, int g, int b, int a); // r, g, b为红、绿、蓝分量,a为透明度
// 透明度a的取值范围为0(完全透明)到255(不透明)。
颜色微调函数:
QColor::lighter(int factor);//使颜色变亮。
QColor::darker(int factor);//使颜色变暗。
QRgb类用于保存颜色值,与QColor可相互转换,获取32位RGB颜色值+alpha值。使用qRed、qGreen、qBlue、qAlpha函数可以分别获取颜色的红、绿、蓝和alpha分量。
QRgb orange = qRgb(255, 127, 0); // 创建新颜色
QRgb overlay = qRgb(255, 0, 0, 100); // 含透明度
int red = qRed(orange); // 获取红色分量
int gray = qGray(orange); // 获取灰度值QRgb orange = qRgb(255, 127, 0); // 创建新颜色
模式画刷
QBrush 提供模式化画刷。
/*构造函数*/
QBrush(const QColor *color, //颜色
Qt::BrushStyle style);//画刷风格
11.5 渐变填充
Qt支持渐变填充画刷,包括线性、圆形和圆锥渐变,这些渐变由颜色变化和路径变化组成,且均继承自QGradient类。
/*创建绘制对象*/
QRadialGradient gradient(x1, y1, radius1, x2, y2, radius2);
gradient.setColorAt(0, QColor(255, 0, 0)); // 起点颜色
gradient.setColorAt(1, QColor(0, 0, 255)); // 终点颜色
/*创建画刷对象,绘制对象应用到画刷*/
QBrush brush(gradient); // 创建渐变画刷
/*画刷应用到画板*/
QPainter painter(widget);
painter.setBrush(brush); // 应用画刷到绘制Qpainter画板对象
// 使用 painter 进行绘制操作
此段代码展示了如何使用QRadialGradient构造一个从红色渐变到蓝色的圆形渐变画刷,并应用到QPainter对象上进行绘制。
线性渐变填充:
- 指定两点进行颜色插值。
- 使用QLinearGradient对象设置画刷。
QPainter painter(this);
QLinearGradient linearGradient(0, 0, 100, 100);
linearGradient.setColorAt(0.0, Qt::white);
linearGradient.setColorAt(1.0, Qt::blue);
painter.setBrush(linearGradient);
painter.drawRect(0, 0, 100, 100);
圆形渐变填充:
- 指定圆心、半径和焦点,沿圆心逆时针插值。
- 使用QRadialGradient对象设置画刷。
QPainter painter(this);
QRadialGradient radialGradient(50, 50, 50, 50, 50); // 圆心(50,50),半径50,焦点同圆心
radialGradient.setColorAt(0.0, Qt::white);
radialGradient.setColorAt(1.0, Qt::blue);
painter.setBrush(radialGradient);
painter.drawRect(0, 0, 100, 100);
11.6 示例代码
#ifndef GRADIENTWIDGET_H
#define GRADIENTWIDGET_H
#include <QWidget>
#include <QPaintEvent>
class GradientWidget : public QWidget {
Q_OBJECT // 使用Qt的宏,允许该类使用Qt的信号和槽机制(尽管此例中未使用)
protected:
void paintEvent(QPaintEvent *event) override; // 声明paintEvent方法
};
#endif // GRADIENTWIDGET_H
/*cpp文件*/
void GradientWidget::paintEvent(QPaintEvent *event) {
Q_UNUSED(event); // 由于我们没有使用event参数,所以可以用这个宏来避免编译器警告
QPainter painter(this);
// 定义径向渐变的参数
int x1 = 50, y1 = 50, radius1 = 20; // 内圆
int x2 = 150, y2 = 150, radius2 = 100; // 外圆
// 创建径向(圆形)渐变
QRadialGradient gradient(x1, y1, radius1, x2, y2, radius2);
gradient.setColorAt(0, QColor(255, 0, 0)); // 起点颜色:红色
gradient.setColorAt(1, QColor(0, 0, 255)); // 终点颜色:蓝色
// 创建渐变画刷
QBrush brush(gradient);
// 使用painter绘制一个矩形,该矩形将填充径向渐变
painter.setBrush(brush);
painter.drawRect(0, 0, width(), height()); // 使用小部件的宽度和高度来绘制矩形
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建GradientWidget实例,并设置其尺寸
GradientWidget widget;
widget.resize(200, 200);
// 显示小部件
widget.show();
// 进入Qt应用程序的事件循环
return app.exec();
}
第十二章 事件和事件处理
12.1 事件
Qt是一个基于C++的框架,用于开发带窗口的应用程序,通过事件驱动机制处理用户交互和系统事件。事件处理流程包括事件派发、过滤、分发和处理几个阶段。
Qt事件处理机制通过QApplication的notify()派发事件,
经过可选的事件过滤器QObject::eventFilter(),
由窗口的事件分类器event()函数分发,
最后由具体的事件处理器xxxEvent()处理。
用户可以重写这些处理器来自定义事件响应。
1、事件产生与QApplication:
每个Qt应用程序有一个唯一的QApplication对象,调用其exec()函数开始事件循环。
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget w;
w.show();
return a.exec();
}
2、事件派发:
事件产生后,QApplication的notify()函数将事件发送到指定窗口。
//事件分发器,返回true代表拦截
[override virtual] bool QApplication::notify(QObject *receiver, //指定对象
QEvent *e); //事件
3、事件过滤:
事件在发送过程中可通过事件过滤器QObject::eventFilter()进行处理,默认不过滤。
// 安装事件过滤器
[virtual] bool QObject::eventFilter(QObject *watched, //对象
QEvent *event); //事件
4、事件分发:
窗口的事件分发器对收到的事件进行分类,通过QWidget::event()函数处理。
[override virtual protected] bool QWidget::event(QEvent *event);//返回true代表拦截
5、事件处理:
事件分发器将事件分发给对应的事件处理器,如鼠标事件处理器。
// 鼠标按下事件处理器
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 鼠标释放事件处理器
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 鼠标移动事件处理器
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
12.2 事件处理器函数
Qt事件处理中,事件处理器函数位于处理链末端,对应唯一事件,便于自定义处理。
这些处理器函数作为回调函数在QWidget类中定义为受保护的虚函数,用户可在任意窗口类中重写它们以改变默认行为。
事件处理函数都是受保护的虚函数,virtual protected,函数名 事件描述+Event。
12.2.1 鼠标事件
//鼠标按下事件
//当鼠标左键、鼠标右键、鼠标中键被按下,该函数被自动调用,通过参数可以得到当前按下的是哪个鼠标键。
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
//鼠标释放事件
//当鼠标左键、鼠标右键、鼠标中键被释放,该函数被自动调用,通过参数可以得到当前释放的是哪个鼠标键。
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
//鼠标移动事件
//当鼠标移动(也可以按住一个或多个鼠标键移动),该函数被自动调用,通过参数可以得到在移动过程中哪些鼠标键被按下了。
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
//鼠标双击事件
//当鼠标双击该函数被调用,通过参数可以得到是通过哪个鼠标键进行了双击操作。
[virtual protected] void QWidget::mouseDoubleClickEvent(QMouseEvent *event);
//鼠标进入事件
//当鼠标进入窗口的一瞬间,触发该事件,注意:只在进入的瞬间触发一次该事件。
[virtual protected] void QWidget::enterEvent(QEvent *event);
//鼠标离开事件
//当鼠标离开窗口的一瞬间,触发该事件,注意:只在离开的瞬间触发一次该事件。
[virtual protected] void QWidget::leaveEvent(QEvent *event);
12.2.2 键盘事件
//键盘按下事件
//当键盘上的按键被按下了,该函数被自动调用,通过参数可以得知按下的是哪个键。
[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event);
//键盘释放事件
//当键盘上的按键被释放了,该函数被自动调用,通过参数可以得知释放的是哪个键。
[virtual protected] void QWidget::keyReleaseEvent(QKeyEvent *event);
12.2.3 窗口事件
//窗口重绘事件
//窗口刷新时,对应的事件处理器函数会自动被调用,该函数常用于处理窗口大小变化、窗口显示及绘制背景图等场景,是常被重写的一个函数。
[virtual protected] void QWidget::paintEvent(QPaintEvent *event);
//窗口关闭事件
//窗口关闭按钮被按下时,会调用该函数,该函数可控制窗口是否关闭。
[virtual protected] void QWidget::closeEvent(QCloseEvent *event);
//重置窗口大小事件
//当窗口的大小发生变化,该函数被调用。
[virtual protected] void QWidget::resizeEvent(QResizeEvent *event);
12.3 自定义按钮
利用Qt事件处理器,可轻松创建不规则视觉效果的按钮,显示指定背景图,并在鼠标不同操作阶段展示不同背景。
12.3.1 添加子类
创建一个从QWidget派生的MyButton类(也可以继承QPushButton类),实现自定义按钮:
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include <QWidget>
#include <QPixmap>
class MyButton : public QWidget {
Q_OBJECT
public:
explicit MyButton(QWidget *parent = nullptr);
void setImage(QString normal, QString hover, QString pressed);
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* ev) override; //鼠标点击事件
void mouseReleaseEvent(QMouseEvent* ev) override; //鼠标松开事件
void enterEvent(QEvent* ev) override; //进入窗口事件
void leaveEvent(QEvent* ev) override; //离开窗口事件
void paintEvent(QPaintEvent* ev) override; //窗口重绘事件
private:
QPixmap m_normal, m_hover, m_press, m_current;
};
#endif // MYBUTTON_H
#include "mybutton.h" // 包含自定义按钮类的头文件
#include <QPainter> // 包含QPainter类,用于绘制图形
// 构造函数,初始化MyButton对象
MyButton::MyButton(QWidget *parent) : QWidget(parent) {}
// 设置按钮在不同状态下的图片
// 参数:normal - 普通状态下的图片路径
// hover - 鼠标悬停状态下的图片路径
// pressed - 鼠标按下状态下的图片路径
void MyButton::setImage(QString normal, QString hover, QString pressed) {
m_normal.load(normal); // 加载普通状态图片
m_hover.load(hover); // 加载悬停状态图片
m_press.load(pressed); // 加载按下状态图片
m_current = m_normal; // 设置当前显示图片为普通状态
setFixedSize(m_normal.size()); // 设置按钮大小为图片大小
}
// 鼠标按下事件处理器
// 当鼠标按下按钮时调用
void MyButton::mousePressEvent(QMouseEvent *ev) {
emit clicked(); // 发射点击信号
m_current = m_press; // 设置当前显示图片为按下状态
update(); // 触发重绘事件,更新按钮显示
}
// 鼠标释放事件处理器
// 当鼠标释放按钮时调用
void MyButton::mouseReleaseEvent(QMouseEvent *ev) {
m_current = m_normal; // 设置当前显示图片为普通状态
update(); // 触发重绘事件,更新按钮显示
}
// 鼠标进入事件处理器
// 当鼠标进入按钮区域时调用
void MyButton::enterEvent(QEvent *ev) {
m_current = m_hover; // 设置当前显示图片为悬停状态
update(); // 触发重绘事件,更新按钮显示
}
// 鼠标离开事件处理器
// 当鼠标离开按钮区域时调用
void MyButton::leaveEvent(QEvent *ev) {
m_current = m_normal; // 设置当前显示图片为普通状态
update(); // 触发重绘事件,更新按钮显示
}
// 重绘事件处理器
// 当按钮需要重绘时调用
void MyButton::paintEvent(QPaintEvent *ev) {
QPainter p(this); // 创建QPainter对象,用于在按钮上绘制图形
p.drawPixmap(rect(), m_current); // 在按钮区域绘制当前状态的图片
}
12.3.2 使用自定控件
在Qt中,自定义控件如MyButton(基于QWidget)不能直接拖入UI。需先拖入一个QWidget标准控件,再右键选中[提升为]进行处理。
12.4 事件分发器
12.4.1 QEvent
窗口接收事件后,通过事件分发器(event)分类,再分发给对应的事件处理器处理。
[override virtual protected] bool QWidget::event(QEvent *event);
事件分发器基于QEvent类型对象参数判断事件类型,QEvent类提供常用API函数。
//窗口接受传递过来的事件,事件不会向父窗口传递。
void QEvent::accept();
//该函数的作用是让窗口忽略传递过来的事件,事件被传递给父窗口(向上传递)。
void QEvent::ignore();
//设置传递过来的事件是被接受还是被忽略
bool QEvent::isAccepted() const;
void QEvent::setAccepted(bool accepted);
- setAccepted(true)等效于accept()
- setAccepted(false)等效于ignore()
//得到传递的窗口的事件的类型,返回值是一个枚举类型
QEvent::Type QEvent::type() const;
12.4.2 事件分发器
事件分发器QWidget::event()根据QEvent类型分发事件,调用相应处理器。如要拦截事件,可在分发器中直接返回true。
bool MainWindow::event(QEvent *ev) {
if (ev->type() == QEvent::MouseButtonPress || ev->type() == QEvent::MouseButtonDblClick) {
return true; // 过滤事件
}
return QWidget::event(ev); // 其他事件按默认流程处理
}
12.5 事件过滤器
12.5.1 事件过滤器介绍
除了事件分发器,Qt还提供了事件过滤器来拦截和处理事件。通过QObject::eventFilter()函数可实现此功能。
重写eventFilter函数:在filterObj对应的类中重写eventFilter()函数,根据需要过滤事件。
安装事件过滤器:使用void QObject::installEventFilter(QObject *filterObj)为对象安装过滤器,filterObj是处理过滤逻辑的对象。
例如,为MainWindow类安装事件过滤器并过滤键盘事件:
// 假设有一个EventFilter类用于事件过滤
class EventFilter : public QObject {
protected:
bool eventFilter(QObject *watched, QEvent *event) override {
//这里没有指定监控的窗口,通用处理
if (event->type() == QEvent::KeyPress) {
// 过滤键盘按下事件
return true;
}
return QObject::eventFilter(watched, event); // 其他事件继续处理
}
};
// 在MainWindow构造函数或适当位置安装过滤器
MainWindow::MainWindow(QWidget *parent) : QWidget(parent) {
EventFilter *filter = new EventFilter(this);
this->installEventFilter(filter); //把MainWindow的事件交给自定义类去过滤了
}
12.5.2 事件过滤器的使用
为实现屏蔽QTextEdit中的回车键,可采用事件过滤器方法,无需定义子类。具体步骤简述如下:
添加QTextEdit控件到主窗口。
创建一个事件过滤器类,重写eventFilter函数以拦截回车键事件。
给QTextEdit控件安装事件过滤器。
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
bool eventFilter(QObject *watched, QEvent *event);
private:
Ui::MainWindow *ui;
};
/*窗口默认构造函数*/
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) {
ui->setupUi(this);
ui->textEdit->installEventFilter(this); //
}
MainWindow::~MainWindow() {
delete ui;
}
/*重写eventFilter*/
bool MainWindow::eventFilter(QObject *watched, QEvent *event) {
// 判断对象和事件
if(watched == ui->textEdit && event->type() == QEvent::KeyPress) {
QKeyEvent* keyEv = (QKeyEvent*)event;
// 小键盘确认,大键盘回车
if(keyEv->key() == Qt::Key_Enter || //大键盘回车
keyEv->key() == Qt::Key_Return) { //小键盘确认
qDebug() << "我是回车, 被按下了...";
return true;
}
}
return false;
}
如果在Qt的窗口中有多层嵌套的窗口,
在这种多层嵌套窗口中如果要过滤掉QTextEdit的某些事件,可以交给A或者B或者C去处理,也可以给QTextEdit同时安装多个过滤器。
ui->textEdit->installEventFilter(窗口A对象);
ui->textEdit->installEventFilter(窗口B对象);
ui->textEdit->installEventFilter(窗口C对象);