Qt学习笔记第71到80讲

第71讲 事件过滤器的方式实现滚轮按键放大

基本概念

事件体系(事件派发 -> 事件过滤->事件分发->事件处理)中,程序员主要操作的是事件分发与事件处理。我们之前已经通过继承QTextEdit来重写事件实现Ctrl加滚轮的检测,还有一种处理方式,叫做事件过滤器 在Qt的事件处理过程中,引入事件过滤器(Event Filter)可以让你在事件达到目标对象之前进行拦截和处理。这是一种强大的机制,允许你在不同对象间共享事件处理逻辑或在父对象中集中处理特定事件。
下面是加入事件过滤器的步骤:

事件过滤器特别适用于以下情况:
①当你想在不修改子类代码的情况下改变事件的行为。
②当多个对象需要共享相同的事件处理逻辑。
③当你需要在更高的层级上监控或修改应用程序的事件流。
通过使用事件过滤器, Qt 应用程序可以获得更大的灵活性和更细粒度的事件处理控制。

编程步骤

①在Widget构造函数内部安装过滤器(函数体内第二行)

cpp 复制代码
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->textEdit->installEventFilter(this);

    QShortcut *shortcutOpen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),
                                        this);
    QShortcut *shortcutSave = new QShortcut(QKeySequence(tr("Ctrl+S", "File|Save")),
                                        this);
    QShortcut *shortcutEnlarge = new QShortcut(QKeySequence(tr("Ctrl+Shift+=", "File|Enlarge")),
                                        this);
    QShortcut *shortcutReduce = new QShortcut(QKeySequence(tr("Ctrl+Shift+-", "File|Reduce")),
                                        this);

    connect(shortcutOpen,&QShortcut::activated,[=](){on_btnOpen_clicked();});
    connect(shortcutSave,&QShortcut::activated,[=](){on_btnSave_clicked();});
    connect(shortcutEnlarge,&QShortcut::activated,[=](){textEditEnlarge();});
    connect(shortcutReduce, &QShortcut::activated,[=](){textEditReduce();});

    this->setLayout(ui->verticalLayout);
    ui->widgetBottom->setLayout(ui->horizontalLayout);

    connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),
    this,SLOT(onCurrentIndexChanged(void)));

    connect(ui->textEdit,SIGNAL(cursorPositionChanged()),
    this,SLOT(onCursorPositionChanged()));
}

②在Widget类内部添加bool类型的事件过滤器函数声明

cpp 复制代码
    bool eventFilter(QObject *watched,QEvent *event);

③在Widget.cpp文件内做出实现,引入了新的类型QGuiApplication

cpp 复制代码
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    //滚轮滑动时用keyboardModifiers方法捕获Ctrl按键是否按下
    if(event->type()==QEvent::Wheel)
    {
        if(QGuiApplication::keyboardModifiers()==Qt::ControlModifier)
        {
            QWheelEvent *wheelEvent=dynamic_cast<QWheelEvent *>(event);
            if(wheelEvent->angleDelta().y()>0)
            {
              textEditEnlarge();
            }else if(wheelEvent->angleDelta().y()<0){
              textEditReduce();
            }
            return true;
        }
    }
    return false;
}

第72讲 记事本项目总结

分享一下现在的两种实现方式:
①自定义键盘按键事件
widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QFileDialog>
#include <QDebug>
#include <QList>
#include <QColor>
#include <QMessageBox>
#include <QShortcut>

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

    QShortcut *shortcutOpen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),
                                        this);
    QShortcut *shortcutSave = new QShortcut(QKeySequence(tr("Ctrl+S", "File|Save")),
                                        this);
    QShortcut *shortcutEnlarge = new QShortcut(QKeySequence(tr("Ctrl+Shift+=", "File|Enlarge")),
                                        this);
    QShortcut *shortcutReduce = new QShortcut(QKeySequence(tr("Ctrl+Shift+-", "File|Reduce")),
                                        this);

    connect(shortcutOpen,&QShortcut::activated,[=](){on_btnOpen_clicked();});
    connect(shortcutSave,&QShortcut::activated,[=](){on_btnSave_clicked();});
    connect(shortcutEnlarge,&QShortcut::activated,[=](){textEditEnlarge();});
    connect(shortcutReduce, &QShortcut::activated,[=](){textEditReduce();});

    this->setLayout(ui->verticalLayout);
    ui->widgetBottom->setLayout(ui->horizontalLayout);

    connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),
    this,SLOT(onCurrentIndexChanged(void)));

    connect(ui->textEdit,SIGNAL(cursorPositionChanged()),
    this,SLOT(onCursorPositionChanged()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::textEditEnlarge()
{
    //获取当前TextEdit内的字体信息
    QFont font=ui->textEdit->font();
    //获取当前字体的大小
    int fontSize=font.pointSize();
    if(fontSize==-1) return;

    //改变大小,并设置字体大小
    int newFontSize=fontSize+1;
    font.setPointSize(newFontSize);
    ui->textEdit->setFont(font);
}

void Widget::textEditReduce()
{
    //获取当前TextEdit内的字体信息
    QFont font=ui->textEdit->font();
    //获取当前字体的大小
    int fontSize=font.pointSize();
    if(fontSize==-1) return;

    //改变大小,并设置字体大小
    int newFontSize=fontSize-1;
    font.setPointSize(newFontSize);
    ui->textEdit->setFont(font);
}


void Widget::on_btnOpen_clicked()
{
    QString fileName=QFileDialog::getOpenFileName(this,tr("Open File"),
    "E:\\6_Qt Projects\\31_NotepadCodec\\files",tr("Text(*.txt *.doc)"));
    //QFileDialog限制程序可打开的文件形式为txt文件或者doc文本

    ui->textEdit->clear();
    //每次打开文件时清除控件区域"textEdit"

    file.setFileName(fileName);

    if(!file.open(QIODevice::ReadWrite|QIODevice::Text))
    {
        qDebug()<<"file open error";
    }

    this->setWindowTitle(fileName+"-NotesBook");

    QTextStream in(&file);

    QString str=ui->comboBox->currentText();
    const char* c_str=str.toStdString().c_str();

    in.setCodec(c_str);

    while(!in.atEnd())
    {
        QString context=in.readLine();
        //qDebug()<<qPrintable(context);

        ui->textEdit->append(context);
        //将读取到的每行内容通过 append 方法添加到界面的文本编辑框(ui->textEdit)中
    }

    // 将此时ui->textEdit中的文本内容保存到initialText变量中,作为初始文本
     initialText = ui->textEdit->toPlainText();
    //主动设置焦点(重要,解决了点击textEdit控件区域之后才能使用滚轮缩放的Bug)
     ui->textEdit->setFocus(Qt::OtherFocusReason);
}

void Widget::on_btnSave_clicked()
{
    //如果当前没有文件打开,就弹窗让用户选择新文件,创建新文件;
    //而不是原来那样,都弹出新的文件保存窗口
    if(!file.isOpen()){
        QString fileName = QFileDialog::getSaveFileName(this, tr("SaveFile"),
        "E:\\6_Qt Projects\\31_NotepadCodec\\files\\untitled file.txt",tr("Text (*.txt*.doc)"));

        file.setFileName(fileName);
        if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
           qDebug() << "file open error";
          }
       this->setWindowTitle(fileName + "- MyNoteBook");
    }else
    {
        // 如果文件已打开,先将文件指针移动到文件开头,准备覆盖原有内容
        file.seek(0);
        // 截断文件内容,清除原有内容
        file.resize(0);
    }

    //当保存被按下,不管是已有打开的文件还是上面if满足后用户选择新文件,
    //都要读取TextEdit内容并写入文件中
    QTextStream out(&file);
    out.setCodec(ui->comboBox->currentText().toStdString().c_str());
    QString context = ui->textEdit->toPlainText();
    out << context;
    initialText=ui->textEdit->toPlainText();
}

void Widget::on_btnClose_clicked()
{
    QString currentText = ui->textEdit->toPlainText();
    if(currentText!= initialText){
        int ret = QMessageBox::warning(this, tr("NotesBook Notice"),
                                       tr("The document has been modified.\n"
                                          "Do you want to save your changes?"),
                                       QMessageBox::Save | QMessageBox::Discard
                                       | QMessageBox::Cancel, QMessageBox::Save);

        switch (ret) {
        case QMessageBox::Save:
            // Save was clicked
            on_btnSave_clicked();
            ui->textEdit->clear();
            break;

        case QMessageBox::Discard:
            // Don't Save was clicked
            ui->textEdit->clear();
            if (file.isOpen()) {
                file.close();
                this->setWindowTitle("NotesBook");
            }
            break;

        case QMessageBox::Cancel:
            // Cancel was clicked
            break;

        default:
            // should never be reached
            break;
        }
    } else {
        ui->textEdit->clear();
        if (file.isOpen()) {
            file.close();
            this->setWindowTitle("NotesBook");
        }
    }
}

void Widget::onCurrentIndexChanged(void)
{
    ui->textEdit->clear();
    if(file.isOpen())
    {
        QTextStream in(&file);
        in.setCodec(ui->comboBox->currentText().toStdString().c_str());
        //链式调用访问成员变量
        file.seek(0);//将光标移动回起始点
        while(!in.atEnd())
        {
            QString context=in.readLine();
            ui->textEdit->append(context);
        }
    }
}

void Widget::onCursorPositionChanged(void)
{
    QTextCursor cursor=ui->textEdit->textCursor();
    //qDebug()<<cursor.blockNumber()+1 <<cursor.columnNumber()+1;

    QString blockNum=QString::number(cursor.blockNumber()+1);
    QString columnNum=QString::number(cursor.columnNumber()+1);
    const QString labelMes="L:"+blockNum+" "+"C:"+columnNum;

    ui->labelPositon->setText(labelMes);

    /*设置当前行高亮*/
    //0.声明了一个 QList 容器 extraSelections
    QList <QTextEdit::ExtraSelection> extraSelections;
      //同时声明了ext这个结构体变量
    QTextEdit::ExtraSelection ext;

    //1.获取当前行的数值
    ext.cursor=ui->textEdit->textCursor();
    //2.配置颜色
    QBrush qBrush(Qt::lightGray);
    ext.format.setBackground(qBrush);
    //3.配置段属性,整行显示
    ext.format.setProperty(QTextFormat::FullWidthSelection,true);
    //4.整体配置
    extraSelections.append(ext);
    ui->textEdit->setExtraSelections(extraSelections);

}

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QFile>
#include <QWidget>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    /*personel public components begin*/
    QFile file;
    QString initialText;  // 新增成员变量,用于保存初始的文件文本内容
    void textEditEnlarge(void);
    void textEditReduce(void);

    /*personel public components end*/

private:
    Ui::Widget *ui;   
   /*personel private components begin*/

private slots:
    void on_btnOpen_clicked();

    void on_btnSave_clicked();

    void on_btnClose_clicked();

    void onCurrentIndexChanged(void);

    void onCursorPositionChanged(void);

   /*personel private components end*/
};
#endif // WIDGET_H

mytextedit.cpp

cpp 复制代码
​
#include "mytextedit.h"
#include <QWheelEvent>
#include <QDebug>
#include "widget.h"


MyTextEdit::MyTextEdit(QWidget *parent) :QTextEdit(parent)
{

}

void MyTextEdit::wheelEvent(QWheelEvent *e)
{
    //qDebug() << "wheelEvent entered";
    if(ctrlKeyPressed==1){
        //qDebug()<<e->angleDelta().y();
        if(e->angleDelta().y()>0)
        {
          zoomIn();
        }else if(e->angleDelta().y()<0){
          zoomOut();
        }
    e->accept();
  }else{
    QTextEdit::wheelEvent(e);
  }
}

void MyTextEdit::keyPressEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_Control){
        qDebug()<<"Ctrl was pressed";
        ctrlKeyPressed=1;
    }
   QTextEdit::keyPressEvent(e);
}

void MyTextEdit::keyReleaseEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_Control){
        qDebug()<<"Ctrl was released";
        ctrlKeyPressed=0;
    }
    QTextEdit::keyReleaseEvent(e);
}

​

mytextedit.h

cpp 复制代码
#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H

#include <QTextEdit>

class MyTextEdit : public QTextEdit
{
public:
    MyTextEdit(QWidget *parent);

    /*personel compenents begin*/
protected:
    void wheelEvent(QWheelEvent *e) override;
    void keyPressEvent(QKeyEvent *e) override;
    void keyReleaseEvent(QKeyEvent *e) override;

private:
    bool ctrlKeyPressed=0;



    /*personel compenents end*/


};

#endif // MYTEXTEDIT_H

运行测试一下,先放大字体:


再缩小试试:

实测这个实现方法更可靠,虽然代码量比较大。
②事件过滤器实现文本字体使用滚轮缩放
widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QFileDialog>
#include <QDebug>
#include <QList>
#include <QColor>
#include <QMessageBox>
#include <QShortcut>

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

    QShortcut *shortcutOpen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),
                                        this);
    QShortcut *shortcutSave = new QShortcut(QKeySequence(tr("Ctrl+S", "File|Save")),
                                        this);
    QShortcut *shortcutEnlarge = new QShortcut(QKeySequence(tr("Ctrl+Shift+=", "File|Enlarge")),
                                        this);
    QShortcut *shortcutReduce = new QShortcut(QKeySequence(tr("Ctrl+Shift+-", "File|Reduce")),
                                        this);

    connect(shortcutOpen,&QShortcut::activated,[=](){on_btnOpen_clicked();});
    connect(shortcutSave,&QShortcut::activated,[=](){on_btnSave_clicked();});
    connect(shortcutEnlarge,&QShortcut::activated,[=](){textEditEnlarge();});
    connect(shortcutReduce, &QShortcut::activated,[=](){textEditReduce();});

    this->setLayout(ui->verticalLayout);
    ui->widgetBottom->setLayout(ui->horizontalLayout);

    connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),
    this,SLOT(onCurrentIndexChanged(void)));

    connect(ui->textEdit,SIGNAL(cursorPositionChanged()),
    this,SLOT(onCursorPositionChanged()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::textEditEnlarge()
{
    //获取当前TextEdit内的字体信息
    QFont font=ui->textEdit->font();
    //获取当前字体的大小
    int fontSize=font.pointSize();
    if(fontSize==-1) return;

    //改变大小,并设置字体大小
    int newFontSize=fontSize+1;
    font.setPointSize(newFontSize);
    ui->textEdit->setFont(font);
}

void Widget::textEditReduce()
{
    //获取当前TextEdit内的字体信息
    QFont font=ui->textEdit->font();
    //获取当前字体的大小
    int fontSize=font.pointSize();
    if(fontSize==-1) return;

    //改变大小,并设置字体大小
    int newFontSize=fontSize-1;
    font.setPointSize(newFontSize);
    ui->textEdit->setFont(font);
}

bool Widget::eventFilter(QObject *watched, QEvent *event)
{
    //滚轮滑动时用keyboardModifiers方法捕获Ctrl按键是否按下
    if(event->type()==QEvent::Wheel)
    {
        if(QGuiApplication::keyboardModifiers()==Qt::ControlModifier)
        {
            QWheelEvent *wheelEvent=dynamic_cast<QWheelEvent *>(event);
            if(wheelEvent->angleDelta().y()>0)
            {
              textEditEnlarge();
            }else if(wheelEvent->angleDelta().y()<0){
              textEditReduce();
            }
            return true;
        }
    }
    return false;
}


void Widget::on_btnOpen_clicked()
{
    QString fileName=QFileDialog::getOpenFileName(this,tr("Open File"),
    "E:\\6_Qt Projects\\31_NotepadCodec\\files",tr("Text(*.txt *.doc)"));
    //QFileDialog限制程序可打开的文件形式为txt文件或者doc文本

    ui->textEdit->clear();
    //每次打开文件时清除控件区域"textEdit"

    file.setFileName(fileName);

    if(!file.open(QIODevice::ReadWrite|QIODevice::Text))
    {
        qDebug()<<"file open error";
    }

    this->setWindowTitle(fileName+"-NotesBook");

    QTextStream in(&file);

    QString str=ui->comboBox->currentText();
    const char* c_str=str.toStdString().c_str();

    in.setCodec(c_str);

    while(!in.atEnd())
    {
        QString context=in.readLine();
        //qDebug()<<qPrintable(context);

        ui->textEdit->append(context);
        //将读取到的每行内容通过 append 方法添加到界面的文本编辑框(ui->textEdit)中
    }

    // 将此时ui->textEdit中的文本内容保存到initialText变量中,作为初始文本
     initialText = ui->textEdit->toPlainText();
     ui->textEdit->setFocus(Qt::OtherFocusReason);
}

void Widget::on_btnSave_clicked()
{
    //如果当前没有文件打开,就弹窗让用户选择新文件,创建新文件;
    //而不是原来那样,都弹出新的文件保存窗口
    if(!file.isOpen()){
        QString fileName = QFileDialog::getSaveFileName(this, tr("SaveFile"),
        "E:\\6_Qt Projects\\31_NotepadCodec\\files\\untitled file.txt",tr("Text (*.txt*.doc)"));

        file.setFileName(fileName);
        if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
           qDebug() << "file open error";
          }
       this->setWindowTitle(fileName + "- MyNoteBook");
    }else
    {
        // 如果文件已打开,先将文件指针移动到文件开头,准备覆盖原有内容
        file.seek(0);
        // 截断文件内容,清除原有内容
        file.resize(0);
    }

    //当保存被按下,不管是已有打开的文件还是上面if满足后用户选择新文件,
    //都要读取TextEdit内容并写入文件中
    QTextStream out(&file);
    out.setCodec(ui->comboBox->currentText().toStdString().c_str());
    QString context = ui->textEdit->toPlainText();
    out << context;
    initialText=ui->textEdit->toPlainText();
}

void Widget::on_btnClose_clicked()
{
    QString currentText = ui->textEdit->toPlainText();
    if(currentText!= initialText){
        int ret = QMessageBox::warning(this, tr("NotesBook Notice"),
                                       tr("The document has been modified.\n"
                                          "Do you want to save your changes?"),
                                       QMessageBox::Save | QMessageBox::Discard
                                       | QMessageBox::Cancel, QMessageBox::Save);

        switch (ret) {
        case QMessageBox::Save:
            // Save was clicked
            on_btnSave_clicked();
            ui->textEdit->clear();
            break;

        case QMessageBox::Discard:
            // Don't Save was clicked
            ui->textEdit->clear();
            if (file.isOpen()) {
                file.close();
                this->setWindowTitle("NotesBook");
            }
            break;

        case QMessageBox::Cancel:
            // Cancel was clicked
            break;

        default:
            // should never be reached
            break;
        }
    } else {
        ui->textEdit->clear();
        if (file.isOpen()) {
            file.close();
            this->setWindowTitle("NotesBook");
        }
    }
}

void Widget::onCurrentIndexChanged(void)
{
    ui->textEdit->clear();
    if(file.isOpen())
    {
        QTextStream in(&file);
        in.setCodec(ui->comboBox->currentText().toStdString().c_str());
        //链式调用访问成员变量
        file.seek(0);//将光标移动回起始点
        while(!in.atEnd())
        {
            QString context=in.readLine();
            ui->textEdit->append(context);
        }
    }
}

void Widget::onCursorPositionChanged(void)
{
    QTextCursor cursor=ui->textEdit->textCursor();
    //qDebug()<<cursor.blockNumber()+1 <<cursor.columnNumber()+1;

    QString blockNum=QString::number(cursor.blockNumber()+1);
    QString columnNum=QString::number(cursor.columnNumber()+1);
    const QString labelMes="L:"+blockNum+" "+"C:"+columnNum;

    ui->labelPositon->setText(labelMes);

    /*设置当前行高亮*/
    //0.声明了一个 QList 容器 extraSelections
    QList <QTextEdit::ExtraSelection> extraSelections;
      //同时声明了ext这个结构体变量
    QTextEdit::ExtraSelection ext;

    //1.获取当前行的数值
    ext.cursor=ui->textEdit->textCursor();
    //2.配置颜色
    QBrush qBrush(Qt::lightGray);
    ext.format.setBackground(qBrush);
    //3.配置段属性,整行显示
    ext.format.setProperty(QTextFormat::FullWidthSelection,true);
    //4.整体配置
    extraSelections.append(ext);
    ui->textEdit->setExtraSelections(extraSelections);

}

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QFile>
#include <QWidget>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    /*personel public components begin*/
    QFile file;
    QString initialText;  // 新增成员变量,用于保存初始的文件文本内容
    void textEditEnlarge(void);
    void textEditReduce(void);

    bool eventFilter(QObject *watched,QEvent *event);
    /*personel public components end*/

private:
    Ui::Widget *ui;   
   /*personel private components begin*/

private slots:
    void on_btnOpen_clicked();

    void on_btnSave_clicked();

    void on_btnClose_clicked();

    void onCurrentIndexChanged(void);

    void onCursorPositionChanged(void);

   /*personel private components end*/
};
#endif // WIDGET_H

第73讲 串口调试助手界面01

本章的任务时绘制串口助手的界面。
①对于数据的接收框,可以使用Group Box。

② 在groupBox内拖入textEdit,随后单击选中groupBox设置为栅格格局。

③修改一下控件的实例名称,方便编程。

相关推荐
mit6.8248 分钟前
[实现Rpc] 通信-Muduo库的实现 | && 完美转发 | reserve | unique_lock
c++·网络协议·rpc
JANGHIGH1 小时前
c++ std::list使用笔记
c++·笔记·list
画个逗号给明天"1 小时前
C++STL容器之list
开发语言·c++
Lqingyyyy3 小时前
P2865 [USACO06NOV] Roadblocks G 与最短路的路径可重复的严格次短路
开发语言·c++·算法
C语言小火车3 小时前
深入解析C++26 Execution Domain:设计原理与实战应用
java·开发语言·c++·异构计算调度·c++26执行模型·domain定制
ox00804 小时前
C++ 设计模式-中介者模式
c++·设计模式·中介者模式
黄铎彦4 小时前
使用GDI+、文件和目录和打印API,批量将图片按文件名分组打包成PDF
c++·windows·pdf
Ciderw4 小时前
LLVM编译器简介
c++·golang·编译·编译器·gcc·llvm·基础设施
和光同尘@5 小时前
74. 搜索二维矩阵(LeetCode 热题 100)
数据结构·c++·线性代数·算法·leetcode·职场和发展·矩阵
无人等人5 小时前
CyberRT(apollo) IPC(shm)通信包重复/丢包 bug 及解决方案
c++·bug