Tải bản đầy đủ (.pdf) (45 trang)

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

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (609.1 KB, 45 trang )

3.1 Dialogs“By MouseClick”
Figure 3.9:
Signal/slot
connectionsare
createdinthe
Designer vi adragand
drop.
Step twoconsistsofspecifyingthe desiredsignaland slot pairfor thetwowidgets.
As soon as you releasethe mousebuttonover thetargetwidget, theDesigner
opens adialog, as showninFigure3.10:Onthe left it showsamenu of themost
frequentlyused signals. If thesignalyou arelookingfor is notthere,clickthe Show
allsignals andslots checkboxto displayallpossible signalsofthe source widget.
Theright selectionboxwill showallthe slotsofthe target wid getmatchingthe
signal selected on theleft. If you confirm thechoice, theconnectionisestablished.
Figure 3.10:
Signals andslots of
twoselectedwidgets
areconnectedbythe
developerinthis
dialog.
Aclickonthe connectingline, followed bypressing the




Del key,willremovethe
connection.
3.1.5The TabSequence
Theso-called tabsequence is important for keyboardusers. This function allows
theinput focustobeshifted,via the





Tab key,tothe nextwidgetthatexpects
input. TheDesignerspecifiesthe tabsequencesothatinitiallythefirstwidgetin
thedialoghas thekeyboardfocus.The focusismoved to th enextinserted GUI
elementwhenthe




Tab keyis pressed. When designingauser interface, you should
89
3 GUI Design Usingthe Qt Designer
payattention to thedefault tabsequenceand modifyit as necessaryin orderto
make yourapplicationasuserfriendlyas possible.
Figure 3.11:
Howthe focusis
passedonwhen
pressingthe




Tabkey
is specifiedinthe Tab
Ordermode
To do this,you switch to the TabOrder mode,via Edit → EditTab Orderorthe icon
withthe numbers123 andanarrowin thetoolbar. NowtheDesignerdisplayseach
widget’scurrent positioninthe tabsequenceinabluebox(Figure3.11).Aclickon

thecorresponding boxincreasesthe rank in thesequencebyone.
3.1.6Shortcutsand Buddies
Thosewho prefer keyboardcontrol willthank you if theycanjump directlyto
as manycommonlyused widgets as possible.GUI elements that displayauser-
defined text, such as buttons,are assigned akeyabbreviation byplacingan am-
persand (&)beforethe characterthatwillserveasthe keyboardshortcut. If the
textitselfcontainsareal ampersand, it is masked byduplicatingit: &&.
If theuserpressesthe combinationof




Alt +




character from nowon,the widget
obtainsthe focusand is activated.InFigure3.12weuse this techniquewiththe
Quit button.
QLabel objectsformanexception, however.Since theyusuallyoccurinalayout
for thepurposeofdescribinganadjacent“partner” widget, theythemselves do
notaccept afocus.However,the Buddypropertyof alabel can be used to spec-
ifyakeyboardshortcuttobeassociatedwiththe partnerwidget, as though the
descriptivetextofthe labelweredir ectlyattached to thepartner elementitself.
In theDesignerviewmode EditBuddies,you can nowspecifywithwhich widget
alabel is apartner.Todothis, clickthe future Buddylabel, which willthenlight
up in red. Holdingdownthe mousebutton, you nowpull aconnectionover to the
widgetthatinfutureshouldbeassociatedtothe label.
90

3.2 IntegratingDesigner-generatedFiles into Your Qt Project
Figure 3.12:
Labels arefriendsto
other widgets:The
Buddy allocationscan
be found in theBuddy
mode of theQt
Designer.
In theexamplefromFigure3.12, th erespectivelineeditnowhasthe focusifthe
user pressesthe lettersunderlined in thelabel inscriptionwhile holdingdownthe




Alt key.
Alternatively,while in thenormaldesignmode,you can setthe name of thede-
siredBuddywidgetinthe PropertyEditor, usingthe Buddyproperty.
3
Using this ap-
proach,wewould setthe value of th eBuddypropertyof theQLabelobject that dis-
playsthe Decimaltextinour byte converter dialog so that it matchesthe value of
theobjectNamepropertyof thecorresponding line-edit object,namely,the string
decEdit.
To undothe relationship,all you need to do is clickthe connectionlineinthe Buddy
mode andpress the




Del key.

3.2IntegratingDesigner-generated FilesintoYour
Qt Project
When savingwiththe menu item File→ SaveFormorSaveFormAs. ,theDesigner
generates a.ui filefromthe information it hasfor each widgetinthe form.
4
This
.uifile is specified in theqmake projectfile,asshowninthe followingline:
FORMS=byteconverterdialog.ui
In ourcase, qmaketakesintoaccount theuserinterfacefile byteconverterdialog.ui;
several files can be specified,separated byaspace, or otherlines can be added
according to thepatternFORMS+= file.ui .
3
Althoughthispropertyhasbeen theresince Qt 3.x ,the Designer forQt4.0doesnot displayit.
Onlyin version 4.1doesitappear again.
4
Using thethird menu item,SaveFormAsTemplate. , you cansaveyourformasatemplate,
which then appearsinthe selectiondialogfor new Fo rms .
91
3 GUI Design Usingthe Qt Designer
When buildingthe project, make then reliesonthe user interface compiler uic
to convertDesigner-generated .uifilesintoC/C++headerfiles.
5
Thereisafixed
naming convention in this step:for example, if theclass represented bythe.ui file
generated bytheDesigneriscalledByteConverterDialog (the value of theobject-
Name propertycan be examined to determine th eclass name), then theresulting
headerfile is given thenameui_byteconverterdialog.hbyuic.
It is important here that at leastone otherfile in theproject includes this generated
headerfile.You mustadd theappropriate #include statemen ts before qmakeis
run. Otherwise, make won’t calluic withthe relevantinterfacedescription fileas

an argument on itsnextrun.
Notice that thegenerated headerfile contains onlyahelpclass withtwomethods:
setupUi(), which generates theGUI,and retranslateUi(), which can be calledifthe
program is to allowtheusertochangethe language while it is running.
Bothmethods expect (asanargument)apointer to th ewidgettowhich th eGUI
object describedinthe Designer is to be bound.Even if you havealreadychosen a
template in theDesigner, you can freelychooseatthispoint thewidgetclass for
which theinterfaceisintended. TheMainWindowtemplate is theonlyonethat
mustbeusedtogether withaQMainWindow.
6
Theclass generated bytheuic is nowavailable as Ui::ByteConverterDialog or Ui_
ByteConverterDialog,ingeneral as Ui::classname or Ui_ classname ,wherebythe
classnamecorresponds to theobjectNameattribute of theformcreated in the
Designer.
Thereare nowthreewaysofusing andfunctionallydeveloping thewidgetcreated.
Whichofthese is best to usedepends on theparticularcontext.
3.2.1Using Designer-generatedClassesasHelperClasses
If you onlywanttodisplayaDesigner-created user interfaceonce, without touch-
ingthe corresponding object againafter it is initialized,itisappropriate to directly
instantiatethe generated classand bindthe instance to apreviouslycreated widget
withsetupUi(). This method fixes theGUI elements describedinthe .uifile on to
thewidgetand anchorsthem—provided this was specified in theDesigner—with
layouts.
We shalldemonstrate this techniqueusing ourDesigner-generated ByteConverter-
Dialog:
5
Note forQt3users: uicnolongergenerates acompleteQObject-based classinQt4,but merely
aframework which canbeappliedtothe widgetofthe matching type.
6
Thewidgetcreated in theDesignerisusedinthiscaseasthe centralwidgetfor th eQMainWin-

dowinstance, anditispositionedwithsetCentralWidget(), instead of withthe help of alayout,
as normal.Inaddition,the Designer menu bars andtoolbars aretreated separatelyfrom Qt 4.1,
afunctionalitythat is equallyavailable onlyforQMainWindowinstances.
92
3.2 IntegratingDesigner-generatedFiles into Your Qt Project
// simple/main.cpp
#include <QtGui>
#include "ui_byteconverterdialog.h"
intmain(intargc, char
*
argv[])
{
QApplication app(argc, argv);
QDialog dlg;
Ui::ByteConverterDialog ui;
ui.setupUi(&dlg);
dlg.setAttribute(Qt::WA_QuitOnClose);
dlg.show();
returnapp.exec();
}
Since thewidgets of theDesigner-generated dialog areavailable as publiclyac-
cessible membersofthe UI class, theycanbefine-tunedinthe code later on by
callingthe methods of therespectivewidgets.Their signalsand slotscan partici-
pateinsignal/slot connections.Whether th eclass Ui::ByteConverterDialog is now
instantiated in themain() function or in theconstructor of aclass inheriting from
QDialog makesnodifference.
In ourexample, however,the approach showninthe listing abovecausesproblems:
We couldcon nect theQuitbutton’sclickedsignaltothe accept()slotofthe dialog,
andwewould then be able to connect theslots binChanged(), hexChanged(), and
binChanged()tothe textChanged() signalsofthe respectiveQTextEditwidgets.But

then wewould notbeable to accessthe pointer to anyuic-generated widgetin
theslotitself.
Forthisreason, theuse of directlycallingsetupUi()isverylimited:Ifwedoso,
weshall restrict ourselves to applyinginstances of theclass generated byuicto
instancesofastandardclass likeQWidgetorQDialog.However,insomesituations
this procedurecould be completelysufficient, for example, in simple modal input
dialogs which arecalledwithexec(). Theexec callstartsaseparateeventloop
andreturnsonlyif accept(), reject (), or anothermethod closes thedialog. Sin ce
thedialogobject doesnot ceasetoexistwhenthe dialog hasbeen closed,the
subsequent code can fetchthe valuesofthe widgets placed inside thedialogby
setupUi()without an ydanger, so that you can getbywithout theQDialog subclass
in thosecases.
It is important that you alwayscallthe setupUi()method of an instance of a
Designer-generated classfirst, beforetryingtoaccess member variablesofthe in-
terfaceobject (inthe currentexample, thoseofui).Otherwise, theprogram will
mess around withuninitialized pointersand crash.
An argument for notinstanciating Designer-generated classesdir ectlyresultsfrom
thefact that public-membervariblesare accessible from theoutside,causing a
93
3 GUI Design Usingthe Qt Designer
violation of secrecy,one of themostimportant principles of object-oriented pro-
gramming.Secrecyenforcesabstractionbyonlygranting theclass membersaccess
to theirownmethods.
Theinternaldetails of theclass arethus “cut off” from theother classes, andyou
can change theinternaldesignofthe classwithout havingtoadjustthe rest of the
program that uses this class. As long as you useonlytheUIclass as ashort-term
setupclass ,the infringement of theencapsulationprinciple is notreallyof any
consequence.
3.2.2AlwaysHavingDesigner-generatedWidgets Available
In ordertodealwiththe shortcomingjustdemonstrated,itisagood idea to include

theclass generated byuicasamember variable.Todothis, wefirstinheritfrom
thedesired class, which in ourcaseisQDialog.
Themain() function matchesthe onefromChapter 2, sinceByteConverterDialog
from itspoint of viewis againa“black box.”
Thecrucial differenceisinthe declaration of theclass .Wedeclare theclass gen-
erated byuicasaprivatememberofaQDialog subclass .Thisallowsfor abitrary
accesstothe widgets withinthe Designer-generated classvia this newlycreated ui
member variable of theByteConverterDialog classinherited from QWidget:
// member/byteconverterdialog.h

#include <QDialog>
#include "ui_byteconverterdialog.h"
class QLineEdit;
class ByteConverterDialog :public QDialog
{

private:
Ui::ByteConverterDialog ui;
} ;
Theconstructor an dall slotsnowaccessthe generated classvia theuimember
variable:
// member/byteconverterdialog.cpp

ByteConverterDialog::ByteConverterDialog(QWidget
*
parent)
:QDialog(parent)
94
3.2 IntegratingDesigner-generatedFiles into Your Qt Project
{

ui.setupUi(this);
connect(ui.decEdit,SIGNAL(textChanged(const QString&)),
this,SLOT(decChanged(const QString&)));
connect(ui.hexEdit,SIGNAL(textChanged(const QString&)),
this,SLOT(hexChanged(const QString&)));
connect(ui.binEdit,SIGNAL(textChanged(const QString&)),
this,SLOT(binChanged(const QString&)));
}
void ByteConverterDialog::decChanged(const QString&newValue)
{
bool ok;
intnum=newValue.toInt(&ok);
if (ok) {
ui.hexEdit->setText(QString::number(num, 16));
ui.binEdit->setText(QString::number(num, 2));
} else {
ui.hexEdit->setText("");
ui.binEdit->setText("");
}
}

Theoverlyingprinciple also applieshere: It is essentialthatsetupUi()iscalledfirst
beforewecan usethe UI classinanywayat all. Thedisadvantage of this method
is itsindirectness, via themembervariable.But theadvantage of this approach
is that it defusesthe encapsulation problem, limitingthe problemtoscope of the
dialog class. Anysinceaccess from outsideofthe dialog is notpossible under any
circumstances.Afurtherbonus:Itisclear from thecode which widgets weregen-
erated in theDesigner. In addition,thisapproachisparticularlysuited for widgets
in librariesthathavetoremainbinary-compatible,because onlythepointer to the
instance of thegenerated classchanges thebinarylayoutinthe compileroutput.

7
3.2.3MultipleInheritance
As the idealsolution, Trolltechrecommendsmultipleinheritance. Butlikethe pre-
vious solution,thisworks onlyif you plan yourownsubclass.
In this method, thenewwidgetinherits notonlyfrom QWidget, butalsofromthe
UI classgen erated byuic. Aparticularhighlight is theuse of theprivatekeyword
in theinheritanceinstruction.Thisensures that allmethods from theUIclass
7
More details of binarycompatibilityin C++havebeen compiledbytheKDE projectat
/>95
3 GUI Design Usingthe Qt Designer
aregiven th estatusofprivateclass variablesinthe newclass, although theyare
actuallypubliclyaccessible in theformerclass itself:
// inherit/byteconverterdialog.h

class ByteConverterDialog :public QDialog,
privateUi::ByteConverterDialog

This method thus solves several problems at onestroke: We can usethe widget
pointersgenerated byuicasstandardmembervariables, without going thelong
wayround,via ahelpobject,and theyremain private, so that encapsulation to the
outsideismaintained.
Forour example, this meansthatthe constructorchanges as follows:
// inherit/byteconverterdialog.cpp

ByteConverterDialog::ByteConverterDialog(QWidget
*
parent)
:QDialog(parent)
{

setupUi(this);
connect(decEdit,SIGNAL(textChanged(const QString&)),
this,SLOT(decChanged(const QString&)));
connect(hexEdit,SIGNAL(textChanged(const QString&)),
this,SLOT(hexChanged(const QString&)));
connect(binEdit,SIGNAL(textChanged(const QString&)),
this,SLOT(binChanged(const QString&)));
}
void ByteConverterDialog::decChanged(const QString&newValue)
{
bool ok;
intnum=newValue.toInt(&ok);
if (ok) {
hexEdit->setText(QString::number(num, 16));
binEdit->setText(QString::number(num, 2));
} else {
hexEdit->setText("");
binEdit->setText("");
}
}

As before, weonlyneed to callthe setu pUi()method in first position, an dasthe
argument weagainuse apointer to thewidgetthatisour currentclass scope.
96
3.3 AutomaticSignal/SlotConnections
Caution: In this approach theinheritancesequenceisimportant.First theclass
mustinheritfromQDialog,and then from theDesignerclass.Ifthisisnot thecase,
thecom pilerwillthrowan errorthatisdifficulttounderstand, andwhich quickly
brings theprogrammertodespair:
moc_byteconverterdialog.cpp:43:error:‘staticMetaObject’ isnota

memberof type‘Ui::ByteConverterDialog’
moc_byteconverterdialog.cpp:Inmemberfunction ‘virtualvoid
*
ByteConverterDialog::qt_metacast(const char
*
)’:
moc_byteconverterdialog.cpp:60:error:’class Ui::ByteConverterDialog’
hasno membernamed ’qt_metacast’
moc_byteconverterdialog.cpp:Inmemberfunction ‘virtualint
ByteConverterDialog::qt_metacall(QMetaObject::Call, int,void
**
)’:
moc_byteconverterdialog.cpp:66:error:’class Ui::ByteConverterDialog’
hasno membernamed ’qt_metacall’
make:
***
[moc_byteconverterdialog.o]Error1
Thereasonisthe behaviorofthe meta-object compiler, which checks onlyin the
first parentclass of theinheritancelistwhether this inherits from QObject or not.
This also meansthatitisgenerallynotpossible to inheritfromseveral classesthat
allhaveQObject as abaseclass.
3.3Automatic Signal/SlotConnections
Developers versedinVisualBasic or Delphi who startonQt/C++developmentfind
thesignal/slot conceptunusual, andtheymiss theeventhandler.Qt4allowsthem
to sticktothe semanticstheyareusedto, permitting slot declarationsofthe form
void on
objectname signalname();
that areconverted into connect() instructions that uicsaves in setupUi(). Inciden-
tally,thisnamingconvention increasesthe readabilityof thesourcetext.
Thewhole point of this functionalityis thestaticQMetaObject::connectSlotsBy

Name() method: It expectsapointer to aQObject andsearchesthrough it for
slotswithmatchingnames.ThenQMetaObject::connectSlotsByName() connects
thefound slotswiththe appropriate signal.Todothisitusesinformation from the
meta-object generated bythemeta-object compiler, moc. This meta-object adds
thecapabilityknowninC++ as introspection (alsoknowninJavaas reflectio n )to
allclassesinheriting from QObject.Atruntimethe classtherefore“knows” itsmeth-
ods,signals,and slots. connectSlotsByName() recursivelylooksatthe sl ot namesof
theobject behind thepointersand allits children, connectingthe respectivesignals
to them.
Trolltechrecommendsthe semanticsshownaboveonlywiththe Designer-genera-
ted classes, sinceinthiscasethe object name andthe name of theuic-generated
97
3 GUI Design Usingthe Qt Designer
pointer to thewidgetmatch,and because th esetupUi()method subsequentlycalls
connectSlotsByName(). Butfor thosewho findthisconsistentnamingpatternirre-
sistible,all therelevantobjectsmustbeassigned anamevia setObjectName(), must
be calledinthe constructororfromoutside QMetaObject::connectSlotsByName(),
andmustpassapointer to thecurrent class(this)tothiscall.
Because theshownsemantics areverypronetoerrors,
8
you should useautomatic
connectiononlywithDesigner-generated widgets withmultipleinheritance.
We wil lmodifyourexamples from abovesothatthe slot namesfollowthecon-
ventionsfor automaticconnection. At thesametimethe connect() calls in the
constructorcease to apply,sothatonlythesetupUi()instruction is left:
// autoconnect/byteconverterdialog.h

privateslots:
void on_decEdit_textChanged(const QString&);
void on_hexEdit_textChanged(const QString&);

void on_binEdit_textChanged(const QString&);

// autoconnect/byteconverterdialog.cpp

ByteConverterDialog::ByteConverterDialog(QWidget
*
parent)
:QDialog(parent)
{
setupUi(this);
}
void ByteConverterDialog::on_decEdit_textChanged(const QString&newValue)
{
bool ok;
intnum=newValue.toInt(&ok);
if (ok) {
hexEdit->setText(QString::number(num, 16));
binEdit->setText(QString::number(num, 2));
} else {
hexEdit->setText("");
binEdit->setText("");
}
}

8
Remember that onlytheobject name is relevantand that in this procedure, Qt cannotissue
warningsabout connections th at fail at runtime.
98
3.4 IncludingDerived Classesinthe Designer
3.4Including DerivedClasses in theDesigner

It is sometimesnecessaryto make minormodificationstoaQt standardwidget. In
such cases you can no longer usethe Designer without registeringthe newwidget
thereasaso-called custom widget ,which involves afairamount of work.
9
To still be able to usesuchawidgetinthe Designer,you select itsQtbasewidget
in theDesignerand clickitwiththe rightmouse buttonafter it hasbeen adjusted.
From thecon textmenu, you nowselect theentryPromotetoCustomWidget.
In thedialogthatappears(seeFigure3.13),you specifythenameofthe newclass
andthatofits headerfile.Althoughthe Designer continuestoshowtheoriginalQt
widget, thefinished program uses themodifiedwidget; so in theimplementation
you obtain apointer to an object of thetype of theinherited widget.
Figure 3.13:
Usinginherited
classesinthe
Designer is very
simple, thanks to
widgetpromotion.It
is oftenall youneed.
To undosuchapromotion, theentryDemote to baseclass canbefound at the
same positioninthe contextmenu.
Formorecomplexmodifications, such as fundamentalchanges to thelayoutbe-
haviororadding properties, this procedureisnot suitable,however,since theDe-
signer doesnot take th em into account.
3.5The Resource Editor
From Qt 4.1on, theDesignersupports thesettingupand administrationofthe
resourcesalreadydiscussedonpage 57.The editor integrated in this (Figure3.14)
can be calledfromthe entryTools → Resource Editor,incaseitisnot already
visible. Navigatinginittakessomegettingusedto, however.The drop-downbox
nexttothe NewandOpenentries showsalistofalreadyopenedresourcefiles. It
doesnot includeasaveaction, as this is performedimplicitlybytheeditor.

9
Notesonthisare provided in theonlinedocumentationfor Qt.
99
3 GUI Design Usingthe Qt Designer
Figure 3.14:
Theresources
example from page
57 in theResource
Editor of theDesigner
In addition,the listofresources displayed in theDesignerisindependent of those
in the.profile.Thisiswhyit is important to ensure that allthe resourcesreallyare
entered thereunder thekeyname RESOURCES. Bysubsequentlyrunning qmake,
theresources become apartofthe project.
To assign an image from aresourcetoaQLabel in theDesigner, for example, you
first search in thePropertyEditorfor thepixmappropertyandclickthere on the
foldericon. In thefollowingdialogyou reach theResourceEditorbyselecting
Specifyaresource, where you can chooseone of theimages. To displaythedesir ed
graphicsinthe currentwidgetsize, thescaledContents propertyin theProperty
Editormustbeset to true;oth erwiseitwill remain in theoriginalsizeofthe image.
100
4
Chapter
Developing aGUI Application
BasedonaMainWindow
In thefollowingsection wewilldevelop an applicationstep bystep,one which
displaysall thetypical features of agenuine graphical applicat ionand which also
performs ausefultask: asmall texteditorcalled CuteEdit.
We design its main window usingthe Designer,which allowsthe basicgraphical
frameworkofmostapplications to be puttogether “bymouseclick” in Qt versions
4.1and later.The basisofthisisthe QMainWindowQt class.

4.1The Anatomy of theMainWindow
TheQMainWindowclassforms thebasis of an applicationwindow:Menubar,sta-
tusbar,toolbars, anddockwindowscan be broughtintothismainwindow.Figure
4.1showsthe individualcomponents.The centralwidget provides theworkspace
for theuser.
101
4 Developing aGUI Application Ba sedonaMainWindow
Figure 4.1:
Anatomyofamain
window
Abaremainwindow,asshowninFigure4.2,initiallyconsists onlyof thecentral
widgetand frame, plus atitle bar.
1
In ordertoconjure this minimalarrangement onto thescreen,nothing more than
asimpleprogram that instantiates aQMainWindowobject andsets alabel an-
nouncingitasthe centralwidgetisrequired. So that theletteringisdisplayed with
centered alignment bythelabel,weuse the<center>tag: QLabel interprets certain
HTML tags as markup,rat herthanastext:
// mainwindow/main.cpp
#include <QApplication>
#include <QMainWindow>
#include <QLabel>
intmain(intargc, char
*
argv[])
{
QApplication a(argc, argv);
QMainWindowmainWindow;
QLabel
*

label =newQLabel("<center>CentralWidget</center>");
mainWindow.setCentralWidget(label);
mainWindow.show();
returna.exec();
}
This exam pleisthereforedifferent from theone introducedinChapter 1.1(page
25),inparticularbecause wedisplayalabel withinaQMainWindowinstance.The
result is showninFigure4.2.
1
UnderX11, thereare afewwindowmanagers that do notshowanydecoration around the
window.
102
4.2 Derivingfrom QMainWindow
Since thelabel is no longer thetop-level widget, it is vital that it is created on the
heap, withnew.Otherwise, theprogram maytryto delete it twiceafter themain()
function hasended:First thecomputer would removethe labelfromthe stack,
andonlythen removethe main window,which in turn would also liketodelete
thelabel,which it toohas adopted as achild throughsetCentralWidget(). Unde r
certaincircumstances this can cause theprogram to crashafter it hasrun normally.
Figure 4.2:
OurMainWindow
example
program—without
menubar,statusbar,
toolbar,and dock
window
4.2Derivingfrom QMainWindow
Moreserious applications usuallyinheritfromQMainWindow,adding features that
provide more control. In contrast to theaboveexample, weshall deriveaseparate
classcalledMainWindowfrom QMainWindow,onthe basisofwhich weshall con-

struct CuteEdit. At thesametimewewillget to knowotheressentialwidgets,such
as QTextEdit, aflexible editor widget.
// cuteedit1/main.cpp
#include <QApplication>
#include "mainwindow.h"
intmain(intargc, char
*
argv[])
{
QApplication a(argc, argv);
MainWindowmainWindow;
mainWindow.show();
returna.exec();
}
103
4 Developing aGUI Application Ba sedonaMainWindow
Themain() function is almost identical to theone from our“Hello,world!” pro-
gram from Sect ion1.1.Insteadofthe QMainWindowclassfromthe mainwin-
dowexampleonpage 102, wenowuseour ownMainWindowclass, derived from
QMainWindow.The corresponding classdefinition can be found in theheaderfile
mainwindow.h.
2
The#includedirectivewhich incorporates thecontents of this
headerfile uses quotationmarks insteadofangle brackets,since thefile is nota
standardheaderfile.
We againsurround thefile contents of mainwindow.h with an #ifdefconstruction
providingthe includeguards to avoidcompilation errors if this headerfile is in-
cluded bymore than onesourcefile.
3
MAINWINDOW_Hwillbedefinedwhenthe

headerfile is processedfor thefirsttime, andthe preproce ssorignores theentire
filecontents for allsubsequentinclusion attempts:
// cuteedit1/mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow:public QMainWindow
{
Q_OBJECT
public:
MainWindow();
} ;
#endif // MAINWINDOW_H
Since theMainWindowclassisderived from QMainWindow,wefirstissueadi-
rectivetoinclude theheaderfile for theQMainWindowclass. To ensure that the
QMainWindowmethods remain accessible even outsidethe MainWindowclass, we
grantthe derivationpublic access.
Because ournewclassalsoinherits from QObject as abaseclass,wemustnot forget
theQ_OBJECT macro. Otherwisethe linkerwillcomplainofundefinedsymbols,
which,inthe caseofself-defined signals, resultsinanerror message.Inthe case
of aTool class, which defines asignalcalledswitchTool(Tool*),thiswillappear as
follows:
tool.o: Infunction ‘Tool::activateTool(bool)’:
tool.cpp:(.text+0x5f):undefined reference to‘Tool::switchTool(Tool
*
)’
collect2:ldreturned status 1
2
Forheader files that wecreateourselves,weuse thefilename extensioncommoninC/C++, .h,
to make clear thefile type.Wedonot useuppercaseinanyfilenames.

3
Seepage62.
104
4.2 Derivingfrom QMainWindow
In theMainWindowclassitself, weonlyhavetodefine theconstructor.For this
reason,the source textfile mainwindow.cpp is also rather short:
// cuteedit1/mainwindow.cpp
#include "mainwindow.h"
#include <QLabel>
MainWindow::MainWindow()
{
setWindowTitle(tr("CuteEdit"));
resize(600,400);
QLabel
*
label =newQLabel(tr("CentralWidget"));
setCentralWidget(label);
label->setAlignment(Qt::AlignCenter);
}
In theconstructor wethe first callthe QWidgetfunction setWindowTitle(). Since
theMainWindowclassisderived from QWidgetasthe base class, it inherits th is
function,and wecan useittoset thetextdisplayed bythetitle barofthe window.
If you leavethisstep out, Qt uses theprogram name as thetitle text.
We setthe textfor thetitle barvia thetr()meth od, which inherits MainWindow
inherits from QObject.Ifthe user wants,thiswilltranslate thetexttoanother
language at runtime; if not, it retu rnsthe string unchanged.
4
Theresize()function th at MainWindowalso inherits from QWidgetspecifiesthe
sizeofthe window.The twoargumentsdetermine thewidth andheightofthe
windowin pixels. If thesizeisnot setexplicitly,Qtwilldetermine it automatically,

basedonthe contenttobedisplayed.But in ourcasethiswould be toosmall, since
wewillsoon fillthe windowwithmorecontent.
In ordertodisplaysomethinginthe main window,wecreateaQLabel object with
thecentral widgettextand make it thefocal point of theapplicationwiththe set-
CentralWidget()function,which MainWindowinherits from QMainWindow.With
this callthe MainWindowobject adoptsthe newQLabel.Accordinglywemustal-
locate it on theheapwithnew,fromwhich it will ultimatelybe deleted bythe
memorymanagement provided byQt for instancesofQObject.
ThesetCentralWidget()callpacksthe QLabel object into alayoutsothatitfills the
entire space in thewindow.Bydefaultthe QLabel classarrangestextcentered ver-
tically,and horizontallyalignedatthe left margin.Tocenter textinbothdirections,
wechangethe alignment withsetAlignment(). This function takesasanargument
valuesfromthe enumeration type (enum) alignment,which is defined in theQt
namespace
5
—hence thevalue AlignCenter is prefixed withQt::.
4
Seealsopage49and Chapter14frompage375 foradetaileddiscussion.
5
Qt uses thenamespace Qt foralargenumber of enumeration types, in ordertoavoidconflicts
whenthe same symbolic namesare used in several contexts.
105
4 Developing aGUI Application Ba sedonaMainWindow
So that qmake can unite theexisting files into aproject,weuse thefollowing.pro
file:
#cuteedit1/cuteedit1.pro
TEMPLATE =app
SOURCES =main.cpp mainwindow.cpp
HEADERS =mainwindow.h
FORMS =mainwindow.ui

Apartfromthe alreadyknownvariablesTEMPLATEand SOURCES, which weuse to
specifythat weare compilinganapplicationand to specifythesourcetextfiles,
theHEADERS variable is also used.Thisspecifiesthe headerfilestobeusedinthe
project: qmakesearchesthrough thoseheaderfilesfor theQ_OBJECT macroand
creates appropriate rulesfor themoc calls.
4.3CreatingaMain Window withthe Qt Designer
Ever sinceQt4.1,the Qt Designerhas enabledthe user to design main windows
as wellasdialogs.Whenusedfor this purpose, allthe descriptions from Chapter 3
apply.Inparticular, just as explainedthere,the user interfacecompileruic creates
aclass from the.ui filegenerated bytheDesigner; thesetupUi()method then
“decorates”amain windowto acertain extent.
After theDesignerhas started,weselectthe Main Windowitem from thetemplate
menu. When designingour editor window,weborrowideasfromthe designsof
othereditors. Thecentral widgetwillbeawidgetthatenablestexttobedisplayed
andedited.Qtprovides aclass calledQTextEditfor this purpose.
Accordinglywepullanemptytexteditelement from theinput widgetcategory
to themiddleofour newmain window,and then clickthe griddedwindowback-
ground.
We nowselect alayoutstrategy,eitherfromthe Contextmenuorfromthe Form
menu. It is completelyirrelevantwhich onewechoose. The9-pixel-wide margin
that is created,which makesavailable thenecessaryspace in dialog widgets,isout
of placeinthe main window,however.Toremoveit, weselectthe centralwidget
entryin theobject inspectorwindowandenter amarginvalue of 0to itslayout.
Forthe texteditoritselfitisrecommended that thefonttype be changedtoa
monospacedfont, usingthe PropertyEditor. To do this weopenthe fontentryin
thePropertyEditorand select theCourier fonttype,for example. In addition weset
thelineWrapMode mode to NoWrap, sincelinewraps areseldom wanted in editors.
If you do wantthem, an actionisfeasible that would switch on thelineWrapMode
property.
106

4.3 CreatingaMain Window with theQtDesigner
In addition weequip theeditorwithamenu barfromwhich theprogram functions
can be controlled. To guarantee rapidaccess to themostimportant functions, such
as loading andsaving, wealsoinsertatoolbarbeneath th is containing an icon for
each of thesecommonlyinvoked actions.Astatus barprovides space fordisplay
of permanentand/or contextual information,suchasthe currentpositionofthe
cursor or thepurposeofthe currentmenuentry.
4.3.1Adding MenuBars
Firstwewilllook at themen ubar.Weprovideitwiththe standardentries that we
areaccustomed to from standardapplicat ions:the File menu, which takescareof
thefile to be edited,the Editmenu, which controls manipulation of thetext, anda
Help menu.
To do this,weselectthe Type here entryin thealreadyexisting menu barand
create thethree entries. When doing this weshouldremembertoplace an amper-
sand (&)beforeeach entry,sothatthe




Alt keywill callupthe respectivemenuin
combinationwitha




F ,





E ,or




H key.
The&instructsthe menu to defineawindow-wideshortcut(called accelerator ),
which,incombinationwiththe




Alt key,jumpstothe corresponding menu item.
It is appropriate to take thefirstletter of amenuentry,but thesameletter may
notbeusedtwice, so you mayneed to useanother letter for theshortcutwhen
twomenuentries beginwiththe same letter.The letter in theentrychosen as the
shortcut charactershouldbeasintuitiveaspossible.
Figure 4.3:
Nowour editor has
an inputwindow and
amenu bar .
Accelerators likethisallowexperiencedusers to operate theapplicationwiththe
keyboard, which can often be muchquickerthanusing themouse andcan improve
107
4 Developing aGUI Application Ba sedonaMainWindow
theuserfriendlinessofthe software. Theyshould thereforebeprovided as amatter
of course in thedesignofuserinterfaces.Withthe acceleratorsinplace,the design
viewof th eeditorshouldcorrespondtothatshowninFigure4.3.
To definethe subitemsofanindividualmenuentry,weselectthe entryin themenu
bar. Adrop-downmen uthenappearsand,for each desiredsubitem,weselectType

here andenter itsname.
Let’sstart in theFile menu, to which wewillassign thesubentriesNew,Open. ,
Save, Saveas , andQuit. We recommend that you add aseparator beforethe
Quit entry,sothatthisspecial action hasvisual distance from theother entries.
Thereisareason behind thefact that onlysome entriesend withdots( )—these
denote entriesthatrequire furtheruserinteractionthrough adialog.
In thesamewayweequip theEditmenuwiththe entriesUndo, Repeat,and after
aseparator,Cut,Copy,and Paste. TheHelpmenugets bywiththe Info. item,the
implementation of which wewilldealwithonpage 117.
4.3.2RecyclingAct ions in theToolbar
If you wanttomake themostimportant entriesinthe menu bareasilyaccessible
for mouseusers, this raises thefollowingquestion: Is it possi ble to recyclethe en-
triesfromthe menu entries? Luckilytheanswer is yes,since Qt encapsulates menu
andtoolbarentries in so-called actions ,for which th eQAction classisresponsible.
When wecreated theentries in themen us of themenubar,the Designer created a
separateactionfor each entry,which wewillnowreuse. An overviewof allexisting
actions is provided bythe Action Editor.Ifitisnot alreadydisplayed as shownin
Figure 4.4, you can make it visible withAct ions→ Action Editor.
Figure 4.4:
The Action Editor lists
allavailableactions
that canbeadjusted
in thePropertyEditor
likewidgets.
At the moment, no iconsare assigned to theactions listed in it,inwhich casethe
full textisdisplayed insteadofanicon, taking up significantlymore space.Icons
108
4.3 CreatingaMain Window with theQtDesigner
can also be of greathelpherebecause thehumanbrain can rerecognizethem
more easily,since it can applyasimplepatternmatchinginsteadofhavingtoparse

atextual description.
In generalthere aretwowaysofrectifyingthe lack of icons. Firstweselectthe
appropriate actioninthe Action Editor. Itspropertiesnowappearinthe Property
Editor. We areinterested in theIconproperty,and weselectthe Open icon in the
value column. Thedialognowallowsustochoosewhether wewanttosearch
for an icon from aresource(seepage 99)oruse an image filedirectlyfrom the
filesystem.
Forour example, wecopytheitems from theCrystal IconsseriesasusedbyKDE 3
andcombinethemintoaresource,using theResourceEditorinthe Designer.For
each action wecan nowselect amatchingicon. We savethe resource fileinthe
same directoryas the.ui file.
Figure 4.5:
Thetoolbar provides
quickaccess to
important actions.
To add anewtoolbar, wemovethe mousecursortothe status baratthe bottom
of thewindowandselectAdd Tool Bar from thecontextmenu. We nowdrag the
actions New,Open, andSavefromthe Action Editorintothe barthatnowappears.
Cut, copy,and paste arealsofrequentlyused actions in editors. If you wantto
includetheminthe same toolbar, as is thecaseinFigure4.5,you should separate
them from theother entriesinthe File menu withaseparator (right mousebutton
→ Insert Separator).
Actionshaveother propertiesthatthe PropertyEditorallowstobeset. These
includethe application-wideshortcut(theso-called shortcut). In contrast to ac-
celerators, shortcutsare activat ed withthe




Ctrl key.The user can often gettothe

actionhewants more quicklywithshortcuts than withacce lerators.
109
4 Developing aGUI Application Ba sedonaMainWindow
This becomesclear in th eexampleofthe Open fileaction:




Ctrl +




O is quicker
to type than




Alt +




F ,followed by




Alt +





O .For thesake of clarity,you should
notuse an excess of shortcuts, butexperiencedusers highlyvalue shortcutsfor
frequentlyused operations. TheQtdocumentationprovides an overviewof the
standardshortcuts for programsinEnglish.
6
It is interesting that theentryfor ashortcutinthe Designer is astring. Thereisno
syntaxcheckwhenthisisdone, so you should alwayscheck theentries yourself.
TheformatisCtrl+key.
ThereasonQtinterprets shortcutsasstrings is duetointernationalization: The
code generated bytheDesignerand theuserinterfacecompilerpassesthe string
to thelocalizationroutine tr(), so that theshortcu tcan be customized.Thisisa
useful feature, sinceabbreviationsthatnoone can remember (onesheldover from
an implementation in anotherlanguage,for example) arejustnot used,whereas
manyuserswillrememberthemifthe abbreviationsare mnemonics for theaction
to be triggered.
Anotherfeature of theQAction classisthe tooltip. Tooltips,which theapplication
displaystothe user as a“paleyellownote” if themouse cursor is held over amenu
or toolbarentry,are setinthe code usingsetToolTip().
Thetextset via thestatusTextpropertyshowsupinthe status bar(if thecurrent
windowhasone)asthe mousehoversover therespectionact ion. Finallythewhat-
sThis propertyallowslongerhelptexts on individualwidgetparts to be displayed.
4.3.3Integrating theMainWindow with Your Source Code
It is nowtime to turn theGUI generated in this wayinto aprogram.Wecan save
ourselves abit of workdoing this anduse thefile main.cpp from theexampleon
page 103:
// cuteedit2/main.cpp

#include <QApplication>
#include "mainwindow.h"
intmain(intargc, char
*
argv[])
{
QApplication a(argc, argv);
MainWindowmainWindow;
mainWindow.show();
returna.exec();
}
6
See />110
4.3 CreatingaMain Window with theQtDesigner
In theimplementation, wenowmake amultiplederivationfromboththe QMain-
Windowclassand from thehelperclass Ui::MainWindowclassgenerated from the
uic; thelatter is aprivatederivationsothat—as alreadydescribedinChapter 3—the
objectsgenerated in theDesignerare made available as member variablesfor th e
newMainWindowclasswiththe correctvisibility:
// cuteedit2/mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "ui_mainwindow.h"
class MainWindow:public QMainWindow,
privateUi::MainWindow
{
Q_OBJECT
public:
MainWindow(QWidget

*
parent=0);
˜MainWindow();
protected:
void setupActions();

Beforediscussing therestofthe declaration on page 112, wewillfirstturntothe
implementation of theconstructor.Itisimportant that wecorrectlyinitializethe
parentclass.C++ doesguarantee theautomatic initializationofQMainWindow,so
thechain of inheritancehas notbeen interrupted.The parentobject is no longer
passedonwhenthisisdone, however,which can lead to memoryleaks andprob-
lems whenusing layouts.Details areexplainedinSection 1.2.2onpage 31.
Thefirstthing theconstructor itself contains is thesetupUi()call, which guarantees
theinitializationofall member variablesfromUi::MainWindow:
#include <QtGui>
#include "mainwindow.h"
MainWindow::MainWindow(QWidget
*
parent)
:QMainWindow(parent)
{
setupUi(this);
setupActions();
}
111
4 Developing aGUI Application Ba sedonaMainWindow
Linking ActionstoFunctionality
Thenextstep is to linkanumber of actionsmanuallyto slotsand providethem
withfunctionality.Toachievebetter clarity,wewillmovethistasktoaseparate
method calledsetupActions().

Here wewillbreathe abit of lifeintothe actions throughasignal/slotconnection.
If theusersets off an action,suchasclicking themenuentry,thiswillsendout the
triggered(bool)signal. Theparameter doesnot interestus, sinceitisonlyrelevant
for alternating (“toggled”) or groupedactions.Wemustinclude it neverthelessso
that connect() can findthe signal:
// cuteedit2/mainwindow.cpp
void MainWindow::setupActions()
{
connect(action_quit,SIGNAL(triggered(bool)),
qApp,SLOT(quit()));
connect(action_open, SIGNAL(triggered(bool)),
this,SLOT(loadFile()));
connect(action_save, SIGNAL(triggered(bool)),
this,SLOT(saveFile()));
connect(action_saveas,SIGNAL(triggered(bool)),
this,SLOT(saveFileAs()));
connect(textEdit,SIGNAL(copyAvailable(bool)),
action_copy,SLOT(setEnabled(bool)));
connect(textEdit,SIGNAL(undoAvailable(bool)),
action_undo, SLOT(setEnabled(bool)));
connect(textEdit,SIGNAL(redoAvailable(bool)),
action_redo, SLOT(setEnabled(bool)));
connect(action_copy,SIGNAL(triggered(bool)),
this,SLOT(copy()));
connect(action_undo, SIGNAL(triggered(bool)),
this,SLOT(undo()));
connect(action_redo, SIGNAL(triggered(bool)),
this,SLOT(redo()));
connect(action_about,SIGNAL(triggered(bool)),
this,SLOT(about()));

}
We linkthe quit actionwiththe quit() signal of theQApplicationobject,which is
accessible from theentireapplicationvia theglobalpointer qApp. This causesthe
applicationtoleavethe eventloop andterminate itself.
In orderfor othercon nections to work, westill need to declareanumber of slotsin
mainwindow.h,the contents of which arediscussedonthe followingpages. Since
weonlyusetheminthe MainWindowclassitself, wedeclare them as protected
methods:
112
4.3 CreatingaMain Window with theQtDesigner
// cuteedit2/mainwindow.h(continued)

protected:
bool mayDiscardDocument();
void saveFile(const QString&);
protected slots:
void newFile();
void loadFile();
void saveFile();
void saveFileAs();
void undo();
void redo();
void copy();
void about();
private:
QString mFilePath;
} ;
#endif // MAINWINDOW_H
Thevariable mFilePath specifies thepathtothe currentfile.Ifthe document has
notbeen saved until now,thisstringisempty.

Since wedonot need to destruct anythingmanually,the destructor remainsempty.
AllWidgetdestruction is takencareofbytheQObject hierarchywhenthe Main-
Windowinstance is destructed at theend of main().
OpeningFiles
Thefirstfunction that CuteEditshouldmaster is theloading of afile,usuallycalled
a document in theterminologyof texteditors. To do this,wefirstrequire afile-
name,for which wequerytheuservia an object of theQFileDialog class.
It is normallycompletelysufficienttouse thestaticmethods of this class, which
merelyrequireapointer to theparentwidget, as wellasanoptionalwindowtitle
andafilter for various filetypes. We willuse thegetOpenFileName() static method,
which returnspreciselyonefilename as aQString.Amore detaileddescription of
various dialog typesisprovided in Chapter 6.
To openafile, Qt uses theQFile class, which allowsplatform-independentaccess
to files.Thisispartofthe Qt input/output conceptthatisexplainedinmoredetail
in Chapter 11 andoccurs in placeofthe FILE pointer familiarfromC.
Theopen() method, similartothe Cfunction fopen(), opens thefile,inthiscasein
read-onlymode:
// cuteedit2/mainwindow.cpp (continued)
void MainWindow::loadFile()
{
113

×