The book of qt 4 the art of building qt applications - phần 6 doc

45 326 0
The book of qt 4 the art of building qt applications - phần 6 doc

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

8 DisplayingDataUsing “Interview” Q_UNUSED(parent); returnaddressBook.count() -2; } To determine thenumber of columns,welook at thedataset from th efirstlineof theCSV file. TheQStringList::cou nt() method is used to determine thenumber of stringsinthe string listthatcontainsthe dataset corresponding to thefirstlineof thefile,obtainedfromaddressBook byinvokingat(0): // addressbook/addressbookmodel.cpp (continued) intAddressbookModel::columnCount(const QModelIndex&parent)const { Q_UNUSED(parent); returnaddressBook.at(0).count(); } Viewsthatuse ouraddressbook modelcan discover thelabelingofthe rowsand columns via theheaderData()method. To do so,theymustspecifythenumeric positionofthe section of datafor which theheading is desired, where asection is either arowor acolumn—whether thedesired heading is arowheading or acolumn heading is decidedbythevalue given for theorientation.Thisisof theenumeration type Qt::Orientation andhas thepossible valuesQt::Vertical or Qt::Horizontal. When it comestoroles,inthisexampleweare interested onlyin supporting the DisplayRole,which is used whenthe viewneedsthe texttobedisplayed.Everything else wepassontothe implementation of theoverclass. QAbstractTableModel does more than just return emptyQVariants: If wehad notreimplemented headerData(), it wou ld number allthe rowsand columns.Inorder to ensure that wecan use themodellater withaQTableViewthat also queriesrowdescriptions,wecallthe headerData()function of theoverclass, particularlyin casethe orientationis not horizontal. This meansthatbydefa ult, alabel to theleftofthe datasets willdenote thedataset number.For horizontalorientation weuse theentries from thefirst dataset in thelist, which as weknowcontains thecolumn names: // addressbook/addressbookmodel.cpp (continued) QVariantAddressbookModel::headerData(intsection, Qt::Orientation orientation, introle)const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { returnaddressBook.at(0).at(section); } } returnQAbstractTableModel::headerData(section, orientation, role); } 224 8.4 ImplementingYourOwn Models Finally,the modeldeliversactualdatatoaviewvia thedata()method. As argu- mentswepassaQModelIndex,which contains thepositionrequested bytheview, andthe requested role: // addressbook/addressbookmodel.cpp (continued) QVariantAddressbookModel::data(const QModelIndex&index, introle)const { if (!index.isValid()) returnQVariant(); QStringList addressRecord=addressBook.at(index.row()+1); if (role == Qt::DisplayRole || role == Qt::EditRole) { returnaddressRecord.at(index.column()); } if (role == Qt::ToolTipRole) { QString tip,key,value; tip="<table>"; intmaxLines=addressRecord.count(); for(inti=0; i<maxLines; i++) { key=headerData(i, Qt::Horizontal, Qt::DisplayRole) .toString(); value=addressRecord.at(i); if (!value.isEmpty()) tip+=QString("<tr><td><b>%1</b>: %2</td></tr>") .arg(key,value); } tip+="</table>"; returntip; } returnQVariant(); } Firstwecheck that theindexpassedisvalid—a good practice that prevents nasty crashesinInterviewprogramming,since out-of-rangeindices can alwaysoccur. Then weretrievethe desireddataset from thelist. In doing this weaccess the dataset followingthe oneinthe rowspecified bytheindex—after all, thecolumn headingsare in thefirstrow. We deliver an address dataset ourselves if theviewasks fordatainthe DisplayRole. To extract this ,weproceed in almost exactlythesamewayas beforewhenreading outthe headings, withone difference: We localizethe dataset via therow() detail of theindex. Since themodelissupposed to be editable,wemustalsohandlethe situation in which theviewasks fordat ainthe EditRole:Because thesam etextshould appearlater on in theeditorasinthe DisplayRole,wehandlethe EditRole-and Qt::DisplayRole queriesinone go. To make theexamplealittlemoreinteresting,wewillalsoimplement theToolTi p- Role at this point.Atooltip is ayellowboxthat containsadescriptionofaview 225 8 DisplayingDataUsing “Interview” elementand appears if you lingerover it withthe mouse. In thetooltipfor alisten- try,our address book viewwill showthecomponen ts of thedataset corresponding to that entry,asdepicted in Figure 8.10.Itisour aimtodisplayonlythenonempty components in thedataset. Figure 8.10: Thanks to the treatment of th e ToolTipRole ,the views nowshowan individual tooltip when themouse lingers over an entry. HTML formatting can be used in tooltiptexts,and weconstruct thedescription of theaddressbook entryusingthe <table>tag. Each rowof thetable consists of twocells,one of which contains thenameofthe address book field (the key)and theother,the matching value.Bothofthemwillbeshowninthe tooltiponlyif the value is notthe emptystring.Weobtainthe keybycallingthe headerData()method just implemented,and weobtainthe value byreading it outofthe currentdataset. We formatthe cells usingthe <tr>and <td>tags andappend theresulting HTML phrase to th estringthatwestarted previouslywiththe table tag. Finally,when allofthe fieldsofthe dataset havebeen processed, wecomplete thetooltiptext string byappending theclosing </table>tag, andreturn thefinished string. We willnowwrite aprogram to testour model. This reads in theCSV file, allocates amodel, andpassesthe file’scontents to themodelasaQString. We then bind themodeltoalistview,atable view,and atreeview.The result is showninFigure 8.11. // addressbook/main.cpp #include <QtGui> #include "addressbookmodel.h" intmain(intargc, char * argv[] ) { QApplication app( argc, argv); // Open the addressbook file in the working directory QFile file("addressbook.csv"); if (!file.open(QIODevice::ReadOnly|QIODevice::Text) ) return1; // Readits contentintoastring QString addresses=QString::fromUtf8(file.readAll()); 226 8.4 ImplementingYourOwn Models AddressbookModel model(addresses); QListViewlistView; listView.setModel(&model); listView.setModelColumn(0); listView.show(); QTreeViewtreeView; treeView.setModel(&model); treeView.show(); QTableViewtableView; tableView.setModel(&model); tableView.show(); returnapp.exec(); } Thecolumn to be displayed bythelistviewcan be selected from themodelwitha setModelColumn() instruction; for example, setModelColumn(2)would displayall first namesinsteadofthe formatted name. Figure 8.11: Thesethree views use ouraddressbook model. If amodelyou havewritten yourselfdoesnot turn outtoworkasyou wished, you should first checkwhether theoverriddenconst methods havebeen declared properly.Since theyshould merelyprovideinformation aboutthe model, Interview doesnot givethemanywrite accesstothe class. If theconst keywordismissing, thecorrect method willnot be available,because th einheritancemechanism of C++makesadistinctionbetween constand non-constversionsofamethod. 8.4.2Making Your OwnModels Writable Just outputting datainthe EditRole is notenoughifyou wanttomodifythedata source via themodel. In ordertobeable do this,weneed to overwrite theflags() andsetData()methods. flags() returnsspecific properties of an indexentry,so-called flags (see Table 8.2). Viewsuse this method to checkwhether operationsare allowed for aspecific item. 227 8 DisplayingDataUsing “Interview” Table8.2: ItemFlags formodels Value Effect Qt::ItemIsSelectable Elementcan be selected Qt::ItemIsEditable Elementcan be modified Qt::ItemIsDragEnabledElement can be used as astartingpoint for drag- and-drop operations Qt::ItemIsDropEnabledElement can be used as atargetfor drag-and- drop operations Qt::ItemIsUserCheckable Elementhas aselection status withtwostates (selected,deselected); requires theimplementa- tion of Qt::CheckStateRole in themodel Qt::ItemIsEnabledElement reactstouserrequests Qt::ItemIsTristate Elementhas aselection status withthree states (selected,not selected,partiallyselected); use- fulinhierarchical models where several child en- triesare select ed andothersnot selected;requires theimplementationofQt::CheckStateRole in the modelinadvance We willcomeback to theservices of this method on page 241, whenweequip our modelwithdrag-and-drop capabilityfor data. Here wemustfirstmake allthe cells editable: // addressbook/addressbookmodel.cpp (continued) Qt::ItemFlagsAddressbookModel::flags(const QModelIndex&index) const { if (!index.isValid()) return0; returnQAbstractItemModel::flags(index) |Qt::ItemIsEditable; } Nowtheusercan edit everyposition. To do this,the viewsuse aQItemDelegateby default, just as forthe display.Onceithas finishedits work, it calls thesetData() method to storethe newdatainthe model. As soon as this hasstoredthe dat a successfullyin themodel, it returnstrue. setData() is thecounterpart to data(): Bothfunctionsmustworktogether.Since thestandardimplementationofsetData()doesnothing more than return false,we need to reimplementitasfollows: // addressbook/addressbookmodel.cpp (continued) 228 8.4 ImplementingYourOwn Models bool AddressbookModel::setData(const QModelIndex&index, const QVariant&value, introle) { if (index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole)) { // add 1tothe rowindextoskipoverthe header addressBook[index.row()+1][index.column()]=value.toString(); emitdataChanged(index,index); returntrue; } returnfalse; } Firstwecheck, as always, whether theindexis valid.Thistimewealsoensurethat weare locatedinthe editingorinthe displayrole.Wedonot havetomake a distinctionbetween thesetworoles for ouraddressbook model, because in both cases thesamestringisinvolved.Other models mayneed to make adistinction between thetworoles;for example, whendeliveringorupdatingimage data, a modelmayneed to workwithactualpixmaps for theDisplayRole,but withpaths to pixmaps wheninthe EditRole. If theseconditionsapply,weset thenewvalue,passedvia value,atthe appropriate point.Hereweuse theindexoperator[], insteadofat()asusual,inorder to avoid thenormallydesirable behaviorofat(): Themethod provides onlyaconst reference to thestring, whereas theindexoperatorprovides asimplereference. 5 Aftersuccessfullychanging thedata, it is important that thedataChanged() signal is emitted so that theviewslinkedtothe modelupdatethe data. This demands twoindices as parameters, of which therowandcolumn propertiesshouldform arectangle.Because usuallyonlyonevalue is beingchanged at atime, wepass thesameindextwiceinorder to indicate thepositionofthe corrected dataitem. Finallywesignalthe successful completion of theprocess withretu rn true.Inall othercases,inwhich wehavenot saved anything, weconsequentlyreturn false. Insertingand RemovingRows So that themodelwillbecompletelyflexible,weimplement theinsertion andre- moval of rows. To do this weoverwrite theinsertRows()and removeRows()meth- ods.The equivalentremoveColumns() andaddColumns() methods also existtore- moveorinsertcolumns,but weare notconcerned withthese at this point.As parameterswepassanindexto therowbeneathwhich wewishtoinsertempty rows, as wellasthe number of rowstobeinserted.Wecan safelyignore theparent argument. To insert an emptyrow,wefirstneed to create an emptydataset. To do this wefill astringlistwithasmanyemptystringsasthere arecolumns in themodel. Then we 5 On th is subject,see alsopage400 in AppendixB. 229 8 DisplayingDataUsing “Interview” informthe model, withbeginInsertRows(), that wewanttoinsertrows. If wedo notdothis, existing selections in this modelcould getmixed up.Next, weinsertthe emptydataset—againincrementingthe rowby1because of theheader—andend theinsertmode.Finally,wesignalthatthe datahavebeen successfullyinserted,by returningtrue: // addressbook/addressbookmodel.cpp (continued) bool AddressbookModel::insertRows(introw,intcount, const QModelIndex&parent) { Q_UNUSED(parent); QStringList emptyRecord; for(inti=0; i<columnCount(QModelIndex()); i++) emptyRecord.append(QString()); beginInsertRows(QModelIndex(),row,row+count-1); for(inti=0; i<count; i++) addressBook.insert(row+1, emptyRecord); endInsertRows(); returntrue; } We implementremoveRows()inthe same way,but withasafetycheckinthiscase: If thereare more lines to be removed than thereare datasets in theaddressbook, wereturn false—otherwise, wewou ld runthe risk of theapplicationcrashing. We also need to announcethe removal of lines andsignalthe endofthe action. If everythingwas successful,wereturn true to thecallerasaconfirmation: // addressbook/addressbookmodel.cpp (continued) bool AddressbookModel::removeRows(introw,intcount, const QModelIndex&parent) { Q_UNUSED(parent); if (row-count-1 >addressBook.count()-1)returnfalse; beginRemoveRows(QModelIndex(),row,row+count-1); for(inti=0; i<count; i++) addressBook.removeAt(row+1); endRemoveRows(); returntrue; } With thesechanges,aprogram that accessesthe modelcan delete datasets by callingremoveRows()oradd th em byinsertingemptydatasets withinsertRows() andfillingthemvia setData()—themethod is notreserved just for delegates. 230 8.5 Sortingand FilteringDatawithProxyModels Outputting theContents of theModel In ordertoround off ourmodeland to complete ourtourthrough theworld of writable models,weconstruct amethod calledtoString(), which converts thecon- tents of themodelback into CSVformand outputsthisasastring. To do this wegothrough allthe datasets anduse theQStringList method join to combineeach string listintoasingle lineinwhich theindividualstrings aresep- arated from oneanother withcommas(,).Weterminate each linewithanewline characterbeforebeginning thenextline, which ensuresthatthe desiredemptyline at theend of theCSV fileiscreated: // addressbook/addressbookmodel.cpp (continued) QString AddressbookModel::toString() const { QString ab; foreach (QStringList record, addressBook) { ab +=" \ ""; record.join("\ ", \ ""); ab +=" \ " \ n"; } returnab; } To savethe currentstatusofthe model, you nowonlyneed to savethe return value from toString(). 8.5Sortingand Filtering Datawith ProxyModels Ourmodelupuntil nowhaslacked th ecapabilityto return itsentries to aviewin asorted form. This is because thereisnosorting criterioninthismodelfor anyof thecolumns.Itisalsopracticallyimpossible to filter outspecific entriesfromthe model. To address this shortcoming, Interviewhasprovided,startingfromQtversi on 4.1, theQSortFilterProxyModel class, after itspredecessorQProxyModel proved to be toounwieldy.Itisbased on theQAbstractProxyModel base class, which represents so-called proxymodels.These lie somewhere between amodeland view,obtain- ingtheir datafromthe modeland returningittothe viewin modifiedform(see Figure 8.14 on page 237).The proxy modelthereforebecomes thesourcemodelfor theview.Onpage 237wewilllook in more detail at howproxy models function andimplement ourownproxy model. Forthe moment,wewilljustlook at what QSortFilterProxyModel can do: sort andfilter. 231 8 DisplayingDataUsing “Interview” Duringfiltering, themodelreturnsthe modelindices for thoserowsinwhich the textinacolumn matchesthe search filter.Duringsorting,the roworderisarranged according to thevaluesinaspecified column, wherebyyou can sort in ascending or descending order. We willdemonstrate both capabilitiesofQSortFilterProxyModel withasmallex- ample, theFilteringView.Thisconsistsofatree view,abovewhich is alineeditthat accepts afilter term. Nexttothisweplace acombo boxcontaining allthe column names. Figure 8.12 showshowyou can usethistoselectthe column that is to act as thesearchcolumn. Figure 8.12: QSortFilterProxyModel helps in sortingand filteringmodels. 8.5.1Adjustments to theUserInterface SimplifyingSorting Since ourviewwill useaQSortFilterProxyModel instance,which can alreadysort, weneed onlyto adjustthe viewer accordingly.The worknecessaryfor this is done bytheconstructor of theclass: // addressbook/filteringview.cpp #include <QtGui> #include "filteringview.h" FilteringView::FilteringView(QAbstractItemModel * model, QWidget * parent) :QWidget(parent) { setWindowTitle(tr("FilterView")); proxyModel =newQSortFilterProxyModel(this); proxyModel->setSourceModel(model); QVBoxLayout * lay=newQVBoxLayout(this); QHBoxLayout * hlay=newQHBoxLayout; QLineEdit * edit=newQLineEdit; QComboBox * comboBox=newQComboBox; intmodelIndex=model->columnCount(QModelIndex()); for(inti=0; i<modelIndex; i++) 232 8.5 Sortingand FilteringDatawithProxyModels comboBox->addItem(model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString()); hlay->addWidget(edit); hlay->addWidget(comboBox); QTreeView * view=newQTreeView; view->setModel(proxyModel); view->setAlternatingRowColors(true); // Make the header"clickable" view->header()->setClickable(true); // Sort Indicatorfestlegen view->header()->setSortIndicator(0,Qt::AscendingOrder); // Sort Indicatoranzeigen view->header()->setSortIndicatorShown(true); // Initialsortieren view->sortByColumn(0); lay->addLayout(hlay); lay->addWidget(view); connect(edit,SIGNAL(textChanged(const QString&)), proxyModel, SLOT(setFilterWildcard(const QString&))); connect(comboBox,SIGNAL(activated(int)), SLOT(setFilterKeyColumn(int))); } Firstwecreateaproxy modeland saveitinamember variable calledproxyModel. Then wecreatebothavertical andahorizontallayoutusedlater to enclosethe widgets:Wegroup thelineeditand thecombo boxtogether in onelinewiththe horizontallayout. With thehelpofthe vertical layout, wepositionthisabovethe viewto which wepassthe proxy modelasthe source. To make reading easier,everyotherlineinthe tree viewis displayed withasecond background color. We activatethisfeature withsetAlternatingRowColors(true). To obtain thewidgetcontainingthe column headers in tree views, weuse header(). So that it can react to clicks,weset setClickable(true).Inaddition weprovide it withaso rting indicator,usuallythis is atrianglethatchartswhether datais shownsorted in ascendingordescendingorder.Inthiscasewesortcolumn 0in ascendingorder (Qt::AscendingOrder) anddisplaytheindicator via setSortIndica- torShown(true).Tomake sure that thelis tisalreadysorted beforethe user clicks theheaderfor thefirsttime, weprearrangethe datasets sorted bythefirstcolumn, withsortByColumn(0). 233 [...]... compile one of these explicitly, the -no-sql-driver switch is used; for example, in the case of SQLite the switch would be -no-sql-sqlite Qt also includes its own SQLite version If you want to use a version of SQLite installed on the system instead, you must also specify the -system-sqlite switch If /configure cannot find an installed database system, despite the development packages installed, then you... specify the include directory of the database system with the -I switch, for example -I/usr/include/mysql, in the case of MySQL It is left to each user to decide whether a driver is built separately as a plugin (-pluginsql-driver) or compiled permanently into the library ( -qt- sql-driver) Plugins are more flexible, whereas compiled-in drivers are simpler to handle if the Qt library is to be included in the. .. able to work with the classes of the module, Qt provides a meta-include for this package as well, which contains all the class definitions from the module The command to integrate it into a source file is as follows: #include Each of the classes of the module belong to one of three layers The driver layer implements the interface between the drivers for various databases and the API layer (see... Server*) If the Qt version originates from packages of a Linux distribution, you may need to install additional packages Ubuntu stores the SQL library in the package libqt4sql, whereas OpenSUSE, in addition to installing qt- sql, requires a DBMS-specific database package, such as qt- sql-mysql for MySQL If you build Qt from the sources, you should take a look at the output of /configure help: -Istring ... AddressbookModel with DndAddressbookModel in the main() function of the address program on page 2 26 and start two instances of the application Drag and drop is now possible between them, and also within the same view 8.9 Your Own Delegates Until now we have accepted that views display their entries themselves We will now reveal the secret of the delegates, which are responsible for the display of individual... used as the basis of Interview models 9.1 Structure of the QtSql Module The QtSql module is an independent library that can load additional plugins if required In contrast to QtCore and QtGui, its contents are not integrated by default (with qmake -project) into the generated projects In order to use the library, the following entry is therefore necessary in the pro file: QT += sql 257 9 The QtSql Module... }; In the constructor we first call the constructor of the overclass, passing it the complete dataset as a string (addresses), as well as the parent widget We then have to find out how many datasets the passed string contains Equipped with this value, we can keep track of the selection status of the respective line in the checkedStates list We can find out the number of datasets through the number of newline... one of the view classes in this manner It is sufficient to modify the model To demonstrate this we will use a subclass of the already-implemented AddressbookModel class, called DndAddressbookModel .6 To provide it with drag-and-drop capability, we must now overwrite the following methods: // addressbook/dndaddressbookmodel.h #ifndef DNDADDRESSBOOKMODEL_H #define DNDADDRESSBOOKMODEL_H #include "addressbookmodel.h"... “Interview” the contents no longer have to be pure ASCII text after transformation through QDataStream is another reason we cannot use text/plain as MIME types, in addition to the issue of distinguishability during the drop procedure The other side of the drag-and-drop procedure is handled by the dropMimeData() method Apart from the MIME data, it also contains the type of drop: Should the data be copied... QListView list view becomes the QListWidget, the counterpart to the QTreeView list view is QTreeWidget, and the table view QTableView is called QTableWidget in the independent version 8.11.1 Items Each entry in these view widgets is an instance of an element or item For each of the three widgets there are separate item classes that do not have a common parent class This also means that the data in a view widget . of theaddressbook entryusingthe <table>tag. Each rowof thetable consists of twocells,one of which contains thenameofthe address book field (the key)and theother ,the matching value.Bothofthemwillbeshowninthe. removed than thereare datasets in theaddressbook, wereturn false—otherwise, wewou ld runthe risk of theapplicationcrashing. We also need to announcethe removal of lines andsignalthe endofthe action “Interview” Q_UNUSED(parent); returnaddressBook.count() -2 ; } To determine thenumber of columns,welook at thedataset from th efirstlineof theCSV file. TheQStringList::cou nt() method is used to determine thenumber of stringsinthe string

Ngày đăng: 13/08/2014, 08:21

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan