| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the documentation of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:FDL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Free Documentation License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Free |
| ** Documentation License version 1.3 as published by the Free Software |
| ** Foundation and appearing in the file included in the packaging of |
| ** this file. Please review the following information to ensure |
| ** the GNU Free Documentation License version 1.3 requirements |
| ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| /*! |
| \example drilldown |
| \title Drill Down Example |
| \ingroup sql_examples |
| |
| \brief The Drill Down example shows how to read data from a database as |
| well as submit changes, using the QSqlRelationalTableModel and |
| QDataWidgetMapper classes. |
| |
| \borderedimage drilldown-example.png Screenshot of the Drill Down Example |
| |
| When running the example application, a user can retrieve |
| information about each item by clicking the corresponding image. |
| The application pops up an information window displaying the data, |
| and allows the users to alter the 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 |
| \li \c ImageItem is a custom graphics item class used to |
| display the images. |
| |
| \li \c View is the main application widget allowing the user to |
| browse through the various items. |
| |
| \li \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 drilldown/informationwindow.h 0 |
| |
| When we create an information window, we pass the associated |
| item 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 drilldown/informationwindow.h 1 |
| |
| Since we allow the users to alter some of the 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 drilldown/informationwindow.h 2 |
| |
| The \c createButtons() function is also a convenience function, |
| provided to simplify the constructor. As mentioned above we store |
| the item 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 name of |
| an item. 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: an item 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 drilldown/informationwindow.cpp 0 |
| \snippet 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 drilldown/informationwindow.cpp 2 |
| |
| In this example, information about the items are stored in a |
| database table called "items". 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 ("itemid" and "file"), we also specify which |
| column we want to be visible using the QComboBox::setModelColumn() |
| function. |
| |
| \snippet 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 |
| item 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 |
| loses 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 "items" table). |
| |
| \snippet 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 need to use lambdas for connecting the \c enableButtons slot |
| because its signature does not match \c QTextEdit::textChanged |
| and \c QComboBox::currentIndexChanged. |
| Since the latter has another overload with the signature |
| \c {const QString &} and the selected signal would be ambiguous, |
| we need to use \c QOverload<int>::of to select a specific overload |
| for \c currentIndexChanged. |
| |
| We add all the widgets into a layout, store the item 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 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 |
| item, 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 drilldown/informationwindow.cpp 6 |
| |
| The \c revert() slot is triggered whenever the user hits the \uicontrol |
| 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 drilldown/informationwindow.cpp 7 |
| |
| Likewise, the \c submit() slot is triggered whenever the users |
| decide to submit their changes by pressing the \uicontrol 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 drilldown/informationwindow.cpp 8 |
| |
| The \c createButtons() function is provided for convenience, i.e., |
| to simplify the constructor. |
| |
| We make the \uicontrol Close button the default button, i.e., the button |
| that is pressed when the user presses \uicontrol 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 \uicontrol |
| Submit and \uicontrol Revert buttons to the corresponding \c submit() |
| and \c revert() slots. |
| |
| \snippet 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 \uicontrol |
| Submit and \uicontrol 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 will be preserved until the user explicitly |
| reverts or submits them. |
| |
| \snippet drilldown/informationwindow.cpp 10 |
| |
| The \c enableButtons() slot is called to enable the buttons |
| whenever the user changes the presented data. Likewise, when the |
| user chooses 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 drilldown/view.h 0 |
| \codeline |
| \snippet drilldown/view.h 1 |
| |
| The QGraphicsView class is part of the \l {Graphics View |
| Framework} which we will use to display the images. To be able to |
| respond to user interaction by displaying the appropriate |
| information window when the image is clicked, 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 items, |
| 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 an image associated with the item. |
| |
| \snippet 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 |
| item (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 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, |
| that is, it cannot be a foreign key. Also note 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 "items" table: |
| |
| \snippet drilldown/view.cpp 0 |
| |
| The reason is that once we have a model with the item 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 drilldown/view.cpp 1 |
| |
| Then we create the contents of our view, i.e., the scene and its |
| items. The labels are regular QGraphicsTextItem objects, whereas |
| the images 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 drilldown/view.cpp 3 |
| |
| The \c addItems() function is called only once 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{Graphics View Framework} documentation |
| and the \l{Graphics View Examples} for more details. |
| |
| \snippet 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. |
| |
| The \l {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 drilldown/view.cpp 6 |
| |
| The \c showInformation() function is given an \c ImageItem object |
| as argument, and starts off by extracting the item's item ID. |
| |
| Then it determines if there already is created an information |
| window for this location. |
| If no window for the given location exists, we create one by |
| passing the item 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. |
| If there is a window for the given location, and that window is |
| visible, it ensures that the window is raised to the top of the |
| widget stack and activated. If it is hidden, calling its \l |
| {QWidget::}{show()} slot gives the same result. |
| |
| |
| \snippet drilldown/view.cpp 7 |
| |
| The \c updateImage() slot takes an item ID and the name of an |
| image file as arguments. It filters out the image items, and |
| updates the one that correspond to the given item ID, with the |
| provided image file. |
| |
| \snippet 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 item ID, or \nullptr 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 drilldown/imageitem.h 0 |
| |
| We declare a \c Type enum value for our custom item and reimplement |
| \l{QGreaphicsItem::}{type()}. This is done so we can safely use |
| qgraphicsitem_cast(). |
| 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 original size when the cursor leaves its |
| borders. |
| |
| Finally, we store the item ID that this particular record is |
| associated with as well as a z-value. In the \l {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 drilldown/imageitem.cpp 0 |
| |
| Then we store the ID for future reference, and ensure that our |
| image 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 {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 drilldown/imageitem.cpp 1 |
| \codeline |
| \snippet drilldown/imageitem.cpp 2 |
| |
| Whenever the mouse cursor enters or leaves 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 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 drilldown/imageitem.cpp 4 |
| \codeline |
| \snippet drilldown/imageitem.cpp 5 |
| \codeline |
| \snippet 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 of the |
| item. |
| */ |