DOM 书签应用程序
为 XML 书签交换语言文件提供阅读器。
DOM 书签应用程序为 XML 书签交换语言(XBEL)文件提供了一个阅读器,它使用 Qt 基于 DOM 的 XML API 来读取和解析文件。QXmlStream 书签示例提供了读取此类文件的另一种方法。
XbelTree 类定义
XbelTree 类具有读取和写入文件系统的功能。它继承于QTreeWidget 类,包含显示书签的模型,并允许对其进行编辑。
class XbelTree : public QTreeWidget { Q_OBJECT public: explicit XbelTree(QWidget *parent = nullptr); bool read(QIODevice *device); bool write(QIODevice *device) const; protected: #if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) void contextMenuEvent(QContextMenuEvent *event) override; #endif private slots: void updateDomElement(const QTreeWidgetItem *item, int column); private: void parseFolderElement(const QDomElement &element, QTreeWidgetItem *parentItem = nullptr); QTreeWidgetItem *createItem(const QDomElement &element, QTreeWidgetItem *parentItem = nullptr); QDomDocument domDocument; QIcon folderIcon; QIcon bookmarkIcon; };
XbelTree 类的实现
XbelTree
构造函数接受一个QWidget ,并将其置于其中。folderIcon
被设置为 QIcon::Normal(QIcon::正常)模式,只有在用户不与图标交互时才显示像素图。QStyle::SP_DirClosedIcon 、QStyle::SP_DirOpenIcon 和QStyle::SP_FileIcon 对应的是符合 GUI 风格的标准像素图。
XbelTree::XbelTree(QWidget *parent) : QTreeWidget(parent) { header()->setSectionResizeMode(QHeaderView::Stretch); setHeaderLabels({tr("Title"), tr("Location")}); folderIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon), QIcon::Normal, QIcon::Off); folderIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon), QIcon::Normal, QIcon::On); bookmarkIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon)); }
read()
函数使用QDomDocument::setContent 打开给定的QIODevice 。如果成功打开文件且顶层标头已通过验证,则通过遍历所有顶层 XML 节点并在每个节点上调用parseFolderElement()
来解析文件内容,然后清除类的内容。
bool XbelTree::read(QIODevice *device) { QDomDocument::ParseResult result = domDocument.setContent(device, QDomDocument::ParseOption::UseNamespaceProcessing); if (!result) { QMessageBox::information(window(), tr("DOM Bookmarks"), tr("Parse error at line %1, column %2:\n%3") .arg(result.errorLine) .arg(result.errorColumn) .arg(result.errorMessage)); return false; } QDomElement root = domDocument.documentElement(); if (root.tagName() != "xbel") { QMessageBox::information(window(), tr("DOM Bookmarks"), tr("The file is not an XBEL file.")); return false; } else if (root.hasAttribute(versionAttribute) && root.attribute(versionAttribute) != "1.0"_L1) { QMessageBox::information(window(), tr("DOM Bookmarks"), tr("The file is not an XBEL version 1.0 " "file.")); return false; } clear(); disconnect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement); QDomElement child = root.firstChildElement(folderElement); while (!child.isNull()) { parseFolderElement(child); child = child.nextSiblingElement(folderElement); } connect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement); return true; }
parseFolderElement()
函数处理不同的元素类型,如果元素是子文件夹,则会递归调用自身。
void XbelTree::parseFolderElement(const QDomElement &element, QTreeWidgetItem *parentItem) { QTreeWidgetItem *item = createItem(element, parentItem); QString title = element.firstChildElement(titleElement).text(); if (title.isEmpty()) title = tr("Folder"); item->setFlags(item->flags() | Qt::ItemIsEditable); item->setIcon(0, folderIcon); item->setText(0, title); bool folded = (element.attribute(foldedAttribute) != "no"_L1); item->setExpanded(!folded); constexpr char16_t midDot = u'\xB7'; static const QString dots = QString(30, midDot); QDomElement child = element.firstChildElement(); while (!child.isNull()) { if (child.tagName() == folderElement) { parseFolderElement(child, item); } else if (child.tagName() == bookmarkElement) { QTreeWidgetItem *childItem = createItem(child, item); QString title = child.firstChildElement(titleElement).text(); if (title.isEmpty()) title = tr("Folder"); childItem->setFlags(item->flags() | Qt::ItemIsEditable); childItem->setIcon(0, bookmarkIcon); childItem->setText(0, title); childItem->setText(1, child.attribute(hrefAttribute)); } else if (child.tagName() == "separator"_L1) { QTreeWidgetItem *childItem = createItem(child, item); childItem->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEditable)); childItem->setText(0, dots); } child = child.nextSiblingElement(); } }
write()
函数使用 QDomDocument::save 将 domDocument 保存到给定的QIODevice 。
bool XbelTree::write(QIODevice *device) const { const int IndentSize = 4; QTextStream out(device); domDocument.save(out, IndentSize); return true; }
MainWindow 类定义
MainWindow
类是QMainWindow 的子类,具有File
菜单和Help
菜单。
class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(); public slots: void open(); void saveAs(); void about(); private: void createMenus(); XbelTree *xbelTree; };
主窗口类的实现
MainWindow
构造函数将成员 XbelTree 对象实例化,并用QStringList 对象labels
设置其标题。构造函数还调用createMenus()
来设置菜单。statusBar()
用于显示消息 "Ready"。
MainWindow::MainWindow() { xbelTree = new XbelTree; setCentralWidget(xbelTree); createMenus(); statusBar()->showMessage(tr("Ready")); setWindowTitle(tr("DOM Bookmarks")); const QSize availableSize = screen()->availableGeometry().size(); resize(availableSize.width() / 2, availableSize.height() / 3); }
createMenus()
函数用于填充菜单和设置键盘快捷键。
void MainWindow::createMenus() { QMenu *fileMenu = menuBar()->addMenu(tr("&File")); QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &MainWindow::open); openAct->setShortcuts(QKeySequence::Open); QAction *saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &MainWindow::saveAs); saveAsAct->setShortcuts(QKeySequence::SaveAs); QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close); exitAct->setShortcuts(QKeySequence::Quit); menuBar()->addSeparator(); QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(tr("&About"), this, &MainWindow::about); helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); }
open()
函数使用户能够使用QFileDialog 打开 XBEL 文件。如果无法读取文件或出现解析错误,则会在fileName
和errorString
的同时显示警告信息。如果成功,则调用XbelTree::read()
。
void MainWindow::open() { QFileDialog fileDialog(this, tr("Open Bookmark File"), QDir::currentPath()); fileDialog.setMimeTypeFilters({"application/x-xbel"_L1}); if (fileDialog.exec() != QDialog::Accepted) return; const QString fileName = fileDialog.selectedFiles().constFirst(); QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(this, tr("DOM Bookmarks"), tr("Cannot read file %1:\n%2.") .arg(QDir::toNativeSeparators(fileName), file.errorString())); return; } if (xbelTree->read(&file)) statusBar()->showMessage(tr("File loaded"), 2000); }
saveAs()
函数显示QFileDialog ,提示用户fileName
。与open()
函数类似,如果文件无法写入,该函数也会显示警告信息。如果成功,则调用XbelTree::write()
。
void MainWindow::saveAs() { QFileDialog fileDialog(this, tr("Save Bookmark File"), QDir::currentPath()); fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setDefaultSuffix("xbel"_L1); fileDialog.setMimeTypeFilters({"application/x-xbel"_L1}); if (fileDialog.exec() != QDialog::Accepted) return; const QString fileName = fileDialog.selectedFiles().constFirst(); QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("DOM Bookmarks"), tr("Cannot write file %1:\n%2.") .arg(QDir::toNativeSeparators(fileName), file.errorString())); return; } if (xbelTree->write(&file)) statusBar()->showMessage(tr("File saved"), 2000); }
about()
函数会显示QMessageBox ,并附有对示例的简要说明。
void MainWindow::about() { QMessageBox::about(this, tr("About DOM Bookmarks"), tr("The <b>DOM Bookmarks</b> example demonstrates how to " "use Qt's DOM classes to read and write XML " "documents.")); }
有关 XBEL 文件的更多信息,请参阅XML 书签交换语言资源页面。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.