Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 45 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
45
Dung lượng
558,13 KB
Nội dung
13.1 TheSAX2API astatusand twocomparisons.Ifthisisnot thecaseweset an errormessage and return from themethod withfalse. If, on theother hand,wecomeacross<rss>,wefirstcheck theversion number. We on lysupportRSS version 2.0(orasubset of this). We thereforerejectother versionsasapreventivemeasure.Nowit is also time to setrssTagParsedtotrueso that weare notwronglyrejected bythefirstcheck on elements that weparse later. If wecomeacross<item>, then wechangethe status of inItem to true to distin- guishthe contextofthe tags,asdescribedabove. We also add anewlin etothe model, which wewillthenfill withvalues. If anewtagstarts, weshouldalsoemptycurrentText, sincethisvariable should onlycontainthe textbetween apairofstart andend tags. Next, thecharactersmethod is used to read in thedatathatliesbetween apair of startand endtags.Ifthe parserinterprets this textasseveral consecutivetexts, for example, anormaltextand aCDATA sectioninwhich datamaybe enclosed in diamondoperators, without it beinginterpreted as XML, wecombineall thetexts into one. Since no errorcan ariseherefromour perspective, wereturn true in all circumstances: // rssreader/rsshandler.cpp (continued) bool RssHandler::characters( const QString &str ) { currentText +=str; returntrue; } We insert thetextcollected in this wayin endElement()intothe ready-to-useline of themodel. Againweare notinterested in namespaces, butmerelyin thecurrent tag, which is waiting in theqName variable: // rssreader/rsshandler.cpp (continued) bool RssHandler::endElement( const QString &/ * namespaceURI * /, const QString &/ * localName * /, const QString &qName ) { if (qName == "item"){ inItem =false; } elseif(qName == "title"){ if (inItem) { QModelIndexidx=itemModel->index(0,0); itemModel->setData(idx,currentText); } } elseif(qName == "pubDate"){ if (inItem) { QModelIndexidx=itemModel->index(0,1); 359 13 Handling XMLwithQtXml itemModel->setData(idx,currentText); } } elseif(qName == "description"){ if (inItem) { QModelIndexidx=itemModel->index(0,0); QString preview; if (preview.length() >= 300 ) preview=currentText.left(300)+" "; else preivew=currentText; itemModel->setData(idx,preview,Qt::ToolTipRole); itemModel->setData(idx,currentText,Qt::UserRole); } } returntrue; } If wecomeacrossan<item>tag, weleavethe contextofanitem, andwetherefore set<inItem>back to false.Ifweare currentlylookingatthe contents of thetags <title>, <pubDate>, or <description>, wemustbesureineach casethatweare locatedwithinanitem, which is whywealsoneed to checkinItem in thesecases. Since weinsertthe dataintolinezero—after all, weinserted thenewelementinto this lineaswell—wewillspecifythemodelindexin column zeroasthe title. There weset currentText, that is thetextreadin, as thecontentbetween thetags.The same is donewithpubDate, exceptthatweselectthe first column here. We proceed in twowayswiththe descriptionfrom<description></description>. On onehand, wearbitrarilycutoff thefirst300 characters to provideatextpreview in thetooltip. To indicate that thetextcontinues, weattach an ellipsis( ) to it. 4 In addition,weplace datafor thefirsttimeinthe UserRole, in this casethe com- plete contents of <description>. We willuse this later to displaythecontents of thecurrent entryin aQTextBrowser. In thefinalpartofthe RssHandlerimplementation, wetake alook at errorhandling. On page 357 it was brieflymentionedthaterrorsthattriggerthe implementation of ourclass is retrievable for theparservia errorString(). This is whythis method simplyreturnsthe last errordescription,written to thevariable errorString: // rssreader/rsshandler.cpp (continued) QString RssHandler::errorString() const { returnerrString; } 4 Since weare in themiddleofanHTMLtag,there is no guaranteethatthe user will actuallysee thethree dots. Aproperfeed reader would havetouse abetteralgorithm to cutthe text. 360 13.1 TheSAX2API This error, as wellasfatal errors that originatefromthe parseritself, andwhich preventthe continuedprocessing of thedocument,sets off acalltothe fatalError() method, butonlyon thefirstparsererror,unlesswereturn true.Events arenot processedfurther after an errorhas occurred: // rssreader/rsshandler.cpp (continued) bool RssHandler::fatalError( const QXmlParseException &exception ) { QMessageBox::information(0,QObject::tr( "RSS-Reader" ), QObject::tr( "Parseerrorin line %1, columne %2: \ n%3" ) .arg(exception.lineNumber() ) .arg(exception.columnNumber() ) .arg(exception.message() )); returnfalse; } We passthe errorontothe user bymeansofQMessageBox.The parameter excep- tion provides details on theerror that hasoccurred. 13.1.3Digression:Equipping theRSS ReaderwithaGUI and Network Capability Nowourparsercan be built into afeed readerthatusesanHTTP address to down- load an RSSfeed,parse it,and displayit.Figure13.1showshowtheapplicationis constructed:The lineeditwaits for theaddressofthe fe ed,the contents of which aredisplayed byaQTextViewon theleft-hand page.Onthe rightwesee thearticle selected from thelistinaQTextBrowser. Figure 13.1: TheSAX-based RSS readerdisplays the blogs of KDE developers. To download thefile from awebserver,weuse theQHttp class, which enables asynchronouscommunication withwebservers. This is oneofthe networkclasses introducedinChapter 11, butwehavenot yet discusseditinmoredetail. We also 361 13 Handling XMLwithQtXml come acrossthe QBufferclass again, where wetemporarilystorethe contents of theRSS file. Later on weneed theinteger jobIdinconnectionwithQHttp.Our windowis basedonQMainWindow,among otherthings, because wewilluse its status bar: // rssreader/mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class QLineEdit; class QTextBrowser; class QTreeView; class QHttp; class QBuffer; class QModelIndex; class MainWindow:public QMainWindow { Q_OBJECT public: MainWindow(QWidget * parent=0); protected slots: void readResponse(intid, bool error); void retrieveRss(); void showArticle(const QModelIndex&index); void showRss(); private: QHttp * http; QLineEdit * lineEdit; QTextBrowser * textBrowser; QTreeView * treeView; QBuffer * rssBuffer; intjobId; } ; #endif // MAINWINDOW_H In theconstructor wegivethe windowanameand arrangethe subwindowin a table layout. Here wedragthe lineeditover onelineand twocolumns,which can be seen in thefourthand fifth parametersofthe first addWidget()details.We insert thetreeviewandthe textbrowserinthe second line, in thefirstand second columns respectively: // rssreader/mainwindow.cpp #include <QtGui> 362 13.1 TheSAX2API #include <QtXml> #include <QtNetwork> #include "mainwindow.h" #include "rsshandler.h" MainWindow::MainWindow(QWidget * parent) :QMainWindow(parent),jobId(0) { setWindowTitle(tr("RSS Reader")); QWidget * cw=newQWidget; QGridLayout * lay=newQGridLayout(cw); lineEdit=newQLineEdit; lay->addWidget(lineEdit,0,0,1,2); treeView=newQTreeView; treeView->setRootIsDecorated(false); lay->addWidget(treeView,1,0); textBrowser=newQTextBrowser; lay->addWidget(textBrowser,1,1); setCentralWidget(cw); rssBuffer=newQBuffer(this); rssBuffer->open(QIODevice::ReadWrite); http =newQHttp(this); connect(lineEdit,SIGNAL(returnPressed()),SLOT(retrieveRss())); connect(treeView,SIGNAL(activated(const QModelIndex&)), SLOT(showArticle(const QModelIndex&))); connect(treeView,SIGNAL(doubleClicked(const QModelIndex&)), SLOT(showArticle(const QModelIndex&))); connect(http,SIGNAL(requestFinished(int,bool)), SLOT(readResponse(int,bool))); statusBar()->showMessage(tr("Welcome toRSS Reader!")); } Theentirelayoutliesonasimple QWidgetbythenameofcw,which weinsertinto themainwindowas thecentral widget. Finally,wegenerateabufferand openit for read andwrite access. In addition wecreatethe QHttp object.Thisclass works in apurelyasynchronous manner, so thereisnot even thetheoretical possibilityof placingthe object on thestack, which would block processing until an eventispresent.Instead, allcalls immediatelyreturn.Whenthe QHttp object hastreated therequest,itsends outa signal. Forthisreason, wecreatesignal/slot connections at theend,the last oneofwhich is connected withthe QHttp instance.Assoon as theusersets off theinputsinthe lineeditwith ✞ ✝ ☎ ✆ Enter ,retrieveRss()beginsdownloading thefile.The second andthird connect() calls connect akeyactionoradouble-clicktoanentryin thelistview withthe showArticle()method, which displaysthe corresponding article. Finally, 363 13 Handling XMLwithQtXml weconnect therequestFinished() signal,which triggers QHttp after an operation hasbeen completed,withthe slot wehavewritten ourselves,readResponse(). In retrieveRss()wetransferthe textfromthe lineeditintoaQUrl object.Ittries automaticallyto parse aURL from thetextpassedtoit: // rssreader/mainwindow.cpp (continued) void MainWindow::retrieveRss() { QUrlurl(lineEdit->text()); if(!url.isValid || url.schema() != "http") { statusBar()->showMessage(tr("Invalid URL:’%1’") .arg(lineEdit->text()); return; } http->setHost(url.host()); jobId=http->get(url.path(),rssBuffer); statusBar()->showMessage(tr("Getting RSS Feed ’%1’ ") .arg(url.toString())); } If thetextdoesnot yield avalid URL(i.e., isValid() returnsfalse)orifthe scheme (i.e., th eprotocol) is not http://,wedonot need to continue,and return without leavingbehindanerror message.Wenowsetthe name of theserver from which wewanttoobtainthe RSSfeed,using setHost(). Thematchinghostnameisalready stored for us byurl.host(). Because of theasynchronousnatureofQHttp,all method calls that workonthe server arearrangedintothe queueand performedone after theother.Each method callreturnsajobID. As soon as ajob hasbeen processed, QHttp emitsthe re- questFinished()signal, thefirstargument of which is thejob ID. Forthisreasonwemake anoteofthe jobID(in themembervariable jobId) for the Getrequest to theserver.Asarguments, theget()meth od demandsthe pathtothe fileand apointer to aQIODevicewhere it can storethe retrieved file. Finally,we informthe user that weare downloading theRSS feed. In thereadResponse() slot wefetch onlytheresultofthe Getjob.The second parameter specifies whether an erroroccurr ed during thefile download, perhaps because theserver was notavailable or thepathwas incorrect. If this is notthe case, weprocess thedatavia showRss()and issueathree-second success message in thestatusbar.Otherwise, an errormessage willappear for thesamelen gthof time: // rssreader/mainwindow.cpp (continued) void MainWindow::readResponse(intid, bool error) { 364 13.1 TheSAX2API if (id == jobId) { if (!error) { showRss(); statusBar()->showMessage( tr("RSS-Feed loaded successfully"),3000); } else statusBar()->showMessage( tr("Fehlerwhile fetching RSS feed!"),3000); } } showRss()doesthe actualwork. Here wecreateastandardmodelwithtwocolumns that welater passontoRssHandler: // rssreader/mainwindow.cpp (continued) void MainWindow::showRss() { QStandardItemModel * model =newQStandardItemModel(0,2); RssHandlerhandler(model); QXmlSimpleReaderreader; reader.setContentHandler(&handler); reader.setErrorHandler(&handler); rssBuffer->reset(); QXmlInputSource source(rssBuffer); if (!reader.parse(source)) return; deletetreeView->model(); treeView->setModel(model); } QXmlSimpleReaderisresponsible for parsing thefile usingthe RssHandlers.Since RssHandlerinherits from QXmlDefaultHandler, andthus from allhandlers, butwe haveimplemented onlythefunctionalityof QXmlContentHandler andQXmlEr- rorHandler, wemustregister theRssHandlerasbothacontentand errorhandler withthe readerobject. As the document source for QXmlSimpleReader, theQXmlInputSourceclass is used, which obtainsits datafromaQIODevice. Butbeforeweinstantiate such an input source,passing on thebufferasanargument at thesametime, wemustset the read position in thebuffertothe beginning of theinternalQByteArraywithreset(), so that thecontentjustwritten can be read out. reader.parse() nowstarts the actualparsing process. If this runs successfully,wefirstdelete anyalreadyexisting modellinkedtothe tree view,and then passour model, equippedwithfresh content, to theview. In thefinalstep wenowneed to implementthe showArticle()slottodisplaythe entryselected in thetreeviewin thetextbrowser. To do this weaccess thedata() 365 13 Handling XMLwithQtXml method of theact ivemodel. We obtain theindexof thecurrent entryfrom the argument of theslot. As theroleweselectUserRole, where wepreviouslystored the complete contents of the<description>tag. We nowconvertthis, usingtoString(), from aQVariantback to aQSt ring andpassthistothe textbrowserasHTML: // rssreader/mainwindow.cpp (continued) void MainWindow::showArticle(const QModelIndex&index) { QVarianttmp=treeView->model()->data(index,Qt::UserRole); QString content=tmp.toString(); textBrowser->setHtml(content); } NowourrudimentaryRSSreaderisfinished.The obligatorymain() method instan- tiates QApplication, andthe MainWindowobject displaysthe windowandsets it to an initialsizeof 640 × 480 pixels. Theapplicationthenentersthe eventloop: // rssreader/main.cpp #include <QtGui> #include "mainwindow.h" intmain(intargc, char * argv[]) { QApplication app(argc, argv); MainWindowmw; mw.show(); mw.resize(640,480); returnapp.exec(); } This simple examplealreadydemonstrates that SAX2 allowsyou to parse docu- mentswithcomparativelysmalloutlay.But themoreexact thechecks become,the more complexthecode.Ifyou wanttoavoidthiscomplexity,and you onlyprocess smalldocumentsanyway,you should take alook at theDocument Object Model, which followsacompletelydifferent approach. 13.2The DOM API QDom,the DOMAPI of Qt, is averyconvenient wayof accessing XMLfiles. Th e QDomDocument classhererepresentsacomplete XMLfile.Its setContent()method is capable of generating DOMtrees outofXML files andconverselywriting the contents of aDOM tree to an XMLdocument. TheDOM tree itself consists of DOM elements (QDomElement). Theirstart tags may containattributes.Between thestart andend tag, DOM elements maycontaintext 366 13.2 TheDOM API or child elements.InthiswaytheDOM tree is built up from theXML structure, and itselementsare without exceptionDOM nodes(QDomNodes). QDomNodesknowtheprinciple of parenthood: If theyareinserted in anotherpart of thetree, theyarenot copied,but change theirlocationinthe tree.The node into which aQDomNode is inserted nowfunctionsasits newparentnode.Not everynode mayposesschild nodes, however.Ifyou try,for exam ple, to givea child to an attribute node,the object willinsertthe newnode as asiblingnode of theattribute node.Thisdeviates from theDOM specification,which at this point divergentlydemandsthatanexceptionshouldbethrown. Here ageneral distinctionfromthe DOMspecificationcan alreadybe seen:Qtdoes notuse exceptions to announceerrors, buteitherusesreturn valuesorchooses an alternativebehavior. This is whyit is recommended that you excludecases of error in advance of acallbymaking as manychecks as possible in themethod’scode, andalsocheck return valuesofmethods after calls. 13.2.1Reading in andProcessing XMLFiles ThefollowingHTMLfile is written in XHTML: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"lang="en"xml:lang="en"> <head> <title>Title</title> </head> <body> <p> <ahref="http://www.example.com">Example.com</a> <ahref="http://www.example.net">Example.net</a> <ahref="http://www.example.org">Example.org</a> </p> </body> </html> We wanttoload this into atestapplicationinaQDomDocument andworkwithit in ordertoobservedifferent aspectsofQDom. Forthispurposeweopenthe file withQFile andreadout itscontents.Thenweinstantiate aQDomDocument and passthe byte arrayread outofthe filetothe QDomDocument withsetcontent(). We useusing namespace std; to simplywrite cout (insteadofstd::cout) to display dataonthe standardoutput. // xhtmldomparser/main.cpp #include <QtCore> #include <QtXml> 367 13 Handling XMLwithQtXml #include <iostream> using namespace std; intmain(intargc, char * argv[]) { QCoreApplication app(argc, argv); QFile file("index.xml"); if (!file.open(QIODevice::ReadOnly)) return1; QByteArraycontent=file.readAll(); QDomDocumentdoc; QString errorMessage; intline, col; if (!doc.setContent(content,&errorMessage, &line, &col)) { cout << "Errorin Line "<< line << ",column "<< col << ":"<< qPrintable(errorMessage)<< endl; return1; } setContent()parsesthe inputand returnsafalse if thereisaparsererror.If—like us—you wanttolearn more aboutthe error, you passtothisfunction,apart from thebytearray,apointer to aQString andtwointegers. If thereisanerror,the function fills theselastthree valueswiththe problemdescription,aswellasthe relevantlineand column in thedocument. Figure 13.2: Everyobjectinthe DOMtreegenerated from theXML is a QDomNode as well. 368 [...]... work, of course To obtain the name of a QDomElements, we use the tagName() method The text() method collects the text nodes of an element and its child elements in a QString Here we receive the text of the headers from the head element, and on 3 69 13 Handling XML with QtXml the next loop pass the texts of all three reference elements () beneath the body element We now fill the node variable with the. .. on the new strings 14. 1.2 Processing Translation Sources with Linguist The most convenient way to open and edit translation sources is with the program Qt Linguist This work can be done by people working independently of the Qt software developers, such as freelance translators Figure 14. 1 shows the main window of the Linguist after it has loaded the file cuteedit_de.ts The context dock window on the. .. want to write the XML file back to a QIODevice The parameter specifies the number of empty spaces that should be used when indenting the XML structure If this is missing, Qt sets the indent depth to one empty space per level In the following example we will write the current status of the DOM tree into the file opened for writing, out.xml Then we close this and send it to the standard 372 13.2 The DOM API... translated strings The command lrelease cuteediti18n.pro creates files from (complete or partial) translation sources in a special binary format that the Qt program can use In our case these are cuteedit_de.qm, cuteedit_fr.qm, and cuteedit_it.qm 14. 1.3 Using Translations in the Program Loading the correct translation when the program starts is the task of the QTranslator class It will search for the translation... for the embedded variants of Qt See http://doc.trolltech.com /4. 1/qtglo bal.html for a list of all compiler- and platform-dependent macros 3 89 A Debugging Help and arguments You can think of a callback as a pointer to a function Whoever holds the pointer can invoke the function from an abitrary place in the application at any given time However, this assumes that the caller knows which arguments the. .. assert.h Qt has the Q_ASSERT() macro, which interrupts the program just like assert() if certain conditions are not fulfilled It is often used to test preconditions or postconditions for specific code segments of methods In contrast to assert(), the Qt variant distinguishes between the release and the debugging versions4 of a program: In the debug version, Q_ASSERT() breaks off with an error message 4 390 ... translations for different languages, such as qt_ de.ts and qt_ de.qm for Germany All you need to do is copy the corresponding files to the current directory so that the QTranslator object will find them Since the organization and application name, as well as the domain, are used in the path for configurations files, these strings should not be translated 14. 1 .4 Adding Notes for the Translation If a string’s meaning... ensured by inserting the CONFIG -= debug line in the project file To use the functions and macros described below, no additional includes are necessary—apart from files that otherwise manage without Qt elements In this case you must include the QtGlobal header: #include If QT_ NO_DEBUG_OUTPUT is undefined, what is done with the error messages depends on the operating system Under Unix-type operating... look at the methods we can use to search specifically for certain elements DOM provides the elementsByTagName() function for this purpose It expects the name of an element type If you call it as a method of a QDomDocument instance, then it will look through all the elements in the entire document, whereas the method of the same name in the QDomElement class looks through all the elements beneath the element... 14 Internationalization Many programs today are intended to reach users in many different countries For this reason it is very important that an application can be modified easily and flexibly to the particularities of another language One aspect of this is the translation of all visible texts into the target language The direction of text flow, on which the arrangement of widgets is based, is also of . independentlyof theQt softwaredevelopers, such as freelance translators. Figure 14. 1showsthe main windowof theLinguistafter it hasloadedthe filecute- edit_de.ts. Thecontextdockwindowon theleft-hand page. theparticularities of anotherlanguage.One aspect of this is thetranslation of allvisibletexts into thetargetlanguage .The directionoftextflow,onwhich the arrangementofwidgets is based, is also of. implementthe showArticle()slottodisplaythe entryselected in thetreeviewin thetextbrowser. To do this weaccess thedata() 365 13 Handling XMLwithQtXml method of theact ivemodel. We obtain theindexof thecurrent