【Qt】 3_Notepad 实例开发 – 可使用

60次阅读

共计 15177 个字符,预计需要花费 38 分钟才能阅读完成。

界面展示

核心概念
应用程序中的主窗口

主窗口是与用户进行长时间交互的顶层窗口
程序的绝大多数功能直接由主窗口提供
主窗口通常是应用程序启动后显示的第一个窗口
装个程序由一个主窗口和多个对话框组成

Qt 中的主窗口
– Qt 开发平台中直接支持主窗口的概念
– QMainWindow 是 Qt 中主窗口的基类
– QMainWindow 继承于 QWidget 是一种容器类型的组件

QMainWindow 中的封装
1. 菜单栏
2. 工具栏
3. 中心组件
4. 停靠组件
5. 状态栏

QMainWindow 中的组件布局

在 Qt 中与菜单相关的类组件

/**
*@brief 创建菜单栏
*/
bool MainWindow::initMenuBar()
{
QMenuBar* mb = menuBar();
bool ret = (mb != nullptr);

ret = ret && initFileMenu(mb);
ret = ret && initEditMenu(mb);
ret = ret && initFormatMenu(mb);
ret = ret && initViewMenu(mb);
ret = ret && initHelpMenu(mb);

return ret;
}

/**
*@brief 创建下拉菜单组
*/
bool MainWindow::initFileMenu(QMenuBar* mb)
{
QMenu* menu = new QMenu(“ 文件 (&F)”, mb);
bool ret = (menu != nullptr);

if(ret)
{
QAction* action = nullptr;

ret = ret && makeAction(action, menu, “ 新建 (&N)”, Qt::CTRL + Qt::Key_N);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileNew()));
menu->addAction(action);
}

ret = ret && makeAction(action, menu, “ 打开 (&O)…”, Qt::CTRL + Qt::Key_O);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileOpen()));
menu->addAction(action);
}

ret = ret && makeAction(action, menu, “ 保存 (&S)”, Qt::CTRL + Qt::Key_S);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileSave()));
menu->addAction(action);
}

ret = ret && makeAction(action, menu, “ 另存为 (&A)…”, 0);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileSaveAs()));
menu->addAction(action);
}

menu->addSeparator();

ret = ret && makeAction(action, menu, “ 页面设置 (&U)…”, Qt::CTRL + Qt::Key_U);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFilePageSetup()));
menu->addAction(action);
}

ret = ret && makeAction(action, menu, “ 打印 (&P)…”, Qt::CTRL + Qt::Key_P);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFilePrint()));
menu->addAction(action);
}

menu->addSeparator();

ret = ret && makeAction(action, menu, “ 退出 (&X)”, 0);
if(ret)
{
menu->addAction(action);
}
}

if(ret)
{
mb->addMenu(menu);
}
else
{
delete menu;
}

return ret;
}

/**
*@brief 创建菜单项
*/
bool MainWindow::makeAction(QAction*& action, QWidget* parent, QString text, int key)
{
bool ret = true;

action = new QAction(text, parent);

if(action != nullptr)
{
action->setShortcut(QKeySequence(key)); // 添加快捷键
}
else
{
ret = false;
}

return ret;
}
主窗口中的工具栏
工具栏的概念和意义

应用程序中集成各种功能实现快捷使用的一个区域
工具栏并不是应用程序中必须存在的组件
工具栏中的元素可以是各种组件窗口
工具栏中的元素通常以图标按钮的方式存在

在 Qt 中与工具栏相关的组件

/**
*@brief 创建工具栏
*/
bool MainWindow::initToolBar()
{
QToolBar* tb = addToolBar(“ 工具栏 ”);
bool ret = true;

tb->setIconSize(QSize(16, 16));
tb->setFloatable(false);
tb->setMovable(false);

ret = ret && initFileToolItem(tb);

tb->addSeparator();

ret = ret && initEditToolItem(tb);

tb->addSeparator();

ret = ret && initFormatToolItem(tb);

tb->addSeparator();

ret = ret && initViewToolItem(tb);

return ret;
}

/**
*@brief 创建与文件操作相关的快捷项
*/
bool MainWindow::initFileToolItem(QToolBar* tb)
{
QAction* action = nullptr;
bool ret = true;

ret = ret && makeAction(action, tb, “ 新建 ”, “:/Res/pic/new.png”);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileNew()));
tb->addAction(action);
}

ret = ret && makeAction(action, tb, “ 打开 ”, “:/Res/pic/open.png”);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileOpen()));
tb->addAction(action);
}

ret = ret && makeAction(action, tb, “ 保存 ”, “:/Res/pic/save.png”);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileSave()));
tb->addAction(action);
}

ret = ret && makeAction(action, tb, “ 另存为 ”, “:/Res/pic/saveas.png”);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFileSaveAs()));
tb->addAction(action);
}

ret = ret && makeAction(action, tb, “ 打印 ”, “:/Res/pic/print.png”);
if(ret)
{
connect(action, SIGNAL(triggered()), this, SLOT(onFilePrint()));
tb->addAction(action);
}

return ret;
}

/**
*@brief 创建具体的快捷项
*/
bool MainWindow::makeAction(QAction*& action, QWidget* parent, QString tip, QString icon)
{
bool ret = true;

action = new QAction(“”, parent);

if(action != nullptr)
{
action->setToolTip(tip);
action->setIcon(QIcon(icon));
}
else
{
ret = false;
}

return ret;
}
主窗口中的状态栏
状态栏的概念和意义

状态栏是应用程序中输出简要信息的区域
状态栏一般位于主窗口的最底部

状态栏中的消息类型

实时消息,如:当前程序状态
永久消息,如:程序版本号,机构名称
进度消息,如:进度条提示,百分比提示

在 Qt 中提供与状态栏相关的类组件

Qt 状态栏的设计原则

左边的区域用于输出实时消息
右边的区域用于设置永久消息
addWidget 在左半部分添加组件
addPermanentWidget 在状态栏右半部分调价组件

/**
*@brief 创建状态栏
*/
bool MainWindow::initStatusBar()
{
QStatusBar* sb = statusBar();
QLabel* label = new QLabel(“D.T.TianSong”);
bool ret = true;

if(label != nullptr)
{
sb->addPermanentWidget(new QLabel());

statusLabel.setMinimumWidth(150);
statusLabel.setAlignment(Qt::AlignCenter);
statusLabel.setText(“length: ” + QString::number(0) + ” lines: ” + QString::number(1));
sb->addPermanentWidget(&statusLabel);

statusCursorLabel.setMinimumWidth(150);
statusCursorLabel.setAlignment(Qt::AlignCenter);
statusCursorLabel.setText(“Ln: ” + QString::number(1) + ” Col: ” + QString::number(1));
sb->addPermanentWidget(&statusCursorLabel);

label->setMinimumWidth(150);
label->setAlignment(Qt::AlignCenter);
sb->addPermanentWidget(label);
}
else
{
ret = false;
}

return ret;
}
Qt 中的文本编辑组件
Qt 中支持 3 中常用的文本编辑组件

QLineEdit 单行文本编辑组件
QTextEdit 多行富文本编辑组件
QPlainTextEdit 多行普通文本编辑组件

Qt 中常用文本编辑组件的集成层次图

不同文本组件的特性比较

单行文本支持
多行文本支持
自定义格式支持
富文本支持

QLineEdit
Yes
No
No
No

QPlainTextEdit
Yes
Yes
No
No

QTextEdit
Yes
Yes
Yes
Yes

Qt 中常用文本编辑组件的内置功能

右键弹出菜单
快捷键功能(复制,粘贴,剪切,等)

/**
*@brief 创建中心组件
*/
bool MainWindow::initMainEditor()
{
bool ret = true;

QPalette p = mainEditor.palette();
p.setColor(QPalette::Inactive, QPalette::Highlight, p.color(QPalette::Active, QPalette::Highlight));
p.setColor(QPalette::Inactive, QPalette::HighlightedText, p.color(QPalette::Active, QPalette::HighlightedText));
mainEditor.setPalette(p);

mainEditor.setParent(this);
mainEditor.setAcceptDrops(false);
setCentralWidget(&mainEditor);

return ret;
}
Qt 中的 IO 操作
Qt 中 IO 操作的处理方式

Qt 通过统一的接口简化了文件与外部设备的操作方式
Qt 中的文件被看作一种特殊的外部设备
Qt 中的文件操作与外部设备的操作相同

IO 操作的微本质:连续存储空间的数据读写
Qt 中 IO 设备的继承层次图

QFile 是 Qt 中用于文件操作的类,对应到计算机上的一个文件
QFileInfo 类用于读取文件信息
QTemporaryFile 安全的创建一个全局唯一的临时文件,对象销毁时临时文件删除

void write(QString f)
{
QFile file(f);

if(file.open(QIODevice::WriteOnly | QIODevice::Text) )
{
file.write(“D.T.Software\n”);
file.write(“Delphi Tang\n”);
file.close();
}
}

void read(QString f)
{
QFile file(f);

if(file.open(QIODevice::ReadOnly | QIODevice::Text) )
{
QByteArray ba = file.readLine();
QString s(ba);

qDebug() << s;

file.close();
}
}

void info(QString f)
{
QFile file(f);
QFileInfo info(file);

qDebug() << info.exists();
qDebug() << info.isFile();
qDebug() << info.isReadable();
qDebug() << info.isWritable();
qDebug() << info.created();
qDebug() << info.lastRead();
qDebug() << info.lastModified();
qDebug() << info.path();
qDebug() << info.fileName();
qDebug() << info.suffix();
qDebug() << info.size();
}
文本流和数据流

Qt 中将文件类型分为 2 大类

文本文件:文件内容是可读的文本字符
数据文件:文件内容是直接的二进制数据

Qt 提供辅助类简化了文本文件 / 数据文件的读写

QTextStream – 写入的数据全部转换为可读文本
QDataStream – 写入的数据根据类型转换为二进制数据

void text_stream_test(QString f)
{
QFile file(f);

if(file.open(QIODevice::WriteOnly | QIODevice::Text) )
{
QTextStream out(&file);

out << QString(“D.T.Software”) << endl;
out << QString(“Result: “) << endl;
out << 5 << ‘*’ << 6 << ‘=’ << 5 * 6 << endl;

file.close();
}

if(file.open(QIODevice::ReadOnly | QIODevice::Text) )
{
QTextStream in(&file);

while(!in.atEnd() )
{
QString line = in.readLine();

qDebug() << line;
}

file.close();
}
}

void data_stream_test(QString f)
{
QFile file(f);

if(file.open(QIODevice::WriteOnly) )
{
QDataStream out(&file);

out.setVersion(QDataStream::Qt_4_7);

out << QString(“D.T.Software”);
out << QString(“Result: “);
out << 3.14;

file.close();
}

if(file.open(QIODevice::ReadOnly) )
{
QDataStream in(&file);
QString dt = “”;
QString result = “”;
double value = 0;

in.setVersion(QDataStream::Qt_4_7);

in >> dt;
in >> result;
in >> value;

file.close();

qDebug() << dt;
qDebug() << result;
qDebug() << value;
}
}

不同 Qt 版本的数据流文件格式可能不同

设置读写版本号:void setVersion(int v)
获取读写版本号:int version() const
当前数据流文件可能在不同版本的 Qt 程序间传递数据时,需要考虑版本问题

缓冲区与目录操作
Qt 中缓冲区的概念

缓冲区的本质为一段连续的存储空间
QBuffer 是 Qt 中缓冲区相关的类
在 Qt 中可以将缓冲区看作一种特殊的 IO 设备
文件辅助类可以直接用于操作缓冲区

QBuffer 缓冲区的使用场合

在线程间进行不同类型的数据传递
缓存外部设备中的数据返回
数据读取速度小于数据写入速度

void write_buffer(int type, QBuffer& buffer)
{
if(buffer.open(QIODevice::WriteOnly) )
{
QDataStream out(&buffer);

out << type;

if(type == 0)
{
out << QString(“D.T.Software”);
out << QString(“3.1415”);
}
else if(type == 1)
{
out << 3;
out << 1415;
}
else if(type == 2)
{
out << 3.1415;
}

buffer.close();
}
}

void read_buffer(QBuffer& buffer)
{
if(buffer.open(QIODevice::ReadOnly) )
{
int type = -1;
QDataStream in(&buffer);

in >> type;

if(type == 0)
{
QString dt = “”;
QString pi = “”;

in >> dt;
in >> pi;

qDebug() << dt;
qDebug() << pi;
}
else if(type == 1)
{
int a = 0;
int b = 0;

in >> a;
in >> b;

qDebug() << a;
qDebug() << b;
}
else if(type == 2)
{
double pi = 0;

in >> pi;

qDebug() << pi;
}

buffer.close();
}
}

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray array;
QBuffer buffer(&array);

write_buffer(2, buffer);
read_buffer(buffer);

return a.exec();
}
QDir 是 Qt 中功能强大的目录操作类

Qt 中的目录分隔符统一使用 ‘/’
QDir 能够对目标目录进行任意操作(创建,删除,重命名)
QDir 能够获取指定目录中的所有条目
QDir 能够获取系统中的所有根目录

QFileSystemWatcher 用于监控文件和目录的状态变化

能够监控特定目录和文件的状态
能够同时对多个目录和文件进行监控
当目录或者文件改变时将触发信号
可以通过信号与槽的机制捕捉信号并作出相应

文本编辑器中的数据存储
QAction 的信号

QAction 被点击之后会产生一个 triggered 信号
通过信号与槽的机制能够捕捉对 QAction 对象的操作
项目中可以将多个信号映射到同一个槽函数

文件打开操作

文件保存操作
定义成员变量 m_filePath 用于标记数据来源

文件另存为操作

int MainWindow::showQueryMessage(QString message)
{
QMessageBox msg(this);

msg.setIcon(QMessageBox::Question);
msg.setWindowTitle(“ 记事本 ”);
msg.setWindowFlag(Qt::Drawer);
msg.setText(message);
msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);

return msg.exec();
}

void MainWindow::preEditChange()
{
if(m_isTextChanged)
{
QString path = (m_filePath != “”) ? m_filePath : “ 无标题 ”;
int r = showQueryMessage(QString(“ 是否将更改保存到 \n”) + “\”” + path + “\” ?”);

switch (r)
{
case QMessageBox::Yes:
saveCurrentData(“ 保存 ”, m_filePath);
break;
case QMessageBox::No:
m_isTextChanged = false;
break;
case QMessageBox::Cancel:
break;
}
}
}

void MainWindow::openFileEditor(QString path)
{
if(path != “”)
{
QFile file(path);

if(file.open(QIODevice::ReadOnly | QIODevice::Text) )
{
QTextStream in(&file);
in.setCodec(“GBK”);

mainEditor.setPlainText(in.readAll());

file.close();

m_filePath = path;

m_isTextChanged = false;

setWindowTitle(m_filePath + “- 记事本 ”);
}
else
{
showErrorMessage(QString(“ 打开文件失败!\n\n”) + “\”” + path + “\”。”);
}
}
}

void MainWindow::openFile(QString path)
{
preEditChange();

if(!m_isTextChanged)
{
openFileEditor(path);
}
}

void MainWindow::onFileOpen()
{
preEditChange();

if(!m_isTextChanged)
{
QString path = showFileDialog(QFileDialog::AcceptOpen, “ 打开 ”, “:/Res/pic/logo.png”);

openFileEditor(path);
}
}

QString MainWindow::saveCurrentData(QString title, QString path)
{
QString ret = path;

if(ret == “”)
{
ret = showFileDialog(QFileDialog::AcceptSave, title, “:/Res/pic/logo.png”);
}

if(ret != “”)
{
QFile file(ret);

if(file.open(QIODevice::WriteOnly | QIODevice::Text) )
{
QTextStream out(&file);

out << mainEditor.toPlainText();

file.close();

setWindowTitle(ret + ” – 记事本 ”);

m_isTextChanged = false;
}
else
{
showErrorMessage(QString(“ 保存文件失败!\n\n”) + “\”。” + ret + “\””);
}
}

return ret;
}

void MainWindow::onFileSave()
{
QString path = saveCurrentData(“ 保存 ”, m_filePath);

if(path != “”)
{
m_filePath = path;
}
}

void MainWindow::onFileSaveAs()
{
QString path = saveCurrentData(“ 另存为 ”);

if(path != “”)
{
m_filePath = path;
}
}
文本编辑器中的功能交互
QPlainTextEdit 相关的信号

使用关键槽函数判断数据状态

void textChanged() ==> 辅助判断是否有数据未保存
void copyAvailabel(bool)
void cursorPositionChanged()
void redoAvailable(bool);
void undoAvailable(bool)

判断是由存在未保存的数据

定义槽函数 void onTextChanged()
映射 textChanged() 到槽函数
定义成员变量 bool m_isTextChanged = false;
文本框中的字符发生变化时:m_isTextChanged = true
当 m_isTextChanged 为真,则存在未保存的数据

void MainWindow::onTextChanged()
{
if(!m_isTextChanged)
{
setWindowTitle(“* ” + windowTitle());
}

m_isTextChanged = true;

statusLabel.setText(“length: ” + QString::number(mainEditor.toPlainText().length()) + ” lines: ” + QString::number(mainEditor.document()->lineCount()));
}
文件新建操作

void MainWindow::onFileNew()
{
preEditChange();

if(!m_isTextChanged)
{
mainEditor.clear();

m_isTextChanged = false;

setWindowTitle(“ 新建文本文档 – 记事本 ”);
}
}
文本编辑器中的后缀映射
通过 QMap 实现
QString MainWindow::showFileDialog(QFileDialog::AcceptMode mode, QString title, QString icon)
{
QFileDialog fd(this);
QStringList filters;
QMap<QString, QString> map;
const char* filterArray[][2] =
{
{“ 文本文档 (*.txt)”, “.txt”},
{“ 所有文件 (*.*)” , “.*” },
{nullptr , nullptr}
};
QString ret = “”;

for(int i=0; filterArray[i][0]!=nullptr; i++)
{
filters.append(filterArray[i][0]);
map.insert(filterArray[i][0], filterArray[i][1]);
}

fd.setWindowTitle(title);
fd.setWindowIcon(QIcon(icon));
fd.setAcceptMode(QFileDialog::AcceptOpen);
fd.setNameFilters(filters);

if(mode == QFileDialog::AcceptOpen)
{
fd.setFileMode(QFileDialog::ExistingFile);
}

if(fd.exec() == QFileDialog::Accepted )
{
ret = fd.selectedFiles()[0];

if(mode == QFileDialog::AcceptSave)
{
QString postfix = map[fd.selectedNameFilter()];

if((postfix != “.*”) && !ret.endsWith(postfix) )
{
ret = ret + postfix;
}
}
}

return ret;
}
Qt 中的事件处理
图形界面应用程序的消息处理模型

Qt 平台将系统产生的消息转换为 Qt 事件

Qt 事件用于描述程序内部或外部发生的动作
任意的 QObject 对象都具备事件处理的能力

GUI 应用程序的事件处理方式

Qt 事件产生后立即被分发到 QWidget 对象
QWidget 中的 event(QEvent*) 进行事件处理
event() 根据事件类型调用不同的事件处理函数
在事件处理函数中发送 Qt 中预定义的信号
调用信号关联的槽函数

事件(QEvent)和信号(SIGNAL)不同

事件由具体对象进行处理
信号由具体对象主动产生
改写事件处理函数可能导致程序行为发生改变
信号是否存在对应的槽函数不会改变程序行为
一般而言,信号在具体的事件处理函数中产生

文本编辑器的关闭操作
Qt 没有提供预定义的关闭信号,因此重写关闭事件
/**
*@brief 重写关闭事件处理函数
*/
void MainWindow::closeEvent(QCloseEvent *event)
{
preEditChange();

if(!m_isTextChanged)
{
QFont font = mainEditor.font();
bool isWrap = (mainEditor.lineWrapMode() == QPlainTextEdit::WidgetWidth);
bool tbVisible = (findMenuBarAction(“ 工具栏 ”)->isCheckable() && findToolBarAction(“ 工具栏 ”)->isChecked());
bool sbVisible = (findMenuBarAction(“ 状态栏 ”)->isCheckable() && findToolBarAction(“ 状态栏 ”)->isChecked());
AppConfig config(mainEditor.font(), size(), pos(), isWrap, tbVisible, sbVisible, this);

config.store();

QMainWindow::closeEvent(event);
}
else
{
event->ignore();
}
}

/**
*@brief 查找菜单栏中对应的 ACtion
*/
QAction* MainWindow::findMenuBarAction(QString text)
{
QAction* ret = nullptr;
const QObjectList& list = menuBar()->children();

for(int i=0; i<list.count(); i++)
{
QMenu* men = dynamic_cast<QMenu*>(list[i]);

if(men != nullptr)
{
QList<QAction*> actions = men->actions();

for(int j=0; j<actions.count(); j++)
{
if(actions[j]->text().startsWith(text) )
{
ret = actions[j];

break;
}
}
}
}

return ret;
}

/**
*@brief 查找工具栏中对应的 ACtion
*/
QAction* MainWindow::findToolBarAction(QString text)
{
QAction* ret = nullptr;

QList<QAction*> actions = toolBar()->actions();

for(int j=0; j<actions.count(); j++)
{
if(actions[j]->toolTip().startsWith(text) )
{
ret = actions[j];
break;
}
}

return ret;
}
Qt 中的拖放事件

拖放一个文件进入窗口时将触发拖放事件
每一个 QWidget 对象都能够处理拖放事件

拖放事件的处理函数为:

void dragEnterEvent(QDragEnterEvent* e);
void dropEvent(QDropEvent* e);

拖放事件中的 QMimeData

QMimeData 是 Qt 中的多媒体数据类
拖放事件通过 QMimeData 对象传递数据
QMimeData 支持多种不同类型的多媒体数据

常用 MIME 类型数据处理函数

自定义拖放事件的步骤

对接收拖放事件的对象调用 setAcceptDrop 成员函数

重写 dragEnterEvent 函数并判断 MIME 类型

期望数据:e->acceptProposedAction();
其他数据:e->ignore();

重写 dropEvent 函数并判断 MIMI 类型

期望数据:从事件对象中获取 MIME 数据并处理
其它数据:e->ignore();

文本编辑器中的拖放操作

void MainWindow::dragEnterEvent(QDragEnterEvent* event)
{
if(event->mimeData()->hasUrls())
{
event->acceptProposedAction();
}
else
{
event->ignore();
}
}

void MainWindow::dropEvent(QDropEvent* event)
{
if(event->mimeData()->hasUrls())
{
QList<QUrl> list = event->mimeData()->urls();
QString path = list[0].toLocalFile();
QFileInfo fi(path);

if(fi.isFile() )
{
preEditChange();

if(!m_isTextChanged)
{
openFileEditor(path);
}
}
else
{
showErrorMessage(QString(“ 对 “) + “\”” + path + “\” 的访问被拒绝。”);
}
}
else
{
event->ignore();
}
}
文本打印与光标定位
QPlainTextEdit 内部的文档结构(数据与界面分离)

QPlainTextEdit 通过 QTextDocument 对象存储文本
QPlainTextEdit 本身只负责界面形态的显示

QTextDocument 是表示文本以及文本属性的数据类

设置文本属性:排版,字体,标题,等
获取文本参数:行数,文本宽度,文本信息,等
实现标准操作:撤销,重做,查找,打印,等

打印功能的实现步骤

连接 QAction 打印对象的信号到槽
在槽函数中定义 QPrintDialog 对象
根据用户选择获取 QPrinter 对象
通过 QTextDocument 对象进行打印

void MainWindow::onFilePrint()
{
QPrintDialog dlg(this);

dlg.setWindowTitle(“ 打印 ”);

if(dlg.exec() == QPrintDialog::Accepted )
{
QPrinter* p = dlg.printer();

p->setPageLayout(m_pPageSetupDlg->printer()->pageLayout());

mainEditor.document()->print(p);
}
}
光标位置的计算

思路

文本框对象的内部包含了 QTextCursor 对象
通过 position() 成员函数获取当前光标的字符位置
根据光标的字符位置计算横纵坐标
当光标位置发生变化时进行计算

算法流程描述

通过 ‘n’ 字符的个数计算所在行
通过最后一个 ‘n’ 字符的下标计算所在列

void MainWindow::onCursorPositionChanged()
{
int col = 0;
int ln = 0;
int flg = -1;
int pos = mainEditor.textCursor().position();
QString text = mainEditor.toPlainText();

for(int i=0; i<pos; i++)
{
if(text[i] == ‘\n’ )
{
ln ++;
flg = i;
}
}

flg ++;

col = pos – flg;

statusCursorLabel.setText(“Ln: ” + QString::number(ln + 1) + ” Col: ” + QString::number(col + 1));
}
在程序中发送自主事件

阻塞型事件发送:时间发送后需要等待事件处理完成
非阻塞型事件发送:事件发送后立即返回;事件被发送到事件队列中等待处理

QApplication 类提供了支持事件发送的静态成员函数

阻塞型发送函数:bool sendEvent(QObject receiver, QEvent event);
非阻塞型事件发送函数:bool postEvent(QObject receiver, QEvent event);

注意事项

sendEvent 中事件对象的生命期由 Qt 平台管理
同时支持栈事件对象和堆事件对象

postEvent 中事件对象的生命期由 Qt 平台管理

正文完
 0