本文续写(继承)自:一个项目带你入门qt - 记事本(上)-CSDN博客
没看过的朋友还请线了解前文
目录
字体缩放(大Ctrl+Shift+= 小Ctrl+Shift+- )
效果演示视频:
什么?你还在用普通的记事本?快来试试QT小项目——记事本,让你成为记事本界的王者!_哔哩哔哩_bilibili
项目学到的qt知识总结:
前置知识
C++模版
基本写法引入:
#include <iostream>
using namespace std;
class PrintIn{
public:
int data;
void printIn();
void setIn(int data);
};
void PrintIn::printIn()
{
cout<<this ->data<<endl;
}
void PrintIn::setIn(int data)
{
this->data = data;
}
class PrintStr{
public:
string data;
void printStr();
void setStr(string data);
};
void PrintStr::printStr()
{
cout<<this ->data<<endl;
}
void PrintStr::setStr(string data)
{
this->data = data;
}
int main()
{
PrintIn in;
in.setIn(10);
in.printIn();
PrintStr str;
str.setStr("mxjun");
str.printStr();
return 0;
}
可以看到只是 成员变量不一样,却要重写类的函数,很麻烦
我们引入类模版可以解决
概念
在 C++ 中,模板(Template)是一种通用的编程工具,允许程序员编写泛型代码,使得类或函数能够适用于多种不同的数据类型而不需要重复编写相似的代码。C++ 提供了两种主要类型的模板:类模板和函
数模板。
类模板(Class Templates):
类模板允许定义通用的类,其中某些类型可以作为参数。这样的类可以处理不同类型的数据,而不需要为每个数据类型编写单独的类
#include <iostream>
using namespace std;
template <typename T>
class PrintEvething{
public:
T data;
void printEvething()
{
cout<<data<<endl;
}
void setEvething(T data)
{
this->data = data;
}
};
// 注意模版不支持类外实例化
//void PrintEvething::printStr()
//{
// cout<<this ->data<<endl;
//}
//void PrintEvething::setEvething(string data)
//{
// this->data = data;
//}
int main()
{
PrintEvething<int> ev; //模版类实例化创建对象的时候鼻血指定类型
ev.setEvething(10);
ev.printEvething();
PrintEvething<string> sev;
sev.setEvething("这是一个泛型");
sev.printEvething();
return 0;
}
函数模版:
#include <iostream>
using namespace std;
template<typename TE>
TE add(TE a,TE b)
{
return a+b;
}
int main()
{
int sumInt = add(4,5);
double sumDouble = add(4.4,5.5);
cout<< sumInt<<endl;
cout<< sumDouble<<endl;
return 0;
}
QList
概念
在 Qt 框架中, QList 是一个容器类,它在内部实现上类似于一个数组,但也提供了一些链表的特性。
QList 的设计旨在提供一个在多数情况下既高效又方便的通用列表容器。
用于存储元素列表。它提供了丰富的功能,包括添加、移除、访问元素等。
QList 的内部工作原理:
1. 数组式存储: QList 在大多数情况下使用连续内存存储其元素,类似于数组。这意味着它提供了快速的索引访问(通过下标操作符 [] ),以及相对高效的迭代性能。
2. 动态调整大小:与静态数组不同, QList 可以动态增长和缩减,自动管理内存分配。
3. 链表特性:虽然 QList 主要基于数组,但它也提供了一些链表的操作,比如在列表的开始或结束
处添加和移除元素。这些操作通常比在数组中间插入或删除元素更高效。
4. 复制时共享内存: QList 使用一种称为“隐式共享”(implicit sharing)或“写时复制”(copy-on -write)的技术。这意味着当你复制一个 QList 时,它不会立即复制所有元素,而是共享相同的数据,直到你尝试修改其中一个列表,此时才进行实际的复制。这使得复制 QList 变得非常高效。
使用场景:
当你需要快速的随机访问(如通过索引访问元素)时, QList 是一个不错的选择。
如果你的主要操作是在列表的两端添加或移除元素, QList 也表现得很好。
基本用法
包含头文件:首先,你需要包含 QList 的头文件
#include <QList>
创建 QList 实例:创建一个 QList 对象,并指定存储的元素类型。
QList<int> list;
添加元素:使用 append 或 push_back 方法添加元素
list.append(1);
list.append(2);
list.append(3);
访问元素:可以使用下标操作符或 at() 方法访问元素。
int firstElement = list[0];
int secondElement = list.at(1);
遍历列表:使用迭代器或范围基的 for 循环遍历列表。
for(int i = 0; i < list.size(); ++i) { // size = sizeof(arr)/sizeof(arr[0])
qDebug() << list[i];
}
// 或者使用范围基的 for 循环
for(int item : list) {
qDebug() << item;
}
移除元素:使用 removeAt 、 removeOne 或 clear 方法移除元素
list.removeAt(1); // 移除索引为 1 的元素
list.removeOne(3); // 移除一个值为 3 的元素
list.clear(); // 清空整个列表
ExtraSelection
简介
QTextEdit::ExtraSelection 是一个在 QTextEdit 中用来表示额外的文本选择和高亮的结构。
如何工作
1. ExtraSelection 结构体: QTextEdit::ExtraSelection 是一个结构体,包含了两个主要成员:
QTextCursor 和 QTextCharFormat 。
QTextCursor 表示在文本中的一个位置或者区间,而
QTextCharFormat 用于定义这个区间的格式,比如背景颜色、字体等。
2. 设置 ExtraSelection:你可以创建一个或多个 ExtraSelection 对象,为它们设置相应的光标位
置和格式,然后通过 QTextEdit 的 setExtraSelections 方法将这些对象应用到文本编辑器中。
这样,你可以对文本的特定部分应用特定的格式,而不影响其他文本。
3. 高亮当前行:要高亮显示当前行,你需要在 cursorPositionChanged() 信号的槽函数中创建一个ExtraSelection 对象。使用当前的 QTextCursor 对象(通过 textCursor() 方法获取)来确
定当前行的位置,并设置背景颜色为你选择的高亮颜色。
QTextCharFormat
QTextCharFormat 类是 Qt 框架中的一部分,用于描述文本字符的格式。这个类提供了丰富的接口来设置和获取文本字符的各种属性,如字体、颜色、背景色等。 QTextCharFormat 通常用于富文本处理,可以在像 QTextEdit 和 QTextDocument 这样的类中使用
常用功能和方法
下面列出了 QTextCharFormat 的一些常用功能和方法:
1. 设置和获取字体样式:
使用 setFont() 方法设置字体。
通过 font() 方法获取当前字体2. 设置字体属性:
setFontWeight() : 设置字体的粗细。
setFontItalic() : 设置字体是否倾斜。
setFontUnderline() : 设置是否有下划线。3. 设置文本颜色和背景色:
setForeground() : 设置文本的前景色(即字体颜色)。
setBackground() : 设置文本的背景色。4. 其他文本属性:
setToolTip() : 设置文本的工具提示。
setAnchor() : 设置文本是否为超链接。
setAnchorHref() : 设置超链接的目标 URL。
总程序:
ui总图
更多空间细节请参考上篇:一个项目带你入门qt - 记事本(上)-CSDN博客
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QShortcut>
#include <QWheelEvent>
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 *shortcutZoomIn = new QShortcut(QKeySequence(tr("Ctrl+Shift+=", "File|Save")),this);
QShortcut *shortcutZoomOut = new QShortcut(QKeySequence(tr("Ctrl+Shift+-", "File|Save")),this);
connect(shortcutOpen,&QShortcut::activated,[=](){
on_openButton_clicked();
});
connect(shortcutSave,&QShortcut::activated,[=](){
on_saveButton_clicked();
});
connect(shortcutZoomIn,&QShortcut::activated,[=](){
zoomIn();
});
connect(shortcutZoomOut,&QShortcut::activated,[=](){
zoomOut();
});
//虽然上面一行代码进行widget和ui的窗口关联,但是如果发生窗口大小变化的时候,里面的布局不会随之变化
//通过下面这行代码进行显示说明,让窗口变化时,布局及其子控件随之调整
this->setLayout(ui->verticalLayout);
connect(ui->comboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(onCurrentIndexChanged(int)));
connect(ui->textEdit,SIGNAL(cursorPositionChanged()),this,SLOT(onCursorPositionChanged()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::zoomIn()
{
//获得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::zoomOut()
{
//获得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)
{
/* QKeyEvent *keyEvent = (QKeyEvent*)event;
if(keyEvent->key() == Qt::Key_Control){
qDebug() << "Ctrl";
}
*/
if(event->type() == QEvent::Wheel){
if(QGuiApplication::keyboardModifiers() == Qt::ControlModifier){
QWheelEvent *wheelEvent = dynamic_cast<QWheelEvent*>(event);
if(wheelEvent->angleDelta().y() > 0){
zoomIn();
}else if(wheelEvent->angleDelta().y()<0){
zoomOut();
}
return true;
}
return false;
}
}
void Widget::on_openButton_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
"F:/myqt/project_of_qt_new",
tr("Text (*.txt)"));
ui->textEdit->clear();
file.setFileName(fileName);
if(!file.open(QIODevice::ReadWrite | QIODevice::Text)){
qDebug() << "file open error";
}
this->setWindowTitle(fileName + "- MyNoteBook");
QTextStream in(&file);
QString str = ui->comboBox->currentText();//把QString转化成char *
const char* c_str = str.toStdString().c_str();
in.setCodec(c_str);
while(!in.atEnd()){
QString context = in.readLine();
ui->textEdit->append(context);
}
}
void Widget::on_saveButton_clicked()
{
if(!file.isOpen()){
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"F:/myqt/project_of_qt_new",
tr("Text (*.txt *.doc)"));
file.setFileName(fileName);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
qDebug() << "file open error";
}
this->setWindowTitle(fileName + "- MyNoteBook");
}
else
{
file.resize(0);//对于已经打开的文件要先清空再添加, 不然追加到后面就出错啦
}
QTextStream out(&file);
out.setCodec(ui->comboBox->currentText().toStdString().c_str());
QString context = ui->textEdit->toPlainText();
out << context;
}
void Widget::on_closeButton_clicked()
{
QMessageBox msgBox;
int ret = QMessageBox::warning(this, tr("MyNoteBook 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_saveButton_clicked();
qDebug() << "QMessageBox::Save:";
break;
case QMessageBox::Discard:
// Don't Save was clicked
ui->textEdit->clear();
if(file.isOpen()){
file.close();
this->setWindowTitle("MyNoteBook");
}
qDebug() << "QMessageBox::Discard:";
break;
case QMessageBox::Cancel:
// Cancel was clicked
qDebug() << "QMessageBox::Cancel:";
break;
default:
// should never be reached
break;
}
}
void Widget::onCurrentIndexChanged(int index)
{
qDebug() << "currentItSignal";
ui->textEdit->clear();
if(file.isOpen()){
qDebug() << "file is Open";
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::cursorPositionChanged()
{
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+" ";
//const QString labelMes = "行:"+blockNum+",列:"+columnNum+" ";
ui->labelLR->setText(labelMes);
//设置当前行高亮
QList<QTextEdit::ExtraSelection> extraSelections;
QTextEdit::ExtraSelection ext;
//1. 知道当前行
ext.cursor = cursor;
QBrush qBrush(Qt::lightGray);
//2. 颜色
ext.format.setBackground(qBrush);
//配置段属性:整行显示,没有这句话不行
ext.format.setProperty(QTextFormat::FullWidthSelection, true);
//ext.format.setFontUnderline(true);
//3. 设置
//把ext加到ext的容器中
extraSelections.append(ext);
ui->textEdit->setExtraSelections(extraSelections);
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QDebug>
#include <QFileDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
QFile file;
Widget(QWidget *parent = nullptr);
~Widget();
void zoomIn(); //增大字体大小
void zoomOut(); //字体大小
bool eventFilter(QObject *watched, QEvent *event);
private slots:
void on_openButton_clicked();
void on_saveButton_clicked();
void on_closeButton_clicked();
void onCurrentIndexChanged(int index);
void cursorPositionChanged();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
mytextedit.h
#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H
#include <QTextEdit>
class MyTextEdit : public QTextEdit
{
public:
MyTextEdit(QWidget *parent);
protected:
void wheelEvent(QWheelEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void keyReleaseEvent(QKeyEvent *e) override;
private:
bool ctrlKeyPressed = 0;
};
#endif // MYTEXTEDIT_H
mytextedit.cpp
#include "mytextedit.h"
#include <QWheelEvent>
#include <QDebug>
MyTextEdit::MyTextEdit(QWidget *parent):QTextEdit(parent )
{
}
void MyTextEdit::wheelEvent(QWheelEvent *e)
{
qDebug()<< e->angleDelta().y(); //我们只需要y,x恒为0
if(ctrlKeyPressed == 1){ // 按下ctrl 后才互惠根据滚轮来放大
if( e->angleDelta().y()>0){ //向上滚动放大
zoomIn();
}
else{ //向下滚动缩小
zoomOut();
}
e->accept(); // 字控件处理完毕 --> 控制权交给父控件-->目的实现放大后能通过滚轮滑动文字
}
else{//ctrl 没被按下的时候丢给他的父亲处理
QTextEdit::wheelEvent(e);
}
}
void MyTextEdit::keyPressEvent(QKeyEvent *e) //控制键盘按下事件
{
if(e->key() == Qt::Key_Control){
qDebug ()<< "control pressed";
ctrlKeyPressed = 1;
}
}
void MyTextEdit::keyReleaseEvent(QKeyEvent *e) //控制键盘松开事件
{
if(e->key() == Qt::Key_Control){
qDebug ()<< "control release";
ctrlKeyPressed = 0;
}
}
添加(优化)功能
实现当前行高亮
需求: 设置当前行高亮
实现步骤:
知道当前行的位置 --> 在前面 光标位置 确定当前行列的那个槽函数 里添加
槽函数
ext.format 就是QTextCharFormat 类型使用方法参考前文
void Widget::cursorPositionChanged()
{// 与光标位置改变的信号进行绑定
// 获取文本框的光标位置
QTextCursor cursor = ui->textEdit->textCursor();
// 输出对应行列
qDebug()<<cursor.blockNumber() +1<<',' <<cursor.columnNumber()+1;
QString lableLine = QString::number(cursor.blockNumber() +1);
QString lableRow = QString::number(cursor.columnNumber() +1);
const QString lableMes ="Line:"+lableLine + "Row:"+lableRow+" ";
//const QString lableMes ="行:"+lableLine + "列:"+lableRow+" "; //需要在设置里面把编码格式改为utf-8
ui->labelLR->setText(lableMes);
// 设置当前行高亮:
QList<QTextEdit::ExtraSelection>extraSelecions;
QTextEdit::ExtraSelection ext;
// 知道当前行
ext.cursor = ui->textEdit->textCursor();
// 设置样式 -- 颜色
ext.format.setBackground(Qt::lightGray); //设置背景颜色为浅灰色
ext.format.setProperty(QTextFormat::FullWidthSelection,true); //设置全行
//对文本框进行设置
extraSelecions.append(ext);
ui->textEdit->setExtraSelections(extraSelecions);
}
效果演示
优化保存功能
之前的不足
没加加上文件是否打开的判断,导致对已经打开的文件还弹窗保存,我们希望达到的效果是
对已经打开的文件直接保存即可不用弹窗
槽函数
void Widget::on_saveButton_clicked()
{
if(!qfile.isOpen()){ //if没有打开文件 --> 我们就弹窗选择文件,对已经打开的文件直接保存即可
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),//设置为保存文件模式
"F:/myqt/project_of_qt_new/default.txt", // 默认打开的路径,加上/default.txt,指定默认文件名
tr("Text (*.txt *.doc)"));//限制能打开文件的格式 ,这里指定保存文件类型了,不写后缀 .txt 只写文件名也可以
qDebug()<< fileName;
this->setWindowTitle(fileName +"-mxjun notebook");
qfile.setFileName(fileName);
//设置读写权限 -->方便修改后保存
if(!qfile.open(QIODevice::ReadWrite | QIODevice::Text)) // Text --> 对\n 做检查,win系统下会转化为\r\n,对换行读取更好
{
qDebug()<<"Open Error";
}
}
QTextStream out(&qfile); // & --拷贝构造函数 -- 指向内存空间的地址
out.setCodec(ui->comboBox->currentText().toStdString().c_str());
// 读取文本框 textEdit 里面的内容
QString context=ui->textEdit->toPlainText();
out << context;
//qfile.close(); // 这里不用关闭,我们后续close 按钮会关闭
}
// 加上 判断 文件是否打开的 判断 ,/if没有打开文件 --> 我们就弹窗选择文件,对已经打开的文件直接保存即可
效果演示
可以看到对打开的文件,我们点击保存,内容就保存了,并且不会跳出弹窗
优化关闭按钮: 添加弹窗地提示
之前的不足,功能不够丰富,我们希望弹窗 提示 我们选择是否保存/舍弃/取消
消息对话框 QMessageBox
1. 显示信息 :向用户显示一些信息性的消息。2. 询问用户决策 :询问用户一个问题,并根据其回答做出相应的操作。3. 报告错误 :向用户报告程序运行中的错误。
槽函数
void Widget::on_saveButton_clicked()
{
if(!file.isOpen()){
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"F:/myqt/project_of_qt_new",
tr("Text (*.txt *.doc)"));
file.setFileName(fileName);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
qDebug() << "file open error";
}
this->setWindowTitle(fileName + "- MyNoteBook");
}
else
{
file.resize(0);//对于已经打开的文件要先清空再添加, 不然追加到后面就出错啦
}
QTextStream out(&file);
out.setCodec(ui->comboBox->currentText().toStdString().c_str());
QString context = ui->textEdit->toPlainText();
out << context;
}
效果演示
点击save
Discard -- 不保存修改直接关闭
可以看到没添加新的内容
cancel
什么都不做,不演示
添加快捷键
快捷键开发基础 - QShortcut
在 Qt 中实现快捷键功能通常涉及到 QShortcut 类的使用。下面是一个简单的代码示例,展示了如何在Qt 应用程序中为特定功能设置快捷键:
// 创建一个快捷键 (Ctrl + N) 并关联到窗口
QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+N"), &window);
// 当快捷键被按下时,显示一个消息框
QObject::connect(shortcut, &QShortcut::activated, [&]() {
QMessageBox::information(&window, "Shortcut Activated", "Ctrl+N was
pressed");
});
在这个示例中,当用户按下 Ctrl + N 时,程序将弹出一个消息框。这是通过创建一个 QShortcut 对象,
并将其快捷键序列设置为 "Ctrl+N" 来实现的。然后,将 activated 信号连接到一个 Lambda 函数,
该函数在快捷键被激活时执行。这种方法非常适用于为特定操作提供快速访问路径。
记事本添加快捷键
打开(Ctrl + O)和保存(Ctrl +S)
// 制作两个快捷键 QShortcut *shortcutOpen = new QShortcut(QKeySequence("Ctrl+O"), this); QShortcut *shortcutSave = new QShortcut(QKeySequence("Ctrl+S"), this); // 把Ctrl+O的信号添加槽,调用打开按键的槽函数 QObject::connect(shortcutOpen, &QShortcut::activated, [&]() { Widget::on_pushButtonOpen_clicked(); }); // 把Ctrl+S的信号添加槽,调用保存按键的槽函数 QObject::connect(shortcutSave, &QShortcut::activated, [&]() { Widget::on_pushButtonSave_clicked(); });
//配置快捷键 QShortcut *shortcutOpen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),this); QShortcut *shortcutSave = new QShortcut(QKeySequence(tr("Ctrl+S", "File|Save")),this); //快捷键与槽函数绑定 // lamdba表达式 = 去捕获所有的 connect(shortcutOpen,&QShortcut::activated,[=](){ on_openButton_clicked(); }); connect(shortcutSave,&QShortcut::activated,[=](){ on_saveButton_clicked(); });
字体缩放(大Ctrl+Shift+= 小Ctrl+Shift+- )
QShortcut *shortcutZoomIn = new QShortcut(QKeySequence(tr("Ctrl+Shift+=", "File|ZoomIn")),this);
// 实现增大字体大小的功能
connect(shortcutZoomIn,&QShortcut::activated,[=](){
qDebug()<<"zoom";
//获取当前字体信息
QFont qfont=ui->textEdit->font();
//获取当前字体大小
int fontSize= qfont.pointSize();
if(-1 == fontSize)return;
//改变字体大小
int newFontSize = fontSize + 1;
qfont.setPointSize(newFontSize);
ui->textEdit->setFont(qfont);
});
QShortcut *shortcutZoomOut = new QShortcut(QKeySequence(tr("Ctrl+Shift+-", "File|ZoomOut")),this);
// 实现减小字体大小的功能
connect(shortcutZoomOut,&QShortcut::activated,[=](){
qDebug()<<"zoom";
//获取当前字体信息
QFont qfont=ui->textEdit->font();
//获取当前字体大小
int fontSize= qfont.pointSize();
if(-1 == fontSize)return;
//改变字体大小
int newFontSize = fontSize - 1;
qfont.setPointSize(newFontSize);
ui->textEdit->setFont(qfont);
});
利用事件特性 实现Ctrl+滚轮缩放
如果对qt事件不够了解的朋友 ,我们推荐先去看这篇: 一文带你入门 qt事件-CSDN博客
思路:
类似 事件 里面自定义控件
1. 定义一个类继承于 原来的QTextEdit --> 目的就是把我们ui里面的textEdit提升为这个自定义类--
通过实现我们的功能
class MyTextEdit : public QTextEdit
2. MyTextEdit(QWidget *parent); --> 构造函数交给父类,让和父类一同进行构造,保持和父类ui(整个widget窗口)的联系
3.重写 事件处理函数
通过 e->angleDelta().y(); 获取滚轮 滑动方向
调用 之前实现的 zoomIn(); zoomOut(); 实现页面缩放
通过key 的按下 松开事件控制 ctrlKeyPressed 的bool 确定ctrl 是否被按下
达到 ctrl + 滚轮控制缩放的效果
说明: 为什么一定用ctrl + 滚轮呢,直接滚轮不行吗
行,但是不太 实用
1.不加ctrl 的话,单独滚轮控制容易误触
2.我们希望滚轮还能承担父类里面的基本功能,通过滚轮实现页面滑动
4.进入ui 界面,选中 我们之前的TextEdit,提升为我们 定义的类 MyTextEdit
mytextedit.h
#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H
#include <QTextEdit>
class MyTextEdit : public QTextEdit
{
public:
MyTextEdit(QWidget *parent);
protected:
void wheelEvent(QWheelEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void keyReleaseEvent(QKeyEvent *e) override;
private:
bool ctrlKeyPressed = 0;
};
#endif // MYTEXTEDIT_H
==================================
mytextedit.cpp
#include "mytextedit.h"
#include <QWheelEvent>
#include <QDebug>
MyTextEdit::MyTextEdit(QWidget *parent):QTextEdit(parent )
{
}
void MyTextEdit::wheelEvent(QWheelEvent *e)
{
qDebug()<< e->angleDelta().y(); //我们只需要y,x恒为0
if(ctrlKeyPressed == 1){ // 按下ctrl 后才互惠根据滚轮来放大
if( e->angleDelta().y()>0){ //向上滚动放大
zoomIn();
}
else{ //向下滚动缩小
zoomOut();
}
e->accept(); // 字控件处理完毕 --> 控制权交给父控件-->目的实现放大后能通过滚轮滑动文字
}
else{//ctrl 没被按下的时候丢给他的父亲处理
QTextEdit::wheelEvent(e);
}
}
void MyTextEdit::keyPressEvent(QKeyEvent *e) //控制键盘按下事件
{
if(e->key() == Qt::Key_Control){
qDebug ()<< "control pressed";
ctrlKeyPressed = 1;
}
}
void MyTextEdit::keyReleaseEvent(QKeyEvent *e) //控制键盘松开事件
{
if(e->key() == Qt::Key_Control){
qDebug ()<< "control release";
ctrlKeyPressed = 0;
}
}
单独说明
void MyTextEdit::wheelEvent(QWheelEvent *e)
{
qDebug()<< e->angleDelta().y(); //我们只需要y,x恒为0
if(ctrlKeyPressed == 1){ // 按下ctrl 后才互惠根据滚轮来放大
if( e->angleDelta().y()>0){ //向上滚动放大
zoomIn();
}
else{ //向下滚动缩小
zoomOut();
}
e->accept(); // 字控件处理完毕 --> 控制权交给父控件-->目的实现放大后能通过滚轮滑动文字
}
else{//ctrl 没被按下的时候丢给他的父亲处理
QTextEdit::wheelEvent(e);
}
}
这个事件处理函数中 ,我们要在每次 ctrl + 滚轮事件处理完成后,调用accept() 进行释放
不然控制权还在MyTextEdit 这个控件上,不能实现滚轮滑动
同理,没按下ctrl 的情况,我们吧控制权交给父类,实现默认的滚轮滑动
效果演示
图片不好演示,博主后续会上传视频演示
这里实现的功能就是: ctrl + 滚轮 控制鼠标缩放,松开ctrl 后滚轮恢复默认的控制上下滑动
事件过滤器实现滚轮的缩放
概念
我们通过继承QTextEdit来重写事件实现Ctrl加滚轮的检测,还有一种处理方式,叫做事件过滤器
在Qt的事件处理过程中,引入事件过滤器(Event Filter)可以让你在事件达到目标对象之前进行拦截和处理。
这是一种强大的机制,允许你在不同对象间共享事件处理逻辑或在父对象中集中处理特定事件。
加入事件过滤器的步骤:
1. 定义事件过滤器: 事件过滤器通常是一个重写了 QObject::eventFilter() 方法的对象。这个方法会在事件传递给目标对象之前被调用。
2. 安装事件过滤器: 使用 QObject::installEventFilter() 方法安装事件过滤器。这个方法告诉Qt
在将事件发送给特定对象之前先通过过滤器对象。例如,如果你想在父窗口中过滤子窗口的事件,你需要在父窗口的对象上调用 installEventFilter() ,并将子窗口作为参数传递。
3. 事件过滤器逻辑: 在 eventFilter() 方法内部,你可以编写自定义逻辑来决定如何处理或忽略事件。如果此方法返回 true ,则表示事件已被处理,不应该继续传递;如果返回 false ,则事件将正常传递给目标对象。
4. 事件分发: 当事件发生时,Qt首先将事件发送到安装了事件过滤器的对象。在这一步,
eventFilter() 方法被调用。
5. 决定是否传递事件: 根据 eventFilter() 方法的返回值,Qt决定是否继续向目标对象传递事件。如果过滤器返回 true ,事件处理到此结束;如果返回 false ,事件继续传递到原始目标对象。 //这一步实现了释放滚轮的目的,即结束后我们滚轮就能滑动文本上下移动了
6. 目标对象处理事件: 如果事件过滤器允许事件继续传递,目标对象将像没有事件过滤器存在时那样处理事件。
适用情况:
事件过滤器特别适用于以下情况:
当你想在不修改子类代码的情况下改变事件的行为。
当多个对象需要共享相同的事件处理逻辑。
当你需要在更高的层级上监控或修改应用程序的事件流。
通过使用事件过滤器,Qt应用程序可以获得更大的灵活性和更细粒度的事件处理控制
实现:
在widget.h中添加
bool eventFilter(QObject *watched, QEvent *event);
构造函数中添加
ui->textEdit->installEventFilter(this); //注意这里要放在初始化this之后不然就是调用野指针,导致段错误,程序崩溃
实现事件过滤器
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
// QKeyEvent *keyEvent = (QKeyEvent *)event;
// if(keyEvent->type() == Qt::Key_Control){
// qDebug() << "ctrl";
// }
// 上面这种方式和下面的滚轮滑动是并行的,互斥了,我们执行下面这种方式
if(event->type() == QEvent::Wheel){ //通过事件过滤器,过滤出一个滚轮事件
if(QGuiApplication::keyboardModifiers() == Qt::ControlModifier) //当滚轮事件发生的时候判断,ctrl是否被按下
{
qDebug() << "ctrl + wheel";
//event 不够用,无法判断 滚轮滑动方向 --> 强转实现
QWheelEvent *wheelEvent = ( QWheelEvent *)event;
if(wheelEvent->angleDelta().y()>0){
zoomIn();
}
else{
zoomOut();
}
return true;
}
return false;
}
}
========================
虽然也能实现,但是存在一个bug,当放大一定程度,右边会出现一个拖动条,当拖动条道中间的时候是不能进行 ctrl + 滚轮缩放的
C++ 强制类型转换的补充:
在 C++ 中,强制类型转换(或类型转换)是一种将变量从一种类型转换为另一种类型的方法。C++ 提供了四种强制转换运算符,每种都有其特定的用途和适用场景:
1. static_cast
static_cast 是最常用的类型转换运算符,用于无风险的转换,如整数到浮点数,字符到整
数等。
它在编译时执行,不执行运行时类型检查(RTTI)。
示例: int x = static_cast<int>(y); 其中 y 可能是 float 类型。
2. dynamic_cast
专门用于处理对象的多态性,只能用于指针和引用,且涉及对象类必须有虚函数。
它在运行时检查类型的安全性,如果转换失败,对于指针类型返回 nullptr ,对于引用类型
抛出异常。
示例: Derived *dp = dynamic_cast<Derived *>(bp); 其中 bp 是基类指针, Derived是派生类。
3. const_cast
用于修改类型的 const 或 volatile 属性。
通常用于去除对象的 const 性质,允许修改原本被声明为 const 的变量。
示例: const int a = 10; int* b = const_cast<int*>(&a);
4. reinterpret_cast
用于进行低级别的重新解释转换,几乎无限制,但也是最危险的。
它可以将一种完全不相关的类型转换为另一种类型,比如将指针类型转换为整数类型。
示例: long p = reinterpret_cast<long>(&object); 其中 object 是某个类的对象。