l2 slides with notes

Upload: maryjane-vlad

Post on 05-Apr-2018

221 views

Category:

Documents


0 download

TRANSCRIPT

  • 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.