Beispiel für QXmlStream-Lesezeichen
Veranschaulicht das Lesen und Schreiben von XBEL-Dateien.
Das QXmlStream-Lesezeichen-Beispiel bietet einen Viewer für XML Bookmark Exchange Language (XBEL)-Dateien. Es kann Lesezeichen mithilfe von Qt's „ QXmlStreamReader “ lesen und sie mithilfe von „ QXmlStreamWriter “ wieder zurückschreiben. Da dieses Beispiel darauf abzielt, die Verwendung dieser Lese- und Schreibtypen zu veranschaulichen, bietet es keine Möglichkeit, ein Lesezeichen zu öffnen, ein neues hinzuzufügen oder zwei Lesezeichendateien zusammenzuführen, und nur minimale Möglichkeiten zur Bearbeitung von Lesezeichen. Dennoch könnte es bei Bedarf sicherlich um solche Funktionen erweitert werden.

Definition der Klasse „XbelWriter“
Die Klasse „ XbelWriter “ nimmt ein „ tree widget “ entgegen, das eine Hierarchie von Ordnern beschreibt, die Lesezeichen enthalten. Ihr „ writeFile() “ bietet die Möglichkeit, diese Hierarchie im XBEL-Format auf ein angegebenes Ausgabegerät zu schreiben.
Intern speichert sie das übergebene Baum-Widget und bündelt eine private Instanz von ` QXmlStreamWriter`, die ihr die Möglichkeit zum Streamen von XML bietet. Sie verfügt über einen internen ` writeItem() `, um jedes Element in ihrem Baum zu schreiben.
class XbelWriter
{
public:
explicit XbelWriter(const QTreeWidget *treeWidget);
bool writeFile(QIODevice *device);
private:
void writeItem(const QTreeWidgetItem *item);
QXmlStreamWriter xml;
const QTreeWidget *treeWidget;
};Implementierung der Klasse `XbelWriter`
Der Konstruktor von ` XbelWriter ` nimmt das ` treeWidget ` entgegen, das beschrieben werden soll. Er speichert dieses und aktiviert die Eigenschaft „auto-formatting“ von ` QXmlStreamWriter`. Letztere teilt die Daten in mehrere Zeilen auf, wobei Einrückungen die Struktur des Baums anzeigen, was die Lesbarkeit der XML-Ausgabe verbessert.
XbelWriter::XbelWriter(const QTreeWidget *treeWidget) : treeWidget(treeWidget)
{
xml.setAutoFormatting(true);
}Die Funktion ` writeFile() ` nimmt ein ` QIODevice `-Objekt entgegen und weist dessen ` QXmlStreamWriter `-Mitglied an, mithilfe von ` setDevice()` in dieses Gerät zu schreiben. Diese Funktion schreibt dann die Dokumenttypdefinition (DTD), das Startelement und die Version und delegiert das Schreiben jedes der obersten Elemente von ` treeWidget` an ` writeItem()`. Schließlich schließt sie das Dokument und kehrt zurück.
bool XbelWriter::writeFile(QIODevice *device)
{
xml.setDevice(device);
xml.writeStartDocument();
xml.writeDTD("<!DOCTYPE xbel>"_L1);
xml.writeStartElement("xbel"_L1);
xml.writeAttribute("version"_L1, "1.0"_L1);
for (int i = 0; i < treeWidget->topLevelItemCount(); ++i)
writeItem(treeWidget->topLevelItem(i));
xml.writeEndDocument();
return true;
}Die Funktion „ writeItem() “ akzeptiert ein „ QTreeWidgetItem “-Objekt und schreibt eine Darstellung des Objekts in dessen XML-Stream. Diese Darstellung hängt vom „ UserRole “ des Objekts ab, das entweder ein „ "folder" “, ein „ "bookmark" “ oder ein „ "separator" “ sein kann. Innerhalb jedes Ordners ruft sie sich selbst rekursiv für jedes untergeordnete Element auf, um eine Darstellung jedes untergeordneten Elements rekursiv in das XML-Element des Ordners einzubinden.
void XbelWriter::writeItem(const QTreeWidgetItem *item)
{
QString tagName = item->data(0, Qt::UserRole).toString();
if (tagName == "folder"_L1) {
bool folded = !item->isExpanded();
xml.writeStartElement(tagName);
xml.writeAttribute("folded"_L1, folded ? "yes"_L1 : "no"_L1);
xml.writeTextElement("title"_L1, item->text(0));
for (int i = 0; i < item->childCount(); ++i)
writeItem(item->child(i));
xml.writeEndElement();
} else if (tagName == "bookmark"_L1) {
xml.writeStartElement(tagName);
if (!item->text(1).isEmpty())
xml.writeAttribute("href"_L1, item->text(1));
xml.writeTextElement("title"_L1, item->text(0));
xml.writeEndElement();
} else if (tagName == "separator"_L1) {
xml.writeEmptyElement(tagName);
}
}Definition der Klasse „XbelReader“
Die Klasse „ XbelReader “ nimmt ein „ tree widget “ entgegen, um es mit Elementen zu füllen, die eine Lesezeichenhierarchie beschreiben. Sie unterstützt das Einlesen von XBEL-Daten aus einer „ QIODevice “ als Quelle für diese Elemente. Wenn das Parsen der XBEL-Daten fehlschlägt, kann sie melden, was schiefgelaufen ist.
Intern speichert sie die „ QTreeWidget “, die sie füllen wird, und verpackt eine Instanz von „ QXmlStreamReader “ – der Begleitklasse zu „ QXmlStreamWriter “ –, die sie zum Lesen der XBEL-Daten verwendet.
class XbelReader
{
public:
XbelReader(QTreeWidget *treeWidget);
bool read(QIODevice *device);
QString errorString() const;
private:
void readXBEL();
void readTitle(QTreeWidgetItem *item);
void readSeparator(QTreeWidgetItem *item);
void readFolder(QTreeWidgetItem *item);
void readBookmark(QTreeWidgetItem *item);
QTreeWidgetItem *createChildItem(QTreeWidgetItem *item);
QXmlStreamReader xml;
QTreeWidget *treeWidget;
QIcon folderIcon;
QIcon bookmarkIcon;
};Implementierung der Klasse `XbelReader`
Da sich der XBEL-Reader ausschließlich mit dem Lesen von XML-Elementen befasst, nutzt er intensiv die Hilfsfunktion ` readNextStartElement()`.
Der Konstruktor von ` XbelReader ` benötigt ein ` QTreeWidget `, das er mit Daten füllt. Er versieht das Baum-Widget mit geeigneten Symbolen: einem Ordnersymbol, dessen Form sich ändert, um anzuzeigen, ob ein Ordner geöffnet oder geschlossen ist, sowie einem Standard-Dateisymbol für die einzelnen Lesezeichen innerhalb dieser Ordner.
XbelReader::XbelReader(QTreeWidget *treeWidget) : treeWidget(treeWidget)
{
QStyle *style = treeWidget->style();
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() ` akzeptiert ein ` QIODevice`. Sie weist ihr Mitglied ` QXmlStreamReader ` an, Inhalte von diesem Gerät zu lesen. Beachten Sie, dass die XML-Eingabe wohlgeformt sein muss, um von ` QXmlStreamReader` akzeptiert zu werden. Zunächst liest sie die äußere Struktur und überprüft, ob es sich bei dem Inhalt um eine XBEL 1.0-Datei handelt; ist dies der Fall, delegiert ` read() ` das eigentliche Einlesen des Inhalts an das interne ` readXBEL()`.
Andernfalls wird die Funktion „ raiseError()“ verwendet, um eine Fehlermeldung zu protokollieren. Der Reader selbst kann dies ebenfalls tun, wenn er auf Fehler in der Eingabe stößt. Wenn „ read() “ beendet ist, gibt es „true“ zurück, sofern keine Fehler aufgetreten sind.
bool XbelReader::read(QIODevice *device)
{
xml.setDevice(device);
if (xml.readNextStartElement()) {
if (xml.name() == "xbel"_L1 && xml.attributes().value("version"_L1) == "1.0"_L1)
readXBEL();
else
xml.raiseError(QObject::tr("The file is not an XBEL version 1.0 file."));
}
return !xml.error();
}Gibt ` read() ` den Wert `false` zurück, kann der Aufrufer durch Aufruf der Funktion ` errorString() ` eine Beschreibung des Fehlers einschließlich Zeilen- und Spaltennummer innerhalb des Streams abrufen.
QString XbelReader::errorString() const
{
return QObject::tr("%1\nLine %2, column %3")
.arg(xml.errorString())
.arg(xml.lineNumber())
.arg(xml.columnNumber());
}Die Funktion ` readXBEL() ` liest den Namen eines `startElement`-Elements und ruft je nachdem, ob dessen Tag-Name ` "folder"`, ` "bookmark" ` oder ` "separator"` lautet, die entsprechende Funktion zum Einlesen auf. Alle anderen gefundenen Elemente werden übersprungen. Die Funktion beginnt mit einer Vorbedingung, bei der überprüft wird, ob der XML-Reader gerade ein ` "xbel" `-Element geöffnet hat.
void XbelReader::readXBEL()
{
Q_ASSERT(xml.isStartElement() && xml.name() == "xbel"_L1);
while (xml.readNextStartElement()) {
if (xml.name() == "folder"_L1)
readFolder(nullptr);
else if (xml.name() == "bookmark"_L1)
readBookmark(nullptr);
else if (xml.name() == "separator"_L1)
readSeparator(nullptr);
else
xml.skipCurrentElement();
}
}Die Funktion „ readBookmark() “ erstellt ein neues bearbeitbares Element, das ein einzelnes Lesezeichen darstellt. Sie speichert das XML-Attribut „ "href" “ des aktuellen Elements als Text in der zweiten Spalte des Elements und setzt dessen Text in der ersten Spalte vorläufig auf „ "Unknown title" “, bevor sie den Rest des Elements nach einem „title“-Element durchsucht, um diesen Wert zu überschreiben, wobei alle nicht erkannten untergeordneten Elemente übersprungen werden.
void XbelReader::readTitle(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == "title"_L1);
item->setText(0, xml.readElementText());
}Die Funktion „ readTitle() “ liest den Titel eines Lesezeichens aus und speichert ihn als Titel (Text in der ersten Spalte) des Elements, für das sie aufgerufen wurde.
void XbelReader::readSeparator(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == "separator"_L1);
constexpr char16_t midDot = u'\xB7';
static const QString dots(30, midDot);
QTreeWidgetItem *separator = createChildItem(item);
separator->setFlags(item ? item->flags() & ~Qt::ItemIsSelectable : Qt::ItemFlags{});
separator->setText(0, dots);
xml.skipCurrentElement();
}Die Funktion „ readSeparator() “ erstellt ein Trennelement und setzt dessen Flags. Der Text des Trennelements wird auf 30 zentrierte Punkte gesetzt. Der Rest des Elements wird anschließend mit „ skipCurrentElement()“ übersprungen.
void XbelReader::readSeparator(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == "separator"_L1);
constexpr char16_t midDot = u'\xB7';
static const QString dots(30, midDot);
QTreeWidgetItem *separator = createChildItem(item);
separator->setFlags(item ? item->flags() & ~Qt::ItemIsSelectable : Qt::ItemFlags{});
separator->setText(0, dots);
xml.skipCurrentElement();
}Die Funktion ` readFolder() ` erstellt ein Element und durchläuft den Inhalt des Ordnerelements, wobei sie diesem Element untergeordnete Elemente hinzufügt, um den Inhalt des Ordnerelements darzustellen. Die Schleife über den Ordnerinhalt ähnelt in ihrer Form der in ` readXBEL()`, mit dem Unterschied, dass sie nun ein Titelelement akzeptiert, um den Titel des Ordners festzulegen.
void XbelReader::readFolder(QTreeWidgetItem *item)
{
Q_ASSERT(xml.isStartElement() && xml.name() == "folder"_L1);
QTreeWidgetItem *folder = createChildItem(item);
bool folded = xml.attributes().value("folded"_L1) != "no"_L1;
folder->setExpanded(!folded);
while (xml.readNextStartElement()) {
if (xml.name() == "title"_L1)
readTitle(folder);
else if (xml.name() == "folder"_L1)
readFolder(folder);
else if (xml.name() == "bookmark"_L1)
readBookmark(folder);
else if (xml.name() == "separator"_L1)
readSeparator(folder);
else
xml.skipCurrentElement();
}
}Die Hilfsfunktion ` createChildItem() ` erstellt ein neues Baum-Widget-Element, das entweder ein untergeordnetes Element des angegebenen Elements ist oder – falls kein übergeordnetes Element angegeben wurde – ein direktes untergeordnetes Element des Baum-Widgets. Sie setzt die ` UserRole ` des neuen Elements auf den Tag-Namen des aktuellen XML-Elements, entsprechend der Art und Weise, wie `XbelWriter::writeFile()` diese ` UserRole` verwendet.
QTreeWidgetItem *XbelReader::createChildItem(QTreeWidgetItem *item)
{
QTreeWidgetItem *childItem = item ? new QTreeWidgetItem(item) : new QTreeWidgetItem(treeWidget);
childItem->setData(0, Qt::UserRole, xml.name().toString());
return childItem;
}Definition der Klasse „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();
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
void onCustomContextMenuRequested(const QPoint &pos);
#endif
private:
void createMenus();
QTreeWidget *const treeWidget;
};Implementierung der Klasse „MainWindow“
Der Konstruktor von „ MainWindow “ richtet das „ QTreeWidget “-Objekt „ treeWidget “ als eigenes zentrales Widget ein, mit Spaltenüberschriften für den Titel und den Speicherort jedes Lesezeichens. Er konfiguriert ein benutzerdefiniertes Menü, über das der Benutzer Aktionen für einzelne Lesezeichen innerhalb des Baum-Widgets ausführen kann.
Es ruft ` createMenus() ` auf, um seine eigenen Menüs und die dazugehörigen Aktionen einzurichten. Es legt seinen Titel fest, meldet sich als bereit und passt seine Größe an einen angemessenen Anteil des verfügbaren Bildschirmplatzes an.
MainWindow::MainWindow() : treeWidget(new QTreeWidget)
{
treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
treeWidget->setHeaderLabels(QStringList{tr("Title"), tr("Location")});
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(treeWidget, &QWidget::customContextMenuRequested,
this, &MainWindow::onCustomContextMenuRequested);
#endif
setCentralWidget(treeWidget);
createMenus();
statusBar()->showMessage(tr("Ready"));
setWindowTitle(tr("QXmlStream Bookmarks"));
const QSize availableSize = screen()->availableGeometry().size();
resize(availableSize.width() / 2, availableSize.height() / 3);
}Ein benutzerdefiniertes Menü, das aufgerufen wird, wenn der Benutzer mit der rechten Maustaste auf ein Lesezeichen klickt, ermöglicht es, das Lesezeichen als Link zu kopieren oder einen Desktop-Browser anzuweisen, die darin enthaltene URL zu öffnen. Dieses Menü wird (sofern die entsprechenden Funktionen aktiviert sind) von onCustomContextMenuRequested() implementiert.
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
void MainWindow::onCustomContextMenuRequested(const QPoint &pos)
{
const QTreeWidgetItem *item = treeWidget->itemAt(pos);
if (!item)
return;
const QString url = item->text(1);
QMenu contextMenu;
QAction *copyAction = contextMenu.addAction(tr("Copy Link to Clipboard"));
QAction *openAction = contextMenu.addAction(tr("Open"));
QAction *action = contextMenu.exec(treeWidget->viewport()->mapToGlobal(pos));
if (action == copyAction)
QGuiApplication::clipboard()->setText(url);
else if (action == openAction)
QDesktopServices::openUrl(QUrl(url));
}
#endif // QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)Die Funktion ` createMenus() ` erstellt die Objekte ` fileMenu ` und ` helpMenu ` und fügt ihnen ` QAction `-Objekte hinzu, die auf verschiedene Weise an die Funktionen ` open()`, ` saveAs() ` und ` about() ` sowie an ` QWidget::close()` und ` QApplication::aboutQt()` gebunden sind. Die Verknüpfungen sind wie folgt dargestellt:
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);
}Dadurch wird das in den folgenden Screenshots gezeigte Menü erstellt:
![]() | ![]() |
Die Funktion ` open() ` öffnet beim Aufruf ein Dateidialogfeld, in dem der Benutzer eine Lesezeichen-Datei auswählen kann. Wird eine Datei ausgewählt, wird diese mithilfe von ` XBelReader ` analysiert, um die Lesezeichen in ` treeWidget ` einzulesen. Sollten beim Öffnen oder Analysieren der Datei Probleme auftreten, wird dem Benutzer eine entsprechende Warnmeldung angezeigt, die den Dateinamen und die Fehlermeldung enthält. Andernfalls werden die aus der Datei gelesenen Lesezeichen angezeigt, und in der Statusleiste des Fensters wird kurz gemeldet, dass die Datei geladen wurde.
void MainWindow::open()
{
QFileDialog fileDialog(this, tr("Open Bookmark File"), QDir::currentPath());
fileDialog.setMimeTypeFilters({"application/x-xbel"_L1});
if (fileDialog.exec() != QDialog::Accepted)
return;
treeWidget->clear();
const QString fileName = fileDialog.selectedFiles().constFirst();
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Cannot read file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), file.errorString()));
return;
}
XbelReader reader(treeWidget);
if (!reader.read(&file)) {
QMessageBox::warning(
this, tr("QXmlStream Bookmarks"),
tr("Parse error in file %1:\n\n%2")
.arg(QDir::toNativeSeparators(fileName), reader.errorString()));
} else {
statusBar()->showMessage(tr("File loaded"), 2000);
}
}Die Funktion „ saveAs() “ zeigt ein „ QFileDialog “ an und fordert den Benutzer auf, einen „ fileName “ anzugeben, in dem eine Kopie der Lesezeichendaten gespeichert werden soll. Ähnlich wie die Funktion „ open() “ zeigt auch diese Funktion eine Warnmeldung an, wenn die Datei nicht beschrieben werden kann.
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("QXmlStream Bookmarks"),
tr("Cannot write file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), file.errorString()));
return;
}
XbelWriter writer(treeWidget);
if (writer.writeFile(&file))
statusBar()->showMessage(tr("File saved"), 2000);
}Die Funktion „ about() “ zeigt ein „ QMessageBox “ mit einer kurzen Beschreibung des Beispiels oder allgemeinen Informationen zu Qt und der verwendeten Version an.
void MainWindow::about()
{
QMessageBox::about(this, tr("About QXmlStream Bookmarks"),
tr("The <b>QXmlStream Bookmarks</b> example demonstrates how to use Qt's "
"QXmlStream classes to read and write XML documents."));
}main() Funktion
Die Funktion ` main() ` instanziiert ` MainWindow ` und ruft die Funktion ` show() ` auf, um diese anzuzeigen, gefolgt von ` open()`, da dies höchstwahrscheinlich das ist, was der Benutzer als Erstes tun möchte.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWin;
mainWin.show();
mainWin.open();
return app.exec();
}Weitere Informationen zu XBEL-Dateien finden Sie auf der Ressourcenseite zur XML Bookmark Exchange Language (XBEL).
© 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.

