L2 Slides With Notes
-
Upload
maryjane-vlad -
Category
Documents
-
view
221 -
download
0
Transcript of L2 Slides With Notes
-
8/2/2019 L2 Slides With Notes
1/32
1
In this lecture we will try to cover the theoretical
aspects of the QObject class, which makes Qt whatQt is. Understanding this is key to the rest of thelectures.
The first half of the lecture will be devoted to theQObject class, meta-information and the possibilitiesgiven by these classes.
The second half of the lecture will be devoted tosignals and slots which is the single most importantconcept of Qt.
Recommended reading before the lecture:http://doc.trolltech.com/4.6/object.html
2
-
8/2/2019 L2 Slides With Notes
2/32
3
The QObject class is essential to Qt
It is the base class of most active classes in Qt,including all widgets (the image shows only a subsetof the classes derived from QObject, it also showsthat those subclasses are themselves inherited byother classes)
The QObject implements many of the features thatmakes Qt what it is, such as:
signalsslotspropertiessimplified memory management
4
The QObject is the base class of many of Qt's
classes, but there are some exceptions.
Graphics view items are not QObjects, as they havebeen designed to be lean (there can be tens ofthousands of them, so every byte is important).
QString, QList, QChar, all represent values, andcannot be QObjects because of that (continues)
-
8/2/2019 L2 Slides With Notes
3/32
5
So, why cannot QChar be a QObject. QObjects areindividuals!
This means that you cannot write obj1 = obj2, copy is not avalid operation.
Why?
QObjects have names, for instance addButton,
deleteButton, etc (from the first lecture's demo).How do you copy this? You don't want two addButtons?
QObjects are structured in hierarchies. How do you copythat context? Do you want them at the same level? Leaflevel? Root level?
QObjects can be interconnected (add calls a slot)
How do you copy this? Do you want to duplicateconnections?
6
QObjects carry meta-data, that is data about theobject itself.
This makes it possible to add introspection to Qt, forinstance to ask a class which methods it has
Every QObject has a meta object which can beretreived using the metaObject method.
The meta object knows about the class, its name,base class, properties, methods, slots and signals.
Continues
-
8/2/2019 L2 Slides With Notes
4/32
7
While providing C++ developers with meta data, Qtis still based 100% on C++. There is no otherlanguage involved.
Instead, moc, the meta object compiler, parses theC++ code and generates even more C++ code.
The figure shows an ordinary C++ build process,headers are included, sources compiled, object fileslinked and the end result is executable code. (Evenlibraries are, more or less, executable code, so thisholds true in all cases.)
Continues
8
So, Qt adds a new step to the build process. Mocparses class declarations and generates C++ codethat implements the specific meta object.
Notice that it is possible to have the moc work on asource file directly, and then include the result intothe same source file, but that is only useful in thespecial case of a QObject derived class only usedfrom within one single file.
All this is handled by QtCreator, so you do not needto worry about it. There are solutions taking care ofthis step for all other build environments as well(command line builds, Visual Studio, Eclipse, Xcode,etc)
Continues
-
8/2/2019 L2 Slides With Notes
5/32
9
What does moc look for in your class declarations?
First of all, you need to inherit QObject directly or indirectly.If you are inheriting multiple classes, your QObject(decendant) must appear first. You cannot inherit fromQObject twice (directly or indirectly).
You then need to put the Q_OBJECT macro in your classdeclaration, in the private section. By convention, and
historical limitations, this is usually placed first.
You can then add class info, and more, through specialmacros. For instance, in this case the key author is giventhe value John Doe.
Qt also adds some special keywords (just macros, from thecompiler's point of view) which will discuss later on.
10
Here, the slides will walk through the different features thatthe QObject class and meta data enable.
First example, the inherits keyword lets you check if a classinherits, i.e. is-a, class. A class inherits itself, so it can beused as a check when deciding how to cast. This showsthat meta data knows of the class hierarchy.
Another example is that the meta data contains informationabout your enums (have you passed the enum through theQ_ENUM macro). That way, you can convert enum valuesfrom and to text easily. This shows that the meta data hasdetailed knowledge about each class.
All this information makes it quite easy to integrate Qt withscripting languages and other dynamic environments(PyQt, Ruby, JavaScript, etc)
-
8/2/2019 L2 Slides With Notes
6/32
-
8/2/2019 L2 Slides With Notes
7/32
-
8/2/2019 L2 Slides With Notes
8/32
but relied upon for performance optimizations.
Continues
14 15
What does a property look like when using a class?
You can use it directly, simply call the getter andsetter.
You can use the property/setProperty calls. Theywork with QVariants (a type that contains all types),so you have to cast it to strings using toString, etc.
You can also ask the meta object about properties.Whether they can be read/written, their name, etc.This can then be used when callingproperty/setProperty.
Continues
-
8/2/2019 L2 Slides With Notes
9/32
16
Using the property/setProperty approach, you canalso create dynamic properties.
This makes it possible to tag objects. For instance,to add a required boolean property to certainQLineEdits. The line editors do not need to knowabout this, but they can still keep track of it as adynamic property. When validating the form, you cancheck this property and see if they are empty or not.
Continues
17
When adding custom properties, it is good to followthe standard template. This makes it more intuitive touse your code with Qt code.
The parts that you need are a getter and a setter. Inaddition, you need to store the state somewhere(private state).
Optionally, you can allow the initialization of common
properties (i.e. text of a label, but not window title ofit as it isn't a top level widget most of the time).
When all parts are there, you can use theQ_PROPERTY macro. From left to right: type, name,getter, setter.
Continues
-
8/2/2019 L2 Slides With Notes
10/32
18
When implementing the custom properties, yousimply implement the functions as always.
Ensure that the constructor calls QObject with theparent pointer.
In the getter, you could have returned a calculatedvalue. For instance, with width, as a part of the size(shown earlier).
In the setter, first update the state, then react to thechange. This way, you can use the getter throughoutthe class. This makes it easier to make changeslater on.
Continues
19
In the special case of using an enumerated type asthe type of a property, the Q_ENUMS macro can beused to inform Qt of the enumeration.
This allows you to convert enumerations to and fromstrings (as shown earlier).
For enums with bitwise values (i.e. that can becombined using OR), the Q_FLAGS macro can be
used instead.
-
8/2/2019 L2 Slides With Notes
11/32
20
QObjects can be used in a way that takes care of allthe dirty parts of memory management. It becomesalmost as easy as working with a garbage collector,but you are still in full control.
The trick is that all QObjects have a parent, or anowner. When the parent is deleted, it deletes all itschildren.
This reduces the number of objects that you need tokeep track of to one, in the ideal case.
Continues
21
The very same parent-child relationship is used torepresent visual hierarchies.
Refer to the tree structure, the box contains the radiobuttons (option1/2). The parent contains the box andthe button. Compare to the previous slide.
Continues
-
8/2/2019 L2 Slides With Notes
12/32
22
So, how does this make memory management easy.I still need to keep track of an object and make surethat I delete it?
No, not if you use the stack cleverly.
First of all, the example from the previous slidewould probably have been implemented in theparent's constructor, i.e. thisis the top-level parent.
Second, when using the dialog, you allocate it on thestack. This means that the dialog, along with all itschildren, will be deleted when the scope ends.
Continues
23
These slides intend to jog the students' memory, notexplain the stack vs heap decision in full.
The heap is used when you allocate memorydynamically. In C++, that means new/delete. In Cyou have used malloc and free.
Heap memory must be explicitly freed, i.e. you mustcall delete on everything that you allocate.
If you do not do so, you will leak memory. This will,eventually, lead to memory shortage and a crash.
Dynamically allocated objects live until you deletethem, so you have full control of when something isconstructed or destructed.
Continues
-
8/2/2019 L2 Slides With Notes
13/32
24
The stack is used for automatic memory allocations(as opposed to dynamic memory).
The stack grows and shrinks when you makefunction calls. It is used for local variables, functionarguments and return addresses.
Objects allocated on the stack are destructed whenthey go out of scope.
The scope ends with a }, or return, or for single-linescopes, at the end of the line.
Continues
25
To get almost automatic memory management usingQt, the trick is to allocate the outermost parents onthe stack, and the rest on the heap.
For instance, the main function scope will be valid foras long as the application is running, so we allocatethe application and window on the stack.
The window, in turn, creates a bunch of child widgets
in its constructor. To avoid destruction when thescope of the constructor ends, they are allocateddynamically. But, they are given the window as theirparent object and are thus also destructed when themain function scope ends.
-
8/2/2019 L2 Slides With Notes
14/32
26
QObjects are structured into parent-child-relationships, butthese hierarchies are not rigid. Instead, it is possible to move
items around.
To change a parent, simply call setParent with a new parentpointer as argument. This will notify both the old and newparent about the change.
As the parent is notified, you can simply delete items from listsand such to remove them.
If you want to move objects around, you can look for methodsthat takeitems. This means that the parent releases the child,and you get a pointer to the child. This can then be used forfurther operations (such as adding it to another parent).
Unsafe/safe examples: the first does not work if item zero doesnot exist, and you cannot check for it and them delete it in one
atomic operation. In the second alternative, item is null if itemzero does not exist, otherwise you have removed it from thelist and are free to work with it.
Continues
26
-
8/2/2019 L2 Slides With Notes
15/32
27
As QObjects all have parents, most QObject-relatedconstructors take a parent pointer.
Exceptions: QCoreApplication (QApplication) are QObjects w/oparents
The parent usually appears as the left-most argument with adefault value. However, as there can be multiple constructors,this can be compensated.
It is common to use the explicit keyword to avoid unwantedautomatic conversion of types.
Example: QPushButton, the parent ends up last in all c'torversions as there are no other default arguments
Example: QLabel, the window flags and parent have defaultvalues. This means that the parent is the first of them, but after
the text as the text property has no default value.
Continues
27
-
8/2/2019 L2 Slides With Notes
16/32
28
As these are recommended guidelines and makingexceptions is always an option. However, following
them, makes your code more Qt-esque.
Allow parent to be null (take that into consideration inyour code).
Have one constructor taking only parent (lets youuse your widgets from Designer). This forces you to
handle the case where all properties are set todefault values or not set at all.
Placing parent in the right place helps reuse.
Multiple constructors avoids the need to passdummy settings to unused properties.
29
-
8/2/2019 L2 Slides With Notes
17/32
30
One of the key factors of Qt is the signals and slotsmechanism.
It is a mechanism to dynamically and loosely tie togetherevents and changes with reactions
Dynamically = at run-timeLoosely = sender and receiver do not know each otherevents = timer event, clicks, etc. Not to be confused withactual events (QEvent).
state changes = size changed, text changed, valuechanged, etcreactions = source code that actually does something
This is what makes a Qt application tick
Continues
31
Looking at an example from the first lecture.
The three buttons all emit the clicked signal whenthey are clicked by the user (or activated by othermeans).
They do this regardless whether something isconnected to them or not, and regardless of what isconnected to them.
Continues
-
8/2/2019 L2 Slides With Notes
18/32
32
We choose to connect the buttons to the list widget'sclear slot, and two custom slots in our custom code.
As said earlier, the buttons do not care what they areconnected to, and the slots are equally independentof what triggers them. You can even call themprogramatically as ordinary functions.
Continues
33
So, the add button emits clicked, ending up in theon_addButton_clicked slot resulting in the following
code being run. It simply asks for input and thenadds it to the list.
The delete button emits clicked, ending up in theon_deleteButton_clicked slot, where all selecteditems are deleted.
The clear button emits clicked, which ends up in theclear slot of the QListWidget, which to us is a blackbox that does what its documentation says.
-
8/2/2019 L2 Slides With Notes
19/32
34
Signals and slots implement a pattern that is quite common.Usually, the mechanism is implemented as callback functions.
It is important to recognize that signals/slots not are callbacks.
For instance, a callback is simply a pointer. There is not actualcheck of signature compatibility. They also always work asdirect calls, making them tricky to use across threads, etc.
Signals and slots are more dynamic. For instance, themechanism is 100% generic, so any QObject can be
connected to any other QObject. The sender and receiver donot have to know of each others' implementations.
Signature compatibility checks provide safety, but alsoflexibility, as they allow you to ignore arguments.
All in all, signals and slots are easier, safer and more flexible.
(for the advanced: compare to functors, which are clumsierfrom an implementation point and less flexible when it comesto skipping arguments, etc).
Continues
34
-
8/2/2019 L2 Slides With Notes
20/32
35
A slot is an ordinary function, just that it can be connectedto signals. They do not have to be connected, you can call
a slot like any other function, and you implement it asusual.
Slots are declared in one of the sections public, protectedand private slots. These access restrictions work asintended when calling the function, but a private orprotected slot can be connected to any other signal, sothey can be triggered from outside the class.
Slots can return values, but connections cannot carryreturn arguments.
Any number of signals can be connected to a single slot.This means that a single slot can serve several sources ofevents think keyboard shortcut, button, etc.
Continues
36
Signals are defined in the signals section. This section canbe considered protected, as a signal can only be emitted
from within a class or its decendants.
Signals always return void, and must not be implemented.Instead, moc provides function bodies that trigger theactual slot-activation-code.
A signal can be connected to any number of slots, so asingle event can trigger multiple reactions.
It is fully possible to connect signals and slots acrossthreads. Third party libraries such as Qxt(http://doc.libqxt.org/0.5.0/classQxtRPCPeer.html).
Inside a signal emitting class, you use the emit keyword toemit a signal.
Continues
-
8/2/2019 L2 Slides With Notes
21/32
37
You can make signals to slots connections betweenany two QObjects.
Qt verifies that the signatures of the signal and slotmatch. The signature consists of the name of thesignal or slot followed by the argument types.
There must be no values nor variable names in thesignature.
It is also recommended to stick to using standardtypes, e.g. the ItemClass custom type reduces thereusability and should thus be avoided.
Continues
38
When matching signatures, Qt is very forgiving. Thebasic rule is that Qt cannot create or convert
values, but apart from that anything is allowed (i.e.skipping arguments).
The examples on the slide demonstrate this. Theerrors are (from the top):
missing the last int (cannot create) QString does not match int (cannot convert) missing the only int (cannot create)
Continues
-
8/2/2019 L2 Slides With Notes
22/32
39
When making connections from Designer to your ownsource, Qt uses the automatic connection mechanism.
It lets signals automatically connect to slots with thecorresponding name (structure and examples on the slide).
The automatic connections are made whenconnectSlotsByName is called. That is done at the end ofthe setupUi function generated by Designer.
When using this mechanism, think about reusability.Sometimes handwriting a couple of connect statementscan greatly improve readability of the code.
Continues
40
A common scenario for signals and slots is tosynchronize a pair of widgets.
This is implemented by interconnecting two objects.(Refer to the example). If the value of dial1 changes,it emits valueChanged, which changes the value ofdial2, that emits valueChanged, which changes thevalue of dial1, that emits...
To avoid an infinite loop (which results in endlessrecursion, which will make the stack grow until yourun out of memory and then crash miserably) thesetValue function ignores attempts to set the currentvalue.
-
8/2/2019 L2 Slides With Notes
23/32
41
Adding custom signals and slots to a class is really quiteeasy.
You can add slots for numerous reasons. For instance, foreach and every possible user action (file open, save, close,copy, cut, paste, help, about). Setter functions also makenatural slots.
Adding slots is just a matter of putting your functions in theright section of the declaration.
Adding signals is just as easy, simply declare them in thesignals section.
If you have properties, it is convention to inform Qt ofsignals using the Q_PROPERTY macro.
Continues
42
Implementing slots is just as implementing commonmethods. However, you must not forget the infinite
loop protection.
Emitting signals is as easy as calling emitsignalName(arguments).
When emitting signals, make sure to update theinternal state first, so that your object is updated
before it is queried.
-
8/2/2019 L2 Slides With Notes
24/32
43
Let's look at a real example that adds slightly morecomplexity to the situation.
We will use two dial LCD pairs and interconnectthem using our custom TempConverter class thatconverts between Celsius and Fahrenheit.
It does not only convert, it monitors and emitssignals when changes take place.
Continues
44
The dialog contains a TempConverter object and theuser interface.
The user interface consists of two halves one forCelsius and one for Fahrenheit. Each consisting of aQGroupBox.
The group boxes each contains a QDial and aQLCDNumber.
Continues
-
8/2/2019 L2 Slides With Notes
25/32
45
The TempConverter class declaration.
QObject, parent and Q_OBJECT macro are neededbefore we can add signals and slots.
The setters are slots.
There are signals for changes in either temperature.
To avoid infinite loops we must have a currenttemperature. In this example we have decided tokeep it in celsius.
As we use integers throughout, this will not be veryaccurate, from a temperature conversion point ofview.
Continues
46
Looking at the slot implementations, the set tempCelsius slot contains the recursion lock, as the
current temperature is kept in Celsius. It thenupdates the internal state and emits both signals.
Notice that the argument of the Fahrenheit signal isretrieved from the getter function, which does theactual conversion C to F.
The set Fahrenheit slot converts the temperature (Fto C) and then uses the set Celsius method.
Continues
-
8/2/2019 L2 Slides With Notes
26/32
47
The window making up the application then containsfour widgets (dial + LCD for Celsius and Fahrenheit)
and a TempConverter object.
The connections between the dials are set up to gothrough the temperature converter object, while theLCDs are directly driven by the dials.
Continues
48
The next slides will show how a signal propagatesthrough the system.
Everything starts with a user event the celsiusDialis moved.
This results in it emitting the valueChanged signal
Continues
-
8/2/2019 L2 Slides With Notes
27/32
49
The valueChanged signal is connected to display ofthe celsiusLcd and setTempCelsius of the
temperature converter.
The display call simply changes the value shown inthe LCD, while the setTempCelsius call changes thevalue of the temperature converter.
Continues
50
Changing the temperature of the temp converterresults in two signals being emitted:
tempCelsiusChanged and tempFahrenheitChanged
Continues
-
8/2/2019 L2 Slides With Notes
28/32
51
The tempCelsiusChanged signal is connected to thecelsiusDial's setValue slot. This slot detects that the
value of the dial isn't changed thus halting there.
The tempFahrenheitChanged signal connects tosetValue of the fahrenheitDial, causing the dial'svalue to change
Continues
52
As the dial's value changes, it emits thevalueChanged signal with the new value
Continues
-
8/2/2019 L2 Slides With Notes
29/32
53
This causes the fahrenheitLcd to be updated(through the display slot)
The setTempFahrenheit slot is also called. This slotdetects that the temperature isn't changed and stopsthere.
Continues
54
At this point, all signals have propagated through thesystem and the TempConverter objects, and all the
widgets are again in sync.
Notice how the slots take responsibility to stopinfinite propagation through the system.
Also notice the importance of picking a scale to usefor the current temperature. A rounding error
causing a mismatch between the Celsius andFahrenheit values could have caused the system toswing forth and back indefinitely.
-
8/2/2019 L2 Slides With Notes
30/32
55
When using signals and slots, a common scenario isto need to pass a value with a signal where the
actual signal does not carry the needed value.
For instance, the keyboard built from QPushButtons.
It is not valid due to two issues: slot signature isn't valid (cannot hold a number) connections cannot hold argument values
Continues
56
The brute force solution would be to add a slot forevery key.
This is quite tedious, and it is easy to get coderepetition inside these slots unless they simply callanother function right away.
Code repetition is bad since changes (e.g. bug fixes)to that code has to be carried out in multiple
locations.
Continues
-
8/2/2019 L2 Slides With Notes
31/32
57
Another option would be to create a new buttonclass emitting the signal with a value.
This requires us to add a new class to the project fora specific case. This class cannot really be re-usedin other scenarios.
Continues
58
Comparing the two.
Solution number one means loads of extra code tomaintain.
Solution number two adds another class that weprobably won't reuse.
(There is a third way, to let the receiving slot look at
the sender of the signal and use a dynamic propertyto hold the value, but that is really not something toshow to the non-advanced students)
Continues
-
8/2/2019 L2 Slides With Notes
32/32
59
To solve this problem, the signal mapper class enters thepicure.
It allows us to associate each sender with a specific value.I.e. it converts a signal without arguments to a signal withone argument, where the argument value depends on thesender to the original signal.
The trick is to put the signal mapper between the buttonsand the keyPressed slot.
Using setMapping, each button is mapped to a value (canbe int, QString, a QWidget pointer or a QObject pointer).
The clicked signal of each button is connected to the mapslot of the signal mapper. The mapped signal of themapper is then connected to the keyPressed slot.
Continues
60
A graphical way to look at the connection is to seethat by passing the signal through the signal mapper,
a value (integer) is added to the signal.