第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设置为栅格格局。
③修改一下控件的实例名称,方便编程。
④