Auf dieser Seite

DOM-Lesezeichen-Anwendung

Bietet einen Reader für XML-Bookmark-Exchange-Language-Dateien.

Die DOM-Lesezeichen-Anwendung stellt einen Reader für XML-Bookmark-Exchange-Language-Dateien (XBEL) bereit, der die DOM-basierte XML-API von Qt zum Lesen und Parsen der Dateien nutzt. Das QXmlStream-Lesezeichen-Beispiel bietet eine alternative Möglichkeit, diesen Dateityp zu lesen.

Lesezeichenbaum mit den Spalten „Titel“ und „Standort“

Die Klassendefinition von `XbelTree`

Die Klasse `XbelTree` verfügt über Funktionen zum Lesen und Schreiben im Dateisystem. Sie erbt von der Klasse ` QTreeWidget `, enthält das Modell für die Anzeige der Lesezeichen und ermöglicht deren Bearbeitung.

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;
};

Die Implementierung der Klasse „XbelTree“

Der Konstruktor von ` XbelTree ` akzeptiert ein ` QWidget `, in das die Klasse eingefügt wird. Die ` folderIcon ` wird auf den Modus `QIcon::Normal` gesetzt, bei dem die Pixmap nur angezeigt wird, wenn der Benutzer nicht mit dem Symbol interagiert. Die ` QStyle::SP_DirClosedIcon`, ` QStyle::SP_DirOpenIcon` und ` QStyle::SP_FileIcon ` entsprechen Standard-Pixmaps, die dem Stil Ihrer GUI entsprechen.

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));
}

Die Funktion ` read() ` öffnet die angegebene ` QIODevice ` mithilfe von ` QDomDocument::setContent`. Wenn das Öffnen der Datei gelingt und die Header der obersten Ebene überprüft wurden, wird der Inhalt der Klasse gelöscht, bevor der Dateiinhalt geparst wird, indem alle XML-Knoten der obersten Ebene durchlaufen und für jeden von ihnen ` parseFolderElement() ` aufgerufen wird.

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;
}

Die Funktion „ parseFolderElement() “ verarbeitet die verschiedenen Elementtypen und ruft sich selbst rekursiv auf, wenn es sich bei dem Element um einen Unterordner handelt.

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();
    }
}

Die Funktion „ write() “ speichert das `domDocument` mithilfe von `QDomDocument::save` in der angegebenen ` QIODevice `.

bool XbelTree::write(QIODevice *device) const
{
    const int IndentSize = 4;

    QTextStream out(device);
    domDocument.save(out, IndentSize);
    return true;
}

Die Klassendefinition von „MainWindow“

Die Klasse „ MainWindow “ ist eine Unterklasse von „ QMainWindow “ und verfügt über die Menüpunkte „ File “ sowie „ Help “.

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

public slots:
    void open();
    void saveAs();
    void about();

private:
    void createMenus();

    XbelTree *xbelTree;
};

Die Implementierung der Klasse „MainWindow“

Der Konstruktor von ` MainWindow ` instanziiert das Member-Objekt `XbelTree` und setzt dessen Header auf ein ` QStringList `-Objekt, nämlich ` labels`. Der Konstruktor ruft außerdem ` createMenus() ` auf, um die Menüs einzurichten. Die Funktion ` statusBar() ` wird verwendet, um die Meldung „Ready“ anzuzeigen.

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);
}

Die Funktion „ createMenus() “ füllt die Menüs und legt Tastenkürzel fest.

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);
}

Die Funktion „ open() “ ermöglicht es dem Benutzer, eine XBEL-Datei mit „ QFileDialog “ zu öffnen. Wenn die Datei nicht gelesen werden kann oder ein Parsing-Fehler auftritt, wird zusammen mit „ fileName “ und „ errorString “ eine Warnmeldung angezeigt. Bei Erfolg wird „ XbelTree::read() “ aufgerufen.

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);
}

Die Funktion „ saveAs() “ zeigt eine „ QFileDialog “ an und fordert den Benutzer zur Eingabe eines „ fileName “ auf. Ähnlich wie die Funktion „ open() “ zeigt auch diese Funktion eine Warnmeldung an, wenn die Datei nicht beschrieben werden kann. Ist der Vorgang erfolgreich, wird „ XbelTree::write() “ aufgerufen.

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);
}

Die Funktion „ about() “ zeigt eine „ QMessageBox “ mit einer kurzen Beschreibung des Beispiels an.

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."));
}

Weitere Informationen zu XBEL-Dateien finden Sie auf der Ressourcen-Seite zur XML Bookmark Exchange Language.

Beispielprojekt auf code.qt.io

© 2026 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.