QXmlStream Bookmarks 例子提供一个使用QXmlStreamReader类读和使用QXmlStreamWriter写的XBEL(XML Bookmark Exchange Language)文件阅读器.
 
 
 
 
XbelWriter类定义
该类包含一个私有的QXmlStreamWriter实例,它提供一个XML写功能。XbelWriter也包含一个存储书签的QTreeWidget。
 class XbelWriter
 {
 public:
     XbelWriter(QTreeWidget *treeWidget);
     bool writeFile(QIODevice *device);
 private:
     void writeItem(QTreeWidgetItem *item);
     QXmlStreamWriter xml;
     QTreeWidget *treeWidget;
 };
XbelWriter类实现:
该类构造器接受一个treeWidget来初始化。我们能用他的自动格式化特性来确保断行。首行缩进被自动增加来分割元素,增加数据的可读性。
XbelWriter::XbelWriter(QTreeWidget *treeWidget)
     : treeWidget(treeWidget)
 {
     xml.setAutoFormatting(true);
 }
 
writeFile()函数接受一个QIODevice对象,使用setDevice()来设置。函数写 文件类型定义(DTD),开始元素,版本,treeWidget的顶层元素。
 bool XbelWriter::writeFile(QIODevice *device)
 {
     xml.setDevice(device);
     xml.writeStartDocument();
     xml.writeDTD("<!DOCTYPE xbel>");
     xml.writeStartElement("xbel");
     xml.writeAttribute("version", "1.0");
     for (int i = 0; i < treeWidget->topLevelItemCount(); ++i)
         writeItem(treeWidget->topLevelItem(i));
     xml.writeEndDocument();
     return true;
 }
writeItem()函数接受一个QTreeWidgetItem对象,写他到流中,依赖他的tagName,它既可以是一个文件夹、书签、或者分割线。
void XbelWriter::writeItem(QTreeWidgetItem *item)
 {
     QString tagName = item->data(0, Qt::UserRole).toString();
     if (tagName == "folder") {
         bool folded = !treeWidget->isItemExpanded(item);
         xml.writeStartElement(tagName);
         xml.writeAttribute("folded", folded ? "yes" : "no");
         xml.writeTextElement("title", item->text(0));
         for (int i = 0; i < item->childCount(); ++i)
             writeItem(item->child(i));
         xml.writeEndElement();
     } else if (tagName == "bookmark") {
         xml.writeStartElement(tagName);
         if (!item->text(1).isEmpty())
             xml.writeAttribute("href", item->text(1));
         xml.writeTextElement("title", item->text(0));
         xml.writeEndElement();
     } else if (tagName == "separator") {
         xml.writeEmptyElement(tagName);
     }
 }
XbelReader类定义
该类包含一个私有的QXmlStreamReader实例,QXmlStreamWriter的伴随类。XbelReader也包含一个QTreeWidget的引用,它通层次结构把书签分组。
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;
 };
XbelReader类实现
构造器接受一个QTreeWidget来初始化treeWidget。一个QStyle对象被用来设置treeWidget的风格属性。folderIcon被设置为QIcon::Normal模式,pixmap只被显示当用户不影响icon。QStyle::SP_DirClosedIcon,QStyle::SP_DirOpenIcon,和SQtyle::SP_FileIcon符合标准的pixmap。
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));
 }
read()函数接受一个QIODevice,并且使用setDevice()设置。只有当文件是一个有效的XBEL 1.0文件。注意XML输入需要完整的标准形式。否则,raiseError()函数被用来显示一个错误信息。因为XBEL阅读器只读XML元素,使用readNextStartElement()函数有广泛的应用。
bool XbelReader::read(QIODevice *device)
 {
     xml.setDevice(device);
     if (xml.readNextStartElement()) {
         if (xml.name() == "xbel" && xml.attributes().value("version") == "1.0")
             readXBEL();
         else
             xml.raiseError(QObject::tr("The file is not an XBEL version 1.0 file."));
     }
     return !xml.error();
 }
errorString()函数被调用如果一个错误发生,为了得到完整的包含行数和列数的错误描述。
QString XbelReader::errorString() const
 {
     return QObject::tr("%1\nLine %2, column %3")
             .arg(xml.errorString())
             .arg(xml.lineNumber())
             .arg(xml.columnNumber());
 }
readXBEL()函数读startElement的名字,调用合适的函数读它,依赖是否他是文件夹、书签或者分割线。否则,他调用skipCurrentElement()。Q_ASSERT()宏被用来提供一个函数的前提条件。
void XbelReader::readXBEL()
 {
     Q_ASSERT(xml.isStartElement() && xml.name() == "xbel");
     while (xml.readNextStartElement()) {
         if (xml.name() == "folder")
             readFolder(0);
         else if (xml.name() == "bookmark")
             readBookmark(0);
         else if (xml.name() == "separator")
             readSeparator(0);
         else
             xml.skipCurrentElement();
     }
 }
readTitle()函数读书签的标题。
void XbelReader::readTitle(QTreeWidgetItem *item)
 {
     Q_ASSERT(xml.isStartElement() && xml.name() == "title");
     QString title = xml.readElementText();
     item->setText(0, title);
 }
readSeparator()函数创造一个分割线并且设置他的标志。文本被设置为30“0xB7”。使用skipCurrentElement()跳过元素。
void XbelReader::readSeparator(QTreeWidgetItem *item)
 {
     Q_ASSERT(xml.isStartElement() && xml.name() == "separator");
     QTreeWidgetItem *separator = createChildItem(item);
     separator->setFlags(item->flags() & ~Qt::ItemIsSelectable);
     separator->setText(0, QString(30, 0xB7));
     xml.skipCurrentElement();
 }
主窗口类定义
主窗口是QMainWindow的子类,带一个File按钮和help按钮
class MainWindow : public QMainWindow
 {
     Q_OBJECT
 public:
     MainWindow();
 public slots:
     void open();
     void saveAs();
     void about();
 private:
     void createActions();
     void createMenus();
     QTreeWidget *treeWidget;
     QMenu *fileMenu;
     QMenu *helpMenu;
     QAction *openAct;
     QAction *saveAsAct;
     QAction *exitAct;
     QAction *aboutAct;
     QAction *aboutQtAct;
 };
主窗口类实现
构造器例示QTreeWidget对象--treeWidget,并设置他的头使用QStringList对象--labels。构造器也唤醒createActions()和createMenus()来设置按钮。statusBar()被用来显示信息。
MainWindow::MainWindow()
 {
     QStringList labels;
     labels << tr("Title") << tr("Location");
     treeWidget = new QTreeWidget;
     treeWidget->header()->setResizeMode(QHeaderView::Stretch);
     treeWidget->setHeaderLabels(labels);
     setCentralWidget(treeWidget);
     createActions();
     createMenus();
     statusBar()->showMessage(tr("Ready"));
     setWindowTitle(tr("QXmlStream Bookmarks"));
     resize(480, 320);
 }
open()函数使用户打开一个XBEL文件使用QFileDialog::getOpenFileName()。一个警告消息被显示如果文件不能被读或者有一个分析错误。
void MainWindow::open()
 {
     QString fileName =
             QFileDialog::getOpenFileName(this, tr("Open Bookmark File"),
                                          QDir::currentPath(),
                                          tr("XBEL Files (*.xbel *.xml)"));
     if (fileName.isEmpty())
         return;
     treeWidget->clear();
     QFile file(fileName);
     if (!file.open(QFile::ReadOnly | QFile::Text)) {
         QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
                              tr("Cannot read file %1:\n%2.")
                              .arg(fileName)
                              .arg(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(fileName)
                              .arg(reader.errorString()));
     } else {
         statusBar()->showMessage(tr("File loaded"), 2000);
     }
 }
saveAs()函数显示一个QFileDialog,使用QFileDialog::getSaveFileName()提示用户输入文件名。与open()函数相似,这个函数也显示一个警告信息,如果文件不能被写。
void MainWindow::saveAs()
 {
     QString fileName =
             QFileDialog::getSaveFileName(this, tr("Save Bookmark File"),
                                          QDir::currentPath(),
                                          tr("XBEL Files (*.xbel *.xml)"));
     if (fileName.isEmpty())
         return;
     QFile file(fileName);
     if (!file.open(QFile::WriteOnly | QFile::Text)) {
         QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
                              tr("Cannot write file %1:\n%2.")
                              .arg(fileName)
                              .arg(file.errorString()));
         return;
     }
     XbelWriter writer(treeWidget);
     if (writer.writeFile(&file))
         statusBar()->showMessage(tr("File saved"), 2000);
 }
about()函数显示一个QMessageBox。
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."));
 }
为了实现open(),saveAs(),exit()和aboutQt()函数,我们连接他们到一个QAction对象,增加他们到fileMenu和helpMenu。
void MainWindow::createActions()
 {
     openAct = new QAction(tr("&Open..."), this);
     openAct->setShortcuts(QKeySequence::Open);
     connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
     saveAsAct = new QAction(tr("&Save As..."), this);
     saveAsAct->setShortcuts(QKeySequence::SaveAs);
     connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
     exitAct = new QAction(tr("E&xit"), this);
     exitAct->setShortcuts(QKeySequence::Quit);
     connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
     aboutAct = new QAction(tr("&About"), this);
     connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
     aboutQtAct = new QAction(tr("About &Qt"), this);
     connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
 }
void MainWindow::createMenus()
 {
     fileMenu = menuBar()->addMenu(tr("&File"));
     fileMenu->addAction(openAct);
     fileMenu->addAction(saveAsAct);
     fileMenu->addAction(exitAct);
     menuBar()->addSeparator();
     helpMenu = menuBar()->addMenu(tr("&Help"));
     helpMenu->addAction(aboutAct);
     helpMenu->addAction(aboutQtAct);
 }
main()函数
int main(int argc, char *argv[])
 {
     QApplication app(argc, argv);
     MainWindow mainWin;
     mainWin.show();
     mainWin.open();
     return app.exec();
 }