目录
1.项目概述
1.1界面预览
1.2功能介绍
-
支持文本创建,打开,保存,关闭的功能
-
UI样式美化
-
添加打开快捷键,添加保存快捷
-
底部显示行列号及文本字符编码
- Ctrl加鼠标滚轮支持字体放大缩小
2.UI设计
2.1控件
- 按键 QPushButton
- 水平布局 QHBoxLayout
- 文本编辑器 TextEdit
- 显示底色Widget
- 弹簧
雏形大概长这个样子,后续插入图片就可以了
2.2图片的插入
点击添加新文件
点击添加前缀
我们不要前缀
再次点击添加
将下载好的文件添加进去
添加好的样子
到UI界面右键点击按键
选择你的图片后点击ok 重复3次就可以把按键添加好了
刚插入图片可能是扁的自行调整图片的大小
3.程序的实现
3.1程序实现按键的功能
这里我们需要用到信号与槽的知识点,如下是对信号与槽的概述。
- 信号 (Signals):是由对象在特定事件发生时发出的消息。例如, QPushButton 有一个
- clicked() 信号,当用户点击按钮时发出。
- 槽 (Slots):是用来响应信号的方法。一个槽可以是任何函数,当其关联的信号被发出时,该槽函数将被调用。
- 连接信号和槽:使用 QObject::connect() 方法将信号连接到槽。当信号发出时,关联的槽函数 会自动执行。
按键我们使用如下方式进行信号与槽的关联(信号与槽关联有许多方式这是最简单的一种)
点击ok即可,分别对3个按键执行上述操作后会在widget.h下生成3个槽函数
在widget.cpp下实现我们的槽函数的功能。
3.2代码分析
3.2.1打开文件操作
我们可能不了解某一个类的使用但是Qt提供了例程和参考我们在这个的基础上修改就可以了
重点:要学会使用翻译软件和Qt的帮助文档这才是学习Qt的精髓
QFileDialog类的使用方法和介绍
QFile类的使用方法和介绍
QFile是Qt中用于文件操作的基本类,提供了读取、写入、拷贝、移动和访问文件属性等功能。
如下是常用的一些方法
open() :打开一个文件。需要指定模式(如只读、只写、读写等)。close() :关闭文件。read() 和 write() :用于读取和写入数据。exists() :检查文件是否存在。remove() :删除文件。copy() :复制文件。
一样的不了解这个类直接照着Qt帮助文档改就好了
void Widget::on_Btopen_clicked()
{
QString fileName;
QString strLine;
// 弹出文件打开对话框,获取用户选择的文件名
fileName = QFileDialog::getOpenFileName(this, tr("Open file"),
"D:/QT/QtProject2/",
tr("Image Files (*.png *.jpg *.bmp);;Text files (*.txt)"));
// 清空文本编辑框内容
ui->textEdit->clear();
// 设置文件名并尝试打开文件
file.setFileName(fileName);
if(!file.open(QIODevice::ReadWrite | QIODevice::Text)){
qDebug() << "open fail!";
}
// 设置窗口标题为文件名
this->setWindowTitle(fileName + "-记事本");
// 创建文本流对象,关联文件
QTextStream in(&file);
// 设置文本编码格式
in.setCodec(ui->comboBox->currentText().toStdString().c_str()); // 将QString转成const char *
// 逐行读取文件内容,并显示在文本编辑框中
while (!in.atEnd()) {
strLine = in.readLine(file.size());
ui->textEdit->append(strLine); // 将每行文本追加到文本编辑框中
}
}
3.2.2保存文件操作
void Widget::on_Btsave_clicked()
{
// 检查文件是否已经打开
if(!file.isOpen()){
// 弹出文件保存对话框,获取文件名
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"D:/QT/QtProject2/",
tr("Text files (*.txt)"));
// 设置文件名并尝试打开文件
file.setFileName(fileName);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
qDebug() << "open fail!";
}
// 设置窗口标题为文件名
this->setWindowTitle(fileName + "-记事本");
}
// 创建文本流对象,关联文件
QTextStream out(&file);
// 设置文本编码格式
out.setCodec(ui->comboBox->currentText().toStdString().c_str()); // 将QString转成const char *
// 获取文本编辑框中的文本
QString strText = ui->textEdit->toPlainText();
// 将文本写入文件
out << strText;
}
3.2.3关闭文件操作
void Widget::on_Btclose_clicked()
{
// 弹出消息框,询问用户是否保存修改的文档
int ret = QMessageBox::warning(this, tr("记事本"),
tr("文档已修改,要保存更改吗?"),
QMessageBox::Save | QMessageBox::Discard
| QMessageBox::Cancel,
QMessageBox::Save);
// 根据用户的选择进行相应的操作
switch (ret) {
case QMessageBox::Save:
// 用户选择保存
on_Btsave_clicked(); // 调用保存按钮的槽函数,保存文档
break;
case QMessageBox::Discard:
// 用户选择不保存
ui->textEdit->clear(); // 清空文本编辑框内容
this->setWindowTitle("记事本"); // 设置窗口标题为默认值
if(file.isOpen()){
file.close(); // 关闭文件
}
break;
case QMessageBox::Cancel:
// 用户选择取消
break;
default:
break;
}
}
经过这3个按键的开发流程是不是Qt的学习很简单照着帮助文档改就可以了
3.2.4切换编码格式
这里的信号与槽使用的是手动关联的方式
connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
widget.h文件也要声明一下这里的槽函数名字要一致
void onCurrentIndexChanged(int index);
widget.cpp对槽函数的具体实现
void Widget::onCurrentIndexChanged(int index)
{
QString strLine;
// 清空文本编辑框内容
ui->textEdit->clear();
// 将文件指针移动到文件开头
file.seek(0);
// 如果文件已经打开
if(file.isOpen()){
// 创建文本流对象,关联文件
QTextStream in(&file);
// 设置文本编码格式
in.setCodec(ui->comboBox->currentText().toStdString().c_str()); // 将QString转成const char *
// 逐行读取文件内容,并显示在文本编辑框中
while (!in.atEnd()) {
strLine = in.readLine(file.size());
ui->textEdit->append(strLine); // 将每行文本追加到文本编辑框中
}
}
}
3.2.5显示当前行列和设置当前行高亮
设置当前行高亮要用到c++模板的知识点
在 C++ 中,模板( Template )是一种通用的编程工具,允许程序员编写 泛型代码 ,使得 类或函数能够适 用于多种不同的数据类型 而不需要重复编写相似的代码
void Widget::onCursorPositionChanged()
{
QTextCursor cursor;
// 获取文本编辑框的光标对象
cursor = ui->textEdit->textCursor();
// 获取光标所在的行号和列号
QString block = QString::number(cursor.blockNumber() + 1);
QString column = QString::number(cursor.columnNumber() + 1);
// 构建显示位置的字符串
const QString labpos = "行:"+block+",列:"+column;
// 在标签上显示位置信息
ui->labelpos->setText(labpos);
// 设置整行高亮
QList<QTextEdit::ExtraSelection> extra;
QTextEdit::ExtraSelection ext;
// 获取当前行的光标
ext.cursor = cursor;
// 设置高亮颜色为浅灰色
QBrush qBrush(Qt::lightGray);
ext.format.setBackground(qBrush);
// 设置选中区域为整行
ext.format.setProperty(QTextFormat::FullWidthSelection, true);
// 将设置好的高亮添加到额外选择列表中
extra.append(ext);
// 将额外选择列表应用到文本编辑框中
ui->textEdit->setExtraSelections(extra);
}
3.2.6快捷键打开和保存文件
部分快捷键
参考QT中的例子
在关联信号与槽时使用了Lambda 表达式(匿名函数)
基本语法如下:
[捕获列表](形参列表) -> 返回值的类型 {
// 函数体
// 可以使用捕获列表中的变量
return expression; // 可选的返回语句
}
QShortcut *shortcutopen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),this);
QShortcut *shortcutsave = new QShortcut(QKeySequence(tr("Ctrl+s", "File|Save")),this);
connect(shortcutopen, &QShortcut::activated, [=](){
on_Btopen_clicked();
});
connect(shortcutsave, &QShortcut::activated, [=](){
on_Btsave_clicked();
});
3.2.7放大和缩小字体
这里用到了事件这个知识点
事件的概述
众所周知 Qt 是一个基于 C++ 的框架,主要用来开发带窗口的应用程序(不带窗口的也行,但不是主流)。 我们使用的基于窗口的应用程序都是基于事件,其目的主要是用来实现回调(因为只有这样程序的效率 才是最高的)。所以在Qt 框架内部为我们提供了一些列的事件处理机制,当窗口事件产生之后,事件会 经过: 事件派发 -> 事件过滤->事件分发->事件处理 几个阶段。 Qt 窗口中对于产生的一系列事件都有默认 的处理动作,如果我们有特殊需求就需要在合适的阶段重写事件的处理动作,比如信号与槽就是一种 事件(event )是由系统或者 Qt 本身在不同的场景下发出的。当用户按下 / 移动鼠标、敲下键盘,或者是 窗口关闭/ 大小发生变化 / 隐藏或显示都会发出一个相应的事件。一些事件在对用户操作做出响应时发出, 如鼠标/ 键盘事件等;另一些事件则是由系统自动发出,如计时器事件。每一个 Qt 应用程序都对应一个唯一的 QApplication 应用程序对象,然后调用这个对象的 exec() 函 数,这样Qt 框架内部的事件检测就开始了( 程序将进入事件循环来监听应用程序的事件 )。
事件在Qt中产生之后,的分发过程是这样的:
//1. 当事件产生之后,Qt使用用应用程序对象调用 notify() 函数将事件发送到指定的窗口:
[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e);
//2. 事件在发送过程中可以通过事件过滤器进行过滤,默认不对任何产生的事件进行过滤。
// 需要先给窗口安装过滤器, 该事件才会触发
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
//3. 当事件发送到指定窗口之后,窗口的事件分发器会对收到的事件进行分类:
[override virtual protected] bool QWidget::event(QEvent *event);
//4. 事件分发器会将分类之后的事件(鼠标事件、键盘事件、绘图事件。。。)分发给对应的事件处理
//器函数进行处理,每个事件处理器函数都有默认的处理动作(我们也可以重写这些事件处理器函
//数),比如:鼠标事件:
// 鼠标按下
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 鼠标释放
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 鼠标移动
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);
鼠标滑轮和按键事件的实现
// 滑轮事件
void myTextEdit::wheelEvent(QWheelEvent *e)
{
// 获取滚轮滚动的度数变化量
qDebug() << e->angleDelta().y();
// 如果 Ctrl 键被按下
if (CtrlKeyCheck == 1) {
// 如果滚轮向上滚动
if (e->angleDelta().y() > 0) {
zoomIn(); // 调用放大函数
}
// 如果滚轮向下滚动
else if (e->angleDelta().y() < 0) {
zoomOut(); // 调用缩小函数
}
e->accept(); // 接受事件
} else {
QTextEdit::wheelEvent(e); // 让事件继续进行 QTextEdit 的操作
}
}
// 键盘按下事件
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
// 如果按下的是 Ctrl 键
if (e->key() == Qt::Key_Control) {
CtrlKeyCheck = 1; // 设置 CtrlKeyCheck 为 1
} else {
QTextEdit::keyPressEvent(e); // 让事件继续进行 QTextEdit 的操作
}
}
// 键盘松开事件
void myTextEdit::keyReleaseEvent(QKeyEvent *e)
{
// 如果松开的是 Ctrl 键
if (e->key() == Qt::Key_Control) {
CtrlKeyCheck = 0; // 设置 CtrlKeyCheck 为 0
} else {
QTextEdit::keyReleaseEvent(e); // 让事件继续进行 QTextEdit 的操作
}
}
这里搞重名了注意一下
void Widget::zoomIn()
{
// 获取当前的字体
QFont qfont = ui->textEdit->font();
// 获取当前字体的大小
int sizeFont = qfont.pointSize();
// 如果字体大小为-1,表示使用的是像素单位,不进行缩放
if (sizeFont == -1)
return;
// 改变字体大小,增加1个点大小
int newSetFont = sizeFont + 1;
qfont.setPointSize(newSetFont);
// 设置新的字体到文本编辑器
ui->textEdit->setFont(qfont);
}
void Widget::zoomOut()
{
// 获取当前的字体
QFont qfont = ui->textEdit->font();
// 获取当前字体的大小
int sizeFont = qfont.pointSize();
// 如果字体大小为-1,表示使用的是像素单位,不进行缩放
if (sizeFont == -1)
return;
// 改变字体大小,减少1个点大小
int newSetFont = sizeFont - 1;
qfont.setPointSize(newSetFont);
// 设置新的字体到文本编辑器
ui->textEdit->setFont(qfont);
}
4.完整程序代码
4.1main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
4.2widget
4.2.1widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QFile>
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
QFile file;
explicit Widget(QWidget *parent = 0);
~Widget();
void zoomIn();
void zoomOut();
private slots:
void on_Btopen_clicked();
void on_Btsave_clicked();
void on_Btclose_clicked();
void onCurrentIndexChanged(int index);
void onCursorPositionChanged();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
4.2.1widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <QShortcut>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//让窗口变化
this->setLayout(ui->verticalLayout);
QShortcut *shortcutopen = new QShortcut(QKeySequence(tr("Ctrl+O", "File|Open")),this);
QShortcut *shortcutsave = new QShortcut(QKeySequence(tr("Ctrl+s", "File|Save")),this);
QShortcut *zommIn = new QShortcut(QKeySequence(tr("Ctrl+Shift+=", "File|ZoomIn")),this);
QShortcut *zommout = new QShortcut(QKeySequence(tr("Ctrl+Shift+-", "File|Zoomout")),this);
connect(shortcutopen, &QShortcut::activated, [=](){
on_Btopen_clicked();
});
connect(shortcutsave, &QShortcut::activated, [=](){
on_Btsave_clicked();
});
connect(zommIn, &QShortcut::activated, [=](){
zoomIn();
});
connect(zommout, &QShortcut::activated, [=](){
zoomOut();
});
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()
{
//获取当前的字体
QFont qfont = ui->textEdit->font();
//获取当前字体的大小
int sizeFont = qfont.pointSize();
if(sizeFont == -1) return;
//改变字体
int newSetFont = sizeFont + 1;
qfont.setPointSize(newSetFont);
ui->textEdit->setFont(qfont);
}
void Widget::zoomOut()
{
//获取当前的字体
QFont qfont = ui->textEdit->font();
//获取当前字体的大小
int sizeFont = qfont.pointSize();
if(sizeFont == -1) return;
//改变字体
int newSetFont = sizeFont - 1;
qfont.setPointSize(newSetFont);
ui->textEdit->setFont(qfont);
}
void Widget::on_Btopen_clicked()
{
QString fileName;
QString strLine;
fileName = QFileDialog::getOpenFileName(this,
tr("Open file"),
"D:/QT/QtProject2/",
tr("Image Files (*.png *.jpg *.bmp);;Text files (*.txt)"));
ui->textEdit->clear();
file.setFileName(fileName);
if(!file.open(QIODevice::ReadWrite | QIODevice::Text)){
qDebug() << "open fail!";
}
this->setWindowTitle(fileName + "-记事本");
QTextStream in(&file);
//in.setCodec("UTF-8");
in.setCodec(ui->comboBox->currentText().toStdString().c_str());//将QString转成const chat *
while (!in.atEnd()) {
strLine = in.readLine(file.size());
//qDebug() << strLine;
//ui->textEdit->setText(strLine);
ui->textEdit->append(strLine);
}
}
void Widget::on_Btsave_clicked()
{
if(!file.isOpen()){
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"D:/QT/QtProject2/",
tr("Text files (*.txt)"));
file.setFileName(fileName);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
qDebug() << "open fail!";
}
this->setWindowTitle(fileName + "-记事本");
}
QTextStream out(&file);
//out.setCodec("UTF-8");
out.setCodec(ui->comboBox->currentText().toStdString().c_str());//将QString转成const chat *
QString strText = ui->textEdit->toPlainText();
out << strText;
}
void Widget::on_Btclose_clicked()
{
int ret = QMessageBox::warning(this, tr("记事本"),
tr("文档已修改,要保存更改吗?"),
QMessageBox::Save | QMessageBox::Discard
| QMessageBox::Cancel,
QMessageBox::Save);
switch (ret) {
case QMessageBox::Save:
// Save was clicked
on_Btsave_clicked();
break;
case QMessageBox::Discard:
// Don't Save was clicked
ui->textEdit->clear();
this->setWindowTitle("记事本");
if(file.isOpen()){
file.close();
}
break;
case QMessageBox::Cancel:
// Cancel was clicked
break;
default:
// should never be reached
break;
}
}
void Widget::onCurrentIndexChanged(int index)
{
QString strLine;
ui->textEdit->clear();
file.seek(0);
if(file.isOpen()){
QTextStream in(&file);
in.setCodec(ui->comboBox->currentText().toStdString().c_str());//将QString转成const chat *
while (!in.atEnd()) {
strLine = in.readLine(file.size());
ui->textEdit->append(strLine);
}
}
}
void Widget::onCursorPositionChanged()
{
QTextCursor cursor;
cursor = ui->textEdit->textCursor();
QString block = QString::number(cursor.blockNumber() + 1);
QString column = QString::number(cursor.columnNumber() + 1);
//qDebug() << block << "," << column ;
const QString labpos = "行:"+block+",列:"+column;
ui->labelpos->setText(labpos);
//设置整行高亮
QList<QTextEdit::ExtraSelection> extra;
QTextEdit::ExtraSelection ext;
//获取当前行
ext.cursor = cursor;
QBrush qBrush(Qt::lightGray);
//选择颜色
ext.format.setBackground(qBrush);
ext.format.setProperty(QTextFormat::FullWidthSelection, true);
//设置
extra.append(ext);
ui->textEdit->setExtraSelections(extra);
}
4.3mytextedit
4.3.1mytextedit.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 CtrlKeyCheck = 0;
};
#endif // MYTEXTEDIT_H
4.3.2mytextedit.cpp
#include "mytextedit.h"
#include <QWheelEvent>
#include <QDebug>
myTextEdit::myTextEdit(QWidget *parent) : QTextEdit(parent)
{
}
//滑轮事件
void myTextEdit::wheelEvent(QWheelEvent *e)
{
//获取滚轮度数的变化量e->angleDelta().y();
qDebug() << e->angleDelta().y();
if(CtrlKeyCheck == 1){
if(e->angleDelta().y() > 0){
zoomIn();
}else if(e->angleDelta().y() < 0){
zoomOut();
}
e->accept();
}else{
QTextEdit::wheelEvent(e);//让事件进行QTextEdit的操作
}
}
//键盘的按下事件
void myTextEdit::keyPressEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Control){
CtrlKeyCheck = 1;
}else{
QTextEdit::keyPressEvent(e);
}
}
//键盘的松开事件
void myTextEdit::keyReleaseEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Control){
CtrlKeyCheck = 0;
}else{
QTextEdit::keyReleaseEvent(e);
}
}