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

45 274 0
The book of qt 4 the art of building qt applications - phần 4 pot

Đ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

4 Developing aGUI Application Ba sedonaMainWin dow Qt::RightDockWidgetArea); templateDocker->setObjectName("TemplateDocker"); templateDocker->setWindowTitle(tr("Templates")); addDockWidget(Qt::LeftDockWidgetArea,templateDocker); QListView * view=newQListView(); templateDocker->setWidget(view); newTemplateHandler(view,textEdit,this); Themainwindowrequires theobje ct name to savethe windowproperties. We willdiscuss this topicatthe endofSection 4.8. If thenameismissing,Qtcom- plains at runtimeonthe standardoutput. Unfortunately,itisnot possible to set thewindowTitle attribute of QDockWidgetinthe Designer,which is whyit is im- portant that this mustbedoneseparatelyin theconstructor.windowTitle labels thewindowandalsogives anametothe toggleactionthatisgenerated bytog- gleViewAction(). In thefinalstep webreathe lifeintothe widgetbyfillingitwithalistview.Wewill later findthe templates in this view.The TemplateHandler classnowinstantiated is responsible for fillingthe listand for insertingtemplates at thecurrent cursor positioninthe editor window: // cuteedit2/templatehandler.cpp TemplateHandler::TemplateHandler(QListView * view,QTextEdit * textEdit, QObject * parent) :QObject( parent),mTextEdit(textEdit) { mModel =newQStringListModel(this); QStringList templates; templates<< "<html>"<< "</html>"<< "<body>"<< "</body>"; mModel->setStringList( templates); view->setModel(mModel); connect(view,SIGNAL(clicked(const QModelIndex&)), SLOT(insertText(const QModelIndex&))); } In Qt 4, listviewsworkonthe basisofthe model/view principleintroducedin Chapter 8: A model is responsible for obtainingdata, while the view displaysthe data. In thecaseofour templates,one modelisenough, which takesdatadir ectly from aQStringList.Asbefore, theseare fedwithseveral templates,inthiscasefor HTML. 16 . We passthe listcreated in this wayto themodelvia setStringList()and turn this into thereference modelfor ourview,the listview.The listviewis nowfilled, and 16 In aproperapplicationthe templatesare notcompiledstatically,ofcourse, butare loaded from afile. 134 4.7 Dock Windows wejustneed to includethe selected templateinthe editor window.Todothis, weconnect theclicked()signalofthe viewandimplement theinsertText()method (which wemustfirstdeclare in theclass definitionasaslot,ofcourse) : // cuteedit2/templatehandler.cpp (continued) void TemplateHandler::insertText( const QModelIndex&index) { QString text =mModel->data(index,Qt::DisplayRole).toString(); QTextCursorcursor=mTextEdit->textCursor(); cursor.insertText(text); mTextEdit->setTextCursor(cursor); } Themodelindexpassedrepresentsthe selected lineinour model. Using thedata() method, wecan obtain thedataasQVariantfromthis, which wemuststill convert into aQString.QVariantworks in asimilarwayto aunion in C++. Theclass can also convertvarious types—both Qt-specificdatatypessuchasQString andQSize, as wellasC++ typessuchasint or double—fromone to another. Figure 4.13: Thetemplate dock window in doc ked condition: Aclick insertsthe text into thecorresponding lineinthe editor window. Themodel/viewconceptofQthas manydifferent roles for amodelindex(see table 8.1onpage 209; forinstance, manyviewscan displayan icon (Qt::DecorationRole), in additiontonormaltext(Qt::DisplayRole). At themoment, however,onlyQt:: DisplayRole is relevanttous. 135 4 Developing aGUI Application Ba sedonaMainWin dow ThetextCursor()method of thetextwindowrepresents thecurrent positionofthe writing cursor. 17 We passthe text, which it should insert at thecursorposition, to theinstance. Nowwemustinsertthe textcursortothe currentcursorposition again, usingsetTextCursor, to updatethe cursor. 18 Ourdockwindowis nowcompletelyimplemented.Thankstothe QObject base classand thefact that wepassthe main windowas theparentobject,wedonot need to delete th einstanceofTemplateHandler manually.The result is shownin Figure 4.13. 4.8SavingPreferences Last butnot least, ourprogram should be able to keep thesettings made bythe user,even after theprogram is restarted.Tothisend,different conventionshave become establishedondifferent operating systems. Dependingonthe platform, applicationdatamaybe stored in theWindowsReg- istry(inthe user scope HKEY_LOCAL_MACHINE\Software) or in thesystem scope HKEY_CURRENT_USER\Software),inanXML-based .plist fileunder MacOSX,orin /etc/xdg, 19 (system-widesettings)or~/.config(user-definedsettings)under Unix. Qt encapsulates accesstothese configuration storage systemswiththe help of theQSettingsclass.Everyfilingsystem in this is a backend .QSettingsobjects can be created either on theheaporonthe stack.Since littleworkisneeded to instan tiatethem, werecommend that you create them on thestack, if this is necessary. In casetwoormoreQSettingsinstances areworking withthe same data, theclass ensuresthatdatabetween different instancesisalwayscorrectlysynchronized,in casetwoormoreQSettingsobjectsare working withthe same file. Thesame appliesfor twothreads,bothofwhich containaQSettingsobject withthe linkto thesamefile,and even for twodifferent processes, in casebothare usingQSettings linkedtoacommonfile.Qtusesinternallocking mechanisms for this purpose. TheQSettingsconstructor normallyrequ ires twoparametersfor theinstantiation in ordertogeneratethe appropriate entryin theconfiguration storage system:the name of theorganizationfor which theprogrammerworks,and thenameofthe program.InWindows, 17 TheQTextCursorcla ss in generaldoesnot havetodescribe thecurrentlyvisible cursor,but it canmanipulatetextatanypositionatall. 18 This is necessarybecauseQTextCursorworks notinapointer-based manner, butinavalue- basedone,and wetherefore workwithacopycreated withthe allocation to thecursorvariable. 19 Thedirectoryname standsfor an abbreviation of XDesktop Group thenow-obsolete generictermfor theFreedesktop.orgdevelopers.See alsohttp://www.r edhat.com/archives/xdg- list/2003-March/msg00041.html. 136 4.8 Saving Preferences QSettingssettings("OpenSourcePress","CuteEdit"); would referencethe registrypath HKEY CURRENT USER\ Software \ OpenSourcePress\ CuteEdit If aprogrammergenerates such QSettingsinstances at manylocationsinthe code, it would be agood idea nottohavetoconstantlypassthe parameters. This is possible if wefeed theapplicationitselfwithprogram andorganizationdetails, preferablystraight in themain() function: QCoreApplication::setOrganizationName("OpenSourcePress"); QCoreApplication::setOrganizationDomain("OpenSourcePress.de"); QCoreApplication::setApplicationName("CuteEdit"); From nowon,QSettingsmakesuse of thesedetails,sothataninstancewithout parametersisall that is needed: QSettingssettings; It is surprising th at setOrganizationDomain() method exists,since wehavejust managedwithout it.But it is justified throughthe waythat MacOSXstores its settings: it triestosortthe organizations according to an inverted domainname pattern. If thedomaindetails aremissing,QSettingscreates artificialdetails from theorganizationname. If setOrganizationDomain() is specified correctly,the file- namesinOSXareasfollows: $HOME/Library/Preferences/de.OpenSourcePress.CuteEdit.plist $HOME/Library/Preferences/de.OpenSourcePress.plist /Library/Preferences/de.OpenSourcePress.CuteEdit.plist /Library/Preferences/de.OpenSourcePress.plist It is notabsolutelyessentialtospecifythedomain, butitshouldnot be left out in casethe organizationhas arealdomainname. Thefirsttwoparts specifythe user scope,and thelasttwospecifythesystem scope,adistinctionthat—as hinted above—concerns allthree platforms. In theuserscope (QSettings::UserScope)anapplicationsaves allthe applications involvingjustthatuser, while in thesystem scope (QSettings::SystemScope )itsaves datathatare important for allusers. Because writing in thesystem scope generally requires root or administrator rights,the followingconstructor is normallyrelevant onlyfor installation programs: 20 20 Never assume that theuserhas administrator rights,even if this is standardpracticeinmany Windowshomeinstallations. 137 4 Developing aGUI Application Ba sedonaMainWin dow QSettingssettings(QSettings::SystemScope); QSettingsnowignoresthe user scope andreads andwrites exclusivelyin thesys- tem scope.Ifyou specifyQSettings::UserScope instead, th eclass behaves as if it was calledvia thestandardconstructor.QSettingslooksinthisfor asetting, first in theuserscope.Ifthe object is notfound there, it then looksfor it in thesystem scope. To write theactualdata, QSettingsprovides thesetValue() call, which expectsa keyandthe actualvalue.The value itself is of theQVarianttype,withwhich we arealreadyfamiliar. Thefollowingcode first stores avalue in thesystem-specific configuration backend an dthenreads it out: // configtest/main.cpp // manufacturer,product QSettingssettings("OpenSourcePress","ConfigTest"); QString hello ="Hello, world!"; // storeavalue settings.setValue("Greeting",hello); // resetvariable hello =""; // readvalueand assign tovariable hello =settings.value("Greeting").toString(); qDebug() << hello;//prints "Hello, world!" Theexplicit conversion to aQString usingtoString()isnecessarybecause C++is notinapositiontocorrectlyconvertthe QVariant value returned byQt because QStringhas no knowledge of QVariant,and thus it doesnot provideanassignment operator. Afteritisrun, theprogram generates afile in Unixcalled~/.config/OpenSource Press/ConfigTest.conf withthe contents [General] Greeting=Hello, world! Since wehavenot specified anygroup, QSettingsstoresthe keyin the[General] standardgroup.There aregenerallytwomethods of naming aspecific group. On onehand, wecan specifythedesired groupbeforeone or more setValue() calls,but wemustremovethissettingafterwardifwewanttocontinue usingthe object for otherpurposes: settings.beginGroup("MyGroup"); settings.setValue("Greeting",hello); settings.endGroup(); 138 4.8 Saving Preferences On theother hand,wecan simplyplacethe name of th egroup in frontofthe ke y, separated byaslash: settings.setValue("MyGroup/Greeting",hello); In both cases theresultlookslikethis: [MyGroup] Greeting=Hello, world! UnderWindows, groups aresubpaths of thecurrent applicationpathinthe Reg- istry,whereas MacOSXstructures them th roughXML tags. 4.8.1Extending CuteEdit To useQSettingsinCuteEdit, wefirstset up twomethods for reading andwriting in MainWindow:readSettings() andwriteSettings(). We callwriteSettings()inthe destructor.Thisgenerates anewQSettingsobject and saves thesizeofthe currentwindowin theSizekeyof theMainWindowgroup. In thenextstep wesaveall internalsettings for theMainWindow;for instance,the positions of thetoolbars anddockwindows. To do this,QMainWindowprovides thesaveState()method, which converts thesepropertiesintoaQByteArray: // cuteedit2/mainwindow.cpp (continued) void MainWindow::writeSettings() { QSettingssettings; settings.setValue("MainWindow/Size",size()); settings.setValue("MainWindow/Properties",saveState()); } We callits counterpart,readSettings(), as thefinalstep in theconstructor of the class. It reads thesettings andappliesthemtothe finishedmainwindow,using restoreState(). restoreState()restoresthe internalstatusofthe main window,us- ingthe read-outQByteArray.But first wemustconvertthe QVariant returned by value() into aQSizeorQByteArray: // cuteedit2/mainwindow.cpp (continued) void MainWindow::readSettings() { QSettingssettings; resize(settings.value("MainWindow/Size",sizeHint()).toSize()); 139 4 Developing aGUI Application Ba sedonaMainWin dow restoreState(settings.value("MainWindow/Properties").toByteArray()); } Thesecondparameter that wepasstovalue()—sizeHint()—is also unusual. It is the defaultvalue if thebackendcannotfind thekey.Inspecific cases it ensuresthat theeditorwindowhasanappropriate initialsize. 140 5 Chapter LayingOut Widgets Even if you leaveituptoQttoarrange thewidgets in adialogormainwindow (aswehavedonesofar), thereisnothing preventing you from doing thelayout manuallyusingthe classlibraryin specialcases.Inpracticethisisseldom done, butacloser look at manuallayoutprovides an understanding of theQtlayout mechanism, which wewillexaminebelow. 5.1ManualLayout In thetraditional version of GUI design,each widgetis“attached byhand”toa point in theoverlyingwindowor widget(that is,the widgetthathas been specified as aparentobject for thegiven GUI element) andfixed valuesfor itsheightand width aredefined. TheQWidgetclass provides thesetGeometry() method as abasis classfor nearlyallgraphical elements.Thisexpectsfourinteger parameters: first 141 5 LayingOut Widgets thevaluesfor thexan dypositions relativetothe parentwidget, followed bythe height an dwidth.Atthispoint in time theparentwidgetdoe snot havetodisplay itsownfinalsize. As an example, wecan look at awindowderived from QWidget(Figure 5.1): // manually/window.cpp #include <QtGui> #include "window.h" Window::Window(QWidget * parent) :QWidget(parent) { setFixedSize(640,480); QTextEdit * txt =newQTextEdit(this); txt->setGeometry(20,20,600,400); QPushButton * btn=newQPushButton(tr("&Close"),this); btn->setGeometry(520,440,100,20); } Figure 5.1: Asimple, manually laid outwidget ThesetFixedSize()method instructsthe windowto acceptafixed,unchanged size. Then wepositionaneditorwindow(a QTextEditwidget 1 )and abutton. From thesesetGeometry() calls it is alreadyevident that it is quitedifficulttoguess thecorrect values. Getting alayoutconstructed in this mannertoworkisacontin- uous cycleofchoosingcandidatevalues, compiling, andthenadjusting th evalues to improvethe appearance. It can also be quiteawkwardifthe widgetordialog 1 Forall thosewho havenot (yet)read,orsofar merelybrowsedthrough Chapter4:The QTextEdit classprovides amultiple-lineinput field fortext, which canbeformattedvia theAPI. In addition to pure text, it canalsoloadstructuredHTML. 142 5.2 AutomaticLayout changes: If you wanttoadd anewbuttoninthe middleofanarrangement,for example, thepositionofall elements placed beneaththe newelementmustbe modified. Now,itcan be argued that none of this is aprobleminpractice, sincethe Qt Designerconsiderablysimplifies thepositioning workinvolved.But even aGUI designer cannotsolveall problems without usingautomatic layouts. Oneofthese problems concerns widgets that would look better if theycouldshrink or grow:Inaninflexible layoutand without additionalaids, such elements—like the editor windowin theexample—alwaysretain thesamesize, although it would be nice if theywould adjusttothe available screen sizeoratleast givethe user the optionofchangingtheir dimensions. To keep thesizeofthe dialog flexible,wecould replacethe setFixedSize()callwith theresize()method, which also expectstwointeger parametersoraQSizeparam- eter.Thisonlyadjusts thesize, anddoesnot fixit.The user can nowchange the dimensions of th edialogwiththe mouse, although thewidgets that it contains retain theirdimen sion s. Alternatively,you couldreimplement theQWidgetmethod resizeEvent(): Qt always invokes this method whenthe widgetsizechanges.You couldwrite code to com- pute th enewsizes andpositions of thewindowelements on each resizeeven t. Butthisprocedure is muchtoo complexin most cases,and also requires manual calculation of thewidgetproportions. 2 In addition,reimplementingresizeEvent()poses aparticularproblemincombina- tion withinternationalization: With localized software, thedimensionsofalabeled widgetmaydepend on thelanguage in which it is displayed.Abuttoncalled Close in Englishhas amuchlongerlabel in th eGermantranslation ( Schließen), andthe textwill be cutoff unlessspecial precautionarymeasures aretaken. Ultimately,wecan onlypatch up thesymptoms in this way.Toactuallysolvethe underlyingproblem, wecannotavoidusing automaticlayout. 5.2Automatic Layout TheQLayoutclass andspecialized layouts derived from it help thedeveloperto positionwidgets dynamically.For this to succeed,each graphic elementderived from QWidgethas asizeHint()method, which returnshowmuchspacethe widget would liketooccupyunder normal circumstances.Inthe same way,there is a minimumSizeHint()method—awidgetmayunder no circumstances be smallerthan thevalue returned byminimumSizeHint(). BothsizeHintand minimumSizeHintare properties, which can be changedwiththe corresponding setmeth od. 2 In some casesthispro cedure is veryuseful,however.Anumber of KDEprogramsuse re- sizeEvent()todisplaystatuswindowsonthe currentlayoutatthe lower-right edge of the window. 143 [...]... remembers the last size of the widget in an uncollapsed state If this is 0 for any reason, the implementation uses the value of the respective sizeHint() The current position of our splitter 155 5 Laying Out Widgets depends on the size of the widget in front of the splitter, which is why the code detects the size of the widget that occupies the space up to the position pos-1, by accessing sizes() If the. .. first determine the alignment of the splitter We obtain the position of the splitter, using the QSplitter::indexOf() method This is also the position of the widget lying to the right of (or directly beneath) the splitter For reasons of symmetry, a zeroth handle exists in every splitter, which QSplitter never displays This guarantees that indexOf() always delivers a sensible position The function makes... in which the number value corresponds to the number of the widget These classes therefore have the setCurrentIndex(int) slot, which causes the widget with the number specified as an argument to be displayed We connect this slot in the connect() instruction in the final lines of the constructor to the currentRowChanged(int) signal Now it is important to create the entry in the list view when the stacked... for by software applications For such texts, it is not the top left, but the top right edge of the screen that is the starting point for the eye when reading Accordingly, a toolkit must be able to invert the layout horizontally The KDE browser Konqueror in Figure 5.7 masters this task correctly, because Qt manages to mirror all layouts horizontally, largely without the help of the user, if the language... using the setStretchFactor() method Since this function also requires the position of the widget, apart from the stretch, you first have to define the position of the widget using the indexOf() method This returns the correct position for a given widget or a handle The example below, documented in Figure 5.6, is derived from the stretch factor example on page 147 , but now uses a splitter instead of a... If these do not block the application, this often means increased programming work, particularly if settings altered using this dialog influence other parts of the GUI In such a case unexpected phenomena could occur for the user, but these can be elegantly avoided with the help of the modality A modal dialog represents a new top level widget In Qt this means that the developer must make instances of. .. QVBoxLayout(this); 144 5.2 Automatic Layout QTextEdit *txt = new QTextEdit(this); lay->addWidget(txt); QPushButton *btn = new QPushButton(tr("&Close"), this); lay->addWidget(btn); } The resize() instruction is not absolutely necessary Without it, Qt adds the minimum sizes of the editor window and the button suggested by minimumSizeHint() to the spacing inserted by the layout, that is, the distance between... margin for the layout and fixes the window size to the total Figure 5.3: The widget with a vertical layout Figure 5.3 clearly shows the weaknesses of the vertical layout: The button takes over the full width, which is not what we had in mind There are two ways of overcoming this problem In the first case we make use of something we are already familiar with, and take a look at the API documentation of QBoxLayout,3... see them This usability requirement can be set using the QDialog API extensions Figure 6.1: Example of the use of extensions: The Run dialog of KDE usually looks neat and tidy These provide a good service, for example in the KDE Run dialog This dialog allows the user to type URLs or program names, and tries to either display the web page or start the correct program (Figure 6.1), as appropriate 2 3 4. .. information on whether the widget with the specified number is collapsible or not Another property of the adjacent widget, maximumSize, ensures that the corresponding area above the splitter cannot be made any smaller once the neighboring widget has achieved its maximum size Splitters can react in two ways if the user pulls the handle in one direction while holding down the mouse button: They either draw a . counterpart,readSettings(), as the nalstep in theconstructor of the class. It reads thesettings andappliesthemtothe finishedmainwindow,using restoreState(). restoreState()restoresthe internalstatusofthe. dow ThetextCursor()method of thetextwindowrepresents thecurrent positionofthe writing cursor. 17 We passthe text, which it should insert at thecursorposition, to theinstance. Nowwemustinsertthe textcursortothe currentcursorposition again,. theexample—alwaysretain thesamesize, although it would be nice if theywould adjusttothe available screen sizeoratleast givethe user the optionofchangingtheir dimensions. To keep thesizeofthe dialog flexible,wecould

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