doc/src/examples/drilldown.qdoc
branchRCL_3
changeset 8 3f74d0d4af4c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/src/examples/drilldown.qdoc	Thu Apr 08 14:19:33 2010 +0300
@@ -0,0 +1,550 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+    \example sql/drilldown
+    \title Drill Down Example
+
+    The Drill Down example shows how to read data from a database as
+    well as submit changes, using the QSqlRelationalTableModel and
+    QDataWidgetMapper classes.
+
+    \image drilldown-example.png Screenshot of the Drill Down Example
+
+    When running the example application, a user can retrieve
+    information about each of Nokia's Qt offices by clicking the
+    corresponding image. The application pops up an information window
+    displaying the data, and allows the users to alter the location
+    description as well as the image. The main view will be updated
+    when the users submit their changes.
+
+    The example consists of three classes:
+
+    \list
+        \o \c ImageItem is a custom graphics item class used to
+        display the office images.
+
+        \o \c View is the main application widget allowing the user to
+        browse through the various locations.
+
+        \o \c InformationWindow displays the requested information,
+        allowing the users to alter it and submit their changes to the
+        database.
+    \endlist
+
+    We will first take a look at the \c InformationWindow class to see
+    how you can read and modify data from a database. Then we will
+    review the main application widget, i.e., the \c View class, and
+    the associated \c ImageItem class.
+
+    \section1 InformationWindow Class Definition
+
+    The \c InformationWindow class is a custom widget inheriting
+    QWidget:
+
+    \snippet examples/sql/drilldown/informationwindow.h 0
+
+    When we create an information window, we pass the associated
+    location ID, a parent, and a pointer to the database, to the
+    constructor. We will use the database pointer to populate our
+    window with data, while passing the parent parameter on to the
+    base class. The ID is stored for future reference.
+
+    Once a window is created, we will use the public \c id() function
+    to locate it whenever information for the given location is
+    requested. We will also use the ID to update the main application
+    widget when the users submit their changes to the database, i.e.,
+    we will emit a signal carrying the ID and file name as parameters
+    whenever the users changes the associated image.
+
+    \snippet examples/sql/drilldown/informationwindow.h 1
+
+    Since we allow the users to alter some of the location data, we
+    must provide functionality for reverting and submitting their
+    changes. The \c enableButtons() slot is provided for convenience
+    to enable and disable the various buttons when required.
+
+    \snippet examples/sql/drilldown/informationwindow.h 2
+
+    The \c createButtons() function is also a convenience function,
+    provided to simplify the constructor. As mentioned above we store
+    the location ID for future reference. We also store the name of
+    the currently displayed image file to be able to determine when to
+    emit the \c imageChanged() signal.
+
+    The information window uses the QLabel class to display the office
+    location and the country. The associated image file is displayed
+    using a QComboBox instance while the description is displayed using
+    QTextEdit. In addition, the window has three buttons to control
+    the data flow and whether the window is shown or not.
+
+    Finally, we declare a \e mapper. The QDataWidgetMapper class
+    provides mapping between a section of a data model to widgets. We
+    will use the mapper to extract data from the given database,
+    updating the database whenever the user modifies the data.
+
+    \section1 InformationWindow Class Implementation
+
+    The constructor takes three arguments: a location ID, a database
+    pointer and a parent widget. The database pointer is actually a
+    pointer to a QSqlRelationalTableModel object providing an editable
+    data model (with foreign key support) for our database table.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 0
+    \snippet examples/sql/drilldown/informationwindow.cpp 1
+
+    First we create the various widgets required to display the data
+    contained in the database. Most of the widgets are created in a
+    straight forward manner. But note the combobox displaying the
+    name of the image file:
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 2
+
+    In this example, the information about the offices are stored in a
+    database table called "offices". When creating the model,
+    we will use a foreign key to establish a relation between this
+    table and a second data base table, "images", containing the names
+    of the available image files. We will get back to how this is done
+    when reviewing the \c View class. The rationale for creating such
+    a relation though, is that we want to ensure that the user only
+    can choose between predefined image files.
+
+    The model corresponding to the "images" database table, is
+    available through the QSqlRelationalTableModel's \l
+    {QSqlRelationalTableModel::}{relationModel()} function, requiring
+    the foreign key (in this case the "imagefile" column number) as
+    argument. We use QComboBox's \l {QComboBox::}{setModel()} function
+    to make the combobox use the "images" model. And, since this model
+    has two columns ("locationid" and "file"), we also specify which
+    column we want to be visible using the QComboBox::setModelColumn()
+    function.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 3
+
+    Then we create the mapper. The QDataWidgetMapper class allows us
+    to create data-aware widgets by mapping them to sections of an
+    item model.
+
+    The \l {QDataWidgetMapper::}{addMapping()} function adds a mapping
+    between the given widget and the specified section of the
+    model. If the mapper's orientation is horizontal (the default) the
+    section is a column in the model, otherwise it is a row. We call
+    the \l {QDataWidgetMapper::}{setCurrentIndex()} function to
+    initialize the widgets with the data associated with the given
+    location ID. Every time the current index changes, all the widgets
+    are updated with the contents from the model.
+
+    We also set the mapper's submit policy to
+    QDataWidgetMapper::ManualSubmit. This means that no data is
+    submitted to the database until the user expliclity requests a
+    submit (the alternative is QDataWidgetMapper::AutoSubmit,
+    automatically submitting changes when the corresponding widget
+    looses focus). Finally, we specify the item delegate the mapper
+    view should use for its items. The QSqlRelationalDelegate class
+    represents a delegate that unlike the default delegate, enables
+    combobox functionality for fields that are foreign keys into other
+    tables (like "imagefile" in our "trolltechoffices" table).
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 4
+
+    Finally, we connect the "something's changed" signals in the
+    editors to our custom \c enableButtons() slot, enabling the users
+    to either submit or revert their changes. We add all the widgets
+    into a layout, store the location ID and the name of the displayed
+    image file for future reference, and set the window title and
+    initial size.
+
+    Note that we also set the Qt::Window window flag to indicate that
+    our widget is in fact a window, with a window system frame and a
+    title bar.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 5
+
+    When a window is created, it is not deleted until the main
+    application exits (i.e., if the user closes the information
+    window, it is only hidden). For this reason we do not want to
+    create more than one \c InformationWindow object for each
+    location, and we provide the public \c id() function to be able to
+    determine whether a window already exists for a given location
+    when the user requests information about it.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 6
+
+    The \c revert() slot is triggered whenever the user hits the \gui
+    Revert button.
+
+    Since we set the QDataWidgetMapper::ManualSubmit submit policy,
+    none of the user's changes are written back to the model unless
+    the user expliclity choose to submit all of them. Nevertheless, we
+    can use the QDataWidgetMapper's \l {QDataWidgetMapper::}{revert()}
+    slot to reset the editor widgets, repopulating all widgets with
+    the current data of the model.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 7
+
+    Likewise, the \c submit() slot is triggered whenever the users
+    decide to submit their changes by pressing the \gui Submit button.
+
+    We use QDataWidgetMapper's \l {QDataWidgetMapper::}{submit()} slot
+    to submit all changes from the mapped widgets to the model,
+    i.e. to the database. For every mapped section, the item delegate
+    will then read the current value from the widget and set it in the
+    model. Finally, the \e model's \l {QAbstractItemModel::}{submit()}
+    function is invoked to let the model know that it should submit
+    whatever it has cached to the permanent storage.
+
+    Note that before any data is submitted, we check if the user has
+    chosen another image file using the previously stored \c
+    displayedImage variable as reference. If the current and stored
+    file names differ, we store the new file name and emit the \c
+    imageChanged() signal.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 8
+
+    The \c createButtons() function is provided for convenience, i.e.,
+    to simplify the constructor.
+
+    We make the \gui Close button the default button, i.e., the button
+    that is pressed when the user presses \gui Enter, and connect its
+    \l {QPushButton::}{clicked()} signal to the widget's \l
+    {QWidget::}{close()} slot. As mentioned above closing the window
+    only hides the widget; it is not deleted. We also connect the \gui
+    Submit and \gui Revert buttons to the corresponding \c submit()
+    and \c revert() slots.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 9
+
+    The QDialogButtonBox class is a widget that presents buttons in a
+    layout that is appropriate to the current widget style. Dialogs
+    like our information window, typically present buttons in a layout
+    that conforms to the interface guidelines for that
+    platform. Invariably, different platforms have different layouts
+    for their dialogs. QDialogButtonBox allows us to add buttons,
+    automatically using the appropriate layout for the user's desktop
+    environment.
+
+    Most buttons for a dialog follow certain roles. We give the \gui
+    Submit and \gui Revert buttons the \l
+    {QDialogButtonBox::ButtonRole}{reset} role, i.e., indicating that
+    pressing the button resets the fields to the default values (in
+    our case the information contained in the database). The \l
+    {QDialogButtonBox::ButtonRole}{reject} role indicates that
+    clicking the button causes the dialog to be rejected. On the other
+    hand, since we only hide the information window, any changes that
+    the user has made wil be preserved until the user expliclity
+    revert or submit them.
+
+    \snippet examples/sql/drilldown/informationwindow.cpp 10
+
+    The \c enableButtons() slot is called to enable the buttons
+    whenever the user changes the presented data. Likewise, when the
+    data the user choose to submit the changes, the buttons are
+    disabled to indicate that the current data is stored in the
+    database.
+
+    This completes the \c InformationWindow class. Let's take a look
+    at how we have used it in our example application.
+
+    \section1 View Class Definition
+
+    The \c View class represents the main application window and
+    inherits QGraphicsView:
+
+    \snippet examples/sql/drilldown/view.h 0
+    \codeline
+    \snippet examples/sql/drilldown/view.h 1
+
+    The QGraphicsView class is part of the \l {The Graphics View
+    Framework} which we will use to display the images of Nokia's
+    Qt offices. To be able to respond to user interaction;
+    i.e., showing the
+    appropriate information window whenever the user clicks one of the
+    office images, we reimplement QGraphicsView's \l
+    {QGraphicsView::}{mouseReleaseEvent()} function.
+
+    Note that the constructor expects the names of two database
+    tables: One containing the detailed information about the offices,
+    and another containing the names of the available image files.  We
+    also provide a private \c updateImage() slot to catch \c
+    {InformationWindow}'s \c imageChanged() signal that is emitted
+    whenever the user changes a location's image.
+
+    \snippet examples/sql/drilldown/view.h 2
+
+    The \c addItems() function is a convenience function provided to
+    simplify the constructor. It is called only once, creating the
+    various items and adding them to the view.
+
+    The \c findWindow() function, on the other hand, is frequently
+    used. It is called from the \c showInformation() function to
+    detemine whether a window is already created for the given
+    location (whenever we create an \c InformationWindow object, we
+    store a reference to it in the \c informationWindows list). The
+    latter function is in turn called from our custom \c
+    mouseReleaseEvent() implementation.
+
+    \snippet examples/sql/drilldown/view.h 3
+
+    Finally we declare a QSqlRelationalTableModel pointer. As
+    previously mentioned, the QSqlRelationalTableModel class provides
+    an editable data model with foreign key support. There are a
+    couple of things you should keep in mind when using the
+    QSqlRelationalTableModel class: The table must have a primary key
+    declared and this key cannot contain a relation to another table,
+    i.e., it cannot be a foreign key. Note also that if a relational
+    table contains keys that refer to non-existent rows in the
+    referenced table, the rows containing the invalid keys will not be
+    exposed through the model. It is the user's or the database's
+    responsibility to maintain referential integrity.
+
+    \section1 View Class Implementation
+
+    Although the constructor requests the names of both the table
+    containing office details as well as the table containing the
+    names of the available image files, we only have to create a
+    QSqlRelationalTableModel object for the office table:
+
+    \snippet examples/sql/drilldown/view.cpp 0
+
+    The reason is that once we have a model with the office details,
+    we can create a relation to the available image files using
+    QSqlRelationalTableModel's \l
+    {QSqlRelationalTableModel::}{setRelation()} function. This
+    function creates a foreign key for the given model column. The key
+    is specified by the provided QSqlRelation object constructed by
+    the name of the table the key refers to, the field the key is
+    mapping to and the field that should be presented to the user.
+
+    Note that setting the table only specifies which table the model
+    operates on, i.e., we must explicitly call the model's \l
+    {QSqlRelationalTableModel::}{select()} function to populate our
+    model.
+
+    \snippet examples/sql/drilldown/view.cpp 1
+
+    Then we create the contents of our view, i.e., the scene and its
+    items. The location labels are regular QGraphicsTextItem objects,
+    and the "Qt" logo is represented by a QGraphicsPixmapItem
+    object. The images, on the other hand, are instances of the \c
+    ImageItem class (derived from QGraphicsPixmapItem). We will get
+    back to this shortly when reviewing the \c addItems() function.
+
+    Finally, we set the main application widget's size constraints and
+    window title.
+
+    \snippet examples/sql/drilldown/view.cpp 3
+
+    The \c addItems() function is called only once, i.e., when
+    creating the main application window. For each row in the database
+    table, we first extract the corresponding record using the model's
+    \l {QSqlRelationalTableModel::}{record()} function. The QSqlRecord
+    class encapsulates both the functionality and characteristics of a
+    database record, and supports adding and removing fields as well
+    as setting and retrieving field values. The QSqlRecord::value()
+    function returns the value of the field with the given name or
+    index as a QVariant object.
+
+    For each record, we create a label item as well as an image item,
+    calculate their position and add them to the scene. The image
+    items are represented by instances of the \c ImageItem class. The
+    reason we must create a custom item class is that we want to catch
+    the item's hover events, animating the item when the mouse cursor
+    is hovering over the image (by default, no items accept hover
+    events). Please see the \l{The Graphics View Framework}
+    documentation and the \l{Graphics View Examples} for more details.
+
+    \snippet examples/sql/drilldown/view.cpp 5
+
+    We reimplement QGraphicsView's \l
+    {QGraphicsView::}{mouseReleaseEvent()} event handler to respond to
+    user interaction. If the user clicks any of the image items, this
+    function calls the private \c showInformation() function to pop up
+    the associated information window.
+
+    \l {The Graphics View Framework} provides the qgraphicsitem_cast()
+    function to determine whether the given QGraphicsItem instance is
+    of a given type. Note that if the event is not related to any of
+    our image items, we pass it on to the base class implementation.
+
+    \snippet examples/sql/drilldown/view.cpp 6
+
+    The \c showInformation() function is given an \c ImageItem object
+    as argument, and starts off by extracting the item's location
+    ID. Then it determines if there already is created an information
+    window for this location. If it is, and the window is visible, it
+    ensures that the window is raised to the top of the widget stack
+    and activated. If the window exists but is hidden, calling its \l
+    {QWidget::}{show()} slot gives the same result.
+
+    If no window for the given location exists, we create one by
+    passing the location ID, a pointer to the model, and our view as a
+    parent, to the \c InformationWindow constructor. Note that we
+    connect the information window's \c imageChanged() signal to \e
+    this widget's \c updateImage() slot, before we give it a suitable
+    position and add it to the list of existing windows.
+
+    \snippet examples/sql/drilldown/view.cpp 7
+
+    The \c updateImage() slot takes a location ID and the name of an
+    image files as arguments. It filters out the image items, and
+    updates the one that correspond to the given location ID, with the
+    provided image file.
+
+    \snippet examples/sql/drilldown/view.cpp 8
+
+    The \c findWindow() function simply searches through the list of
+    existing windows, returning a pointer to the window that matches
+    the given location ID, or 0 if the window doesn't exists.
+
+    Finally, let's take a quick look at our custom \c ImageItem class:
+
+    \section1 ImageItem Class Definition
+
+    The \c ImageItem class is provided to facilitate animation of the
+    image items. It inherits QGraphicsPixmapItem and reimplements its
+    hover event handlers:
+
+    \snippet examples/sql/drilldown/imageitem.h 0
+
+    In addition, we implement a public \c id() function to be able to
+    identify the associated location and a public \c adjust() function
+    that can be called to ensure that the image item is given the
+    preferred size regardless of the original image file.
+
+    The animation is implemented using the QTimeLine class together
+    with the event handlers and the private \c setFrame() slot: The
+    image item will expand when the mouse cursor hovers over it,
+    returning back to its orignal size when the cursor leaves its
+    borders.
+
+    Finally, we store the location ID that this particular record is
+    associated with as well as a z-value. In the \l {The Graphics View
+    Framework}, an item's z-value determines its position in the item
+    stack. An item of high Z-value will be drawn on top of an item
+    with a lower z-value if they share the same parent item. We also
+    provide an \c updateItemPosition() function to refresh the view
+    when required.
+
+    \section1 ImageItem Class Implementation
+
+    The \c ImageItem class is really only a QGraphicsPixmapItem with
+    some additional features, i.e., we can pass most of the
+    constructor's arguments (the pixmap, parent and scene) on to the
+    base class constructor:
+
+    \snippet examples/sql/drilldown/imageitem.cpp 0
+
+    Then we store the ID for future reference, and ensure that our
+    item will accept hover events. Hover events are delivered when
+    there is no current mouse grabber item. They are sent when the
+    mouse cursor enters an item, when it moves around inside the item,
+    and when the cursor leaves an item. As we mentioned earlier, none
+    of the \l {The Graphics View Framework}'s items accept hover
+    event's by default.
+
+    The QTimeLine class provides a timeline for controlling
+    animations. Its \l {QTimeLine::}{duration} property holds the
+    total duration of the timeline in milliseconds. By default, the
+    time line runs once from the beginning and towards the end. The
+    QTimeLine::setFrameRange() function sets the timeline's frame
+    counter; when the timeline is running, the \l
+    {QTimeLine::}{frameChanged()} signal is emitted each time the
+    frame changes. We set the duration and frame range for our
+    animation, and connect the time line's \l
+    {QTimeLine::}{frameChanged()} and \l {QTimeLine::}{finished()}
+    signals to our private \c setFrame() and \c updateItemPosition()
+    slots.
+
+    Finally, we call \c adjust() to ensure that the item is given the
+    preferred size.
+
+    \snippet examples/sql/drilldown/imageitem.cpp 1
+    \codeline
+    \snippet examples/sql/drilldown/imageitem.cpp 2
+
+    Whenever the mouse cursor enters or leave the image item, the
+    corresponding event handlers are triggered: We first set the time
+    line's direction, making the item expand or shrink,
+    respectively. Then we alter the item's z-value if it is not already
+    set to the expected value.
+
+    In the case of hover \e enter events, we immediately update the
+    item's position since we want the item to appear on top of all
+    other items as soon as it starts expanding. In the case of hover
+    \e leave events, on the other hand, we postpone the actual update
+    to achieve the same result. But remember that when we constructed
+    our item, we connected the time line's \l
+    {QTimeLine::}{finished()} signal to the \c updateItemPosition()
+    slot. In this way the item is given the correct position in the
+    item stack once the animation is completed. Finally, if the time
+    line is not already running, we start it.
+
+    \snippet examples/sql/drilldown/imageitem.cpp 3
+
+    When the time line is running, it triggers the \c setFrame() slot
+    whenever the current frame changes due to the connection we
+    created in the item constructor. It is this slot that controls the
+    animation, expanding or shrinking the image item step by step.
+
+    We first call the \c adjust() function to ensure that we start off
+    with the item's original size. Then we scale the item with a
+    factor depending on the animation's progress (using the \c frame
+    parameter). Note that by default, the transformation will be
+    relative to the item's top-left corner. Since we want the item to
+    be transformed relative to its center, we must translate the
+    coordinate system before we scale the item.
+
+    In the end, only the following convenience functions remain:
+
+    \snippet examples/sql/drilldown/imageitem.cpp 4
+    \codeline
+    \snippet examples/sql/drilldown/imageitem.cpp 5
+    \codeline
+    \snippet examples/sql/drilldown/imageitem.cpp 6
+
+    The \c adjust() function defines and applies a transformation
+    matrix, ensuring that our image item appears with the preferred
+    size regardless of the size of the source image. The \c id()
+    function is trivial, and is simply provided to be able to identify
+    the item. In the \c updateItemPosition() slot we call the
+    QGraphicsItem::setZValue() function, setting the elevation (i.e.,
+    the position) of the item.
+*/