QT开发--定时器和事件

第十章 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值。使用qRedqGreenqBlueqAlpha函数可以分别获取颜色的红、绿、蓝和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标准控件,再右键选中[提升为]进行处理。

将ui的基础类提升为自定义类

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对象);
上一篇:QToss——基于.NET架构的跨境电商的工具,助力企业实现智能数据营销


下一篇:Android -- [SelfView] 自定义多色渐变背景板