当前位置: 首页 > news >正文

QT三 自定义控件,自定义控件的事件处理自定义事件过滤,原始事件过滤

一 自定义控件

现在的需求是这样:

假设我们要在QWidget 上做定制,这个定制包括了关于 一些事件处理,意味着要重写QWidget的一些代码,这是不实际的,因此我们需要自己写一个MyWidget继承QWidget,然后再MyWidget.cpp中重写事件处理的函数。

而且我们希望在 mainwidget.ui上就有自己写的MyWidget,而不是默认的QWidget,这样我们就能在mainwidget.ui上预留我们想要的位置。这时候怎么办呢?

1. 写一个自己的MyWidget,将自己写的MyWidget可以在mainwindow.ui上显示

如下是我们在ui上的弄了一个QWidget,我们的目标是将这个QWidget变成MyWidget,然后再MyWidget.cpp中重写我们的方法

新建MyWidget.cpp类

我们看到 就多出来了mywidget.h 和 mywidget.cpp文件

然后再回到 ui文件,提升ui上的widget为mywidget

取消提升

2. 给自己写的MyWidget上添加想要的UI

我们现在是想给这个Mywidget 中加入 两个控件,一个是spin box,一个是horizontal slider

当spin box 的值变化的时候,会影响 horizontal slider的变化。

这里为了区分 主要的widget,和我们写的MyWidget,我们将主要的widget改名为mainwidget。主要是为了方便理解,没有额外的意义。

一种方法当前是从ui 上拖过去

我们从ui上拖过去控件,主要是在代码中为了给从这些控件得到数据---例如得到这个控件是否被点击,这个控件的值,这个控件中填写的数据等。那么我们如何得到这个控件呢?

得到从ui上拖过去的spin box 和horizontal slider

每一个控件都有一个唯一的objectName,(从ui上的可以看到)。因此要获得某一个控件,在代码上一般通过ui->objectName可以获得(注意是一般情况下)。

那么这个objectName是个啥呢?

如下图:我们拖进去了两个 SpinBox,随便选中一个,就可以看到 这个SpinBox有一个objectName,我们可以改动这个objectName的值,如下,但是objectName是唯一的值,因此不等重复,我们可以试图将两个SpinBox改名为一样,发现不行,QT会帮我们自动改回来。

通过ui->objectName 获得控件代码

 _spinBox = ui->spinBox;
_horizontalSlider =  ui->horizontalSlider;

看一下源码(ui_mainwidget.h),可以看到实际上QT 在将xxx.ui  构建成 ui_xxx.h 的时候,是先通过 objectName,new 出来组件,new出来组件的名字和objectName名字一致。因此给我们的感觉是ui->objectName,就能得到组件。实际上new出来的组件的名字并不一定和objectName名字一致,因此写代码的时候要注意看源码。

扩展,灯下黑--这里还有一个问题:如何得到最外层的widget呢?也就是主widget。

实际上我们ui就是widget的 成员变量,因此this 就最大的widget。

我们知道这个最大的widget,也就是this有啥用呢?

可以通过this 获得所有我们想要找的组件,比如 findchildren();如果想使用,可以参考 qt api 说明

扩展 在mainwidget构造方法中,让spin box 和 horizontal slider 的值使用信号和槽关联起来

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

    //注意我们写的位置,并不是在mywidget.cpp中写的,
    //是在main 加载的最大的widget上写的,这是因为只有最大的widget才有ui变量,我们要通过ui变量找到对应的控件
    _spinBox = ui->spinBox;
    _horizontalSlider =  ui->horizontalSlider;


    //我们想将 _spinBox 的valuechanged 信号发出去后,让 _horizontalSlider调用setvalue方法
    //A段代码 ------ A,B,C代码段实现的功能是一样的,如果不理解,先在自己的博客search 一下 函数指针  ,然后再 seacher一下 static_cast,将这两个文章弄明白就理解了
    void (QSpinBox::* testSignal)(int) = &QSpinBox::valueChanged;
    connect(_spinBox,
            testSignal,
            _horizontalSlider,
            &QSlider::setValue);

     //B段代码 ------ A,B,C代码段实现的功能是一样的
    typedef void (QSpinBox ::*PFUNCTYPE5 )(int) ;
    PFUNCTYPE5 testSignal4 = &QSpinBox::valueChanged;
    connect(_spinBox,
            testSignal4,
            _horizontalSlider,
            &QSlider::setValue);


    //C段代码 ------ A,B,C代码段实现的功能是一样的
    connect(_spinBox,
            static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
            _horizontalSlider,
            &QSlider::setValue);

    
    connect(_horizontalSlider, &QSlider::valueChanged,
            _spinBox, &QSpinBox::setValue);
}

一种方法是在Mywidget.cpp中使用代码实现

我们先通过UI将 自己给Mywidget中的画上去的spinbox删除,将horizontal slider 删除。

以免得影响我们测试

那么当前UI上的Mywidget就啥也没有,我们在MyWidget的构造方法中就可以构建新的UI,并加上去。

代码部分

注意这个代码实现的地方。

另外注意:这个代码偷懒,并没有将QHBoxLayout写到.h中,这是不好的。

另外,还需要注意的是,如果我们不加 QHBoxLayout,那么就要手动的标识 spinbox 和 slider的位置,很容易发生问题,因此在自己写,或者使用ui的时候,最好都用 这样layout

MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{


    QHBoxLayout *layout = new QHBoxLayout(this);


    //注意我们写的位置,并不是在mywidget.cpp中写的,
    //是在main 加载的最大的widget上写的,这是因为只有最大的widget才有ui变量,我们要通过ui变量找到对应的控件
    _spinBox = new QSpinBox(this);
    layout->addWidget(_spinBox);
    _horizontalSlider =  new QSlider(Qt::Horizontal,this);
    layout->addWidget(_horizontalSlider);


    //我们想将 _spinBox 的valuechanged 信号发出去后,让 _horizontalSlider调用setvalue方法
    //A段代码 ------ A,B,C代码段实现的功能是一样的,如果不理解,先在自己的博客search 一下 函数指针  ,然后再 seacher一下 static_cast,将这两个文章弄明白就理解了
    void (QSpinBox::* testSignal)(int) = &QSpinBox::valueChanged;
    connect(_spinBox,
            testSignal,
            _horizontalSlider,
            &QSlider::setValue);

     //B段代码 ------ A,B,C代码段实现的功能是一样的
    typedef void (QSpinBox ::*PFUNCTYPE5 )(int) ;
    PFUNCTYPE5 testSignal4 = &QSpinBox::valueChanged;
    connect(_spinBox,
            testSignal4,
            _horizontalSlider,
            &QSlider::setValue);


    //C段代码 ------ A,B,C代码段实现的功能是一样的
    connect(_spinBox,
            static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
            _horizontalSlider,
            &QSlider::setValue);


    connect(_horizontalSlider, &QSlider::valueChanged,
            _spinBox, &QSpinBox::setValue);
}

二 自定义控件的事件的处理,自定义控件的事件过滤器

当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。

一些事件在对用户操作做出响应时发出,如键盘事件等;

另一些事件则是由系统自动发出,如计时器事件。

处理流程一:当事件发生时,Qt 将创建一个事件对象。Qt 中所有事件类都继承于QEvent

处理流程二:Qt 将这个事件对象传递给QObject的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(event handler)

由于event()函数是负责分发的,那么我们就可以定制那么事件分发,那么事件不分发了。

处理流程三:重写我们想要的回调函数-也就是 event handler

Object 将这些event handler都定义成 virtual 的

使用方法:我们在自定义的组件中,重写我们想要额外处理的event handler 函数。

  1. keyPressEvent()
  2. keyReleaseEvent()
  3. mouseDoubleClickEvent()
  4. mouseMoveEvent()
  5. mousePressEvent()
  6. mouseReleaseEvent() 等。

因此我们首先能想到的就是在最后的处理流程中,对于我们感兴趣的event handler函数,进行自定义处理。

我们自定义一个MyLable,继承于QLable,重写几个eventhandler函数,看一下,这里还自己写一个MyWidget,主要是看一下,写两个自定义的Ui的嵌套case下,是怎么样子的。

MyLable.h

#ifndef MYLABLE_H
#define MYLABLE_H

#include <QWidget>
#include <QLabel>
#include <QEvent>
#include <QMouseEvent>
#include <QDebug>>

class MyLable : public QLabel
{
    Q_OBJECT
public:
    explicit MyLable(QWidget *parent = nullptr);

signals:

protected:
    void enterEvent(QEvent *event) override;
    void leaveEvent(QEvent *event) override ;
    void mouseMoveEvent(QMouseEvent *event)override;

};

#endif // MYLABLE_H

MyLable.cpp

#include "mylable.h"

MyLable::MyLable(QWidget *parent) : QLabel(parent)

{
    //这里设置鼠标不用点击,就能看到mousemoveevnet
    this->setMouseTracking(true);
}


void MyLable::enterEvent(QEvent *event) {
    qDebug()<<"enterevent call";
    this->setText("进入了lable");


}

void MyLable::leaveEvent(QEvent *event){

    qDebug()<<"leaveEvent call";
    this->setText("离开了lable");
}

void MyLable::mouseMoveEvent(QMouseEvent *event){

        qDebug()<<"mouseMoveEvent call";
        qDebug()<<event->x() <<"  " << event->y()<< endl;
}

三 原始控件的 特殊事件处理流程-事件过滤器

前面我们学习了自定义控件,以及自定义控件的 event()和eventhandler处理。

但是要求都是要自定义控件,如果我们有很多的控件,这些自定义控件都有或多或少的event事件要处理,那岂不是工作量很大,因此QT 提供了让我们可以过滤处理的方法,即事件过滤器。

事件过滤器的位置:

使用方法:

1. 注册想要过滤的ui控件,

         void installEventFilter(QObject *filterObj)

                _pushbutton.installEventFilter(this);

        参数 filterObj的含义是:注册完成的ui的过滤事件在 filterIObj的eventFilter()方法中,当前是在 mainwidget中写的,也就是 会在mainwidget 的 eventFilter 函数中过滤 _pushbutton

2. 重写 eventFilter方法,watched就是要过滤的ui,event就是要过滤的event
virtual bool eventFilter(QObject *watched, QEvent *event)

不想让它继续转发,就返回 true,

如果不想处理否则返回 false。实际上 return 给自己的父类去处理

return QWidget::eventFilter(watched,event);

代码实现:

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MainWidget)
{
    ui->setupUi(this);
    _pushButton = ui->pushButton;
    _radioButton = ui->radioButton;

    //正常点下,放松,改动了button 上面的文字
    connect(_pushButton,&QPushButton::pressed,
            [=]()
            {
                _pushButton->setText("该button被按下");

            }
    );

    connect(_pushButton,&QPushButton::released,
            [=]()
            {
                _pushButton->setText("该button被放松");

            }
    );

    connect(_pushButton,&QPushButton::clicked,
            [=]()
            {
                _pushButton->setText("该button被点击");

            }
    );

    //注册pushbutton有过滤事件,原本 button 的pressed 后,会有显示,现在我们要过滤pressed事件,因此代码后,再点击就没有 setText("该button被按下"); 发生了

    _pushButton->installEventFilter(this);

}

//重写eventFulter事件
bool MainWidget::eventFilter(QObject *watched, QEvent *event){
    if(watched == _pushButton){
        if(event->type()==QEvent::MouseButtonPress){
            qDebug()<<"ccc";
            return true;
        }else{
            return QWidget::eventFilter(watched,event);
        }

    }else{
        //如果是其他的 object,则要让父类处理,不能直接return false
        return QWidget::eventFilter(watched,event);

    }
}

总结

注意,

事件过滤器和被安装过滤器的组件必须在同一线程,否则,过滤器将不起作用。另外,如果在安装过滤器之后,这两个组件到了不同的线程,那么,只有等到二者重新回到同一线程的时候过滤器才会有效。

相关文章:

  • 爬虫——将数据保存到MongoDB中
  • conda极速上手记录
  • 如何部署自己的本地大模型
  • Hadoop三 分布式sql计算hive入门
  • 基于PyTorch的艺术风格迁移系统:卷积神经网络与迁移学习在图像生成的应用
  • 【Node.js入门教程:从零到精通】
  • 关于优麒麟ukylin如何更换清华源以及ubuntu24.04安装gcc-i686-linux-gnu找不到包的问题
  • AI视频生成技术的革新之路:Video-T1项目的深度解析
  • 计算机期刊推荐 | 计算机-人工智能、信息系统、理论和算法、软件工程、网络系统、图形学和多媒体, 工程技术-制造, 数学-数学跨学科应用
  • 深度分页优化思路
  • 数据可视化TensorboardX和tensorBoard安装及使用
  • Mybatis配置文件解析(详细)
  • 设计模式,创建型设计模式,工厂模式,建造者模式,单例模式
  • UE5新材质系统效果Demo展示
  • 简单易懂易操作的liunx安装es集群
  • C语言复习笔记--函数栈帧创建与销毁
  • python之selenium中的窗口切换
  • 23 种设计模式中的模板模式
  • 【CXX-Qt】4.1 extern “RustQt“
  • JAVA开发:实例成员与静态成员
  • 乌方公布矿产协议详情:未提债务义务,包含美再援助条款
  • 五部门:开展新就业形态劳动者劳动权益保障水平提升专项行动
  • 马上评|什么才是地方文旅宣传的正确姿势
  • 五一“大车流”来了,今日午后G40沪陕高速开始迎来出沪高峰
  • 国泰海通合并后首份业绩报告出炉:一季度净利润增逾391%
  • 澎湃读报丨解放日报9个版聚焦:上海,加快建成具有全球影响力的科技创新高地