用Qt写一个简单的Markdown编辑器
ID:技术让梦想更伟大
作者:李肖遥
Markdown编辑器
Markdown 是一种具有纯文本格式语法的轻量级标记语言,Markdown Editor
演示了如何使用 QWebChannel 和 JavaScript 库为自定义标记语言提供富文本预览工具。
Markdown 编辑器主窗口分为编辑器和预览区,编辑器支持 Markdown 语法,使用 QPlainTextEdit 实现;文档在预览区渲染为富文本,通过QWebEngineView实现。
为了呈现文本,Markdown 文本在 Web 引擎内的 JavaScript 库的帮助下转换为 HTML 格式,预览是通过 QWebChannel 从编辑器更新的。
下图演示如何将 Web 引擎集成到混合桌面应用程序中。
我们使用例子,左侧输入Markdown语法的文本,右侧就是其预览的样式了。
实现原理以及步骤
公开文档文本
通过 QWebChannel 将要渲染的当前 Markdown 文本暴露给 Web 引擎,所以我们需要以某种方式通过 Qt 元类型系统使当前文本可用,这是通过使用将文档文本公开为 Q_PROPERTY
的专用 Document 类来完成的:
通过 QWebChannel 将要渲染的当前 Markdown 文本暴露给 Web 引擎,将文档文本公开为 Q_PROPERTY
的专用 Document 类,然后通过 Qt 元类型系统使当前文本可用。
Document 类使用setText()方法包装要在C++ 端设置的 QString,并在运行时将其作为带有 textChanged 信号的文本属性公开。
定义 setText 方法如下:
void Document::setText(const QString &text)
{
if (text == m_text)
return;
m_text = text;
emit textChanged(m_text);
}
预览文本
实现自己的 PreviewPage 类,该类公开继承自 QWebEnginePage。
重新实现了虚拟的 acceptNavigationRequest 方法来阻止页面导航离开当前文档,从而将外部链接重定向到系统浏览器,代码如下:
bool PreviewPage::acceptNavigationRequest(const QUrl &url,
QWebEnginePage::NavigationType /*type*/,
bool /*isMainFrame*/)
{
// Only allow qrc:/index.html.
if (url.scheme() == QString("qrc"))
return true;
QDesktopServices::openUrl(url);
return false;
}
创建主窗口
MainWindow 类继承了 QMainWindow 类,该类声明了与菜单中的操作相匹配的私有槽,以及 isModified() 辅助方法。
主窗口的实际布局在.ui
文件中指定,小部件和操作在运行时在 ui 成员变量中可用。
m_filePath 保存当前加载文档的文件路径, m_content 是 Document 类的一个实例。
不同对象的实际设置是在 MainWindow 构造函数中完成的,代码如下:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->editor->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
ui->preview->setContextMenuPolicy(Qt::NoContextMenu);
构造函数首先调用 setupUi 来根据 UI 文件构造小部件和菜单操作,文本编辑器字体设置为具有固定字符宽度的字体,并且 QWebEngineView 小部件不显示上下文菜单。
PreviewPage *page = new PreviewPage(this);
ui->preview->setPage(page);
这里构造函数确保我们的自定义 PreviewPage 被 ui->preview 中的 QWebEngineView 实例使用。
connect(ui->editor, &QPlainTextEdit::textChanged,
[this]() { m_content.setText(ui->editor->toPlainText()); });
QWebChannel *channel = new QWebChannel(this);
channel->registerObject(QStringLiteral("content"), &m_content);
page->setWebChannel(channel);
这里编辑器的 textChanged 信号连接到更新 m_content 中的文本的 lambda, 然后这个对象被QWebChannel以content为名暴露给JS端。
ui->preview->setUrl(QUrl("qrc:/index.html"));
现在我们实际上可以从资源中加载 index.html 文件。有关该文件的更多信息,请参阅创建索引文件。
connect(ui->actionNew, &QAction::triggered, this, &MainWindow::onFileNew);
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onFileOpen);
connect(ui->actionSave, &QAction::triggered, this, &MainWindow::onFileSave);
connect(ui->actionSaveAs, &QAction::triggered, this, &MainWindow::onFileSaveAs);
connect(ui->actionExit, &QAction::triggered, this, &MainWindow::onExit);
connect(ui->editor->document(), &QTextDocument::modificationChanged,ui->actionSave, &QAction::setEnabled);
菜单项连接到相应的成员槽,保存项目的激活或停用取决于用户是否已编辑内容。
QFile defaultTextFile(":/default.md");
defaultTextFile.open(QIODevice::ReadOnly);
ui->editor->setPlainText(defaultTextFile.readAll());
}
最后,我们从资源中加载一个默认文档 default.md
就可以了。
创建索引文件
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<link rel="stylesheet" type="text/css" href="3rdparty/markdown.css">
<script src="3rdparty/marked.js"></script>
<script src="qrc:/qtwebchannel/qwebchannel.js"></script>
</head>
<body>
<div id="placeholder"></div>
<script>
'use strict';
var placeholder = document.getElementById('placeholder');
var updateText = function(text) {
placeholder.innerHTML = marked(text);
}
new QWebChannel(qt.webChannelTransport,
function(channel) {
var content = channel.objects.content;
updateText(content.text);
content.textChanged.connect(updateText);
}
);
</script>
</body>
</html>
在 index.html
中加载了一个自定义样式表和两个 JavaScript 库。markdown.css
是一个 Markdown 友好样式表;marked.js
是一个 Markdown 解析器和编译器,旨在提高速度;qwebchannel.js
是 QWebChannel 模块的一部分。
在 元素中,首先定义一个占位符元素,并将其作为 JavaScript 变量使用,然后定义 updateText 辅助方法,最后设置 Web 通道来访问内容代理对象,并确保在 content.text
更改时调用 updateText()。
最后
Qt安装目录找到源码
\Q{你的Qt版本}\Examples{你的Qt版本}\webenginewidgets\markdowneditor
相关链接
https://doc.qt.io/qt-5/qtwebengine-webenginewidgets-markdowneditor-example.html
嵌入式编程专辑 Linux 学习专辑 C/C++编程专辑 Qt进阶学习专辑
关注我的微信公众号,回复“加群”按规则加入技术交流群。
点击“阅读原文”查看更多分享。