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