qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,948 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page tutorials-addressbook.html
\title Address Book Tutorial
\ingroup examples-layout
\brief An introduction to GUI programming, showing how to put together a
simple yet fully-functioning application.
This tutorial is an introduction to GUI programming with the Qt
cross-platform framework.
\image addressbook-tutorial-screenshot.png
\omit
It doesn't cover everything; the emphasis is on teaching the programming
philosophy of GUI programming, and Qt's features are introduced as needed.
Some commonly used features are never used in this tutorial.
\endomit
In this tutorial, you will learn about some of the basic
components of Qt, including:
\list
\li Widgets and layout managers
\li Container classes
\li Signals and slots
\li Input and output devices
\endlist
Tutorial contents:
\list 1
\li \l{tutorials/addressbook/part1}{Designing the User Interface}
\li \l{tutorials/addressbook/part2}{Adding Addresses}
\li \l{tutorials/addressbook/part3}{Navigating between Entries}
\li \l{tutorials/addressbook/part4}{Editing and Removing Addresses}
\li \l{tutorials/addressbook/part5}{Adding a Find Function}
\li \l{tutorials/addressbook/part6}{Loading and Saving}
\li \l{tutorials/addressbook/part7}{Additional Features}
\endlist
The tutorial source code is located in \c{tutorials/addressbook}.
Although this little application does not look much like a
fully-fledged modern GUI application, it uses many of the basic
elements that are used in more complex applications. After you
have worked through this tutorial, we recommend reading the
\l{mainwindows/application}{Application} example, which presents a
small GUI application, with menus, toolbars, a status bar, and so
on.
*/
/*!
\example tutorials/addressbook/part1
\title Part 1 - Designing the User Interface
\brief Describes how to code the user interface of the Address Book Example.
This first part covers the design of the basic graphical user
interface (GUI) for our address book application.
The first step in creating a GUI program is to design the user
interface. Here the our goal is to set up the labels and input
fields to implement a basic address book. The figure below is a
screenshot of the expected output.
\image addressbook-tutorial-part1-screenshot.png
We require two QLabel objects, \c nameLabel and \c addressLabel, as well
as two input fields, a QLineEdit object, \c nameLine, and a QTextEdit
object, \c addressText, to enable the user to enter a contact's name and
address. The widgets used and their positions are shown in the figure
below.
\image addressbook-tutorial-part1-labeled-screenshot.png
There are three files used to implement this address book:
\list
\li \c{addressbook.h} - the definition file for the \c AddressBook
class,
\li \c{addressbook.cpp} - the implementation file for the
\c AddressBook class, and
\li \c{main.cpp} - the file containing a \c main() function, with
an instance of \c AddressBook.
\endlist
\section1 Qt Programming - Subclassing
When writing Qt programs, we usually subclass Qt objects to add
functionality. This is one of the essential concepts behind creating
custom widgets or collections of standard widgets. Subclassing to
extend or change the behavior of a widget has the following advantages:
\list
\li We can write implementations of virtual or pure virtual functions to
obtain exactly what we need, falling back on the base class's implementation
when necessary.
\li It allows us to encapsulate parts of the user interface within a class,
so that the other parts of the application don't need to know about the
individual widgets in the user interface.
\li The subclass can be used to create multiple custom widgets in the same
application or library, and the code for the subclass can be reused in other
projects.
\endlist
Since Qt does not provide a specific address book widget, we subclass a
standard Qt widget class and add features to it. The \c AddressBook class
we create in this tutorial can be reused in situations where a basic address
book widget is needed.
\section1 Defining the AddressBook Class
The \c{tutorials/addressbook/part1/addressbook.h} file is
used to define the \c AddressBook class.
We start by defining \c AddressBook as a QWidget subclass and declaring
a constructor. We also use the Q_OBJECT macro to indicate that the class
uses internationalization and Qt's signals and slots features, even
if we do not use all of these features at this stage.
\snippet tutorials/addressbook/part1/addressbook.h class definition
The class holds declarations of \c nameLine and \c addressText,
the private instances of QLineEdit and QTextEdit mentioned
earlier. The data stored in \c nameLine and \c addressText will
be needed for many of the address book functions.
We don't include declarations of the QLabel objects we will use
because we will not need to reference them once they have been
created. The way Qt tracks the ownership of objects is explained
in the next section.
The Q_OBJECT macro itself implements some of the more advanced features of Qt.
For now, it is useful to think of the Q_OBJECT macro as a shortcut which allows
us to use the \l{QObject::}{tr()} and \l{QObject::}{connect()} functions.
We have now completed the \c addressbook.h file and we move on to
implement the corresponding \c addressbook.cpp file.
\section1 Implementing the AddressBook Class
The constructor of \c AddressBook accepts a QWidget parameter, \a parent.
By convention, we pass this parameter to the base class's constructor.
This concept of ownership, where a parent can have one or more children,
is useful for grouping widgets in Qt. For example, if you delete a parent,
all of its children will be deleted as well.
\snippet tutorials/addressbook/part1/addressbook.cpp constructor and input fields
In this constructor, the QLabel objects \c nameLabel and \c
addressLabel are instantiated, as well as \c nameLine and \c
addressText. The \l{QObject::tr()}{tr()} function returns a
translated version of the string, if there is one
available. Otherwise it returns the string itself. This function
marks its QString parameter as one that should be translated into
other languages. It should be used wherever a translatable string
appears.
When programming with Qt, it is useful to know how layouts work.
Qt provides three main layout classes: QHBoxLayout, QVBoxLayout
and QGridLayout to handle the positioning of widgets.
\image addressbook-tutorial-part1-labeled-layout.png
We use a QGridLayout to position our labels and input fields in a
structured manner. QGridLayout divides the available space into a grid and
places widgets in the cells we specify with row and column numbers. The
diagram above shows the layout cells and the position of our widgets, and
we specify this arrangement using the following code:
\snippet tutorials/addressbook/part1/addressbook.cpp layout
Notice that \c addressLabel is positioned using Qt::AlignTop as an
additional argument. This is to make sure it is not vertically centered in
cell (1,0). For a basic overview on Qt Layouts, refer to the
\l{Layout Management} documentation.
In order to install the layout object onto the widget, we have to invoke
the widget's \l{QWidget::setLayout()}{setLayout()} function:
\snippet tutorials/addressbook/part1/addressbook.cpp setting the layout
Lastly, we set the widget's title to "Simple Address Book".
\section1 Running the Application
A separate file, \c main.cpp, is used for the \c main() function. Within
this function, we instantiate a QApplication object, \c app. QApplication
is responsible for various application-wide resources, such as the default
font and cursor, and for running an event loop. Hence, there is always one
QApplication object in every GUI application using Qt.
\snippet tutorials/addressbook/part1/main.cpp main function
We construct a new \c AddressBook widget on the stack and invoke
its \l{QWidget::show()}{show()} function to display it.
However, the widget will not be shown until the application's event loop
is started. We start the event loop by calling the application's
\l{QApplication::}{exec()} function; the result returned by this function
is used as the return value from the \c main() function. At this point,
it becomes apparent why we instantiated \c AddressBook on the stack: It
will now go out of scope. Therefore, \c AddressBook and all its child widgets
will be deleted, thus preventing memory leaks.
*/
/*!
\example tutorials/addressbook/part2
\title Part 2 - Adding Addresses
\brief Describes the code for inserting records in the Address Book Example.
The next step in creating the address book is to implement some
user interactions.
\image addressbook-tutorial-part2-add-contact.png
We will provide a push button that the user can click to add a new contact.
Also, some form of data structure is needed to store these contacts in an
organized way.
\section1 Defining the AddressBook Class
Now that we have the labels and input fields set up, we add push buttons to
complete the process of adding a contact. This means that our
\c addressbook.h file now has three QPushButton objects declared and three
corresponding public slots.
\snippet tutorials/addressbook/part2/addressbook.h slots
A slot is a function that responds to a particular signal. We will discuss
this concept in further detail when implementing the \c AddressBook class.
However, for an overview of Qt's signals and slots concept, you can refer
to the \l{Signals and Slots} document.
Three QPushButton objects (\c addButton, \c submitButton, and
\c cancelButton) are now included in our private variable declarations,
along with \c nameLine and \c addressText.
\snippet tutorials/addressbook/part2/addressbook.h pushbutton declaration
We need a container to store our address book contacts, so that we can
traverse and display them. A QMap object, \c contacts, is used for this
purpose as it holds a key-value pair: the contact's name as the \e key,
and the contact's address as the \e{value}.
\snippet tutorials/addressbook/part2/addressbook.h remaining private variables
We also declare two private QString objects, \c oldName and \c oldAddress.
These objects are needed to hold the name and address of the contact that
was last displayed, before the user clicked \uicontrol Add. So, when the user clicks
\uicontrol Cancel, we can revert to displaying the details of the last contact.
\section1 Implementing the AddressBook Class
Within the constructor of \c AddressBook, we set the \c nameLine and
\c addressText to read-only, so that we can only display but not edit
existing contact details.
\dots
\snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 1
\dots
\snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 2
Then, we instantiate our push buttons: \c addButton, \c submitButton, and
\c cancelButton.
\snippet tutorials/addressbook/part2/addressbook.cpp pushbutton declaration
The \c addButton is displayed by invoking the \l{QPushButton::show()}
{show()} function, while the \c submitButton and \c cancelButton are
hidden by invoking \l{QPushButton::hide()}{hide()}. These two push
buttons will only be displayed when the user clicks \uicontrol Add and this is
handled by the \c addContact() function discussed below.
\snippet tutorials/addressbook/part2/addressbook.cpp connecting signals and slots
We connect the push buttons' \l{QPushButton::clicked()}{clicked()} signal
to their respective slots. The figure below illustrates this.
\image addressbook-tutorial-part2-signals-and-slots.png
Next, we arrange our push buttons neatly to the right of our address book
widget, using a QVBoxLayout to line them up vertically.
\snippet tutorials/addressbook/part2/addressbook.cpp vertical layout
The \l{QBoxLayout::addStretch()}{addStretch()} function is used to ensure
the push buttons are not evenly spaced, but arranged closer to the top of
the widget. The figure below shows the difference between using
\l{QBoxLayout::addStretch()}{addStretch()} and not using it.
\image addressbook-tutorial-part2-stretch-effects.png
We then add \c buttonLayout1 to \c mainLayout, using
\l{QGridLayout::addLayout()}{addLayout()}. This gives us nested layouts
as \c buttonLayout1 is now a child of \c mainLayout.
\snippet tutorials/addressbook/part2/addressbook.cpp grid layout
Our layout coordinates now look like this:
\image addressbook-tutorial-part2-labeled-layout.png
In the \c addContact() function, we store the last displayed contact
details in \c oldName and \c oldAddress. Then we clear these input
fields and turn off the read-only mode. The focus is set on \c nameLine
and we display \c submitButton and \c cancelButton.
\snippet tutorials/addressbook/part2/addressbook.cpp addContact
The \c submitContact() function can be divided into three parts:
\list 1
\li We extract the contact's details from \c nameLine and \c addressText
and store them in QString objects. We also validate to make sure that the
user did not click \uicontrol Submit with empty input fields; otherwise, a
QMessageBox is displayed to remind the user for a name and address.
\snippet tutorials/addressbook/part2/addressbook.cpp submitContact part1
\li We then proceed to check if the contact already exists. If it does not
exist, we add the contact to \c contacts and we display a QMessageBox to
inform the user that the contact has been added.
\snippet tutorials/addressbook/part2/addressbook.cpp submitContact part2
If the contact already exists, again, we display a QMessageBox to inform
the user about this, preventing the user from adding duplicate contacts.
Our \c contacts object is based on key-value pairs of name and address,
hence, we want to ensure that \e key is unique.
\li Once we have handled both cases mentioned above, we restore the push
buttons to their normal state with the following code:
\snippet tutorials/addressbook/part2/addressbook.cpp submitContact part3
\endlist
The screenshot below shows the QMessageBox object we use to display
information messages to the user.
\image addressbook-tutorial-part2-add-successful.png
The \c cancel() function restores the last displayed contact details and
enables \c addButton, as well as hides \c submitButton and
\c cancelButton.
\snippet tutorials/addressbook/part2/addressbook.cpp cancel
The general idea behind adding a contact is to give the user the
flexibility to click \uicontrol Submit or \uicontrol Cancel at any time. The flowchart below
further explains this concept:
\image addressbook-tutorial-part2-add-flowchart.png
*/
/*!
\example tutorials/addressbook/part3
\title Part 3 - Navigating between Entries
\brief Explains the code that enables navigating the contacts.
The address book is now about half complete. We should add the
capability to navigate the contacts, but first we must
decide what sort of a data structure we need for containing these
contacts.
In the previous section, we used a QMap of key-value pairs with
the contact's name as the \e key, and the contact's address as the
\e value. This works well for our case. However, in order to
navigate and display each entry, a little bit of enhancement is
needed.
We enhance the QMap by making it replicate a data structure similar to a
circularly-linked list, where all elements are connected, including the
first element and the last element. The figure below illustrates this data
structure.
\image addressbook-tutorial-part3-linkedlist.png
\section1 Defining the AddressBook Class
To add navigation functions to the address book, we must add two
more slots to the \c AddressBook class: \c next() and \c
previous() to the \c addressbook.h file:
\snippet tutorials/addressbook/part3/addressbook.h navigation functions
We also require another two QPushButton objects, so we declare \c nextButton
and \c previousButton as private variables:
\snippet tutorials/addressbook/part3/addressbook.h navigation pushbuttons
\section1 Implementing the AddressBook Class
In the \c AddressBook constructor in \c addressbook.cpp, we instantiate
\c nextButton and \c previousButton and disable them by default. This is
because navigation is only enabled when there is more than one contact
in the address book.
\snippet tutorials/addressbook/part3/addressbook.cpp navigation pushbuttons
We then connect these push buttons to their respective slots:
\snippet tutorials/addressbook/part3/addressbook.cpp connecting navigation signals
The image below is the expected graphical user interface.
\image addressbook-tutorial-part3-screenshot.png
We follow basic conventions for \c next() and \c previous() functions by
placing the \c nextButton on the right and the \c previousButton on the
left. In order to achieve this intuitive layout, we use QHBoxLayout to
place the widgets side-by-side:
\snippet tutorials/addressbook/part3/addressbook.cpp navigation layout
The QHBoxLayout object, \c buttonLayout2, is then added to \c mainLayout.
\snippet tutorials/addressbook/part3/addressbook.cpp adding navigation layout
The figure below shows the coordinates of the widgets in \c mainLayout.
\image addressbook-tutorial-part3-labeled-layout.png
Within our \c addContact() function, we have to disable these buttons so
that the user does not attempt to navigate while adding a contact.
\snippet tutorials/addressbook/part3/addressbook.cpp disabling navigation
Also, in our \c submitContact() function, we enable the navigation
buttons, \c nextButton and \c previousButton, depending on the size
of \c contacts. As mentioned earlier, navigation is only enabled when
there is more than one contact in the address book. The following lines
of code demonstrates how to do this:
\snippet tutorials/addressbook/part3/addressbook.cpp enabling navigation
We also include these lines of code in the \c cancel() function.
Recall that we intend to emulate a circularly-linked list with our QMap
object, \c contacts. So, in the \c next() function, we obtain an iterator
for \c contacts and then:
\list
\li If the iterator is not at the end of \c contacts, we increment it
by one.
\li If the iterator is at the end of \c contacts, we move it to the
beginning of \c contacts. This gives us the illusion that our QMap is
working like a circularly-linked list.
\endlist
\snippet tutorials/addressbook/part3/addressbook.cpp next() function
Once we have iterated to the correct object in \c contacts, we display
its contents on \c nameLine and \c addressText.
Similarly, for the \c previous() function, we obtain an iterator for
\c contacts and then:
\list
\li If the iterator is at the end of \c contacts, we clear the
display and return.
\li If the iterator is at the beginning of \c contacts, we move it to
the end.
\li We then decrement the iterator by one.
\endlist
\snippet tutorials/addressbook/part3/addressbook.cpp previous() function
Again, we display the contents of the current object in \c contacts.
*/
/*!
\example tutorials/addressbook/part4
\title Part 4 - Editing and Removing Addresses
\brief Explains how to add edit and remove functionality.
Now we look at ways to modify the contents of contacts stored in
the address book.
\image addressbook-tutorial-screenshot.png
We now have an address book that not only holds contacts in an
organized manner, but also allows navigation. It would be
convenient to include edit and remove functions so that a
contact's details can be changed when needed. However, this
requires a little improvement, in the form of enums. We defined
two modes: \c{AddingMode} and \c{NavigationMode}, but they were
not defined as enum values. Instead, we enabled and disabled the
corresponding buttons manually, resulting in multiple lines of
repeated code.
Here we define the \c Mode enum with three different values:
\list
\li \c{NavigationMode},
\li \c{AddingMode}, and
\li \c{EditingMode}.
\endlist
\section1 Defining the AddressBook Class
The \c addressbook.h file is updated to contain the \c Mode enum:
\snippet tutorials/addressbook/part4/addressbook.h Mode enum
We also add two new slots, \c editContact() and \c removeContact(), to
our current list of public slots.
\snippet tutorials/addressbook/part4/addressbook.h edit and remove slots
In order to switch between modes, we introduce the \c updateInterface() function
to control the enabling and disabling of all QPushButton objects. We also
add two new push buttons, \c editButton and \c removeButton, for the edit
and remove functions mentioned earlier.
\snippet tutorials/addressbook/part4/addressbook.h updateInterface() declaration
\dots
\snippet tutorials/addressbook/part4/addressbook.h buttons declaration
\dots
\snippet tutorials/addressbook/part4/addressbook.h mode declaration
Lastly, we declare \c currentMode to keep track of the enum's current mode.
\section1 Implementing the AddressBook Class
We now implement the mode-changing features of the address
book. The \c editButton and \c removeButton are instantiated and
disabled by default. The address book starts with zero contacts
in memory.
\snippet tutorials/addressbook/part4/addressbook.cpp edit and remove buttons
These buttons are then connected to their respective slots, \c editContact()
and \c removeContact(), and we add them to \c buttonLayout1.
\snippet tutorials/addressbook/part4/addressbook.cpp connecting edit and remove
\dots
\snippet tutorials/addressbook/part4/addressbook.cpp adding edit and remove to the layout
The \c editContact() function stores the contact's old details in
\c oldName and \c oldAddress, before switching the mode to \c EditingMode.
In this mode, the \c submitButton and \c cancelButton are both enabled,
hence, the user can change the contact's details and click either button.
\snippet tutorials/addressbook/part4/addressbook.cpp editContact() function
The \c submitContact() function has been divided in two with an \c{if-else}
statement. We check \c currentMode to see if it's in \c AddingMode. If it is,
we proceed with our adding process.
\snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function beginning
\dots
\snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part1
Otherwise, we check to see if \c currentMode is in \c EditingMode. If it
is, we compare \c oldName with \c name. If the name has changed, we remove
the old contact from \c contacts and insert the newly updated contact.
\snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part2
If only the address has changed (i.e., \c oldAddress is not the same as \c address),
we update the contact's address. Lastly, we set \c currentMode to
\c NavigationMode. This is an important step as it re-enables all the
disabled push buttons.
To remove a contact from the address book, we implement the
\c removeContact() function. This function checks to see if the contact
exists in \c contacts.
\snippet tutorials/addressbook/part4/addressbook.cpp removeContact() function
If it does, we display a QMessageBox, to confirm the removal with the
user. Once the user has confirmed, we call \c previous() to ensure that the
user interface shows another contact, and we remove the contact using \l{QMap}'s
\l{QMap::remove()}{remove()} function. As a courtesy, we display a QMessageBox
to inform the user. Both the message boxes used in this function are shown below:
\image addressbook-tutorial-part4-remove.png
\section2 Updating the User Interface
We mentioned the \c updateInterface() function earlier as a means to
enable and disable the push buttons depending on the current mode.
The function updates the current mode according to the \c mode argument
passed to it, assigning it to \c currentMode before checking its value.
Each of the push buttons is then enabled or disabled, depending on the
current mode. The code for \c AddingMode and \c EditingMode is shown below:
\snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 1
For \c NavigationMode, however, we include conditions within the parameters
of the QPushButton::setEnabled() function. This is to ensure that
\c editButton and \c removeButton are enabled when there is at least one
contact in the address book; \c nextButton and \c previousButton are only
enabled when there is more than one contact in the address book.
\snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 2
By setting the mode and updating the user interface in the same
function, we avoid the possibility of the user interface getting
out of sync with the internal state of the application.
*/
/*!
\example tutorials/addressbook/part5
\title Part 5 - Adding a Find Function
\brief Describes how to add a find function.
Here we look at ways to locate contacts and addresses in the
address book.
\image addressbook-tutorial-part5-screenshot.png
As we add contacts to our address book, it becomes tedious to
navigate the list with the \e Next and \e Previous buttons. A \e
Find function would be more efficient. The screenshot above shows
the \e Find button and its position on the panel of buttons.
When the user clicks on the \e Find button, it is useful to
display a dialog that prompts for a contact's name. Qt provides
QDialog, which we subclass here to implement a \c FindDialog
class.
\section1 Defining the FindDialog Class
\image addressbook-tutorial-part5-finddialog.png
In order to subclass QDialog, we first include the header for QDialog in
the \c finddialog.h file. Also, we use forward declaration to declare
QLineEdit and QPushButton since we will be using those widgets in our
dialog class.
As in our \c AddressBook class, the \c FindDialog class includes
the Q_OBJECT macro and its constructor is defined to accept a parent
QWidget, even though the dialog will be opened as a separate window.
\snippet tutorials/addressbook/part5/finddialog.h FindDialog header
We define a public function, \c getFindText(), to be used by classes that
instantiate \c FindDialog. This function allows these classes to obtain the
search string entered by the user. A public slot, \c findClicked(), is also
defined to handle the search string when the user clicks the \uicontrol Find
button.
Lastly, we define the private variables, \c findButton, \c lineEdit
and \c findText, corresponding to the \uicontrol Find button, the line edit
into which the user types the search string, and an internal string
used to store the search string for later use.
\section1 Implementing the FindDialog Class
Within the constructor of \c FindDialog, we set up the private variables,
\c lineEdit, \c findButton and \c findText. We use a QHBoxLayout to
position the widgets.
\snippet tutorials/addressbook/part5/finddialog.cpp constructor
We set the layout and window title, as well as connect the signals to their
respective slots. Notice that \c{findButton}'s \l{QPushButton::clicked()}
{clicked()} signal is connected to \c findClicked() and
\l{QDialog::accept()}{accept()}. The \l{QDialog::accept()}{accept()} slot
provided by QDialog hides the dialog and sets the result code to
\l{QDialog::}{Accepted}. We use this function to help \c{AddressBook}'s
\c findContact() function know when the \c FindDialog object has been
closed. We will explain this logic in further detail when discussing the
\c findContact() function.
\image addressbook-tutorial-part5-signals-and-slots.png
In \c findClicked(), we validate \c lineEdit to ensure that the user
did not click the \uicontrol Find button without entering a contact's name. Then, we set
\c findText to the search string, extracted from \c lineEdit. After that,
we clear the contents of \c lineEdit and hide the dialog.
\snippet tutorials/addressbook/part5/finddialog.cpp findClicked() function
The \c findText variable has a public getter function, \c getFindText(),
associated with it. Since we only ever set \c findText directly in both the
constructor and in the \c findClicked() function, we do not create a
setter function to accompany \c getFindText().
Because \c getFindText() is public, classes instantiating and using
\c FindDialog can always access the search string that the user has
entered and accepted.
\snippet tutorials/addressbook/part5/finddialog.cpp getFindText() function
\section1 Defining the AddressBook Class
To ensure we can use \c FindDialog from within our \c AddressBook class, we
include \c finddialog.h in the \c addressbook.h file.
\snippet tutorials/addressbook/part5/addressbook.h include finddialog's header
So far, all our address book features have a QPushButton and a
corresponding slot. Similarly, for the \uicontrol Find feature we have
\c findButton and \c findContact().
The \c findButton is declared as a private variable and the
\c findContact() function is declared as a public slot.
\snippet tutorials/addressbook/part5/addressbook.h findContact() declaration
\dots
\snippet tutorials/addressbook/part5/addressbook.h findButton declaration
Lastly, we declare the private variable, \c dialog, which we will use to
refer to an instance of \c FindDialog.
\snippet tutorials/addressbook/part5/addressbook.h FindDialog declaration
Once we have instantiated a dialog, we will want to use it more than once;
using a private variable allows us to refer to it from more than one place
in the class.
\section1 Implementing the AddressBook Class
Within the \c AddressBook class's constructor, we instantiate our private
objects, \c findButton and \c findDialog:
\snippet tutorials/addressbook/part5/addressbook.cpp instantiating findButton
\dots
\snippet tutorials/addressbook/part5/addressbook.cpp instantiating FindDialog
Next, we connect the \c{findButton}'s
\l{QPushButton::clicked()}{clicked()} signal to \c findContact().
\snippet tutorials/addressbook/part5/addressbook.cpp signals and slots for find
Now all that is left is the code for our \c findContact() function:
\snippet tutorials/addressbook/part5/addressbook.cpp findContact() function
We start out by displaying the \c FindDialog instance, \c dialog. This is
when the user enters a contact name to look up. Once the user clicks
the dialog's \c findButton, the dialog is hidden and the result code is
set to QDialog::Accepted. This ensures that
our \c if statement is always true.
We then proceed to extract the search string, which in this case is
\c contactName, using \c{FindDialog}'s \c getFindText() function. If the
contact exists in our address book, we display it immediately. Otherwise,
we display the QMessageBox shown below to indicate that their search
failed.
\image addressbook-tutorial-part5-notfound.png
*/
/*!
\example tutorials/addressbook/part6
\title Part 6 - Loading and Saving
\brief Describes how to add save and load functionality.
This part covers the Qt file handling features we use to write
loading and saving routines for the address book.
\image addressbook-tutorial-part6-screenshot.png
Although browsing and searching the contact list are useful
features, our address book is not complete until we can save
existing contacts and load them again at a later time.
Qt provides a number of classes for \l{Input/Output and Networking}
{input and output}, but we have chosen to use two which are simple to use
in combination: QFile and QDataStream.
A QFile object represents a file on disk that can be read from and written
to. QFile is a subclass of the more general QIODevice class which
represents many different kinds of devices.
A QDataStream object is used to serialize binary data so that it can be
stored in a QIODevice and retrieved again later. Reading from a QIODevice
and writing to it is as simple as opening the stream - with the respective
device as a parameter - and reading from or writing to it.
\section1 Defining the AddressBook Class
We declare two public slots, \c saveToFile() and \c loadFromFile(), as well
as two QPushButton objects, \c loadButton and \c saveButton.
\snippet tutorials/addressbook/part6/addressbook.h save and load functions declaration
\dots
\snippet tutorials/addressbook/part6/addressbook.h save and load buttons declaration
\section1 Implementing the AddressBook Class
In our constructor, we instantiate \c loadButton and \c saveButton.
Ideally, it would be more user-friendly to set the push buttons' labels
to "Load contacts from a file" and "Save contacts to a file". However, due
to the size of our other push buttons, we set the labels to \uicontrol{Load...}
and \uicontrol{Save...}. Fortunately, Qt provides a simple way to set tooltips with
\l{QWidget::setToolTip()}{setToolTip()} and we use it in the following way
for our push buttons:
\snippet tutorials/addressbook/part6/addressbook.cpp tooltip 1
\dots
\snippet tutorials/addressbook/part6/addressbook.cpp tooltip 2
Although it is not shown here, just like the other features we implemented,
we add the push buttons to the layout panel on the right, \c buttonLayout1,
and we connect the push buttons' \l{QPushButton::clicked()}{clicked()}
signals to their respective slots.
For the saving feature, we first obtain \c fileName using
QFileDialog::getSaveFileName(). This is a convenience function provided
by QFileDialog, which pops up a modal file dialog and allows the user to
enter a file name or select any existing \c{.abk} file. The \c{.abk} file
is our Address Book extension that we create when we save contacts.
\snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part1
The file dialog that pops up is displayed in the screenshot below:
\image addressbook-tutorial-part6-save.png
If \c fileName is not empty, we create a QFile object, \c file, with
\c fileName. QFile works with QDataStream as QFile is a QIODevice.
Next, we attempt to open the file in \l{QIODeviceBase::}{WriteOnly} mode.
If this is unsuccessful, we display a QMessageBox to inform the user.
\snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part2
Otherwise, we instantiate a QDataStream object, \c out, to write the open
file. QDataStream requires that the same version of the stream is used
for reading and writing. We ensure that this is the case by setting the
version used to the \l{QDataStream::Qt_4_5}{version introduced with Qt 4.5}
before serializing the data to \c file.
\snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part3
For the loading feature, we also obtain \c fileName using
QFileDialog::getOpenFileName(). This function, the counterpart to
QFileDialog::getSaveFileName(), also pops up the modal file dialog and
allows the user to enter a file name or select any existing \c{.abk} file
to load it into the address book.
\snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part1
On Windows, for example, this function pops up a native file dialog, as
shown in the following screenshot.
\image addressbook-tutorial-part6-load.png
If \c fileName is not empty, again, we use a QFile object, \c file, and
attempt to open it in \l{QIODeviceBase::}{ReadOnly} mode. Similar to our
implementation of \c saveToFile(), if this attempt is unsuccessful, we
display a QMessageBox to inform the user.
\snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part2
Otherwise, we instantiate a QDataStream object, \c in, set its version as
above and read the serialized data into the \c contacts data structure.
The \c contacts object is emptied before data is read into it to simplify
the file reading process. A more advanced method would be to read the
contacts into a temporary QMap object, and copy over non-duplicate contacts
into \c contacts.
\snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part3
To display the contacts that have been read from the file, we must first
validate the data obtained to ensure that the file we read from actually
contains address book contacts. If it does, we display the first contact;
otherwise, we display a QMessageBox to inform the user about the problem.
Lastly, we update the interface to enable and disable the push buttons
accordingly.
*/
/*!
\example tutorials/addressbook/part7
\title Part 7 - Additional Features
\brief Describes how to export data in VCard format.
This part covers some additional features that make the address
book more convenient for the frequent user.
\image addressbook-tutorial-part7-screenshot.png
Although our address book is useful in isolation, it would be
better if we could exchange contact data with other applications.
The vCard format is a popular file format that can be used for
this purpose. Here we extend our address book client to allow
contacts to be exported to vCard \c{.vcf} files.
\section1 Defining the AddressBook Class
We add a QPushButton object, \c exportButton, and a corresponding public
slot, \c exportAsVCard() to our \c AddressBook class in the
\c addressbook.h file.
\snippet tutorials/addressbook/part7/addressbook.h exportAsVCard() declaration
\dots
\snippet tutorials/addressbook/part7/addressbook.h exportButton declaration
\section1 Implementing the AddressBook Class
Within the \c AddressBook constructor, we connect \c{exportButton}'s
\l{QPushButton::clicked()}{clicked()} signal to \c exportAsVCard().
We also add this button to our \c buttonLayout1, the layout responsible
for our panel of buttons on the right.
In our \c exportAsVCard() function, we start by extracting the contact's
name into \c name. We declare \c firstName, \c lastName and \c nameList.
Next, we look for the index of the first white space in \c name. If there
is a white space, we split the contact's name into \c firstName and
\c lastName. Then, we replace the space with an underscore ("_").
Alternately, if there is no white space, we assume that the contact only
has a first name.
\snippet tutorials/addressbook/part7/addressbook.cpp export function part1
As with the \c saveToFile() function, we open a file dialog to let the user
choose a location for the file. Using the file name chosen, we create an
instance of QFile to write to.
We attempt to open the file in \l{QIODeviceBase::}{WriteOnly} mode. If this
process fails, we display a QMessageBox to inform the user about the
problem and return. Otherwise, we pass the file as a parameter to a
QTextStream object, \c out. Like QDataStream, the QTextStream class
provides functionality to read and write plain text to files. As a result,
the \c{.vcf} file generated can be opened for editing in a text editor.
\snippet tutorials/addressbook/part7/addressbook.cpp export function part2
We then write out a vCard file with the \c{BEGIN:VCARD} tag, followed by
the \c{VERSION:2.1} tag. The contact's name is written with the \c{N:}
tag. For the \c{FN:} tag, which fills in the "File as" property of a vCard,
we have to check whether the contact has a last name or not. If the contact
does, we use the details in \c nameList to fill it. Otherwise, we write
\c firstName only.
\snippet tutorials/addressbook/part7/addressbook.cpp export function part3
We proceed to write the contact's address. The semicolons in the address
are escaped with "\\", the newlines are replaced with semicolons, and the
commas are replaced with spaces. Lastly, we write the \c{ADR;HOME:;}
tag, followed by \c address and then the \c{END:VCARD} tag.
\snippet tutorials/addressbook/part7/addressbook.cpp export function part4
In the end, a QMessageBox is displayed to inform the user that the vCard
has been successfully exported.
\e{vCard is a trademark of the \l{http://www.imc.org}
{Internet Mail Consortium}}.
*/

View File

@ -0,0 +1,412 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/addressbook
\title Address Book
\ingroup examples-itemviews
\brief The address book example shows how to use proxy models to display
different views onto data from a single model.
\image addressbook-example.png Screenshot of the Address Book example
This example provides an address book that allows contacts to be
grouped alphabetically into 9 groups: ABC, DEF, GHI, ... , VW,
..., XYZ. This is achieved by using multiple views on the same
model, each of which is filtered using an instance of the
QSortFilterProxyModel class.
\section1 Overview
The address book contains 5 classes: \c MainWindow,
\c AddressWidget, \c TableModel, \c NewAddressTab and
\c AddDialog. The \c MainWindow class uses \c AddressWidget as
its central widget and provides \uicontrol File and \uicontrol Tools menus.
\image addressbook-classes.png Diagram for Address Book example
The \c AddressWidget class is a QTabWidget subclass that is used
to manipulate the 10 tabs displayed in the example: the 9
alphabet group tabs and an instance of \c NewAddressTab.
The \c NewAddressTab class is a subclass of QWidget that
is only used whenever the address book is empty, prompting the
user to add some contacts. \c AddressWidget also interacts with
an instance of \c TableModel to add, edit and remove entries to
the address book.
\c TableModel is a subclass of QAbstractTableModel that provides
the standard model/view API to access data. It holds a list of
added contacts.
However, this data is not all visible in a single tab. Instead,
QTableView is used to provide 9 different views of the same
data, according to the alphabet groups.
QSortFilterProxyModel is the class responsible for filtering
the contacts for each group of contacts. Each proxy model uses
a QRegularExpression to filter out contacts that do not belong in the
corresponding alphabetical group. The \c AddDialog class is
used to obtain information from the user for the address book.
This QDialog subclass is instantiated by \c NewAddressTab to
add contacts, and by \c AddressWidget to add and edit contacts.
We begin by looking at the \c TableModel implementation.
\section1 TableModel Class Definition
The \c TableModel class provides standard API to access data in
its list of contacts by subclassing QAbstractTableModel. The
basic functions that must be implemented in order to do so are:
\c rowCount(), \c columnCount(), \c data(), \c headerData().
For TableModel to be editable, it has to provide implementations
\c insertRows(), \c removeRows(), \c setData() and \c flags()
functions.
\snippet itemviews/addressbook/tablemodel.h 0
Two constructors are used, a default constructor which uses
\c TableModel's own \c {QList<Contact>} and one that takes
\c {QList<Contact>} as an argument, for convenience.
\section1 TableModel Class Implementation
We implement the two constructors as defined in the header file.
The second constructor initializes the list of contacts in the
model, with the parameter value.
\snippet itemviews/addressbook/tablemodel.cpp 0
The \c rowCount() and \c columnCount() functions return the
dimensions of the model. Whereas, \c rowCount()'s value will vary
depending on the number of contacts added to the address book,
\c columnCount()'s value is always 2 because we only need space
for the \b Name and \b Address columns.
\snippet itemviews/addressbook/tablemodel.cpp 1
The \c data() function returns either a \b Name or
\b {Address}, based on the contents of the model index
supplied. The row number stored in the model index is used to
reference an item in the list of contacts. Selection is handled
by the QItemSelectionModel, which will be explained with
\c AddressWidget.
\snippet itemviews/addressbook/tablemodel.cpp 2
The \c headerData() function displays the table's header,
\b Name and \b Address. If you require numbered entries
for your address book, you can use a vertical header which we
have hidden in this example (see the \c AddressWidget
implementation).
\snippet itemviews/addressbook/tablemodel.cpp 3
The \c insertRows() function is called before new data is added,
otherwise the data will not be displayed. The
\c beginInsertRows() and \c endInsertRows() functions are called
to ensure all connected views are aware of the changes.
\snippet itemviews/addressbook/tablemodel.cpp 4
The \c removeRows() function is called to remove data. Again,
\l{QAbstractItemModel::}{beginRemoveRows()} and
\l{QAbstractItemModel::}{endRemoveRows()} are called to ensure
all connected views are aware of the changes.
\snippet itemviews/addressbook/tablemodel.cpp 5
The \c setData() function is the function that inserts data into
the table, item by item and not row by row. This means that to
fill a row in the address book, \c setData() must be called
twice, as each row has 2 columns. It is important to emit the
\l{QAbstractItemModel::}{dataChanged()} signal as it tells all
connected views to update their displays.
\snippet itemviews/addressbook/tablemodel.cpp 6
The \c flags() function returns the item flags for the given
index.
\snippet itemviews/addressbook/tablemodel.cpp 7
We set the Qt::ItemIsEditable flag because we want to allow the
\c TableModel to be edited. Although for this example we don't
use the editing features of the QTableView object, we enable
them here so that we can reuse the model in other programs.
The last function in \c {TableModel}, \c getContacts() returns the
QList<Contact> object that holds all the contacts in the address
book. We use this function later to obtain the list of contacts to
check for existing entries, write the contacts to a file and read
them back. Further explanation is given with \c AddressWidget.
\snippet itemviews/addressbook/tablemodel.cpp 8
\section1 AddressWidget Class Definition
The \c AddressWidget class is technically the main class
involved in this example as it provides functions to add, edit
and remove contacts, to save the contacts to a file and to load
them from a file.
\snippet itemviews/addressbook/addresswidget.h 0
\c AddressWidget extends QTabWidget in order to hold 10 tabs
(\c NewAddressTab and the 9 alphabet group tabs) and also
manipulates \c table, the \c TableModel object, \c proxyModel,
the QSortFilterProxyModel object that we use to filter the
entries, and \c tableView, the QTableView object.
\section1 AddressWidget Class Implementation
The \c AddressWidget constructor accepts a parent widget and
instantiates \c NewAddressTab, \c TableModel and
QSortFilterProxyModel. The \c NewAddressTab object, which is
used to indicate that the address book is empty, is added
and the rest of the 9 tabs are set up with \c setupTabs().
\snippet itemviews/addressbook/addresswidget.cpp 0
The \c setupTabs() function is used to set up the 9 alphabet
group tabs, table views and proxy models in
\c AddressWidget. Each proxy model in turn is set to filter
contact names according to the relevant alphabet group using a
case-insensitive QRegularExpression object. The
table views are also sorted in ascending order using the
corresponding proxy model's \l{QSortFilterProxyModel::}{sort()}
function.
Each table view's \l{QTableView::}{selectionMode} is set to
QAbstractItemView::SingleSelection and
\l{QTableView::}{selectionBehavior} is set to
QAbstractItemView::SelectRows, allowing the user to select
all the items in one row at the same time. Each QTableView object
is automatically given a QItemSelectionModel that keeps track
of the selected indexes.
\snippet itemviews/addressbook/addresswidget.cpp 1
The QItemSelectionModel class provides a
\l{QItemSelectionModel::selectionChanged()}{selectionChanged}
signal that is connected to \c{AddressWidget}'s
\c selectionChanged() signal. We also connect
QTabWidget::currentChanged() signal to the lambda expression which
emits \c{AddressWidget}'s \c selectionChanged() as well. These
connections are necessary to enable the \uicontrol{Edit Entry...} and
\uicontrol{Remove Entry} actions in \c MainWindow's Tools menu.
It is further explained in \c MainWindow's implementation.
Each table view in the address book is added as a tab to the
QTabWidget with the relevant label, obtained from the QStringList
of groups.
\image addressbook-signals.png Signals and Slots Connections
We provide two \c addEntry() functions: One which is intended to be
used to accept user input, and the other which performs the actual
task of adding new entries to the address book. We divide the
responsibility of adding entries into two parts to allow
\c newAddressTab to insert data without having to popup a dialog.
The first \c addEntry() function is a slot connected to the
\c MainWindow's \uicontrol{Add Entry...} action. This function creates an
\c AddDialog object and then calls the second \c addEntry()
function to actually add the contact to \c table.
\snippet itemviews/addressbook/addresswidget.cpp 2
Basic validation is done in the second \c addEntry() function to
prevent duplicate entries in the address book. As mentioned with
\c TableModel, this is part of the reason why we require the
getter method \c getContacts().
\snippet itemviews/addressbook/addresswidget.cpp 3
If the model does not already contain an entry with the same name,
we call \c setData() to insert the name and address into the
first and second columns. Otherwise, we display a QMessageBox
to inform the user.
\note The \c newAddressTab is removed once a contact is added
as the address book is no longer empty.
Editing an entry is a way to update the contact's address only,
as the example does not allow the user to change the name of an
existing contact.
Firstly, we obtain the active tab's QTableView object using
QTabWidget::currentWidget(). Then we extract the
\c selectionModel from the \c tableView to obtain the selected
indexes.
\snippet itemviews/addressbook/addresswidget.cpp 4a
Next we extract data from the row the user intends to
edit. This data is displayed in an instance of \c AddDialog
with a different window title. The \c table is only
updated if changes have been made to data in \c aDialog.
\snippet itemviews/addressbook/addresswidget.cpp 4b
\image addressbook-editdialog.png Screenshot of Dialog to Edit a Contact
Entries are removed using the \c removeEntry() function.
The selected row is removed by accessing it through the
QItemSelectionModel object, \c selectionModel. The
\c newAddressTab is re-added to the \c AddressWidget only if
the user removes all the contacts in the address book.
\snippet itemviews/addressbook/addresswidget.cpp 5
The \c writeToFile() function is used to save a file containing
all the contacts in the address book. The file is saved in a
custom \c{.dat} format. The contents of the list of contacts
are written to \c file using QDataStream. If the file cannot be
opened, a QMessageBox is displayed with the related error message.
\snippet itemviews/addressbook/addresswidget.cpp 6
The \c readFromFile() function loads a file containing all the
contacts in the address book, previously saved using
\c writeToFile(). QDataStream is used to read the contents of a
\c{.dat} file into a list of contacts and each of these is added
using \c addEntry().
\snippet itemviews/addressbook/addresswidget.cpp 7
\section1 NewAddressTab Class Definition
The \c NewAddressTab class provides an informative tab telling
the user that the address book is empty. It appears and
disappears according to the contents of the address book, as
mentioned in \c{AddressWidget}'s implementation.
\image addressbook-newaddresstab.png Screenshot of NewAddressTab
The \c NewAddressTab class extends QWidget and contains a QLabel
and QPushButton.
\snippet itemviews/addressbook/newaddresstab.h 0
\section1 NewAddressTab Class Implementation
The constructor instantiates the \c addButton,
\c descriptionLabel and connects the \c{addButton}'s signal to
the \c{addEntry()} slot.
\snippet itemviews/addressbook/newaddresstab.cpp 0
The \c addEntry() function is similar to \c AddressWidget's
\c addEntry() in the sense that both functions instantiate an
\c AddDialog object. Data from the dialog is extracted and sent
to \c AddressWidget's \c addEntry() slot by emitting the
\c sendDetails() signal.
\snippet itemviews/addressbook/newaddresstab.cpp 1
\image signals-n-slots-aw-nat.png
\section1 AddDialog Class Definition
The \c AddDialog class extends QDialog and provides the user
with a QLineEdit and a QTextEdit to input data into the
address book.
\snippet itemviews/addressbook/adddialog.h 0
\image addressbook-adddialog.png
\section1 AddDialog Class Implementation
The \c AddDialog's constructor sets up the user interface,
creating the necessary widgets and placing them into layouts.
\snippet itemviews/addressbook/adddialog.cpp 0
To give the dialog the desired behavior, we connect the \uicontrol OK
and \uicontrol Cancel buttons to the dialog's \l{QDialog::}{accept()} and
\l{QDialog::}{reject()} slots. Since the dialog only acts as a
container for name and address information, we do not need to
implement any other functions for it.
\section1 MainWindow Class Definition
The \c MainWindow class extends QMainWindow and implements the
menus and actions necessary to manipulate the address book.
\table
\row \li \inlineimage addressbook-filemenu.png
\li \inlineimage addressbook-toolsmenu.png
\endtable
\snippet itemviews/addressbook/mainwindow.h 0
The \c MainWindow class uses an \c AddressWidget as its central
widget and provides the File menu with \uicontrol Open, \uicontrol Close and
\uicontrol Exit actions, as well as the \uicontrol Tools menu with
\uicontrol{Add Entry...}, \uicontrol{Edit Entry...} and \uicontrol{Remove Entry}
actions.
\section1 MainWindow Class Implementation
The constructor for \c MainWindow instantiates AddressWidget,
sets it as its central widget and calls the \c createMenus()
function.
\snippet itemviews/addressbook/mainwindow.cpp 0
The \c createMenus() function sets up the \uicontrol File and
\uicontrol Tools menus, connecting the actions to their respective slots.
Both the \uicontrol{Edit Entry...} and \uicontrol{Remove Entry} actions are
disabled by default as such actions cannot be carried out on an empty
address book. They are only enabled when one or more contacts
are added.
\snippet itemviews/addressbook/mainwindow.cpp 1a
\dots
\codeline
\snippet itemviews/addressbook/mainwindow.cpp 1b
Apart from connecting all the actions' signals to their
respective slots, we also connect \c AddressWidget's
\c selectionChanged() signal to its \c updateActions() slot.
The \c openFile() function opens a custom \c{addressbook.dat} file that
contains address book contacts. This function is a slot connected to
\c openAct in the \uicontrol File menu.
\snippet itemviews/addressbook/mainwindow.cpp 2
The \c saveFile() function saves a custom \c{addressbook.dat} file that
will contain the address book contacts. This function is a slot connected
to \c saveAct in the \uicontrol File menu.
\snippet itemviews/addressbook/mainwindow.cpp 3
The \c updateActions() function enables and disables
\uicontrol{Edit Entry...} and \uicontrol{Remove Entry} depending on the contents of
the address book. If the address book is empty, these actions
are disabled; otherwise, they are enabled. This function is a slot
is connected to the \c AddressWidget's \c selectionChanged()
signal.
\snippet itemviews/addressbook/mainwindow.cpp 4
\section1 main() Function
The main function for the address book instantiates QApplication
and opens a \c MainWindow before running the event loop.
\snippet itemviews/addressbook/main.cpp 0
*/

View File

@ -0,0 +1,26 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/affine
\title Affine Transformations
\ingroup examples-painting
\brief Demonstrates how affine transformations in QPainter works.
\brief In this example we show Qt's ability to perform affine transformations
on painting operations.
\image affine-demo.png
Transformations can be performed on any kind of graphics drawn using QPainter.
The transformations used to display the vector graphics, images, and text can be adjusted
in the following ways:
\list
\li Dragging the red circle in the centre of each drawing moves it to a new position.
\li Dragging the displaced red circle causes the current drawing to be rotated about the
central circle. Rotation can also be controlled with the \uicontrol Rotate slider.
\li Scaling is controlled with the \uicontrol Scale slider.
\li Each drawing can be sheared with the \uicontrol Shear slider.
\endlist
*/

View File

@ -0,0 +1,134 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/analogclock
\examplecategory {Graphics}
\meta tags {widgets}
\title Analog Clock
\ingroup examples-widgets
\brief The Analog Clock example shows how to draw the contents of a
custom widget.
\borderedimage analogclock-example.png
\caption Screenshot of the Analog Clock example
This example also demonstrates how the transformation and scaling
features of QPainter can be used to make drawing custom widgets
easier.
\section1 AnalogClock Class Definition
The \c AnalogClock class provides a clock widget with hour and minute
hands that is automatically updated every few seconds.
We subclass \l QWidget and reimplement the standard
\l{QWidget::paintEvent()}{paintEvent()} function to draw the clock face:
\snippet widgets/analogclock/analogclock.h 0
\section1 AnalogClock Class Implementation
\snippet widgets/analogclock/analogclock.cpp 1
When the widget is constructed, we set up a one-second timer to
keep track of the current time, and we connect it to the standard
\l{QWidget::update()}{update()} slot so that the clock face is
updated when the timer emits the \l{QTimer::timeout()}{timeout()}
signal.
Finally, we resize the widget so that it is displayed at a
reasonable size.
\snippet widgets/analogclock/analogclock.cpp 8
\snippet widgets/analogclock/analogclock.cpp 10
The \c paintEvent() function is called whenever the widget's
contents need to be updated. This happens when the widget is
first shown, and when it is covered then exposed, but it is also
executed when the widget's \l{QWidget::update()}{update()} slot
is called. Since we connected the timer's
\l{QTimer::timeout()}{timeout()} signal to this slot, it will be
called at least once every five seconds.
Before we set up the painter and draw the clock, we first define
two lists of \l {QPoint}s and two \l{QColor}s that will be used
for the hour and minute hands. The minute hand's color has an
alpha component of 191, meaning that it's 75% opaque.
We also determine the length of the widget's shortest side so that we
can fit the clock face inside the widget. It is also useful to determine
the current time before we start drawing.
\snippet widgets/analogclock/analogclock.cpp 11
\snippet widgets/analogclock/analogclock.cpp 12
\snippet widgets/analogclock/analogclock.cpp 13
\snippet widgets/analogclock/analogclock.cpp 14
The contents of custom widgets are drawn with a QPainter.
Painters can be used to draw on any QPaintDevice, but they are
usually used with widgets, so we pass the widget instance to the
painter's constructor.
We call QPainter::setRenderHint() with QPainter::Antialiasing to
turn on antialiasing. This makes drawing of diagonal lines much
smoother.
The translation moves the origin to the center of the widget, and
the scale operation ensures that the following drawing operations
are scaled to fit within the widget. We use a scale factor that
let's us use x and y coordinates between -100 and 100, and that
ensures that these lie within the length of the widget's shortest
side.
To make our code simpler, we will draw a fixed size clock face that will
be positioned and scaled so that it lies in the center of the widget.
The painter takes care of all the transformations made during the
paint event, and ensures that everything is drawn correctly. Letting
the painter handle transformations is often easier than performing
manual calculations just to draw the contents of a custom widget.
\image analogclock-viewport.png
We draw the hour hand first, using a formula that rotates the coordinate
system counterclockwise by a number of degrees determined by the current
hour and minute. This means that the hand will be shown rotated clockwise
by the required amount.
\snippet widgets/analogclock/analogclock.cpp 15
\snippet widgets/analogclock/analogclock.cpp 16
We set the pen to be Qt::NoPen because we don't want any outline,
and we use a solid brush with the color appropriate for
displaying hours. Brushes are used when filling in polygons and
other geometric shapes.
\snippet widgets/analogclock/analogclock.cpp 17
\snippet widgets/analogclock/analogclock.cpp 19
We save and restore the transformation matrix before and after the
rotation because we want to place the minute hand without having to
take into account any previous rotations.
\snippet widgets/analogclock/analogclock.cpp 20
\codeline
\snippet widgets/analogclock/analogclock.cpp 21
We draw markers around the edge of the clock for each hour. We
draw each marker then rotate the coordinate system so that the
painter is ready for the next one.
\snippet widgets/analogclock/analogclock.cpp 22
\snippet widgets/analogclock/analogclock.cpp 23
The minute hand is rotated in a similar way to the hour hand.
\snippet widgets/analogclock/analogclock.cpp 25
\codeline
\snippet widgets/analogclock/analogclock.cpp 26
Again, we draw markers around the edge of the clock, but this
time to indicate minutes. We skip multiples of 5 to avoid drawing
minute markers on top of hour markers.
*/

View File

@ -0,0 +1,370 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example mainwindows/application
\title Qt Widgets - Application Example
\ingroup examples-mainwindow
\brief The Application example shows how to implement a standard
widget application with menus, toolbars, and a status bar. The example
itself is a simple text editor program built around QPlainTextEdit.
\image application.png Screenshot of the Application example
Nearly all of the code for the Application example is in the \c
MainWindow class, which inherits QMainWindow. QMainWindow
provides the framework for windows that have menus, toolbars,
dock windows, and a status bar. The application provides
\uicontrol{File}, \uicontrol{Edit}, and \uicontrol{Help} entries in the menu
bar, with the following popup menus:
\image application-menus.png The Application example's menu system
The status bar at the bottom of the main window shows a
description of the menu item or toolbar button under the cursor.
To keep the example simple, recently opened files aren't shown in
the \uicontrol{File} menu, even though this feature is desired in 90%
of applications. Furthermore, this example can only load one file at a
time. The \l{mainwindows/mdi}{MDI} example shows how to lift these
restrictions and how to implement recently opened files handling.
\section1 MainWindow Class Definition
Here's the class definition:
\snippet mainwindows/application/mainwindow.h 0
The public API is restricted to the constructor. In the \c
protected section, we reimplement QWidget::closeEvent() to detect
when the user attempts to close the window, and warn the user
about unsaved changes. In the \c{private slots} section, we
declare slots that correspond to menu entries, as well as a
mysterious \c documentWasModified() slot. Finally, in the \c
private section of the class, we have various members that will
be explained in due time.
\section1 MainWindow Class Implementation
\snippet mainwindows/application/mainwindow.cpp 0
We start by including \c <QtWidgets>, a header file that contains the
definition of all classes in the Qt Core, Qt GUI and Qt Widgets
modules. This saves us from the trouble of having to include
every class individually. We also include \c mainwindow.h.
You might wonder why we don't include \c <QtWidgets> in \c
mainwindow.h and be done with it. The reason is that including
such a large header from another header file can rapidly degrade
performances. Here, it wouldn't do any harm, but it's still
generally a good idea to include only the header files that are
strictly necessary from another header file.
\snippet mainwindows/application/mainwindow.cpp 1
\snippet mainwindows/application/mainwindow.cpp 2
In the constructor, we start by creating a QPlainTextEdit widget as a
child of the main window (the \c this object). Then we call
QMainWindow::setCentralWidget() to tell that this is going to be
the widget that occupies the central area of the main window,
between the toolbars and the status bar.
Then we call \c createActions() and \c createStatusBar(), two private
functions that set up the user interface. After that, we call \c
readSettings() to restore the user's preferences.
We establish a signal-slot connection between the QPlainTextEdit's
document object and our \c documentWasModified() slot. Whenever
the user modifies the text in the QPlainTextEdit, we want to update
the title bar to show that the file was modified.
At the end, we set the window title using the private
\c setCurrentFile() function. We'll come back to this later.
\target close event handler
\snippet mainwindows/application/mainwindow.cpp 3
\snippet mainwindows/application/mainwindow.cpp 4
When the user attempts to close the window, we call the private
function \c maybeSave() to give the user the possibility to save
pending changes. The function returns true if the user wants the
application to close; otherwise, it returns false. In the first
case, we save the user's preferences to disk and accept the close
event; in the second case, we ignore the close event, meaning
that the application will stay up and running as if nothing
happened.
\snippet mainwindows/application/mainwindow.cpp 5
\snippet mainwindows/application/mainwindow.cpp 6
The \c newFile() slot is invoked when the user selects
\uicontrol{File|New} from the menu. We call \c maybeSave() to save any
pending changes and if the user accepts to go on, we clear the
QPlainTextEdit and call the private function \c setCurrentFile() to
update the window title and clear the
\l{QWidget::windowModified}{windowModified} flag.
\snippet mainwindows/application/mainwindow.cpp 7
\snippet mainwindows/application/mainwindow.cpp 8
The \c open() slot is invoked when the user clicks
\uicontrol{File|Open}. We pop up a QFileDialog asking the user to
choose a file. If the user chooses a file (i.e., \c fileName is
not an empty string), we call the private function \c loadFile()
to actually load the file.
\snippet mainwindows/application/mainwindow.cpp 9
\snippet mainwindows/application/mainwindow.cpp 10
The \c save() slot is invoked when the user clicks
\uicontrol{File|Save}. If the user hasn't provided a name for the file
yet, we call \c saveAs(); otherwise, we call the private function
\c saveFile() to actually save the file.
\snippet mainwindows/application/mainwindow.cpp 11
\snippet mainwindows/application/mainwindow.cpp 12
In \c saveAs(), we start by popping up a QFileDialog asking the
user to provide a name. If the user clicks \uicontrol{Cancel}, the
returned file name is empty, and we do nothing.
\snippet mainwindows/application/mainwindow.cpp 13
\snippet mainwindows/application/mainwindow.cpp 14
The application's About box is done using one statement, using
the QMessageBox::about() static function and relying on its
support for an HTML subset.
The \l{QObject::tr()}{tr()} call around the literal string marks
the string for translation. It is a good habit to call
\l{QObject::tr()}{tr()} on all user-visible strings, in case you
later decide to translate your application to other languages.
The \l{Internationalization with Qt} overview covers
\l{QObject::tr()}{tr()} in more detail.
\snippet mainwindows/application/mainwindow.cpp 15
\snippet mainwindows/application/mainwindow.cpp 16
The \c documentWasModified() slot is invoked each time the text
in the QPlainTextEdit changes because of user edits. We call
QWidget::setWindowModified() to make the title bar show that the
file was modified. How this is done varies on each platform.
\snippet mainwindows/application/mainwindow.cpp 17
\snippet mainwindows/application/mainwindow.cpp 18
\dots
\snippet mainwindows/application/mainwindow.cpp 22
The \c createActions() private function, which is called from the
\c MainWindow constructor, creates \l{QAction}s and populates
the menus and two toolbars. The code is very
repetitive, so we show only the actions corresponding to
\uicontrol{File|New}, \uicontrol{File|Open}, and \uicontrol{Help|About Qt}.
A QAction is an object that represents one user action, such as
saving a file or invoking a dialog. An action can be put in a
QMenu or a QToolBar, or both, or in any other widget that
reimplements QWidget::actionEvent().
An action has a text that is shown in the menu, an icon, a
shortcut key, a tooltip, a status tip (shown in the status bar),
a "What's This?" text, and more. It emits a
\l{QAction::triggered()}{triggered()} signal whenever the user
invokes the action (e.g., by clicking the associated menu item or
toolbar button).
Instances of QAction can be created by passing a parent QObject or
by using one of the convenience functions of QMenu, QMenuBar or QToolBar.
We create the actions that are in a menu as well as in a toolbar
parented on the window to prevent ownership issues. For actions
that are only in the menu, we use the convenience function
QMenu::addAction(), which allows us to pass text, icon and the
target object and its slot member function.
Creating toolbars is very similar to creating menus. The same
actions that we put in the menus can be reused in the toolbars.
After creating the action, we add it to the toolbar using
QToolBar::addAction().
The code above contains one more idiom that must be explained.
For some of the actions, we specify an icon as a QIcon to the
QAction constructor. We use QIcon::fromTheme() to obtain
the correct standard icon from the underlying window system.
If that fails due to the platform not supporting it, we
pass a file name as fallback. Here, the file name starts
with \c{:}. Such file names aren't ordinary file names, but
rather path in the executable's stored resources. We'll come back
to this when we review the \c application.qrc file that's part of
the project.
\snippet mainwindows/application/mainwindow.cpp 23
\snippet mainwindows/application/mainwindow.cpp 24
The \uicontrol{Edit|Cut} and \uicontrol{Edit|Copy} actions must be available
only when the QPlainTextEdit contains selected text. We disable them
by default and connect the QPlainTextEdit::copyAvailable() signal to
the QAction::setEnabled() slot, ensuring that the actions are
disabled when the text editor has no selection.
Just before we create the \uicontrol{Help} menu, we call
QMenuBar::addSeparator(). This has no effect for most widget
styles (e.g., Windows and \macos styles), but for some
styles this makes sure that \uicontrol{Help} is pushed to the right
side of the menu bar.
\snippet mainwindows/application/mainwindow.cpp 32
\snippet mainwindows/application/mainwindow.cpp 33
QMainWindow::statusBar() returns a pointer to the main window's
QStatusBar widget. Like with \l{QMainWindow::menuBar()}, the
widget is automatically created the first time the function is
called.
\snippet mainwindows/application/mainwindow.cpp 34
\snippet mainwindows/application/mainwindow.cpp 36
The \c readSettings() function is called from the constructor to
load the user's preferences and other application settings. The
QSettings class provides a high-level interface for storing
settings permanently on disk. On Windows, it uses the (in)famous
Windows registry; on \macos, it uses the native XML-based
CFPreferences API; on Unix/X11, it uses text files.
The QSettings constructor takes arguments that identify your
company and the name of the product. This ensures that the
settings for different applications are kept separately.
We use QSettings::value() to extract the value of the geometry setting.
The second argument to QSettings::value() is
optional and specifies a default value for the setting if there
exists none. This value is used the first time the application is
run.
We use QWidget::saveGeometry() and Widget::restoreGeometry() to
save the position. They use an opaque QByteArray to store
screen number, geometry and window state.
\snippet mainwindows/application/mainwindow.cpp 37
\snippet mainwindows/application/mainwindow.cpp 39
The \c writeSettings() function is called from \c closeEvent().
Writing settings is similar to reading them, except simpler. The
arguments to the QSettings constructor must be the same as in \c
readSettings().
\snippet mainwindows/application/mainwindow.cpp 40
\snippet mainwindows/application/mainwindow.cpp 41
The \c maybeSave() function is called to save pending changes. If
there are pending changes, it pops up a QMessageBox giving the
user to save the document. The options are QMessageBox::Yes,
QMessageBox::No, and QMessageBox::Cancel. The \uicontrol{Yes} button is
made the default button (the button that is invoked when the user
presses \uicontrol{Return}) using the QMessageBox::Default flag; the
\uicontrol{Cancel} button is made the escape button (the button that is
invoked when the user presses \uicontrol{Esc}) using the
QMessageBox::Escape flag.
The \c maybeSave() function returns \c true in all cases, except
when the user clicks \uicontrol{Cancel} or saving the file fails.
The caller must check the return value and stop whatever it was
doing if the return value is \c false.
\snippet mainwindows/application/mainwindow.cpp 42
\snippet mainwindows/application/mainwindow.cpp 43
In \c loadFile(), we use QFile and QTextStream to read in the
data. The QFile object provides access to the bytes stored in a
file.
We start by opening the file in read-only mode. The QFile::Text
flag indicates that the file is a text file, not a binary file.
On Unix and \macos, this makes no difference, but on Windows,
it ensures that the "\\r\\n" end-of-line sequence is converted to
"\\n" when reading.
If we successfully opened the file, we use a QTextStream object
to read in the data. QTextStream automatically converts the 8-bit
data into a Unicode QString and supports various encodings. If no
encoding is specified, QTextStream assumes the file is encoded in
UTF-8.
Since the call to QTextStream::readAll() might take some time, we
set the cursor to be Qt::WaitCursor for the entire application
while it goes on.
At the end, we call the private \c setCurrentFile() function,
which we'll cover in a moment, and we display the string "File
loaded" in the status bar for 2 seconds (2000 milliseconds).
\snippet mainwindows/application/mainwindow.cpp 44
\snippet mainwindows/application/mainwindow.cpp 45
Saving a file is similar to loading one. We use QSaveFile to ensure
all data are safely written and existing files are not damaged
should writing fail.
We use the QFile::Text flag to make sure that on Windows, "\\n"
is converted into "\\r\\n" to conform to the Windows convention.
\snippet mainwindows/application/mainwindow.cpp 46
\snippet mainwindows/application/mainwindow.cpp 47
The \c setCurrentFile() function is called to reset the state of
a few variables when a file is loaded or saved, or when the user
starts editing a new file (in which case \c fileName is empty).
We update the \c curFile variable, clear the
QTextDocument::modified flag and the associated \c
QWidget:windowModified flag, and update the window title to
contain the new file name (or \c untitled.txt).
The \c strippedName() function call around \c curFile in the
QWidget::setWindowTitle() call shortens the file name to exclude
the path. Here's the function:
\snippet mainwindows/application/mainwindow.cpp 48
\snippet mainwindows/application/mainwindow.cpp 49
\section1 The main() Function
The \c main() function for this application is typical of
applications that contain one main window:
\snippet mainwindows/application/main.cpp 0
The main function uses QCommandLineParser to check whether some file
argument was passed to the application and loads it via
MainWindow::loadFile().
\section1 The Resource File
As you will probably recall, for some of the actions, we
specified icons with file names starting with \c{:} and mentioned
that such file names aren't ordinary file names, but path in the
executable's stored resources. These resources are compiled
The resources associated with an application are specified in a
\c .qrc file, an XML-based file format that lists files on the
disk. Here's the \c application.qrc file that's used by the
Application example:
\quotefile mainwindows/application/application.qrc
The \c .png files listed in the \c application.qrc file are files
that are part of the Application example's source tree. Paths are
relative to the directory where the \c application.qrc file is
located (the \c mainwindows/application directory).
The resource file must be mentioned in the \c application.pro
file so that \c qmake knows about it:
\snippet mainwindows/application/application.pro 0
\c qmake will produce make rules to generate a file called \c
qrc_application.cpp that is linked into the application. This
file contains all the data for the images and other resources as
static C++ arrays of compressed binary data. See
\l{resources.html}{The Qt Resource System} for more information
about resources.
*/

View File

@ -0,0 +1,434 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/basicdrawing
\title Basic Drawing Example
\ingroup examples-painting
\brief The Basic Drawing example shows how to display basic
graphics primitives in a variety of styles using the QPainter
class.
\brief The Basic Drawing example shows how to display basic graphics
primitives in a variety of styles using the QPainter class.
QPainter performs low-level painting on widgets and other paint
devices. The class can draw everything from simple lines to
complex shapes like pies and chords. It can also draw aligned text
and pixmaps. Normally, it draws in a "natural" coordinate system,
but it can in addition do view and world transformation.
\image basicdrawing-example.png
The example provides a render area, displaying the currently
active shape, and lets the user manipulate the rendered shape and
its appearance using the QPainter parameters: The user can change
the active shape (\uicontrol Shape), and modify the QPainter's pen (\uicontrol
{Pen Width}, \uicontrol {Pen Style}, \uicontrol {Pen Cap}, \uicontrol {Pen Join}),
brush (\uicontrol {Brush Style}) and render hints (\uicontrol
Antialiasing). In addition the user can rotate a shape (\uicontrol
Transformations); behind the scenes we use QPainter's ability to
manipulate the coordinate system to perform the rotation.
The Basic Drawing example consists of two classes:
\list
\li \c RenderArea is a custom widget that renders multiple
copies of the currently active shape.
\li \c Window is the application's main window displaying a
\c RenderArea widget in addition to several parameter widgets.
\endlist
First we will review the \c Window class, then we will take a
look at the \c RenderArea class.
\section1 Window Class Definition
The Window class inherits QWidget, and is the application's main
window displaying a \c RenderArea widget in addition to several
parameter widgets.
\snippet painting/basicdrawing/window.h 0
We declare the various widgets, and three private slots updating
the \c RenderArea widget: The \c shapeChanged() slot updates the
\c RenderArea widget when the user changes the currently active
shape. We call the \c penChanged() slot when either of the
QPainter's pen parameters changes. And the \c brushChanged() slot
updates the \c RenderArea widget when the user changes the
painter's brush style.
\section1 Window Class Implementation
In the constructor we create and initialize the various widgets
appearing in the main application window.
\snippet painting/basicdrawing/window.cpp 1
First we create the \c RenderArea widget that will render the
currently active shape. Then we create the \uicontrol Shape combobox,
and add the associated items (i.e. the different shapes a QPainter
can draw).
\snippet painting/basicdrawing/window.cpp 2
QPainter's pen is a QPen object; the QPen class defines how a
painter should draw lines and outlines of shapes. A pen has
several properties: Width, style, cap and join.
A pen's width can be \e zero or greater, but the most common width
is zero. Note that this doesn't mean 0 pixels, but implies that
the shape is drawn as smoothly as possible although perhaps not
mathematically correct.
We create a QSpinBox for the \uicontrol {Pen Width} parameter.
\snippet painting/basicdrawing/window.cpp 3
The pen style defines the line type. The default style is solid
(Qt::SolidLine). Setting the style to none (Qt::NoPen) tells the
painter to not draw lines or outlines. The pen cap defines how
the end points of lines are drawn. And the pen join defines how
two lines join when multiple connected lines are drawn. The cap
and join only apply to lines with a width of 1 pixel or greater.
We create \l {QComboBox}es for each of the \uicontrol {Pen Style}, \uicontrol
{Pen Cap} and \uicontrol {Pen Join} parameters, and adds the associated
items (i.e the values of the Qt::PenStyle, Qt::PenCapStyle and
Qt::PenJoinStyle enums respectively).
\snippet painting/basicdrawing/window.cpp 4
The QBrush class defines the fill pattern of shapes drawn by a
QPainter. The default brush style is Qt::NoBrush. This style tells
the painter to not fill shapes. The standard style for filling is
Qt::SolidPattern.
We create a QComboBox for the \uicontrol {Brush Style} parameter, and add
the associated items (i.e. the values of the Qt::BrushStyle enum).
\snippet painting/basicdrawing/window.cpp 5
\snippet painting/basicdrawing/window.cpp 6
Antialiasing is a feature that "smoothes" the pixels to create
more even and less jagged lines, and can be applied using
QPainter's render hints. QPainter::RenderHints are used to specify
flags to QPainter that may or may not be respected by any given
engine.
We simply create a QCheckBox for the \uicontrol Antialiasing option.
\snippet painting/basicdrawing/window.cpp 7
The \uicontrol Transformations option implies a manipulation of the
coordinate system that will appear as if the rendered shape is
rotated in three dimensions.
We use the QPainter::translate(), QPainter::rotate() and
QPainter::scale() functions to implement this feature represented
in the main application window by a simple QCheckBox.
\snippet painting/basicdrawing/window.cpp 8
Then we connect the parameter widgets with their associated slots
using the static QObject::connect() function, ensuring that the \c
RenderArea widget is updated whenever the user changes the shape,
or any of the other parameters.
\snippet painting/basicdrawing/window.cpp 9
\snippet painting/basicdrawing/window.cpp 10
Finally, we add the various widgets to a layout, and call the \c
shapeChanged(), \c penChanged(), and \c brushChanged() slots to
initialize the application. We also turn on antialiasing.
\snippet painting/basicdrawing/window.cpp 11
The \c shapeChanged() slot is called whenever the user changes the
currently active shape.
First we retrieve the shape the user has chosen using the
QComboBox::itemData() function. This function returns the data for
the given role in the given index in the combobox. We use
QComboBox::currentIndex() to retrieve the index of the shape, and
the role is defined by the Qt::ItemDataRole enum; \c IdRole is an
alias for Qt::UserRole.
Note that Qt::UserRole is only the first role that can be used for
application-specific purposes. If you need to store different data
in the same index, you can use different roles by simply
incrementing the value of Qt::UserRole, for example: 'Qt::UserRole
+ 1' and 'Qt::UserRole + 2'. However, it is a good programming
practice to give each role their own name: 'myFirstRole =
Qt::UserRole + 1' and 'mySecondRole = Qt::UserRole + 2'. Even
though we only need a single role in this particular example, we
add the following line of code to the beginning of the \c
window.cpp file.
\snippet painting/basicdrawing/window.cpp 0
The QComboBox::itemData() function returns the data as a QVariant,
so we need to cast the data to \c RenderArea::Shape. If there is
no data for the given role, the function returns
QVariant::Invalid.
In the end we call the \c RenderArea::setShape() slot to update
the \c RenderArea widget.
\snippet painting/basicdrawing/window.cpp 12
We call the \c penChanged() slot whenever the user changes any of
the pen parameters. Again we use the QComboBox::itemData()
function to retrieve the parameters, and then we call the \c
RenderArea::setPen() slot to update the \c RenderArea widget.
\snippet painting/basicdrawing/window.cpp 13
The brushChanged() slot is called whenever the user changes the
brush parameter which we retrieve using the QComboBox::itemData()
function as before.
\snippet painting/basicdrawing/window.cpp 14
If the brush parameter is a gradient fill, special actions are
required.
The QGradient class is used in combination with QBrush to specify
gradient fills. Qt currently supports three types of gradient
fills: linear, radial and conical. Each of these is represented by
a subclass of QGradient: QLinearGradient, QRadialGradient and
QConicalGradient.
So if the brush style is Qt::LinearGradientPattern, we first
create a QLinearGradient object with interpolation area between
the coordinates passed as arguments to the constructor. The
positions are specified using logical coordinates. Then we set the
gradient's colors using the QGradient::setColorAt() function. The
colors is defined using stop points which are composed by a
position (between 0 and 1) and a QColor. The set of stop points
describes how the gradient area should be filled. A gradient can
have an arbitrary number of stop points.
In the end we call \c RenderArea::setBrush() slot to update the \c
RenderArea widget's brush with the QLinearGradient object.
\snippet painting/basicdrawing/window.cpp 15
A similar pattern of actions, as the one used for QLinearGradient,
is used in the cases of Qt::RadialGradientPattern and
Qt::ConicalGradientPattern.
The only difference is the arguments passed to the constructor:
Regarding the QRadialGradient constructor the first argument is
the center, and the second the radial gradient's radius. The third
argument is optional, but can be used to define the focal point of
the gradient inside the circle (the default focal point is the
circle center). Regarding the QConicalGradient constructor, the
first argument specifies the center of the conical, and the second
specifies the start angle of the interpolation.
\snippet painting/basicdrawing/window.cpp 16
If the brush style is Qt::TexturePattern we create a QBrush from a
QPixmap. Then we call \c RenderArea::setBrush() slot to update the
\c RenderArea widget with the newly created brush.
\snippet painting/basicdrawing/window.cpp 17
Otherwise we simply create a brush with the given style and a
green color, and then call \c RenderArea::setBrush() slot to
update the \c RenderArea widget with the newly created brush.
\section1 RenderArea Class Definition
The \c RenderArea class inherits QWidget, and renders multiple
copies of the currently active shape using a QPainter.
\snippet painting/basicdrawing/renderarea.h 0
First we define a public \c Shape enum to hold the different
shapes that can be rendered by the widget (i.e the shapes that can
be rendered by a QPainter). Then we reimplement the constructor as
well as two of QWidget's public functions: \l
{QWidget::minimumSizeHint()}{minimumSizeHint()} and \l
{QWidget::sizeHint()}{sizeHint()}.
We also reimplement the QWidget::paintEvent() function to be able
to draw the currently active shape according to the specified
parameters.
We declare several private slots: The \c setShape() slot changes
the \c RenderArea's shape, the \c setPen() and \c setBrush() slots
modify the widget's pen and brush, and the \c setAntialiased() and
\c setTransformed() slots modify the widget's respective
properties.
\section1 RenderArea Class Implementation
In the constructor we initialize some of the widget's variables.
\snippet painting/basicdrawing/renderarea.cpp 0
We set its shape to be a \uicontrol Polygon, its antialiased property to
be false and we load an image into the widget's pixmap
variable. In the end we set the widget's background role, defining
the brush from the widget's \l {QWidget::palette}{palette} that
will be used to render the background. QPalette::Base is typically
white.
\snippet painting/basicdrawing/renderarea.cpp 2
The \c RenderArea inherits QWidget's \l
{QWidget::sizeHint()}{sizeHint} property holding the recommended
size for the widget. If the value of this property is an invalid
size, no size is recommended.
The default implementation of the QWidget::sizeHint() function
returns an invalid size if there is no layout for the widget, and
returns the layout's preferred size otherwise.
Our reimplementation of the function returns a QSize with a 400
pixels width and a 200 pixels height.
\snippet painting/basicdrawing/renderarea.cpp 1
\c RenderArea also inherits QWidget's
\l{QWidget::minimumSizeHint()}{minimumSizeHint} property holding
the recommended minimum size for the widget. Again, if the value
of this property is an invalid size, no size is recommended.
The default implementation of QWidget::minimumSizeHint() returns
an invalid size if there is no layout for the widget, and returns
the layout's minimum size otherwise.
Our reimplementation of the function returns a QSize with a 100
pixels width and a 100 pixels height.
\snippet painting/basicdrawing/renderarea.cpp 3
\codeline
\snippet painting/basicdrawing/renderarea.cpp 4
\codeline
\snippet painting/basicdrawing/renderarea.cpp 5
The public \c setShape(), \c setPen() and \c setBrush() slots are
called whenever we want to modify a \c RenderArea widget's shape,
pen or brush. We set the shape, pen or brush according to the
slot parameter, and call QWidget::update() to make the changes
visible in the \c RenderArea widget.
The QWidget::update() slot does not cause an immediate
repaint; instead it schedules a paint event for processing when Qt
returns to the main event loop.
\snippet painting/basicdrawing/renderarea.cpp 6
\codeline
\snippet painting/basicdrawing/renderarea.cpp 7
With the \c setAntialiased() and \c setTransformed() slots we
change the state of the properties according to the slot
parameter, and call the QWidget::update() slot to make the changes
visible in the \c RenderArea widget.
\snippet painting/basicdrawing/renderarea.cpp 8
Then we reimplement the QWidget::paintEvent() function. The first
thing we do is to create the graphical objects we will need to
draw the various shapes.
We create a vector of four \l {QPoint}s. We use this vector to
render the \uicontrol Points, \uicontrol Polyline and \uicontrol Polygon
shapes. Then we create a QRect, defining a rectangle in the plane,
which we use as the bounding rectangle for all the shapes excluding
the \uicontrol Path and the \uicontrol Pixmap.
We also create a QPainterPath. The QPainterPath class provides a
container for painting operations, enabling graphical shapes to be
constructed and reused. A painter path is an object composed of a
number of graphical building blocks, such as rectangles, ellipses,
lines, and curves. For more information about the QPainterPath
class, see the \l {painting/painterpaths}{Painter Paths}
example. In this example, we create a painter path composed of one
straight line and a Bezier curve.
In addition we define a start angle and an arc length that we will
use when drawing the \uicontrol Arc, \uicontrol Chord and \uicontrol Pie shapes.
\snippet painting/basicdrawing/renderarea.cpp 9
We create a QPainter for the \c RenderArea widget, and set the
painters pen and brush according to the \c RenderArea's pen and
brush. If the \uicontrol Antialiasing parameter option is checked, we
also set the painter's render hints. QPainter::Antialiasing
indicates that the engine should antialias edges of primitives if
possible.
\snippet painting/basicdrawing/renderarea.cpp 10
Finally, we render the multiple copies of the \c RenderArea's
shape. The number of copies is depending on the size of the \c
RenderArea widget, and we calculate their positions using two \c
for loops and the widgets height and width.
For each copy we first save the current painter state (pushes the
state onto a stack). Then we translate the coordinate system,
using the QPainter::translate() function, to the position
determined by the variables of the \c for loops. If we omit this
translation of the coordinate system all the copies of the shape
will be rendered on top of each other in the top left cormer of
the \c RenderArea widget.
\snippet painting/basicdrawing/renderarea.cpp 11
If the \uicontrol Transformations parameter option is checked, we do an
additional translation of the coordinate system before we rotate
the coordinate system 60 degrees clockwise using the
QPainter::rotate() function and scale it down in size using the
QPainter::scale() function. In the end we translate the coordinate
system back to where it was before we rotated and scaled it.
Now, when rendering the shape, it will appear as if it was rotated
in three dimensions.
\snippet painting/basicdrawing/renderarea.cpp 12
Next, we identify the \c RenderArea's shape, and render it using
the associated QPainter drawing function:
\list
\li QPainter::drawLine(),
\li QPainter::drawPoints(),
\li QPainter::drawPolyline(),
\li QPainter::drawPolygon(),
\li QPainter::drawRect(),
\li QPainter::drawRoundedRect(),
\li QPainter::drawEllipse(),
\li QPainter::drawArc(),
\li QPainter::drawChord(),
\li QPainter::drawPie(),
\li QPainter::drawPath(),
\li QPainter::drawText() or
\li QPainter::drawPixmap()
\endlist
Before we started rendering, we saved the current painter state
(pushes the state onto a stack). The rationale for this is that we
calculate each shape copy's position relative to the same point in
the coordinate system. When translating the coordinate system, we
lose the knowledge of this point unless we save the current
painter state \e before we start the translating process.
\snippet painting/basicdrawing/renderarea.cpp 13
Then, when we are finished rendering a copy of the shape we can
restore the original painter state, with its associated coordinate
system, using the QPainter::restore() function. In this way we
ensure that the next shape copy will be rendered in the correct
position.
We could translate the coordinate system back using
QPainter::translate() instead of saving the painter state. But
since we in addition to translating the coordinate system (when
the \uicontrol Transformation parameter option is checked) both rotate
and scale the coordinate system, the easiest solution is to save
the current painter state.
*/

View File

@ -0,0 +1,142 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/basicgraphicslayouts
\title Basic Graphics Layouts Example
\ingroup examples-graphicsview-layout
\brief Demonstrates how to create basic graphics layout.
The Basic Graphics Layouts example shows how to use the layout classes
in QGraphicsView: QGraphicsLinearLayout and QGraphicsGridLayout.
In addition to that it shows how to write your own custom layout item.
\image basicgraphicslayouts-example.png Screenshot of the Basic Layouts Example
\section1 Window Class Definition
The \c Window class is a subclass of QGraphicsWidget. It has a
constructor with a QGraphicsWidget \a parent as its parameter.
\snippet graphicsview/basicgraphicslayouts/window.h 0
\section1 Window Class Implementation
The constructor of \c Window instantiates a QGraphicsLinearLayout object,
\c windowLayout, with vertical orientation. We instantiate another
QGraphicsLinearLayout object, \c linear, whose parent is \c windowLayout.
Next, we create a \c LayoutItem object, \c item and add it to \c linear
with the \l{QGraphicsLinearLayout::}{addItem()} function. We also provide
\c item with a \l{QGraphicsLinearLayout::setStretchFactor()}
{stretchFactor}.
\snippet graphicsview/basicgraphicslayouts/window.cpp 0
We repeat the process:
\list
\li create a new \c LayoutItem,
\li add the item \c linear, and
\li provide a stretch factor.
\endlist
\snippet graphicsview/basicgraphicslayouts/window.cpp 1
We then add \c linear to \c windowLayout, nesting two
QGraphicsLinearLayout objects. Apart from the QGraphicsLinearLayout, we
also use a QGraphicsGridLayout object, \c grid, which is a 4x3 grid with
some cells spanning to other rows.
We create seven \c LayoutItem objects and place them into \c grid with
the \l{QGraphicsGridLayout::}{addItem()} function as shown in the code
snippet below:
\snippet graphicsview/basicgraphicslayouts/window.cpp 2
The first item we add to \c grid is placed in the top left cell,
spanning four rows. The next two items are placed in the second column,
and they span two rows. Each item's \l{QGraphicsWidget::}{maximumHeight()}
and \l{QGraphicsWidget::}{minimumHeight()} are set to be equal so that
they do not expand vertically. As a result, these items will not
fit vertically in their cells. So, we specify that they should be
vertically aligned in the center of the cell using Qt::AlignVCenter.
Finally, \c grid itself is added to \c windowLayout. Unlike
QGridLayout::addItem(), QGraphicsGridLayout::addItem() requires a row
and a column for its argument, specifying which cell the item should be
positioned in. Also, if the \c rowSpan and \c columnSpan arguments
are omitted, they will default to 1.
Note that we do not specify a parent for each \c LayoutItem that we
construct, as all these items will be added to \c windowLayout. When we
add an item to a layout, it will be automatically reparented to the widget
on which the layout is installed.
\snippet graphicsview/basicgraphicslayouts/window.cpp 3
Now that we have set up \c grid and added it to \c windowLayout, we
install \c windowLayout onto the window object using
QGraphicsWidget::setLayout() and we set the window title.
\section1 LayoutItem Class Definition
The \c LayoutItem class is a subclass of QGraphicsLayoutItem and
QGraphicsItem. It has a constructor, a destructor, and some required
reimplementations.
Since it inherits QGraphicsLayoutItem it must reimplement
{QGraphicsLayoutItem::setGeometry()}{setGeometry()} and
{QGraphicsLayoutItem::sizeHint()}{sizeHint()}.
In addition to that it inherits QGraphicsItem, so it must reimplement
{QGraphicsItem::boundingRect()}{boundingRect()} and
{QGraphicsItem::paint()}{paint()}.
\snippet graphicsview/basicgraphicslayouts/layoutitem.h 0
The \c LayoutItem class also has a private instance of QPixmap, \c m_pix.
\section1 LayoutItem Class Implementation
In \c{LayoutItem}'s constructor, \c m_pix is instantiated and the
\c{block.png} image is loaded into it.
\snippet graphicsview/basicgraphicslayouts/layoutitem.cpp 0
We use the Q_UNUSED() macro to prevent the compiler from generating
warnings regarding unused parameters.
\snippet graphicsview/basicgraphicslayouts/layoutitem.cpp 1
The idea behind the \c paint() function is to paint the
background rect then paint a rect around the pixmap.
\snippet graphicsview/basicgraphicslayouts/layoutitem.cpp 2
The reimplementation of \l{QGraphicsItem::}{boundingRect()}
will set the top left corner at (0,0), and the size of it will be
the size of the layout items
\l{QGraphicsLayoutItem::}{geometry()}. This is the area that
we paint within.
\snippet graphicsview/basicgraphicslayouts/layoutitem.cpp 3
The reimplementation of \l{QGraphicsLayoutItem::setGeometry()}{setGeometry()}
simply calls its baseclass implementation. However, since this will change
the boundingRect we must also call
\l{QGraphicsItem::prepareGeometryChange()}{prepareGeometryChange()}.
Finally, we move the item according to \c geom.topLeft().
\snippet graphicsview/basicgraphicslayouts/layoutitem.cpp 4
Since we don't want the size of the item to be smaller than the pixmap, we
must make sure that we return a size hint that is larger than \c m_pix.
We also add some extra space around for borders that we will paint later.
Alternatively, you could scale the pixmap to prevent the item from
becoming smaller than the pixmap.
The preferred size is the same as the minimum size hint, while we set
maximum to be a large value
\snippet graphicsview/basicgraphicslayouts/layoutitem.cpp 5
*/

View File

@ -0,0 +1,170 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example layouts/basiclayouts
\title Basic Layouts Example
\brief Shows how to use the standard layout managers.
\e{Basic Layouts} shows how to use the standard layout managers that are
available in Qt: QBoxLayout, QGridLayout, and QFormLayout.
\image basiclayouts-example.png Screenshot of the Basic Layouts example
The QBoxLayout class lines up widgets horizontally or vertically.
QHBoxLayout and QVBoxLayout are convenience subclasses of QBoxLayout.
QGridLayout lays out widgets in cells by dividing the available space
into rows and columns. QFormLayout, on the other hand, sets its
children in a two-column form with labels in the left column and
input fields in the right column.
For more information, visit the \l{Layout Management} page.
\include examples-run.qdocinc
\section1 Dialog Class Definition
\snippet layouts/basiclayouts/dialog.h 0
The \c Dialog class inherits QDialog. It is a custom widget that
displays its child widgets using the geometry managers:
QHBoxLayout, QVBoxLayout, QGridLayout, and QFormLayout.
There are four private functions to simplify the class
constructor: the \c createMenu(), \c createHorizontalGroupBox(),
\c createGridGroupBox(), and \c createFormGroupBox() functions create
several widgets that the example uses to demonstrate how the layout
affects their appearances.
\section1 Dialog Class Implementation
\snippet layouts/basiclayouts/dialog.cpp 0
In the constructor, we first use the \c createMenu() function to
create and populate a menu bar and the \c createHorizontalGroupBox()
function to create a group box containing four buttons with a
horizontal layout. Next, we use the \c createGridGroupBox() function
to create a group box containing several line edits and a small text
editor which are displayed in a grid layout. Finally, we use the
\c createFormGroupBox() function to create a group box with
three labels and three input fields: a line edit, a combo box, and
a spin box.
\snippet layouts/basiclayouts/dialog.cpp 1
We also create a big text editor and a dialog button box. The
QDialogButtonBox class is a widget that presents buttons in a
layout that is appropriate to the current widget style. The
preferred buttons can be specified as arguments to the
constructor, using the QDialogButtonBox::StandardButtons enum.
Note that we don't have to specify a parent for the widgets when
we create them. The reason is that all the widgets we create here
will be added to a layout, and when we add a widget to a layout,
it is automatically reparented to the widget the layout is
installed on.
\snippet layouts/basiclayouts/dialog.cpp 2
The main layout is a QVBoxLayout object. QVBoxLayout is a
convenience class for a box layout with vertical orientation.
In general, the QBoxLayout class takes the space it gets (from its
parent layout or from the parent widget), divides it up into a
series of boxes, and makes each managed widget fill one box. If
the QBoxLayout's orientation is Qt::Horizontal the boxes are
placed in a row. If the orientation is Qt::Vertical, the boxes are
placed in a column. The corresponding convenience classes are
QHBoxLayout and QVBoxLayout, respectively.
\snippet layouts/basiclayouts/dialog.cpp 3
When we call the QLayout::setMenuBar() function, the layout places
the provided menu bar at the top of the parent widget, and outside
the widget's \l {QWidget::contentsRect()}{content margins}. All
child widgets are placed below the bottom edge of the menu bar.
\snippet layouts/basiclayouts/dialog.cpp 4
We use the QBoxLayout::addWidget() function to add the widgets to
the end of the layout. Each widget will get at least its minimum size
and at most its maximum size. It is possible to specify a stretch
factor in the \l {QBoxLayout::addWidget()}{addWidget()} function,
and any excess space is shared according to these stretch
factors. If not specified, a widget's stretch factor is 0.
\snippet layouts/basiclayouts/dialog.cpp 5
We install the main layout on the \c Dialog widget using the
QWidget::setLayout() function, and all of the layout's widgets are
automatically reparented to be children of the \c Dialog widget.
\snippet layouts/basiclayouts/dialog.cpp 6
In the private \c createMenu() function we create a menu bar, and
add a pull-down \uicontrol File menu containing an \uicontrol Exit option.
\snippet layouts/basiclayouts/dialog.cpp 7
When we create the horizontal group box, we use a QHBoxLayout as
the internal layout. We create the buttons we want to put in the
group box, add them to the layout and install the layout on the
group box.
\snippet layouts/basiclayouts/dialog.cpp 8
In the \c createGridGroupBox() function we use a QGridLayout which
lays out widgets in a grid. It takes the space made available to
it (by its parent layout or by the parent widget), divides it up
into rows and columns, and puts each widget it manages into the
correct cell.
\snippet layouts/basiclayouts/dialog.cpp 9
For each row in the grid we create a label and an associated line
edit, and add them to the layout. The QGridLayout::addWidget()
function differ from the corresponding function in QBoxLayout: It
needs the row and column specifying the grid cell to put the
widget in.
\snippet layouts/basiclayouts/dialog.cpp 10
QGridLayout::addWidget() can in addition take arguments
specifying the number of rows and columns the cell will be
spanning. In this example, we create a small editor which spans
three rows and one column.
For both the QBoxLayout::addWidget() and QGridLayout::addWidget()
functions it is also possible to add a last argument specifying
the widget's alignment. By default it fills the whole cell. But we
could, for example, align a widget with the right edge by
specifying the alignment to be Qt::AlignRight.
\snippet layouts/basiclayouts/dialog.cpp 11
Each column in a grid layout has a stretch factor. The stretch
factor is set using QGridLayout::setColumnStretch() and determines
how much of the available space the column will get over and above
its necessary minimum.
In this example, we set the stretch factors for columns 1 and 2.
The stretch factor is relative to the other columns in this grid;
columns with a higher stretch factor take more of the available
space. So column 2 in our grid layout will get more of the
available space than column 1, and column 0 will not grow at all
since its stretch factor is 0 (the default).
Columns and rows behave identically; there is an equivalent
stretch factor for rows, as well as a QGridLayout::setRowStretch()
function.
\snippet layouts/basiclayouts/dialog.cpp 12
In the \c createFormGroupBox() function, we use a QFormLayout
to neatly arrange objects into two columns - name and field.
There are three QLabel objects for names with three
corresponding input widgets as fields: a QLineEdit, a QComboBox
and a QSpinBox. Unlike QBoxLayout::addWidget() and
QGridLayout::addWidget(), we use QFormLayout::addRow() to add widgets
to the layout.
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/basicsortfiltermodel
\title Basic Sort/Filter Model Example
\ingroup examples-itemviews
\brief The Basic Sort/Filter Model example illustrates how to use
QSortFilterProxyModel to perform basic sorting and filtering.
\image basicsortfiltermodel-example.png Screenshot of the Basic Sort/Filter Model Example
*/

View File

@ -0,0 +1,20 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example effects/blurpicker
\title Blur Picker Effect Example
\ingroup examples-graphicsview-graphicseffects
\brief Demonstrates how to apply graphical effects on items in the view.
\image blurpickereffect-example.png
The Blur Picker example displays a circle of application icons.
All icons are blurred, except the one on the bottom left side of
the screen, which is the one in focus.
Clicking anywhere on the left side of the screen moves the icon
circle clockwise to the next icon
Clicking on the right side advances the circle counterclockwise.
\sa QGraphicsBlurEffect
*/

View File

@ -0,0 +1,46 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example layouts/borderlayout
\title Border Layout Example
\ingroup examples-layout
\brief Shows how to arrange child widgets along a border.
\e{Border Layout} implements a layout that arranges child widgets to
surround the main area.
\image borderlayout-example.png
The constructor of the Window class creates a QTextBrowser object,
to which a BorderLayout named \c layout is added. The declaration
of the BorderLayout class is quoted at the end of this document.
\quotefromfile layouts/borderlayout/window.cpp
\skipto Window::Window()
\printuntil BorderLayout
Several labeled widgets are added to \c layout with the orientation
\c {Center}, \c {North}, \c {West}, \c {East 1}, \c {East 2}, and
\c {South}.
\skipto layout->addWidget
\printuntil setWindowTitle
createLabel() in class \c Window sets the text of the labeled widgets
and the style.
\skipto QLabel *Window::createLabel
\printuntil /^\}/
Class BorderLayout contains all the utilitarian functions for formatting
the widgets it contains.
\quotefromfile layouts/borderlayout/borderlayout.h
\skipto class
\printuntil /^\}/
For more information, visit the \l{Layout Management} page.
\include examples-run.qdocinc
*/

View File

@ -0,0 +1,357 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/calculator
\title Calculator Example
\ingroup examples-widgets
\ingroup examples-layout
\brief The example shows how to use signals and slots to implement the
functionality of a calculator widget, and how to use QGridLayout
to place child widgets in a grid.
\borderedimage calculator-example.png
\caption Screenshot of the Calculator example
The example consists of two classes:
\list
\li \c Calculator is the calculator widget, with all the
calculator functionality.
\li \c Button is the widget used for each of the calculator
button. It derives from QToolButton.
\endlist
We will start by reviewing \c Calculator, then we will take a
look at \c Button.
\section1 Calculator Class Definition
\snippet widgets/calculator/calculator.h 0
The \c Calculator class provides a simple calculator widget. It
inherits from QDialog and has several private slots associated
with the calculator's buttons. QObject::eventFilter() is
reimplemented to handle mouse events on the calculator's display.
Buttons are grouped in categories according to their behavior.
For example, all the digit buttons (labeled \uicontrol 0 to \uicontrol 9)
append a digit to the current operand. For these, we connect
multiple buttons to the same slot (e.g., \c digitClicked()). The
categories are digits, unary operators (\uicontrol{Sqrt}, \uicontrol{x\unicode{178}},
\uicontrol{1/x}), additive operators (\uicontrol{+}, \uicontrol{-}), and
multiplicative operators (\uicontrol{\unicode{215}}, \uicontrol{\unicode{247}}). The other buttons
have their own slots.
\snippet widgets/calculator/calculator.h 1
\snippet widgets/calculator/calculator.h 2
The private \c createButton() function is used as part of the
widget construction. \c abortOperation() is called whenever a
division by zero occurs or when a square root operation is
applied to a negative number. \c calculate() applies a binary
operator (\uicontrol{+}, \uicontrol{-}, \uicontrol{\unicode{215}}, or \uicontrol{\unicode{247}}).
\snippet widgets/calculator/calculator.h 3
\snippet widgets/calculator/calculator.h 4
\snippet widgets/calculator/calculator.h 5
\snippet widgets/calculator/calculator.h 6
\snippet widgets/calculator/calculator.h 7
\snippet widgets/calculator/calculator.h 8
These variables, together with the contents of the calculator
display (a QLineEdit), encode the state of the calculator:
\list
\li \c sumInMemory contains the value stored in the calculator's memory
(using \uicontrol{MS}, \uicontrol{M+}, or \uicontrol{MC}).
\li \c sumSoFar stores the value accumulated so far. When the user
clicks \uicontrol{=}, \c sumSoFar is recomputed and shown on the
display. \uicontrol{Clear All} resets \c sumSoFar to zero.
\li \c factorSoFar stores a temporary value when doing
multiplications and divisions.
\li \c pendingAdditiveOperator stores the last additive operator
clicked by the user.
\li \c pendingMultiplicativeOperator stores the last multiplicative operator
clicked by the user.
\li \c waitingForOperand is \c true when the calculator is
expecting the user to start typing an operand.
\endlist
Additive and multiplicative operators are treated differently
because they have different precedences. For example, \uicontrol{1 + 2 \unicode{247}
3} is interpreted as \uicontrol{1 + (2 \unicode{247} 3)} because \uicontrol{\unicode{247}} has higher
precedence than \uicontrol{+}.
The table below shows the evolution of the calculator state as
the user enters a mathematical expression.
\table
\header \li User Input \li Display \li Sum so Far \li Add. Op. \li Factor so Far \li Mult. Op. \li Waiting for Operand?
\row \li \li 0 \li 0 \li \li \li \li \c true
\row \li \uicontrol{1} \li 1 \li 0 \li \li \li \li \c false
\row \li \uicontrol{1 +} \li 1 \li 1 \li \uicontrol{+} \li \li \li \c true
\row \li \uicontrol{1 + 2} \li 2 \li 1 \li \uicontrol{+} \li \li \li \c false
\row \li \uicontrol{1 + 2 \unicode{247}} \li 2 \li 1 \li \uicontrol{+} \li 2 \li \uicontrol{\unicode{247}} \li \c true
\row \li \uicontrol{1 + 2 \unicode{247} 3} \li 3 \li 1 \li \uicontrol{+} \li 2 \li \uicontrol{\unicode{247}} \li \c false
\row \li \uicontrol{1 + 2 \unicode{247} 3 -} \li 1.66667 \li 1.66667 \li \uicontrol{-} \li \li \li \c true
\row \li \uicontrol{1 + 2 \unicode{247} 3 - 4} \li 4 \li 1.66667 \li \uicontrol{-} \li \li \li \c false
\row \li \uicontrol{1 + 2 \unicode{247} 3 - 4 =} \li -2.33333 \li 0 \li \li \li \li \c true
\endtable
Unary operators, such as \uicontrol Sqrt, require no special handling;
they can be applied immediately since the operand is already
known when the operator button is clicked.
\snippet widgets/calculator/calculator.h 9
\codeline
\snippet widgets/calculator/calculator.h 10
Finally, we declare the variables associated with the display and the
buttons used to display numerals.
\section1 Calculator Class Implementation
\snippet widgets/calculator/calculator.cpp 0
In the constructor, we initialize the calculator's state. The \c
pendingAdditiveOperator and \c pendingMultiplicativeOperator
variables don't need to be initialized explicitly, because the
QString constructor initializes them to empty strings.
It is also possible to initialize those variable directly in the
header. This is called \c member-initializaton and avoids a long
initialization list.
\snippet widgets/calculator/calculator.cpp 1
\snippet widgets/calculator/calculator.cpp 2
We create the QLineEdit representing the calculator's display and
set up some of its properties. In particular, we set it to be
read-only.
We also enlarge \c{display}'s font by 8 points.
\snippet widgets/calculator/calculator.cpp 4
For each button, we call the private \c createButton() function with
the proper text label and a slot to connect to the button.
\snippet widgets/calculator/calculator.cpp 5
\snippet widgets/calculator/calculator.cpp 6
The layout is handled by a single QGridLayout. The
QLayout::setSizeConstraint() call ensures that the \c Calculator
widget is always shown as its optimal size (its
\l{QWidget::sizeHint()}{size hint}), preventing the user from
resizing the calculator. The size hint is determined by the size
and \l{QWidget::sizePolicy()}{size policy} of the child widgets.
Most child widgets occupy only one cell in the grid layout. For
these, we only need to pass a row and a column to
QGridLayout::addWidget(). The \c display, \c backspaceButton, \c
clearButton, and \c clearAllButton widgets occupy more than one
column; for these we must also pass a row span and a column
span.
\snippet widgets/calculator/calculator.cpp 7
Pressing one of the calculator's digit buttons will emit the
button's \l{QToolButton::clicked()}{clicked()} signal, which will
trigger the \c digitClicked() slot.
First, we find out which button sent the signal using
QObject::sender(). This function returns the sender as a QObject
pointer. Since we know that the sender is a \c Button object, we
can safely cast the QObject. We could have used a C-style cast or
a C++ \c static_cast<>(), but as a defensive programming
technique we use a \l qobject_cast(). The advantage is that if
the object has the wrong type, a null pointer is returned.
Crashes due to null pointers are much easier to diagnose than
crashes due to unsafe casts. Once we have the button, we extract
the operator using QToolButton::text().
The slot needs to consider two situations in particular. If \c
display contains "0" and the user clicks the \uicontrol{0} button, it
would be silly to show "00". And if the calculator is in
a state where it is waiting for a new operand,
the new digit is the first digit of that new operand; in that case,
any result of a previous calculation must be cleared first.
At the end, we append the new digit to the value in the display.
\snippet widgets/calculator/calculator.cpp 8
\snippet widgets/calculator/calculator.cpp 9
The \c unaryOperatorClicked() slot is called whenever one of the
unary operator buttons is clicked. Again a pointer to the clicked
button is retrieved using QObject::sender(). The operator is
extracted from the button's text and stored in \c
clickedOperator. The operand is obtained from \c display.
Then we perform the operation. If \uicontrol Sqrt is applied to a
negative number or \uicontrol{1/x} to zero, we call \c
abortOperation(). If everything goes well, we display the result
of the operation in the line edit and we set \c waitingForOperand
to \c true. This ensures that if the user types a new digit, the
digit will be considered as a new operand, instead of being
appended to the current value.
\snippet widgets/calculator/calculator.cpp 10
\snippet widgets/calculator/calculator.cpp 11
The \c additiveOperatorClicked() slot is called when the user
clicks the \uicontrol{+} or \uicontrol{-} button.
Before we can actually do something about the clicked operator,
we must handle any pending operations. We start with the
multiplicative operators, since these have higher precedence than
additive operators:
\snippet widgets/calculator/calculator.cpp 12
\snippet widgets/calculator/calculator.cpp 13
If \uicontrol{\unicode{215}} or \uicontrol{\unicode{247}} has been clicked earlier, without clicking
\uicontrol{=} afterward, the current value in the display is the right
operand of the \uicontrol{\unicode{215}} or \uicontrol{\unicode{247}} operator and we can finally
perform the operation and update the display.
\snippet widgets/calculator/calculator.cpp 14
\snippet widgets/calculator/calculator.cpp 15
If \uicontrol{+} or \uicontrol{-} has been clicked earlier, \c sumSoFar is
the left operand and the current value in the display is the
right operand of the operator. If there is no pending additive
operator, \c sumSoFar is simply set to be the text in the
display.
\snippet widgets/calculator/calculator.cpp 16
\snippet widgets/calculator/calculator.cpp 17
Finally, we can take care of the operator that was just clicked.
Since we don't have the right-hand operand yet, we store the clicked
operator in the \c pendingAdditiveOperator variable. We will
apply the operation later, when we have a right operand, with \c
sumSoFar as the left operand.
\snippet widgets/calculator/calculator.cpp 18
The \c multiplicativeOperatorClicked() slot is similar to \c
additiveOperatorClicked(). We don't need to worry about pending
additive operators here, because multiplicative operators have
precedence over additive operators.
\snippet widgets/calculator/calculator.cpp 20
Like in \c additiveOperatorClicked(), we start by handling any
pending multiplicative and additive operators. Then we display \c
sumSoFar and reset the variable to zero. Resetting the variable
to zero is necessary to avoid counting the value twice.
\snippet widgets/calculator/calculator.cpp 22
The \c pointClicked() slot adds a decimal point to the content in
\c display.
\snippet widgets/calculator/calculator.cpp 24
The \c changeSignClicked() slot changes the sign of the value in
\c display. If the current value is positive, we prepend a minus
sign; if the current value is negative, we remove the first
character from the value (the minus sign).
\snippet widgets/calculator/calculator.cpp 26
The \c backspaceClicked() removes the rightmost character in the
display. If we get an empty string, we show "0" and set \c
waitingForOperand to \c true.
\snippet widgets/calculator/calculator.cpp 28
The \c clear() slot resets the current operand to zero. It is
equivalent to clicking \uicontrol Backspace enough times to erase the
entire operand.
\snippet widgets/calculator/calculator.cpp 30
The \c clearAll() slot resets the calculator to its initial state.
\snippet widgets/calculator/calculator.cpp 32
The \c clearMemory() slot erases the sum kept in memory, \c
readMemory() displays the sum as an operand, \c setMemory()
replace the sum in memory with the current sum, and \c
addToMemory() adds the current value to the value in memory. For
\c setMemory() and \c addToMemory(), we start by calling \c
equalClicked() to update \c sumSoFar and the value in the
display.
\snippet widgets/calculator/calculator.cpp 34
The private \c createButton() function is called from the
constructor to create calculator buttons.
\snippet widgets/calculator/calculator.cpp 36
The private \c abortOperation() function is called whenever a
calculation fails. It resets the calculator state and displays
"####".
\snippet widgets/calculator/calculator.cpp 38
The private \c calculate() function performs a binary operation.
The right operand is given by \c rightOperand. For additive
operators, the left operand is \c sumSoFar; for multiplicative
operators, the left operand is \c factorSoFar. The function
return \c false if a division by zero occurs.
\section1 Button Class Definition
Let's now take a look at the \c Button class:
\snippet widgets/calculator/button.h 0
The \c Button class has a convenience constructor that takes a
text label and a parent widget, and it reimplements QWidget::sizeHint()
to provide more space around the text than the amount QToolButton
normally provides.
\section1 Button Class Implementation
\snippet widgets/calculator/button.cpp 0
The buttons' appearance is determined by the layout of the
calculator widget through the size and
\l{QWidget::sizePolicy}{size policy} of the layout's child
widgets. The call to the
\l{QWidget::setSizePolicy()}{setSizePolicy()} function in the
constructor ensures that the button will expand horizontally to
fill all the available space; by default, \l{QToolButton}s don't
expand to fill available space. Without this call, the different
buttons in a same column would have different widths.
\snippet widgets/calculator/button.cpp 1
\snippet widgets/calculator/button.cpp 2
In \l{QWidget::sizeHint()}{sizeHint()}, we try to return a size
that looks good for most buttons. We reuse the size hint of the
base class (QToolButton) but modify it in the following ways:
\list
\li We add 20 to the \l{QSize::height()}{height} component of the size hint.
\li We make the \l{QSize::width()}{width} component of the size
hint at least as much as the \l{QSize::width()}{height}.
\endlist
This ensures that with most fonts, the digit and operator buttons
will be square, without truncating the text on the
\uicontrol{Backspace}, \uicontrol{Clear}, and \uicontrol{Clear All} buttons.
The screenshot below shows how the \c Calculator widget would
look like if we \e didn't set the horizontal size policy to
QSizePolicy::Expanding in the constructor and if we didn't
reimplement QWidget::sizeHint().
\borderedimage calculator-ugly.png
\caption The Calculator example with default size policies and size hints
*/

View File

@ -0,0 +1,202 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example richtext/calendar
\title Calendar Example
\ingroup examples-richtext
\brief The Calendar example shows how to create rich text content
and display it using a rich text editor.
\brief The Calendar example shows how to create rich text content and display it using
a rich text editor.
\image calendar-example.png
Specifically, the example demonstrates the following:
\list
\li Use of a text editor with a text document
\li Insertion of tables and frames into a document
\li Navigation within a table
\li Insert text in different styles
\endlist
The rich text editor used to display the document is used within a main window
application.
\section1 MainWindow Class Definition
The \c MainWindow class provides a text editor widget and some controls to
allow the user to change the month and year shown. The font size used for the
text can also be adjusted.
\snippet richtext/calendar/mainwindow.h 0
The private \c insertCalendar() function performs most of the work, relying on
the \c fontSize and \c selectedDate variables to write useful information to
the \c editor.
\section1 MainWindow Class Implementation
The \c MainWindow constructor sets up the user interface and initializes
variables used to generate a calendar for each month.
\snippet richtext/calendar/mainwindow.cpp 0
We begin by setting default values for the selected date that will be highlighted
in the calendar and the font size to be used. Since we are using a QMainWindow
for the user interface, we construct a widget for use as the central widget.
The user interface will include a line of controls above the generated calendar;
we construct a label and a combobox to allow the month to be selected, and a
spin box for the year. These widgets are configured to provide a reasonable range
of values for the user to try:
\snippet richtext/calendar/mainwindow.cpp 1
We use the \c selectedDate object to obtain the current month and year, and we
set these in the combobox and spin box:
The font size is displayed in a spin box which we restrict to a sensible range
of values:
\snippet richtext/calendar/mainwindow.cpp 2
We construct an editor and use the \c insertCalendar() function to create
a calendar for it. Each calendar is displayed in the same text editor; in
this example we use a QTextBrowser since we do not allow the calendar to be
edited.
The controls used to set the month, year, and font size will not have any
effect on the appearance of the calendar unless we make some signal-slot
connections:
\snippet richtext/calendar/mainwindow.cpp 3
The signals are connected to some simple slots in the \c MainWindow class
which we will describe later.
We create layouts to manage the widgets we constructed:
\snippet richtext/calendar/mainwindow.cpp 4
Finally, the central widget is set for the window.
Each calendar is created for the editor by the \c insertCalendar() function
which uses the date and font size, defined by the private \a selectedDate
and \c fontSize variables, to produce a suitable plan for the specified
month and year.
\snippet richtext/calendar/mainwindow.cpp 5
We begin by clearing the editor's rich text document, and obtain a text
cursor from the editor that we will use to add content. We also create a
QDate object based on the currently selected date.
The calendar is made up of a table with a gray background color that contains
seven columns: one for each day of the week. It is placed in the center of the
page with equal space to the left and right of it. All of these properties are
set in a QTextTableFormat object:
\snippet richtext/calendar/mainwindow.cpp 6
Each cell in the table will be padded and spaced to make the text easier to
read.
We want the columns to have equal widths, so we provide a list containing
percentage widths for each of them and set the constraints in the
QTextTableFormat:
\snippet richtext/calendar/mainwindow.cpp 7
The constraints used for the column widths are only useful if the table has
an appropriate number of columns. With the format for the table defined, we
construct a new table with one row and seven columns at the current cursor
position:
\snippet richtext/calendar/mainwindow.cpp 8
We only need one row to start with; more can be added as we need them. Using
this approach means that we do not need to perform any date calculations
until we add cells to the table.
When inserting objects into a document with the cursor's insertion functions,
the cursor is automatically moved inside the newly inserted object. This means
that we can immediately start modifying the table from within:
\snippet richtext/calendar/mainwindow.cpp 9
Since the table has an outer frame, we obtain the frame and its format so that
we can customize it. After making the changes we want, we set the frame's format
using the modified format object. We have given the table an outer border one
pixel wide.
\snippet richtext/calendar/mainwindow.cpp 10
In a similar way, we obtain the cursor's current character format and
create customized formats based on it.
We do not set the format on the cursor because this would change the default
character format; instead, we use the customized formats explicitly when we
insert text. The following loop inserts the days of the week into the table
as bold text:
\snippet richtext/calendar/mainwindow.cpp 11
For each day of the week, we obtain an existing table cell in the first row
(row 0) using the table's \l{QTextTable::cellAt()}{cellAt()} function. Since
we start counting the days of the week at day 1 (Monday), we subtract 1 from
\c weekDay to ensure that we obtain the cell for the correct column of the
table.
Before text can be inserted into a cell, we must obtain a cursor with the
correct position in the document. The cell provides a function for this
purpose, and we use this cursor to insert text using the \c boldFormat
character format that we created earlier:
\snippet richtext/calendar/mainwindow.cpp 12
Inserting text into document objects usually follows the same pattern.
Each object can provide a new cursor that corresponds to the first valid
position within itself, and this can be used to insert new content. We
continue to use this pattern as we insert the days of the month into the
table.
Since every month has more than seven days, we insert a single row to begin
and add days until we reach the end of the month. If the current date is
encountered, it is inserted with a special format (created earlier) that
makes it stand out:
\snippet richtext/calendar/mainwindow.cpp 13
We add a new row to the table at the end of each week only if the next week
falls within the currently selected month.
For each calendar that we create, we change the window title to reflect the
currently selected month and year:
\snippet richtext/calendar/mainwindow.cpp 14
The \c insertCalendar() function relies on up-to-date values for the month,
year, and font size. These are set in the following slots:
\snippet richtext/calendar/mainwindow.cpp 15
The \c setFontSize() function simply changes the private \c fontSize variable
before updating the calendar.
\snippet richtext/calendar/mainwindow.cpp 16
The \c setMonth slot is called when the QComboBox used to select the month is
updated. The value supplied is the currently selected row in the combobox.
We add 1 to this value to obtain a valid month number, and create a new QDate
based on the existing one. The calendar is then updated to use this new date.
\snippet richtext/calendar/mainwindow.cpp 17
The \c setYear() slot is called when the QDateTimeEdit used to select the
year is updated. The value supplied is a QDate object; this makes
the construction of a new value for \c selectedDate simple. We update the
calendar afterwards to use this new date.
*/

View File

@ -0,0 +1,268 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\title Calendar Widget Example
\example widgets/calendarwidget
\ingroup examples-widgets
\ingroup examples-layout
\brief The Calendar Widget example shows use of QCalendarWidget.
\borderedimage calendarwidgetexample.png
QCalendarWidget displays one calendar month
at a time and lets the user select a date.
The calendar consists of four components: a navigation
bar that lets the user change the month that is
displayed, a grid where each cell represents one day
in the month, and two headers that display weekday names
and week numbers.
The Calendar Widget example displays a QCalendarWidget and lets the user
configure its appearance and behavior using
\l{QComboBox}es, \l{QCheckBox}es, and \l{QDateEdit}s. In
addition, the user can influence the formatting of individual dates
and headers.
The properties of the QCalendarWidget are summarized in the table
below.
\table
\header \li Property
\li Description
\row \li \l{QCalendarWidget::}{selectedDate}
\li The currently selected date.
\row \li \l{QCalendarWidget::}{minimumDate}
\li The earliest date that can be selected.
\row \li \l{QCalendarWidget::}{maximumDate}
\li The latest date that can be selected.
\row \li \l{QCalendarWidget::}{firstDayOfWeek}
\li The day that is displayed as the first day of the week
(usually Sunday or Monday).
\row \li \l{QCalendarWidget::}{gridVisible}
\li Whether the grid should be shown.
\row \li \l{QCalendarWidget::}{selectionMode}
\li Whether the user can select a date or not.
\row \li \l{QCalendarWidget::}{horizontalHeaderFormat}
\li The format of the day names in the horizontal header
(e.g., "M", "Mon", or "Monday").
\row \li \l{QCalendarWidget::}{verticalHeaderFormat}
\li The format of the vertical header.
\row \li \l{QCalendarWidget::}{navigationBarVisible}
\li Whether the navigation bar at the top of the calendar
widget is shown.
\endtable
The example consists of one class, \c Window, which creates and
lays out the QCalendarWidget and the other widgets that let the
user configure the QCalendarWidget.
\section1 Window Class Definition
Here is the definition of the \c Window class:
\snippet widgets/calendarwidget/window.h 0
\dots
\snippet widgets/calendarwidget/window.h 1
As is often the case with classes that represent self-contained
windows, most of the API is private. We will review the private
members as we stumble upon them in the implementation.
\section1 Window Class Implementation
Let's now review the class implementation, starting with the constructor:
\snippet widgets/calendarwidget/window.cpp 0
We start by creating the four \l{QGroupBox}es and their child
widgets (including the QCalendarWidget) using four private \c
create...GroupBox() functions, described below. Then we arrange
the group boxes in a QGridLayout.
We set the grid layout's resize policy to QLayout::SetFixedSize to
prevent the user from resizing the window. In that mode, the
window's size is set automatically by QGridLayout based on the
size hints of its contents widgets.
To ensure that the window isn't automatically resized every time
we change a property of the QCalendarWidget (for example, hiding the
navigation bar, the vertical header, or the grid), we set the
minimum height of row 0 and the minimum width of column 0 to the
initial size of the QCalendarWidget.
Let's move on to the \c createPreviewGroupBox() function:
\snippet widgets/calendarwidget/window.cpp 9
The \uicontrol Preview group box contains only one widget: the
QCalendarWidget. We set it up, connect its
\l{QCalendarWidget::}{currentPageChanged()} signal to our \c
reformatCalendarPage() slot to make sure that every new page gets
the formatting specified by the user.
The \c createGeneralOptionsGroupBox() function is somewhat large
and several widgets are set up in the same way. We will look at
parts of its implementation here and skip the rest:
\snippet widgets/calendarwidget/window.cpp 10
\dots
We start with the setup of the \uicontrol{Week starts on} combobox.
This combobox controls which day should be displayed as the first
day of the week.
The QComboBox class lets us attach user data as a QVariant to
each item. The data can later be retrieved with QComboBox's
\l{QComboBox::}{itemData()} function. QVariant doesn't directly
support the Qt::DayOfWeek data type, but it supports \c int, and
C++ will happily convert any enum value to \c int.
\dots
\snippet widgets/calendarwidget/window.cpp 11
\dots
After having created the widgets, we connect the signals and slots. We
connect the comboboxes to private slots of \c Window or to
public slots provided by QComboBox.
\dots
\snippet widgets/calendarwidget/window.cpp 12
At the end of the function, we call the slots that update the calendar to ensure
that the QCalendarWidget is synchronized with the other widgets on startup.
Let's now take a look at the \c createDatesGroupBox() private function:
\snippet widgets/calendarwidget/window.cpp 13
In this function, we create the \uicontrol {Minimum Date}, \uicontrol {Maximum Date},
and \uicontrol {Current Date} editor widgets,
which control the calendar's minimum, maximum, and selected dates.
The calendar's minimum and maximum dates have already been
set in \c createPrivewGroupBox(); we can then set the widgets
default values to the calendars values.
\snippet widgets/calendarwidget/window.cpp 14
\dots
\snippet widgets/calendarwidget/window.cpp 15
We connect the \c currentDateEdit's
\l{QDateEdit::}{dateChanged()} signal directly to the calendar's
\l{QCalendarWidget::}{setSelectedDate()} slot. When the calendar's
selected date changes, either as a result of a user action or
programmatically, our \c selectedDateChanged() slot updates
the \uicontrol {Current Date} editor. We also need to react when the user
changes the \uicontrol{Minimum Date} and \uicontrol{Maximum Date} editors.
Here is the \c createTextFormatsGroup() function:
\snippet widgets/calendarwidget/window.cpp 16
We set up the \uicontrol {Weekday Color} and \uicontrol {Weekend Color} comboboxes
using \c createColorCombo(), which instantiates a QComboBox and
populates it with colors ("Red", "Blue", etc.).
\snippet widgets/calendarwidget/window.cpp 17
The \uicontrol {Header Text Format} combobox lets the user change the
text format (bold, italic, or plain) used for horizontal and
vertical headers. The \uicontrol {First Friday in blue} and \uicontrol {May 1
in red} check box affect the rendering of specific dates.
\snippet widgets/calendarwidget/window.cpp 18
We connect the check boxes and comboboxes to various private
slots. The \uicontrol {First Friday in blue} and \uicontrol {May 1 in red}
check boxes are both connected to \c reformatCalendarPage(),
which is also called when the calendar switches month.
\dots
\snippet widgets/calendarwidget/window.cpp 19
At the end of \c createTextFormatsGroupBox(), we call private
slots to synchronize the QCalendarWidget with the other widgets.
We're now done reviewing the four \c create...GroupBox()
functions. Let's now take a look at the other private functions
and slots.
\snippet widgets/calendarwidget/window.cpp 20
In \c createColorCombo(), we create a combobox and populate it with
standard colors. The second argument to QComboBox::addItem()
is a QVariant storing user data (in this case, QColor objects).
This function was used to set up the \uicontrol {Weekday Color}
and \uicontrol {Weekend Color} comboboxes.
\snippet widgets/calendarwidget/window.cpp 1
When the user changes the \uicontrol {Week starts on} combobox's
value, \c firstDayChanged() is invoked with the index of the
combobox's new value. We retrieve the custom data item
associated with the new current item using
\l{QComboBox::}{itemData()} and cast it to a Qt::DayOfWeek.
\c selectionModeChanged(), \c horizontalHeaderChanged(), and \c
verticalHeaderChanged() are very similar to \c firstDayChanged(),
so they are omitted.
\snippet widgets/calendarwidget/window.cpp 2
The \c selectedDateChanged() updates the \uicontrol{Current Date}
editor to reflect the current state of the QCalendarWidget.
\snippet widgets/calendarwidget/window.cpp 3
When the user changes the minimum date, we tell the
QCalenderWidget. We also update the \uicontrol {Maximum Date} editor,
because if the new minimum date is later than the current maximum
date, QCalendarWidget will automatically adapt its maximum date
to avoid a contradicting state.
\snippet widgets/calendarwidget/window.cpp 4
\c maximumDateChanged() is implemented similarly to \c
minimumDateChanged().
\snippet widgets/calendarwidget/window.cpp 5
Each combobox item has a QColor object as user data corresponding to the
item's text. After fetching the colors from the comboboxes, we
set the text format of each day of the week.
The text format of a column in the calendar is given as a
QTextCharFormat, which besides the foreground color lets us
specify various character formatting information. In this
example, we only show a subset of the possibilities.
\snippet widgets/calendarwidget/window.cpp 6
\c weekendFormatChanged() is the same as \c
weekdayFormatChanged(), except that it affects Saturday and
Sunday instead of Monday to Friday.
\snippet widgets/calendarwidget/window.cpp 7
The \c reformatHeaders() slot is called when the user
changes the text format of
the headers. We compare the current text of the \uicontrol {Header Text Format}
combobox to determine which format to apply. (An alternative would
have been to store \l{QTextCharFormat} values alongside the combobox
items.)
\snippet widgets/calendarwidget/window.cpp 8
In \c reformatCalendarPage(), we set the text format of the first
Friday in the month and May 1 in the current year. The text
formats that are actually used depend on which check boxes are
checked and what the weekday/weekend formats are.
QCalendarWidget lets us set the text format of individual dates
with the \l{QCalendarWidget::}{setDateTextFormat()}. We chose to
set the date formats when the calendar page changes - i.e. a new month is
displayed - and when the weekday/weekend format is changed.
We check which of the \c mayFirstCheckBox and \c firstDayCheckBox, if any,
are checked and set the text formats accordingly.
*/

View File

@ -0,0 +1,251 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/charactermap
\title Character Map Example
\ingroup examples-widgets
\brief The Character Map example shows how to create a custom widget that can
both display its own content and respond to user input.
The example displays an array of characters which the user can click on
to enter text in a line edit. The contents of the line edit can then be
copied into the clipboard, and pasted into other applications. The
purpose behind this sort of tool is to allow users to enter characters
that may be unavailable or difficult to locate on their keyboards.
\borderedimage charactermap-example.png
\caption Screenshot of the Character Map example
The example consists of the following classes:
\list
\li \c CharacterWidget displays the available characters in the current
font and style.
\li \c MainWindow provides a standard main window that contains font and
style information, a view onto the characters, a line edit, and a push
button for submitting text to the clipboard.
\endlist
\section1 CharacterWidget Class Definition
The \c CharacterWidget class is used to display an array of characters in
a user-specified font and style. For flexibility, we subclass QWidget and
reimplement only the functions that we need to provide basic rendering
and interaction features.
The class definition looks like this:
\snippet widgets/charactermap/characterwidget.h 0
The widget does not contain any other widgets, so it must provide its own
size hint to allow its contents to be displayed correctly.
We reimplement \l{QWidget::paintEvent()} to draw custom content. We also
reimplement \l{QWidget::mousePressEvent()} to allow the user to interact
with the widget.
The updateFont() and updateStyle() slots are used to update the font and
style of the characters in the widget whenever the user changes the
settings in the application.
The class defines the characterSelected() signal so that other parts
of the application are informed whenever the user selects a character in
the widget.
As a courtesy, the widget provides a tooltip that shows the current
character value. We reimplement the \l{QWidget::mouseMoveEvent()} event
handler and define showToolTip() to enable this feature.
The \c columns, \c displayFont and \c currentKey private data members
are used to record the number of columns to be shown, the current font,
and the currently highlighted character in the widget.
\section1 CharacterWidget Class Implementation
Since the widget is to be used as a simple canvas, the constructor just
calls the base class constructor and defines some default values for
private data members.
\snippet widgets/charactermap/characterwidget.cpp 0
We initialize \c currentKey with a value of -1 to indicate
that no character is initially selected. We enable mouse tracking to
allow us to follow the movement of the cursor across the widget.
The class provides two functions to allow the font and style to be set up.
Each of these modify the widget's display font and call update():
\snippet widgets/charactermap/characterwidget.cpp 1
\codeline
\snippet widgets/charactermap/characterwidget.cpp 2
We use a fixed size font for the display. Similarly, a fixed size hint is
provided by the sizeHint() function:
\snippet widgets/charactermap/characterwidget.cpp 3
Three standard event functions are implemented so that the widget
can respond to clicks, provide tooltips, and render the available
characters. The paintEvent() shows how the contents of the widget are
arranged and displayed:
\snippet widgets/charactermap/characterwidget.cpp 6
A QPainter is created for the widget and, in all cases, we ensure that the
widget's background is painted. The painter's font is set to the
user-specified display font.
The area of the widget that needs to be redrawn is used to determine which
characters need to be displayed:
\snippet widgets/charactermap/characterwidget.cpp 7
Using integer division, we obtain the row and column numbers of each
characters that should be displayed, and we draw a square on the widget
for each character displayed.
\snippet widgets/charactermap/characterwidget.cpp 8
\snippet widgets/charactermap/characterwidget.cpp 9
The symbols for each character in the array are drawn within each square,
with the symbol for the most recently selected character displayed in red:
\snippet widgets/charactermap/characterwidget.cpp 10
We do not need to take into account the difference between the area
displayed in the viewport and the area we are drawing on because
everything outside the visible area will be clipped.
The mousePressEvent() defines how the widget responds to mouse clicks.
\snippet widgets/charactermap/characterwidget.cpp 5
We are only interested when the user clicks with the left mouse button
over the widget. When this happens, we calculate which character was
selected and emit the characterSelected() signal.
The character's number is found by dividing the x and y-coordinates of
the click by the size of each character's grid square. Since the number
of columns in the widget is defined by the \c columns variable, we
simply multiply the row index by that value and add the column number
to obtain the character number.
If any other mouse button is pressed, the event is passed on to the
QWidget base class. This ensures that the event can be handled properly
by any other interested widgets.
The mouseMoveEvent() maps the mouse cursor's position in global
coordinates to widget coordinates, and determines the character that
was clicked by performing the calculation
\snippet widgets/charactermap/characterwidget.cpp 4
The tooltip is given a position defined in global coordinates.
\section1 MainWindow Class Definition
The \c MainWindow class provides a minimal user interface for the example,
with only a constructor, slots that respond to signals emitted by standard
widgets, and some convenience functions that are used to set up the user
interface.
The class definition looks like this:
\snippet widgets/charactermap/mainwindow.h 0
The main window contains various widgets that are used to control how
the characters will be displayed, and defines the findFonts() function
for clarity and convenience. The findStyles() slot is used by the widgets
to determine the styles that are available, insertCharacter() inserts
a user-selected character into the window's line edit, and
updateClipboard() synchronizes the clipboard with the contents of the
line edit.
\section1 MainWindow Class Implementation
In the constructor, we set up the window's central widget and fill it with
some standard widgets (two comboboxes, a line edit, and a push button).
We also construct a CharacterWidget custom widget, and add a QScrollArea
so that we can view its contents:
\snippet widgets/charactermap/mainwindow.cpp 0
QScrollArea provides a viewport onto the \c CharacterWidget when we set
its widget and handles much of the work needed to provide a scrolling
viewport.
The font combo box is automatically populated with a list of available
fonts. We list the available styles for the current font in the style
combobox using the following function:
\snippet widgets/charactermap/mainwindow.cpp 1
The line edit and push button are used to supply text to the clipboard:
\snippet widgets/charactermap/mainwindow.cpp 2
We also obtain a clipboard object so that we can send text entered by the
user to other applications.
Most of the signals emitted in the example come from standard widgets.
We connect these signals to slots in this class, and to the slots provided
by other widgets.
\snippet widgets/charactermap/mainwindow.cpp 4
The font combobox's
\l{QFontComboBox::currentFontChanged()}{currentFontChanged()} signal is
connected to the findStyles() function so that the list of available styles
can be shown for each font that is used. Since both the font and the style
can be changed by the user, the font combobox's currentFontChanged() signal
and the style combobox's
\l{QComboBox::currentIndexChanged()}{currentIndexChanged()} are connected
directly to the character widget.
The final two connections allow characters to be selected in the character
widget, and text to be inserted into the clipboard:
\snippet widgets/charactermap/mainwindow.cpp 5
The character widget emits the characterSelected() custom signal when
the user clicks on a character, and this is handled by the insertCharacter()
function in this class. The clipboard is changed when the push button emits
the clicked() signal, and we handle this with the updateClipboard() function.
The remaining code in the constructor sets up the layout of the central widget,
and provides a window title:
\snippet widgets/charactermap/mainwindow.cpp 6
The font combobox is automatically populated with a list of available font
families. The styles that can be used with each font are found by the
findStyles() function. This function is called whenever the user selects a
different font in the font combobox.
\snippet widgets/charactermap/mainwindow.cpp 7
We begin by recording the currently selected style, and we clear the
style combobox so that we can insert the styles associated with the
current font family.
\snippet widgets/charactermap/mainwindow.cpp 8
We use the font database to collect the styles that are available for the
current font, and insert them into the style combobox. The current item is
reset if the original style is not available for this font.
The last two functions are slots that respond to signals from the character
widget and the main window's push button. The insertCharacter() function is
used to insert characters from the character widget when the user clicks a
character:
\snippet widgets/charactermap/mainwindow.cpp 9
The character is inserted into the line edit at the current cursor position.
The main window's "To clipboard" push button is connected to the
updateClipboard() function so that, when it is clicked, the clipboard is
updated to contain the contents of the line edit:
\snippet widgets/charactermap/mainwindow.cpp 10
We copy all the text from the line edit to the clipboard, but we do not clear
the line edit.
*/

View File

@ -0,0 +1,58 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/chart
\title Chart Example
\ingroup examples-itemviews
\brief The Chart example shows how to create a custom view for the model/view framework.
\image chart-example.png
In this example, the items in a table model are represented as slices in a pie chart,
relying on the flexibility of the model/view architecture to handle custom editing
and selection features.
\b{Note that you only need to create a new view class if your data requires a
specialized representation.} You should first consider using a standard QListView,
QTableView, or QTreeView with a custom QItemDelegate subclass if you need to
represent data in a special way.
\omit
\section1 PieView Class Definition
The \c PieView class is a subclass of QAbstractItemView. The base class provides
much of the functionality required by view classes, so we only need to provide
implementations for three public functions: visualRect(), scrollTo(), and
indexAt(). However, the view needs to maintain strict control over its look and
feel, so we also provide implementations for a number of other functions:
\snippet itemviews/chart/pieview.h 0
\section1 PieView Class Implementation
The paint event renders the data from the standard item model as a pie chart.
We interpret the data in the following way:
\list
\li Column 0 contains data in two different roles:
The \l{Qt::ItemDataRole}{DisplayRole} contains a label, and the
\l{Qt::ItemDataRole}{DecorationRole} contains the color of the pie slice.
\li Column 1 contains a quantity which we will convert to the angular extent of
the slice.
\endlist
The figure is always drawn with the chart on the left and the key on
the right. This means that we must try and obtain an area that is wider
than it is tall. We do this by imposing a particular aspect ratio on
the chart and applying it to the available vertical space. This ensures
that we always obtain the maximum horizontal space for the aspect ratio
used.
We also apply fixed size margin around the figure.
We use logical coordinates to draw the chart and key, and position them
on the view using viewports.
\endomit
*/

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/chip
\title 40000 Chips
\ingroup examples-graphicsview
\brief Visualizes a huge graphic view scene with 40000 chip items.
This examples demonstrates Graphics View's powerful navigation
and interaction features, allowing you to zoom and rotate each of four
views independently, and you can select and move items around the scene.
\image chip-demo.png
*/

View File

@ -0,0 +1,167 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example dialogs/classwizard
\title Class Wizard Example
\ingroup examples-dialogs
\brief The Class Wizard example shows how to implement linear
wizards using QWizard.
\image classwizard.png Screenshot of the Class Wizard example
Most wizards have a linear structure, with page 1 followed by
page 2 and so on until the last page. Some wizards are more
complex in that they allow different traversal paths based on the
information provided by the user. The
\l{dialogs/licensewizard}{License Wizard} example shows how to
create such wizards.
The Class Wizard example consists of the following classes:
\list
\li \c ClassWizard inherits QWizard and provides a
three-step wizard that generates the skeleton of a C++ class
based on the user's input.
\li \c IntroPage, \c ClassInfoPage, \c CodeStylePage, \c
OutputFilesPage, and \c ConclusionPage are QWizardPage
subclasses that implement the wizard pages.
\endlist
\section1 ClassWizard Class Definition
\image classwizard-flow.png The Class Wizard pages
We will see how to subclass QWizard to implement our own wizard.
The concrete wizard class is called \c ClassWizard and provides
five pages:
\list
\li The first page is an introduction page, telling the user what
the wizard is going to do.
\li The second page asks for a class name and a base class, and
allows the user to specify whether the class should have a \c
Q_OBJECT macro and what constructors it should provide.
\li The third page allows the user to set some options related to the code
style, such as the macro used to protect the header file from
multiple inclusion (e.g., \c MYDIALOG_H).
\li The fourth page allows the user to specify the names of the
output files.
\li The fifth page is a conclusion page.
\endlist
Although the program is just an example, if you press \uicontrol Finish
(\uicontrol Done on \macos), actual C++ source files will actually be
generated.
\section1 The ClassWizard Class
Here's the \c ClassWizard definition:
\snippet dialogs/classwizard/classwizard.h 0
The class reimplements QDialog's \l{QDialog::}{accept()} slot.
This slot is called when the user clicks \uicontrol{Finish}.
Here's the constructor:
\snippet dialogs/classwizard/classwizard.cpp 1
We instantiate the five pages and insert them into the wizard
using QWizard::addPage(). The order in which they are inserted
is also the order in which they will be shown later on.
We call QWizard::setPixmap() to set the banner and the
background pixmaps for all pages. The banner is used as a
background for the page header when the wizard's style is
\l{QWizard::}{ModernStyle}; the background is used as the
dialog's background in \l{QWizard::}{MacStyle}. (See \l{Elements
of a Wizard Page} for more information.)
\snippet dialogs/classwizard/classwizard.cpp 3
\snippet dialogs/classwizard/classwizard.cpp 4
\dots
\snippet dialogs/classwizard/classwizard.cpp 5
\snippet dialogs/classwizard/classwizard.cpp 6
If the user clicks \uicontrol Finish, we extract the information from
the various pages using QWizard::field() and generate the files.
The code is long and tedious (and has barely anything to do with
noble art of designing wizards), so most of it is skipped here.
See the actual example in the Qt distribution for the details if
you're curious.
\section1 The IntroPage Class
The pages are defined in \c classwizard.h and implemented in \c
classwizard.cpp, together with \c ClassWizard. We will start with
the easiest page:
\snippet dialogs/classwizard/classwizard.h 1
\codeline
\snippet dialogs/classwizard/classwizard.cpp 7
A page inherits from QWizardPage. We set a
\l{QWizardPage::}{title} and a
\l{QWizard::WatermarkPixmap}{watermark pixmap}. By not setting
any \l{QWizardPage::}{subTitle}, we ensure that no header is
displayed for this page. (On Windows, it is customary for wizards
to display a watermark pixmap on the first and last pages, and to
have a header on the other pages.)
Then we create a QLabel and add it to a layout.
\section1 The ClassInfoPage Class
The second page is defined and implemented as follows:
\snippet dialogs/classwizard/classwizard.h 2
\codeline
\snippet dialogs/classwizard/classwizard.cpp 9
\dots
\snippet dialogs/classwizard/classwizard.cpp 12
\dots
\snippet dialogs/classwizard/classwizard.cpp 13
First, we set the page's \l{QWizardPage::}{title},
\l{QWizardPage::}{subTitle}, and \l{QWizard::LogoPixmap}{logo
pixmap}. The logo pixmap is displayed in the page's header in
\l{QWizard::}{ClassicStyle} and \l{QWizard::}{ModernStyle}.
Then we create the child widgets, create \l{Registering and Using
Fields}{wizard fields} associated with them, and put them into
layouts. The \c className field is created with an asterisk (\c
*) next to its name. This makes it a \l{mandatory fields}{mandatory field}, that
is, a field that must be filled before the user can press the
\uicontrol Next button (\uicontrol Continue on \macos). The fields' values
can be accessed from any other page using QWizardPage::field(),
or from the wizard code using QWizard::field().
\section1 The CodeStylePage Class
The third page is defined and implemented as follows:
\snippet dialogs/classwizard/classwizard.h 3
\codeline
\snippet dialogs/classwizard/classwizard.cpp 14
\dots
\snippet dialogs/classwizard/classwizard.cpp 15
\codeline
\snippet dialogs/classwizard/classwizard.cpp 16
The code in the constructor is very similar to what we did for \c
ClassInfoPage, so we skipped most of it.
The \c initializePage() function is what makes this class
interesting. It is reimplemented from QWizardPage and is used to
initialize some of the page's fields with values from the
previous page (namely, \c className and \c baseClass). For
example, if the class name on page 2 is \c SuperDuperWidget, the
default macro name on page 3 is \c SUPERDUPERWIDGET_H.
The \c OutputFilesPage and \c ConclusionPage classes are very
similar to \c CodeStylePage, so we won't review them here.
\sa QWizard, {License Wizard Example}, {Trivial Wizard Example}
*/

View File

@ -0,0 +1,242 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/collidingmice
\title Colliding Mice Example
\brief Demonstrates how to animate items on a graphics view.
\ingroup examples-graphicsview
The Colliding Mice example shows how to use the Graphics View
framework to implement animated items and detect collision between
items.
\image collidingmice-example.png
Graphics View provides the QGraphicsScene class for managing and
interacting with a large number of custom-made 2D graphical items
derived from the QGraphicsItem class, and a QGraphicsView widget
for visualizing the items, with support for zooming and rotation.
The example consists of an item class and a main function:
the \c Mouse class represents the individual mice extending
QGraphicsItem, and the \c main() function provides the main
application window.
We will first review the \c Mouse class to see how to animate
items and detect item collisions, and then we will review the \c
main() function to see how to put the items into a scene and how to
implement the corresponding view.
\section1 Mouse Class Definition
The \c mouse class inherits from QGraphicsItem. The
QGraphicsItem class is the base class for all graphical items in
the Graphics View framework, and provides a light-weight
foundation for writing your own custom items.
\snippet graphicsview/collidingmice/mouse.h 0
When writing a custom graphics item, you must implement
QGraphicsItem's two pure virtual public functions: \l
{QGraphicsItem::}{boundingRect()}, which returns an estimate of
the area painted by the item, and \l {QGraphicsItem::}{paint()},
which implements the actual painting. In addition, we reimplement
the \l {QGraphicsItem::}{shape()} and \l {QGraphicsItem::}{advance()}.
We reimplement \l {QGraphicsItem::}{shape()} to return an accurate
shape of our mouse item; the default implementation simply returns
the item's bounding rectangle. We reimplement \l {QGraphicsItem::}{advance()}
to handle the animation so it all happens on one update.
\section1 Mouse Class Definition
When constructing a mouse item, we first ensure that all the item's
private variables which were no yet initialized directly in the class
are properly initialized:
\snippet graphicsview/collidingmice/mouse.cpp 0
To calculate the various components of the mouse's color, we use
\l QRandomGenerator.
Then we call the \l {QGraphicsItem::setRotation()}{setRotation()} function
inherited from QGraphicsItem. Items live in their own local
coordinate system. Their coordinates are usually centered around
(0, 0), and this is also the center for all transformations. By
calling the item's \l {QGraphicsItem::setRotation()}{setRotation()} function
we alter the direction in which the mouse will start moving.
When the QGraphicsScene decides to advance the scene by a frame, it will
call QGraphicsItem::advance() on each of the items. This enables us to
animate our mouse using our reimplementation of the advance() function.
\snippet graphicsview/collidingmice/mouse.cpp 4
\snippet graphicsview/collidingmice/mouse.cpp 5
\snippet graphicsview/collidingmice/mouse.cpp 6
First, we don't bother doing any advance if the step is \c 0. This is because
advance() is called twice: once with step == \c 0, indicating that items
are about to advance, and then with step == \c 1 for the actual advance.
We also ensure that the mouse stays within a circle with a radius of 150 pixels.
Note the \l {QGraphicsItem::mapFromScene()}{mapFromScene()}
function provided by QGraphicsItem. This function maps a position
given in \e scene coordinates, to the item's coordinate system.
\snippet graphicsview/collidingmice/mouse.cpp 7
\snippet graphicsview/collidingmice/mouse.cpp 8
\snippet graphicsview/collidingmice/mouse.cpp 9
\codeline
\snippet graphicsview/collidingmice/mouse.cpp 10
Then we try to avoid colliding with other mice.
\snippet graphicsview/collidingmice/mouse.cpp 11
Finally, we calculate the mouse's speed and its eye direction (for
use when painting the mouse), and set its new position.
The position of an item describes its origin (local coordinate (0,
0)) in the parent coordinates. The \l {QGraphicsItem::setPos()}
function sets the position of the item to the given position in
the parent's coordinate system. For items with no parent, the
given position is interpreted as scene coordinates. QGraphicsItem
also provides a \l {QGraphicsItem::}{mapToParent()} function to
map a position given in item coordinates to the parent's
coordinate system. If the item has no parent, the position will be
mapped to the scene's coordinate system instead.
Then it is time to provide an implementation for the pure virtual
functions inherited from QGraphicsItem. Let's first take a look at
the \l {QGraphicsItem::}{boundingRect()} function:
\snippet graphicsview/collidingmice/mouse.cpp 1
The \l {QGraphicsItem::boundingRect()}{boundingRect()} function
defines the outer bounds of the item as a rectangle. Note that the
Graphics View framework uses the bounding rectangle to determine
whether the item requires redrawing, so all painting must be
done inside this rectangle.
\snippet graphicsview/collidingmice/mouse.cpp 3
The Graphics View framework calls the \l
{QGraphicsItem::paint()}{paint()} function to paint the contents
of the item; the function paints the item in local coordinates.
Note the painting of the ears: whenever a mouse item collides with
other mice items its ears are filled with red; otherwise they are
filled with dark yellow. We use the
QGraphicsScene::collidingItems() function to check if there are
any colliding mice. The actual collision detection is handled by
the Graphics View framework using shape-shape intersection. All we
have to do is to ensure that the QGraphicsItem::shape() function
returns an accurate shape for our item:
\snippet graphicsview/collidingmice/mouse.cpp 2
Because the complexity of arbitrary shape-shape intersection grows
with an order of magnitude when the shapes are complex, this
operation can be noticeably time consuming. An alternative approach
is to reimplement the \l
{QGraphicsItem::collidesWithItem()}{collidesWithItem()} function
to provide your own custom item and shape collision algorithm.
This completes the \c Mouse class implementation; it is now ready
for use. Let's take a look at the \c main() function to see how to
implement a scene for the mice and a view for displaying the
contents of the scene.
\section1 The Main() Function
The \c main() function provides the main application window,
as well as creating the items, their scene, and a corresponding view.
\snippet graphicsview/collidingmice/main.cpp 0
First, we create an application object and create the scene:
\snippet graphicsview/collidingmice/main.cpp 1
The QGraphicsScene class serves as a container for
QGraphicsItems. It also provides functionality that lets you
efficiently determine the location of items as well as determining
which items are visible within an arbitrary area on the
scene.
When creating a scene it is recommended to set the scene's
rectangle; the rectangle that defines the extent of the
scene. It is primarily used by QGraphicsView to determine the
view's default scrollable area, and by QGraphicsScene to manage
item indexing. If not explicitly set, the scene's default
rectangle will be the largest bounding rectangle of all the items
on the scene since the scene was created. This means that the
rectangle will grow when items are added or moved in the scene,
but it will never shrink.
\snippet graphicsview/collidingmice/main.cpp 2
The item index function is used to speed up item discovery. \l
{QGraphicsScene::NoIndex}{NoIndex} implies that item location is
of linear complexity, as all items on the scene are
searched. Adding, moving and removing items, however, is done in
constant time. This approach is ideal for dynamic scenes, where
many items are added, moved or removed continuously. The
alternative is \l {QGraphicsScene::BspTreeIndex}{BspTreeIndex},
which makes use of a binary search to achieve item location
algorithms that are of an order closer to logarithmic complexity.
\snippet graphicsview/collidingmice/main.cpp 3
Then we add the mice to the scene.
\snippet graphicsview/collidingmice/main.cpp 4
To be able to view the scene, we must also create a QGraphicsView
widget. The QGraphicsView class visualizes the contents of a scene
in a scrollable viewport. We also ensure that the contents are
rendered using antialiasing, and we create the cheese background
by setting the view's background brush.
The image used for the background is stored as a binary file in
the application's executable using Qt's \l {The Qt Resource
System}{resource system}. The QPixmap constructor accepts both
file names that refer to actual files on disk and file names that
refer to the application's embedded resources.
\snippet graphicsview/collidingmice/main.cpp 5
Then we set the cache mode; QGraphicsView can cache pre-rendered
content in a pixmap, which is then drawn onto the viewport. The
purpose of such caching is to speed up the total rendering time
for areas that are slow to render, for example: texture, gradient, and
alpha blended backgrounds. The \l
{QGraphicsView::CacheMode}{CacheMode} property holds which parts
of the view are cached, and the \l
{QGraphicsView::CacheBackground}{CacheBackground} flag enables
caching of the view's background.
By setting the \l {QGraphicsView::dragMode}{dragMode} property, we
define what should happen when the user clicks on the scene
background and drags the mouse. The \l
{QGraphicsView::ScrollHandDrag}{ScrollHandDrag} flag makes the
cursor change into a pointing hand, and dragging the mouse around
will scroll the scrollbars.
\snippet graphicsview/collidingmice/main.cpp 6
In the end, we set the application window's title and size before
we enter the main event loop using the QApplication::exec()
function.
Finally, we create a QTimer and connect its timeout() signal to the
advance() slot of the scene. Every time the timer fires, the scene
will advance one frame.
We then tell the timer to fire every 1000/33 milliseconds. This will
give us a frame rate of 30 frames a second, which is fast enough for most
animations. Doing the animation with a single timer connection to advance the
scene ensures that all the mice are moved at one point and, more
importantly, only one update is sent to the screen after all the mice have
moved.
*/

View File

@ -0,0 +1,131 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/coloreditorfactory
\title Color Editor Factory Example
\ingroup examples-itemviews
\brief This example shows how to create an editor that can be used by
a QItemDelegate.
\image coloreditorfactoryimage.png
When editing data in a QListView, QTableView, or QTreeView,
editors are created and displayed by a \l{Delegate
Classes}{delegate}. QItemDelegate, which is the default delegate
used by Qt's \l{View Classes}{item views}, uses a
QItemEditorFactory to create editors for it. A unique instance
provided by QItemEditorFactory is by default installed on all
item delegates.
An item editor factory contains a collection of
QItemEditorCreatorBase instances, which are specialized factories
that produce editors for one particular QVariant data type (all
models in Qt store their data in \l{QVariant}s). An editor can be any
Qt or custom widget.
In this example, we will create an editor (implemented in the \c
ColorListEditor class) that can edit the QColor data type and be
used by \l{QItemDelegate}s. We do this by creating a new
QItemEditorCreatorBase that produces \c ColorListEditors and
register it with a new factory, which we set as the default editor
item factory (the unique factory instance). To test our editor, we
have implemented the \c Window class, which displays a
QTableWidget in which \l{QColor}s can be edited.
\section1 Window Class Implementation
In the Window class, we create the item editor creator
base for our color editor and add it to the default factory.
We also create a QTableWidget in which our editor can be
tested. It is filled with some data and displayed in a window.
We take a closer look at the constructor:
\snippet itemviews/coloreditorfactory/window.cpp 0
The QStandardItemEditorCreator is a convenience class that
inherits QItemEditorCreatorBase. Its constructor takes a template
class, of which instances are returned from
\l{QItemEditorCreatorBase::}{createWidget()}. The creator uses a
constructor that takes a QWidget as its only parameter; the
template class must provide this. This way, there is no need to
subclass QStandardItemEditorCreator.
After the new factory has been set, all standard item delegates
will use it (i.e, also delegates that were created before the new
default factory was set).
The \c createGUI() function sets up the table and fills it
with data.
\section1 ColorListEditor Definition
The ColorListEditor inherits QComboBox and lets the user
select a QColor from its popup list.
\snippet itemviews/coloreditorfactory/colorlisteditor.h 0
QItemDelegate manages the interaction between the editor and
the model, i.e., it retrieves data to edit from the model and
store data from the editor in the model. The data that is edited
by an editor is stored in the editor's user data property, and the
delegate uses Qt's \l{Qt's Property System}{property system} to
access it by name. We declare our user data property with the
Q_PROPERTY macro. The property is set to be the user type with the
USER keyword.
\section1 ColorListEditor Implementation
The constructor of \c ColorListEditor simply calls \c
populateList(), which we will look at later. We move on to the
\c color() function:
\snippet itemviews/coloreditorfactory/colorlisteditor.cpp 0
We return the data that is selected in the combobox. The data
is stored in the Qt::DecorationRole as the color is then also
displayed in the popup list (as shown in the image above).
\snippet itemviews/coloreditorfactory/colorlisteditor.cpp 1
The \c findData() function searches the items in the combobox
and returns the index of the item that has \c color in the
Qt::Decoration role.
\snippet itemviews/coloreditorfactory/colorlisteditor.cpp 2
Qt knows some predefined colors by name. We simply loop
through these to fill our editor with items.
\section1 Further Customization of Item View Editors
You can customize Qt's \l{Model/View Programming}{model view
framework} in many ways. The procedure shown in this example is
usually sufficient to provide custom editors. Further
customization is achieved by subclassing QItemEditorFactory
and QItemEditorCreatorBase. It is also possible to subclass
QItemDelegate if you don't wish to use a factory at all.
Possible suggestions are:
\list
\li If the editor widget has no user property defined, the delegate
asks the factory for the property name, which it in turn
asks the item editor creator for. In this case, you can use
the QItemEditorCreator class, which takes the property
name to use for editing as a constructor argument.
\li If the editor requires other constructors or other
initialization than provided by QItemEditorCreatorBase, you
must reimplement
QItemEditorCreatorBase::createWidget().
\li You could also subclass QItemEditorFactory if you only want
to provide editors for certain kinds of data or use another
method of creating the editors than using creator bases.
\endlist
In this example, we use a standard QVariant data type. You can
also use custom types. In the \l{Star Delegate Example}, we
show how to store a custom data type in a QVariant and paint
and edit it in a class that inherits QItemDelegate.
*/

View File

@ -0,0 +1,143 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/combowidgetmapper
\title Combo Widget Mapper Example
\ingroup examples-itemviews
\brief The Combo Widget Mapper example shows how to use a custom delegate to
map information from a model to specific widgets on a form.
\image combowidgetmapper-example.png
In the \l{Simple Widget Mapper Example}, we showed the basic use of a
widget mapper to relate data exposed by a model to simple input widgets
in a user interface. However, sometimes we want to use input widgets that
expose data as choices to the user, such as QComboBox, and we need a way
to relate their input to the values stored in the model.
This example is very similar to the \l{Simple Widget Mapper Example}.
Again, we create a \c Window class with an almost identical user interface,
except that, instead of providing a spin box so that each person's age
can be entered, we provide a combo box to allow their addresses to be
classified as "Home", "Work" or "Other".
\section1 Window Class Definition
The class provides a constructor, a slot to keep the buttons up to date,
and a private function to set up the model:
\snippet itemviews/combowidgetmapper/window.h Window definition
In addition to the QDataWidgetMapper object and the controls used to make
up the user interface, we use a QStandardItemModel to hold our data and
a QStringListModel to hold information about the types of address that
can be applied to each person's data.
\section1 Window Class Implementation
The constructor of the \c Window class can be explained in three parts.
In the first part, we set up the widgets used for the user interface:
\snippet itemviews/combowidgetmapper/window.cpp Set up widgets
Note that we set up the mapping the combo box in the same way as for other
widgets, but that we apply its own model to it so that it will display
data from its own model, the \c typeModel, rather than from the model
containing data about each person.
Next, we set up the widget mapper, relating each input widget to a column
in the model specified by the call to \l{QDataWidgetMapper::}{setModel()}:
\snippet itemviews/combowidgetmapper/window.cpp Set up the mapper
For the combo box, we pass an extra argument to tell the widget mapper
which property to relate to values from the model. As a result, the user
is able to select an item from the combo box, and the corresponding
value stored in the widget's \c currentIndex property will be stored in
the model.
\omit
However, we also set a delegate on the mapper. As with \l{Delegate Classes},
this changes the way that data is presented to the user. In this case, the
delegate acts as a proxy between the mapper and the input widgets,
translating the data into a suitable form for the combo box but not
interfering with the other input widgets. The implementation is shown later.
\endomit
The rest of the constructor is very similar to that of the
\l{Simple Widget Mapper Example}:
\snippet itemviews/combowidgetmapper/window.cpp Set up connections and layouts
The model is initialized in the window's \c{setupModel()} function. Here,
we create a standard model with 5 rows and 3 columns. In each row, we
insert a name, address, and a value that indicates the type of address.
The address types are stored in a string list model.
\snippet itemviews/combowidgetmapper/window.cpp Set up the model
As we insert each row into the model, like a record in a database, we
store values that correspond to items in \c typeModel for each person's
address type. When the widget mapper reads these values from the final
column of each row, it will need to use them as references to values in
\c typeModel, as shown in the following diagram. This is where the
delegate is used.
\image widgetmapper-combo-mapping.png
We show the implementation of the \c{updateButtons()} slot for
completeness:
\snippet itemviews/combowidgetmapper/window.cpp Slot for updating the buttons
\omit
\section1 Delegate Class Definition and Implementation
The delegate we use to mediate interaction between the widget mapper and
the input widgets is a small QItemDelegate subclass:
\snippet itemviews/combowidgetmapper/delegate.h Delegate class definition
This provides implementations of the two standard functions used to pass
data between editor widgets and the model (see the \l{Delegate Classes}
documentation for a more general description of these functions).
Since we only provide an empty implementation of the constructor, we
concentrate on the other two functions.
The \l{QItemDelegate::}{setEditorData()} implementation takes the data
referred to by the model index supplied and processes it according to
the presence of a \c currentIndex property in the editor widget:
\snippet itemviews/combowidgetmapper/delegate.cpp setEditorData implementation
If, like QComboBox, the editor widget has this property, it is set using
the value from the model. Since we are passing around QVariant values,
the strings stored in the model are automatically converted to the integer
values needed for the \c currentIndex property.
As a result, instead of showing "0", "1" or "2" in the combo box, one of
its predefined set of items is shown. We call QItemDelegate::setEditorData()
for widgets without the \c currentIndex property.
The \l{QItemDelegate::}{setModelData()} implementation performs the reverse
process, taking the value stored in the widget's \c currentIndex property
and storing it back in the model:
\snippet itemviews/combowidgetmapper/delegate.cpp setModelData implementation
\endomit
\section1 Summary and Further Reading
The use of a separate model for the combo box provides a menu of choices
that are separate from the data stored in the main model. Using a named
mapping that relates the combo box's \c currentIndex property to a column
in the model effectively allows us to store a look-up value in the model.
However, when reading the model outside the context of the widget mapper,
we need to know about the \c typeModel to make sense of these look-up
values. It would be useful to be able to store both the data and the
choices held by the \c typeModel in one place.
This is covered by the \l{SQL Widget Mapper Example}.
*/

View File

@ -0,0 +1,219 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/completer
\title Completer Example
\ingroup examples-widgets-tools
\brief The Completer example shows how to provide string-completion facilities
for an input widget based on data provided by a model.
\image completer-example.png
This example uses a custom item model, \c FileSystemModel, and a QCompleter object.
QCompleter is a class that provides completions based on an item model. The
type of model, the completion mode, and the case sensitivity can be
selected using combo boxes.
\section1 The Resource File
The Completer example requires a resource file in order to store the
\e{countries.txt} and \e{words.txt}. The resource file contains the
following code:
\quotefile tools/completer/completer.qrc
\section1 FileSystemModel Class Definition
The \c FileSystemModel class is a subclass of QFileSystemModel, which provides a data
model for the local filesystem.
\snippet tools/completer/fsmodel.h 0
This class only has a constructor and a \c data() function as it is only
created to enable \c data() to return the entire file path for the
display role, unlike \l{QFileSystemModel}'s \c data() function that only returns
the folder and not the drive label. This is further explained in
\c FileSystemModel's implementation.
\section1 FileSystemModel Class Implementation
The constructor for the \c FileSystemModel class is used to pass \a parent to
QFileSystemModel.
\snippet tools/completer/fsmodel.cpp 0
As mentioned earlier, the \c data() function is reimplemented in order to
get it to return the entire file parth for the display role. For example,
with a QFileSystemModel, you will see "Program Files" in the view. However, with
\c FileSystemModel, you will see "C:\\Program Files".
\snippet tools/completer/fsmodel.cpp 1
The Qt::EditRole, which QCompleter uses to look for matches, is left
unchanged.
\section1 MainWindow Class Definition
The \c MainWindow class is a subclass of QMainWindow and implements five
private slots - \c about(), \c changeCase(), \c changeMode(), \c changeModel(),
and \c changeMaxVisible().
\snippet tools/completer/mainwindow.h 0
Within the \c MainWindow class, we have two private functions:
\c createMenu() and \c modelFromFile(). We also declare the private widgets
needed - three QComboBox objects, a QCheckBox, a QCompleter, a QLabel, and
a QLineEdit.
\snippet tools/completer/mainwindow.h 1
\section1 MainWindow Class Implementation
The constructor of \c MainWindow constructs a \c MainWindow with a parent
widget and initializes the private members. The \c createMenu() function
is then invoked.
We set up three QComboBox objects, \c modelComb, \c modeCombo and
\c caseCombo. By default, the \c modelCombo is set to QFileSystemModel,
the \c modeCombo is set to "Filtered Popup" and the \c caseCombo is set
to "Case Insensitive".
\snippet tools/completer/mainwindow.cpp 0
The \c maxVisibleSpinBox is created and determines the number of visible
item in the completer
The \c wrapCheckBox is then set up. This \c checkBox determines if the
\c{completer}'s \l{QCompleter::setWrapAround()}{setWrapAround()} property
is enabled or disabled.
\snippet tools/completer/mainwindow.cpp 1
We instantiate \c contentsLabel and set its size policy to
\l{QSizePolicy::Fixed}{fixed}. The combo boxes' \l{QComboBox::activated()}
{activated()} signals are then connected to their respective slots.
\snippet tools/completer/mainwindow.cpp 2
The \c lineEdit is set up and then we arrange all the widgets using a
QGridLayout. The \c changeModel() function is called, to initialize the
\c completer.
\snippet tools/completer/mainwindow.cpp 3
The \c createMenu() function is used to instantiate the QAction objects
needed to fill the \c fileMenu and \c helpMenu. The actions'
\l{QAction::triggered()}{triggered()} signals are connected to their
respective slots.
\snippet tools/completer/mainwindow.cpp 4
The \c modelFromFile() function accepts the \a fileName of a file and
processes it depending on its contents.
We first validate the \c file to ensure that it can be opened in
QFile::ReadOnly mode. If this is unsuccessful, the function returns an
empty QStringListModel.
\snippet tools/completer/mainwindow.cpp 5
The mouse cursor is then overridden with Qt::WaitCursor before we fill
a QStringList object, \c words, with the contents of \c file. Once this
is done, we restore the mouse cursor.
\snippet tools/completer/mainwindow.cpp 6
As mentioned earlier, the resources file contains two files -
\e{countries.txt} and \e{words.txt}. If the \c file read is \e{words.txt},
we return a QStringListModel with \c words as its QStringList and
\c completer as its parent.
\snippet tools/completer/mainwindow.cpp 7
If the \c file read is \e{countries.txt}, then we require a
QStandardItemModel with \c words.count() rows, 2 columns, and \c completer
as its parent.
\snippet tools/completer/mainwindow.cpp 8
A standard line in \e{countries.txt} is:
\quotation
Norway NO
\endquotation
Hence, to populate the QStandardItemModel object, \c m, we have to
split the country name and its symbol. Once this is done, we return
\c m.
\snippet tools/completer/mainwindow.cpp 9
The \c changeMode() function sets the \c{completer}'s mode, depending on
the value of \c index.
\snippet tools/completer/mainwindow.cpp 10
The \c changeModel() function changes the item model used based on the
model selected by the user.
A \c switch statement is used to change the item model based on the index
of \c modelCombo. If \c case is 0, we use an unsorted QFileSystemModel, providing
us with a file path excluding the drive label.
\snippet tools/completer/mainwindow.cpp 11
Note that we create the model with \c completer as the parent as this
allows us to replace the model with a new model. The \c completer will
ensure that the old one is deleted the moment a new model is assigned
to it.
If \c case is 1, we use the \c DirModel we defined earlier, resulting in
full paths for the files.
\snippet tools/completer/mainwindow.cpp 12
When \c case is 2, we attempt to complete names of countries. This requires
a QTreeView object, \c treeView. The country names are extracted from
\e{countries.txt} and set the popup used to display completions to
\c treeView.
\snippet tools/completer/mainwindow.cpp 13
The screenshot below shows the Completer with the country list model.
\image completer-example-country.png
If \c case is 3, we attempt to complete words. This is done using a
QStringListModel that contains data extracted from \e{words.txt}. The
model is sorted \l{QCompleter::CaseInsensitivelySortedModel}
{case insensitively}.
The screenshot below shows the Completer with the word list model.
\image completer-example-word.png
Once the model type is selected, we call the \c changeMode() function and
the \c changeCase() function and set the wrap option accordingly. The
\c{wrapCheckBox}'s \l{QCheckBox::clicked()}{clicked()} signal is connected
to the \c{completer}'s \l{QCompleter::setWrapAround()}{setWrapAround()}
slot.
\snippet tools/completer/mainwindow.cpp 14
The \c changeMaxVisible() update the maximum number of visible items in
the completer.
\snippet tools/completer/mainwindow.cpp 15
The \c about() function provides a brief description about the example.
\snippet tools/completer/mainwindow.cpp 16
\section1 \c main() Function
The \c main() function instantiates QApplication and \c MainWindow and
invokes the \l{QWidget::show()}{show()} function.
\snippet tools/completer/main.cpp 0
*/

View File

@ -0,0 +1,22 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/composition
\title Composition Modes
\ingroup examples-painting
\brief Demonstrates how Composition Modes work in QPainter.
\brief This demo shows some of the more advanced composition modes supported by Qt.
\image composition-demo.png
The two most common forms of composition are \b{Source} and \b{SourceOver}.
\b{Source} is used to draw opaque objects onto a paint device. In this mode,
each pixel in the source replaces the corresponding pixel in the destination.
In \b{SourceOver} composition mode, the source object is transparent and is
drawn on top of the destination.
In addition to these standard modes, Qt defines the complete set of composition modes
as defined by X. Porter and Y. Duff. See the QPainter documentation for details.
*/

View File

@ -0,0 +1,209 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/concentriccircles
\title Concentric Circles Example
\ingroup examples-painting
\brief Demonstrates the improved quality that antialiasing and floating point precision gives.
\brief The Concentric Circles example shows the improved rendering
quality that can be obtained using floating point precision and
anti-aliasing when drawing custom widgets. The example also shows
how to do simple animations.
The application's main window displays several widgets which are
drawn using the various combinations of precision and
anti-aliasing.
\image concentriccircles-example.png
Anti-aliasing is one of QPainter's render hints. The
QPainter::RenderHints are used to specify flags to QPainter that
may, or may not, be respected by any given
engine. QPainter::Antialiasing indicates that the engine should
anti-alias the edges of primitives if possible, i.e. put
additional pixels around the original ones to smooth the edges.
The difference between floating point precision and integer
precision is a matter of accuracy, and is visible in the
application's main window: Even though the logic that is
calculating the circles' geometry is the same, floating points
ensure that the white spaces between each circle are of the same
size, while integers make two and two circles appear as if they
belong together. The reason is that the integer based precision
rely on rounding off non-integer calculations.
The example consists of two classes:
\list
\li \c CircleWidget is a custom widget which renders several animated
concentric circles.
\li \c Window is the application's main window displaying four \c
{CircleWidget}s drawn using different combinations of precision
and aliasing.
\endlist
First we will review the CircleWidget class, then we will take a
look at the Window class.
\section1 CircleWidget Class Definition
The CircleWidget class inherits QWidget, and is a custom widget
which renders several animated concentric circles.
\snippet painting/concentriccircles/circlewidget.h 0
We declare the \c floatBased and \c antialiased variables to hold
whether an instance of the class should be rendered with integer
or float based precision, and whether the rendering should be
anti-aliased or not. We also declare functions setting each of
these variables.
In addition we reimplement the QWidget::paintEvent() function to
apply the various combinations of precision and anti-aliasing when
rendering, and to support the animation. We reimplement the
QWidget::minimumSizeHint() and QWidget::sizeHint() functions to
give the widget a reasonable size within our application.
We declare the private \c nextAnimationFrame() slot, and the
associated \c frameNo variable holding the number of "animation
frames" for the widget, to facilitate the animation.
\section1 CircleWidget Class Implementation
In the constructor we make the widget's rendering integer based
and aliased by default:
\snippet painting/concentriccircles/circlewidget.cpp 0
We initialize the widget's \c frameNo variable, and set the
widget's background color using the QWidget::setBackgroundColor()
function which takes a \l {QPalette::ColorRole}{color role} as
argument; the QPalette::Base color role is typically white.
Then we set the widgets size policy using the
QWidget::setSizePolicy() function. QSizePolicy::Expanding means
that the widget's \l {QWidget::sizeHint()}{sizeHint()} is a
sensible size, but that the widget can be shrunk and still be
useful. The widget can also make use of extra space, so it should
get as much space as possible.
\snippet painting/concentriccircles/circlewidget.cpp 1
\codeline
\snippet painting/concentriccircles/circlewidget.cpp 2
The public \c setFloatBased() and \c setAntialiased() functions
update the widget's rendering preferences, i.e. whether the widget
should be rendered with integer or float based precision, and
whether the rendering should be anti-aliased or not.
The functions also generate a paint event by calling the
QWidget::update() function, forcing a repaint of the widget with
the new rendering preferences.
\snippet painting/concentriccircles/circlewidget.cpp 3
\codeline
\snippet painting/concentriccircles/circlewidget.cpp 4
The default implementations of the QWidget::minimumSizeHint() and
QWidget::sizeHint() functions return invalid sizes if there is no
layout for the widget, otherwise they return the layout's minimum and
preferred size, respectively.
We reimplement the functions to give the widget minimum and
preferred sizes which are reasonable within our application.
\snippet painting/concentriccircles/circlewidget.cpp 5
The nextAnimationFrame() slot simply increments the \c frameNo
variable's value, and calls the QWidget::update() function which
schedules a paint event for processing when Qt returns to the main
event loop.
\snippet painting/concentriccircles/circlewidget.cpp 6
A paint event is a request to repaint all or part of the
widget. The \c paintEvent() function is an event handler that can
be reimplemented to receive the widget's paint events. We
reimplement the event handler to apply the various combinations of
precision and anti-aliasing when rendering the widget, and to
support the animation.
First, we create a QPainter for the widget, and set its
antialiased flag to the widget's preferred aliasing. We also
translate the painters coordinate system, preparing to draw the
widget's cocentric circles. The translation ensures that the
center of the circles will be equivalent to the widget's center.
\snippet painting/concentriccircles/circlewidget.cpp 7
When painting a circle, we use the number of "animation frames" to
determine the alpha channel of the circle's color. The alpha
channel specifies the color's transparency effect, 0 represents a
fully transparent color, while 255 represents a fully opaque
color.
\snippet painting/concentriccircles/circlewidget.cpp 8
If the calculated alpha channel is fully transparent, we don't
draw anything since that would be equivalent to drawing a white
circle on a white background. Instead we skip to the next circle
still creating a white space. If the calculated alpha channel is
fully opaque, we set the pen (the QColor passed to the QPen
constructor is converted into the required QBrush by default) and
draw the circle. If the widget's preferred precision is float
based, we specify the circle's bounding rectangle using QRectF and
double values, otherwise we use QRect and integers.
The animation is controlled by the public \c nextAnimationFrame()
slot: Whenever the \c nextAnimationFrame() slot is called the
number of frames is incremented and a paint event is
scheduled. Then, when the widget is repainted, the alpha-blending
of the circles' colors change and the circles appear as animated.
\section1 Window Class Definition
The Window class inherits QWidget, and is the application's main
window rendering four \c {CircleWidget}s using different
combinations of precision and aliasing.
\snippet painting/concentriccircles/window.h 0
We declare the various components of the main window, i.e., the text
labels and a double array that will hold reference to the four \c
{CircleWidget}s. In addition we declare the private \c
createLabel() function to simplify the constructor.
\section1 Window Class Implementation
\snippet painting/concentriccircles/window.cpp 0
In the constructor, we first create the various labels and put
them in a QGridLayout.
\snippet painting/concentriccircles/window.cpp 1
Then we create a QTimer. The QTimer class is a high-level
programming interface for timers, and provides repetitive and
single-shot timers.
We create a timer to facilitate the animation of our concentric
circles; when we create the four CircleWidget instances (and add
them to the layout), we connect the QTimer::timeout() signal to
each of the widgets' \c nextAnimationFrame() slots.
\snippet painting/concentriccircles/window.cpp 2
Before we set the layout and window title for our main window, we
make the timer start with a timeout interval of 100 milliseconds,
using the QTimer::start() function. That means that the
QTimer::timeout() signal will be emitted, forcing a repaint of the
four \c {CircleWidget}s, every 100 millisecond which is the reason
the circles appear as animated.
\snippet painting/concentriccircles/window.cpp 3
The private \c createLabel() function is implemented to simlify
the constructor.
*/

View File

@ -0,0 +1,265 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/customsortfiltermodel
\title Custom Sort/Filter Model Example
\ingroup examples-itemviews
\brief The Custom Sort/Filter Model example illustrates how to subclass
QSortFilterProxyModel to perform advanced sorting and filtering.
\image customsortfiltermodel-example.png Screenshot of the Custom Sort/Filter Model Example
The QSortFilterProxyModel class provides support for sorting and
filtering data passed between another model and a view.
The model transforms the structure of a source model by mapping
the model indexes it supplies to new indexes, corresponding to
different locations, for views to use. This approach allows a
given source model to be restructured as far as views are
concerned, without requiring any transformations on the underlying
data and without duplicating the data in memory.
The Custom Sort/Filter Model example consists of two classes:
\list
\li The \c MySortFilterProxyModel class provides a custom proxy
model.
\li The \c Window class provides the main application window,
using the custom proxy model to sort and filter a standard
item model.
\endlist
We will first take a look at the \c MySortFilterProxyModel class
to see how the custom proxy model is implemented, then we will
take a look at the \c Window class to see how the model is
used. Finally we will take a quick look at the \c main() function.
\section1 MySortFilterProxyModel Class Definition
The \c MySortFilterProxyModel class inherits the
QSortFilterProxyModel class.
Since QAbstractProxyModel and its subclasses are derived from
QAbstractItemModel, much of the same advice about subclassing
normal models also applies to proxy models.
On the other hand, it is worth noting that many of
QSortFilterProxyModel's default implementations of functions are
written so that they call the equivalent functions in the relevant
source model. This simple proxying mechanism may need to be
overridden for source models with more complex behavior. In this
example we derive from the QSortFilterProxyModel class to ensure
that our filter can recognize a valid range of dates, and to
control the sorting behavior.
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.h 0
We want to be able to filter our data by specifying a given period
of time. For that reason, we implement the custom \c
setFilterMinimumDate() and \c setFilterMaximumDate() functions as
well as the corresponding \c filterMinimumDate() and \c
filterMaximumDate() functions. We reimplement
QSortFilterProxyModel's \l
{QSortFilterProxyModel::filterAcceptsRow()}{filterAcceptsRow()}
function to only accept rows with valid dates, and
QSortFilterProxyModel::lessThan() to be able to sort the senders
by their email addresses. Finally, we implement a \c dateInRange()
convenience function that we will use to determine if a date is
valid.
\section1 MySortFilterProxyModel Class Implementation
The \c MySortFilterProxyModel constructor is trivial, passing the
parent parameter on to the base class constructor:
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 0
The most interesting parts of the \c MySortFilterProxyModel
implementation are the reimplementations of
QSortFilterProxyModel's \l
{QSortFilterProxyModel::filterAcceptsRow()}{filterAcceptsRow()}
and \l {QSortFilterProxyModel::lessThan()}{lessThan()}
functions. Let's first take a look at our customized \c lessThan()
function.
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 4
We want to sort the senders by their email addresses. The \l
{QSortFilterProxyModel::}{lessThan()} function is used as the <
operator when sorting. The default implementation handles a
collection of types including QDateTime and String, but in order
to be able to sort the senders by their email addresses we must
first identify the address within the given string:
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 6
We use QRegularExpression to define a pattern for the addresses we
are looking for. The \l {QRegularExpression::match()}{match()} function
returns a QRegularExpressionMatch object which contains the result of
the matching. If there is a match,
\l {QRegularExpressionMatch::hasMatch()}{hasMatch()} returns true. The
result of the match can be retrieved with QRegularExpressionMatch's
\l {QRegularExpressionMatch::captured()}{captured()} function.
The entire match has index 0 and the parenthesized
subexpressions have indexes starting from 1 (excluding
non-capturing parentheses).
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 3
The \l
{QSortFilterProxyModel::filterAcceptsRow()}{filterAcceptsRow()}
function, on the other hand, is expected to return true if the
given row should be included in the model. In our example, a row
is accepted if either the subject or the sender contains the given
regular expression, and the date is valid.
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 7
We use our custom \c dateInRange() function to determine if a date
is valid.
To be able to filter our data by specifying a given period of
time, we also implement functions for getting and setting the
minimum and maximum dates:
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 1
\codeline
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 2
The get functions, \c filterMinimumDate() and \c
filterMaximumDate(), are trivial and implemented as inline
function in the header file.
This completes our custom proxy model. Let's see how we can use it
in an application.
\section1 Window Class Definition
The \c CustomFilter class inherits QWidget, and provides this
example's main application window:
\snippet itemviews/customsortfiltermodel/window.h 0
We implement two private slots, \c textFilterChanged() and \c
dateFilterChanged(), to respond to the user changing the filter
pattern, case sensitivity, or any of the dates. In addition, we
implement a public \c setSourceModel() convenience function to set
up the model/ view relation.
\section1 Window Class Implementation
In this example, we have chosen to create and set the source model
in the \c main () function (which we will come back to later). So
when constructing the main application window, we assume that a
source model already exists and start by creating an instance of
our custom proxy model:
\snippet itemviews/customsortfiltermodel/window.cpp 0
We set the \l
{QSortFilterProxyModel::dynamicSortFilter}{dynamicSortFilter}
property that holds whether the proxy model is dynamically sorted
and filtered. By setting this property to true, we ensure that the
model is sorted and filtered whenever the contents of the source
model change.
The main application window shows views of both the source model
and the proxy model. The source view is quite simple:
\snippet itemviews/customsortfiltermodel/window.cpp 1
The QTreeView class provides a default model/view implementation
of a tree view. Our view implements a tree representation of items
in the application's source model.
\snippet itemviews/customsortfiltermodel/window.cpp 2
The QTreeView class provides a default model/view implementation
of a tree view; our view implements a tree representation of items
in the application's source model. We add our view widget to a
layout that we install on a corresponding group box.
The proxy model view, on the other hand, contains several widgets
controlling the various aspects of transforming the source model's
data structure:
\snippet itemviews/customsortfiltermodel/window.cpp 3
\snippet itemviews/customsortfiltermodel/window.cpp 4
Note that whenever the user changes one of the filtering options,
we must explicitly reapply the filter. This is done by connecting
the various editors to functions that update the proxy model.
\snippet itemviews/customsortfiltermodel/window.cpp 5
The sorting will be handled by the view. All we have to do is to
enable sorting for our proxy view by setting the
QTreeView::sortingEnabled property (which is false by
default). Then we add all the filtering widgets and the proxy view
to a layout that we install on a corresponding group box.
\snippet itemviews/customsortfiltermodel/window.cpp 6
Finally, after putting our two group boxes into another layout
that we install on our main application widget, we customize the
application window.
As mentioned above, we create the source model in the \c main ()
function, calling the \c Window::setSourceModel() function to make
the application use it:
\snippet itemviews/customsortfiltermodel/window.cpp 7
The QSortFilterProxyModel::setSourceModel() function makes the
proxy model process the data in the given model, in this case out
mail model. The \l {QAbstractItemView::}{setModel()} that the
view widget inherits from the QAbstractItemModel class, sets the
model for the view to present. Note that the latter function will
also create and set a new selection model.
\snippet itemviews/customsortfiltermodel/window.cpp 8
The \c textFilterChanged() function is called whenever the user
changes the filter pattern or the case sensitivity.
We first retrieve the preferred syntax (the FilterWidget::PatternSyntax
enum is used to interpret the meaning of the given pattern), then
we determine the preferred case sensitivity. Based on these
preferences and the current filter pattern, we set the proxy
model's \l {QSortFilterProxyModel::}{filterRegularExpression} property. The
\l {QSortFilterProxyModel::}{filterRegularExpression} property holds the
regular expression used to filter the contents of the source
model. Note that calling QSortFilterProxyModel's \l
{QSortFilterProxyModel::}{setFilterRegularExpression()} function also updates
the model.
\snippet itemviews/customsortfiltermodel/window.cpp 9
The \c dateFilterChanged() function is called whenever the user
modifies the range of valid dates. We retrieve the new dates from
the user interface, and call the corresponding functions (provided
by our custom proxy model) to set the proxy model's minimum and
maximum dates. As we explained above, calling these functions also
updates the model.
\section1 The Main() Function
In this example, we have separated the application from the source
model by creating the model in the \c main () function. First we
create the application, then we create the source model:
\snippet itemviews/customsortfiltermodel/main.cpp 0
The \c createMailModel() function is a convenience function
provided to simplify the constructor. All it does is to create and
return a model describing a collection of emails. The model is an
instance of the QStandardItemModel class, i.e., a generic model
for storing custom data typically used as a repository for
standard Qt data types. Each mail description is added to the
model using \c addMail(), another convenience function. See \c
{itemviews/customsortfiltermodel/main.cpp} for details.
*/

View File

@ -0,0 +1,29 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/deform
\title Vector Deformation
\ingroup examples-painting
\brief Demonstrates how to manipulate the elements of a QPainterPath.
\brief This example shows how to use advanced vector techniques to draw text
using a \c QPainterPath.
\image deform-demo.png
We define a vector deformation field in the shape of a lens and apply
this to all points in a path. This means that what is rendered on
screen is not pixel manipulation, but modified vector representations of
the glyphs themselves. This is visible from the high quality of the
antialiased edges for the deformed glyphs.
To get a fairly complex path we allow the user to type in text and
convert the text to paths. This is done using the
QPainterPath::addText() function.
The lens is drawn using a single call to QPainter::drawEllipse(),
using a QRadialGradient to fill it with a specialized color
table, giving the effect of the sun's reflection and a drop
shadow. The lens is cached as a pixmap for better performance.
*/

View File

@ -0,0 +1,812 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/diagramscene
\title Diagram Scene Example
\ingroup examples-graphicsview
\brief Demonstrate how to use the Graphics View framework.
\image diagramscene.png
The Diagram Scene example is an application in which you can
create a flowchart diagram. It is possible to add flowchart shapes
and text and connect the shapes by arrows as shown in the image
above. The shapes, arrows, and text can be given different
colors, and it is possible to change the font, style, and
underline of the text.
The Qt graphics view framework is designed to manage and display
custom 2D graphics items. The main classes of the framework are
QGraphicsItem, QGraphicsScene and QGraphicsView. The graphics
scene manages the items and provides a surface for them.
QGraphicsView is a widget that is used to render a scene on the
screen. See the \l{Graphics View Framework} for a more detailed
description of the framework.
In this example we show how to create such custom graphics
scenes and items by implementing classes that inherit
QGraphicsScene and QGraphicsItem.
In particular we show how to:
\list
\li Create custom graphics items.
\li Handle mouse events and movement of items.
\li Implement a graphics scene that can manage our custom items.
\li Custom painting of items.
\li Create a movable and editable text item.
\endlist
The example consists of the following classes:
\list
\li \c MainWindow creates the widgets and display
them in a QMainWindow. It also manages the interaction
between the widgets and the graphics scene, view and
items.
\li \c DiagramItem inherits QGraphicsPolygonItem and
represents a flowchart shape.
\li \c TextDiagramItem inherits QGraphicsTextItem and
represents text items in the diagram. The class adds
support for moving the item with the mouse, which is not
supported by QGraphicsTextItem.
\li \c Arrow inherits QGraphicsLineItem and is an arrow
that connect two DiagramItems.
\li \c DiagramScene inherits QGraphicsDiagramScene and
provides support for \c DiagramItem, \c Arrow and
\c DiagramTextItem (In addition to the support already
handled by QGraphicsScene).
\endlist
\section1 MainWindow Class Definition
\snippet graphicsview/diagramscene/mainwindow.h 0
The \c MainWindow class creates and lays out the widgets in a
QMainWindow. The class forwards input from the widgets to the
DiagramScene. It also updates its widgets when the diagram
scene's text item changes, or a diagram item or a diagram text item
is inserted into the scene.
The class also deletes items from the scene and handles the
z-ordering, which decides the order in which items are drawn when
they overlap each other.
\section1 MainWindow Class Implementation
We start with a look at the constructor:
\snippet graphicsview/diagramscene/mainwindow.cpp 0
In the constructor we call methods to create the widgets and
layouts of the example before we create the diagram scene.
The toolbars must be created after the scene as they connect
to its signals. We then lay the widgets out in the window.
We connect to the \c itemInserted() and \c textInserted() slots of
the diagram scenes as we want to uncheck the buttons in the tool
box when an item is inserted. When an item is selected in
the scene we receive the \c itemSelected() signal. We use this to
update the widgets that display font properties if the item
selected is a \c DiagramTextItem.
The \c createToolBox() function creates and lays out the widgets
of the \c toolBox QToolBox. We will not examine it with a
high level of detail as it does not deal with graphics framework
specific functionality. Here is its implementation:
\snippet graphicsview/diagramscene/mainwindow.cpp 21
This part of the function sets up the tabbed widget item that
contains the flowchart shapes. An exclusive QButtonGroup always
keeps one button checked; we want the group to allow all buttons
to be unchecked.
We still use a button group since we can associate user
data, which we use to store the diagram type, with each button.
The \c createCellWidget() function sets up the buttons in the
tabbed widget item and is examined later.
The buttons of the background tabbed widget item is set up in the
same way, so we skip to the creation of the tool box:
\snippet graphicsview/diagramscene/mainwindow.cpp 22
We set the preferred size of the toolbox as its maximum. This
way, more space is given to the graphics view.
Here is the \c createActions() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 23
We show an example of the creation of an action. The
functionality the actions trigger is discussed in the slots we
connect the actions to. You can see the \l{Qt Widgets - Application
Example}{application example} if you need a high-level
introduction to actions.
The is the \c createMenus() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 24
We create the three menus' of the example.
The \c createToolbars() function sets up the examples tool
bars. The three \l{QToolButton}s in the \c colorToolBar, the \c
fontColorToolButton, \c fillColorToolButton, and \c
lineColorToolButton, are interesting as we create icons for them
by drawing on a QPixmap with a QPainter. We show how the \c
fillColorToolButton is created. This button lets the user select a
color for the diagram items.
\snippet graphicsview/diagramscene/mainwindow.cpp 25
\dots
\snippet graphicsview/diagramscene/mainwindow.cpp 26
We set the menu of the tool button with
\l{QToolButton::}{setMenu()}. We need the \c fillAction QAction
object to always be pointing to the selected action of the menu.
The menu is created with the \c createColorMenu() function and, as
we shall see later, contains one menu item for each color that the
items can have. When the user presses the button, which trigger
the \l{QToolButton::}{clicked()} signal, we can set the color of
the selected item to the color of \c fillAction. It is with \c
createColorToolButtonIcon() we create the icon for the button.
\dots
\snippet graphicsview/diagramscene/mainwindow.cpp 27
Here is the \c createBackgroundCellWidget() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 28
This function creates \l{QWidget}s containing a tool button
and a label. The widgets created with this function are used for
the background tabbed widget item in the tool box.
Here is the \c createCellWidget() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 29
This function returns a QWidget containing a QToolButton with
an image of one of the \c DiagramItems, i.e., flowchart shapes.
The image is created by the \c DiagramItem through the \c image()
function. The QButtonGroup class lets us attach an id (int) with
each button; we store the diagram's type, i.e., the
DiagramItem::DiagramType enum. We use the stored diagram type when
we create new diagram items for the scene. The widgets created
with this function is used in the tool box.
Here is the \c createColorMenu() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 30
This function creates a color menu that is used as the
drop-down menu for the tool buttons in the \c colorToolBar. We
create an action for each color that we add to the menu. We fetch
the actions data when we set the color of items, lines, and text.
Here is the \c createColorToolButtonIcon() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 31
This function is used to create the QIcon of the \c
fillColorToolButton, \c fontColorToolButton, and \c
lineColorToolButton. The \a imageFile string is either the text,
flood-fill, or line symbol that is used for the buttons. Beneath
the image we draw a filled rectangle using \a color.
Here is the \c createColorIcon() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 32
This function creates an icon with a filled rectangle in the
color of \a color. It is used for creating icons for the color
menus in the \c fillColorToolButton, \c fontColorToolButton, and
\c lineColorToolButton.
Here is the \c backgroundButtonGroupClicked() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 1
In this function we set the QBrush that is used to draw the
background of the diagramscene. The background can be a grid of
squares of blue, gray, or white tiles, or no grid at all. We have
\l{QPixmap}s of the tiles from png files that we create the brush
with.
When one of the buttons in the background tabbed widget item is
clicked we change the brush; we find out which button it is by
checking its text.
Here is the implementation of \c buttonGroupClicked():
\snippet graphicsview/diagramscene/mainwindow.cpp 2
This slot is called when a button in \c buttonGroup is checked.
When a button is checked the user can click on the graphics view
and a \c DiagramItem of the selected type will be inserted into
the \c DiagramScene. We must loop through the buttons in the group
to uncheck other buttons as only one button is allowed to be
checked at a time.
\c QButtonGroup assigns an id to each button. We have set the id
of each button to the diagram type, as given by DiagramItem::DiagramType
that will be inserted into the scene when it is clicked. We can
then use the button id when we set the diagram type with
\c setItemType(). In the case of text we assigned an id that has a
value that is not in the DiagramType enum.
Here is the implementation of \c deleteItem():
\snippet graphicsview/diagramscene/mainwindow.cpp 3
This slot deletes the selected item, if any, from the scene. It
deletes the arrows first in order to avoid to delete them twice. If
the item to be deleted is a \c DiagramItem, we also need to delete
arrows connected to it; we don't want arrows in the scene that
aren't connected to items in both ends.
This is the implementation of pointerGroupClicked():
\snippet graphicsview/diagramscene/mainwindow.cpp 4
The \c pointerTypeGroup decides whether the scene is in ItemMove
or InsertLine mode. This button group is exclusive, i.e., only
one button is checked at any time. As with the \c buttonGroup above
we have assigned an id to the buttons that matches values of the
DiagramScene::Mode enum, so that we can use the id to set the
correct mode.
Here is the \c bringToFront() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 5
Several items may collide, i.e., overlap, with each other in
the scene. This slot is called when the user requests that an
item should be placed on top of the items it collides with.
\l{QGraphicsItem}{QGrapicsItems} have a z-value that decides the
order in which items are stacked in the scene; you can think of it
as the z-axis in a 3D coordinate system. When items collide the
items with higher z-values will be drawn on top of items with
lower values. When we bring an item to the front we can loop
through the items it collides with and set a z-value that is
higher than all of them.
Here is the \c sendToBack() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 6
This slot works in the same way as \c bringToFront() described
above, but sets a z-value that is lower than items the item that
should be send to the back collides with.
This is the implementation of \c itemInserted():
\snippet graphicsview/diagramscene/mainwindow.cpp 7
This slot is called from the \c DiagramScene when an item has been
added to the scene. We set the mode of the scene back to the mode
before the item was inserted, which is ItemMove or InsertText
depending on which button is checked in the \c pointerTypeGroup.
We must also uncheck the button in the in the \c buttonGroup.
Here is the implementation of \c textInserted():
\snippet graphicsview/diagramscene/mainwindow.cpp 8
We simply set the mode of the scene back to the mode it had before
the text was inserted.
Here is the \c currentFontChanged() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 9
When the user requests a font change, by using one of the
widgets in the \c fontToolBar, we create a new QFont object and
set its properties to match the state of the widgets. This is done
in \c handleFontChange(), so we simply call that slot.
Here is the \c fontSizeChanged() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 10
When the user requests a font change, by using one of the
widgets in the \c fontToolBar, we create a new QFont object and
set its properties to match the state of the widgets. This is done
in \c handleFontChange(), so we simply call that slot.
Here is the implementation of \c sceneScaleChanged():
\snippet graphicsview/diagramscene/mainwindow.cpp 11
The user can increase or decrease the scale, with the \c
sceneScaleCombo, the scene is drawn in.
It is not the scene itself that changes its scale, but only the
view.
Here is the \c textColorChanged() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 12
This slot is called when an item in the drop-down menu of the \c
fontColorToolButton is pressed. We need to change the icon on
the button to the color of the selected QAction. We keep a pointer
to the selected action in \c textAction. It is in \c
textButtonTriggered() we change the text color to the color of \c
textAction, so we call that slot.
Here is the \c itemColorChanged() implementation:
\snippet graphicsview/diagramscene/mainwindow.cpp 13
This slot handles requests for changing the color of \c
DiagramItems in the same manner as \c textColorChanged() does for
\c DiagramTextItems.
Here is the implementation of \c lineColorChanged():
\snippet graphicsview/diagramscene/mainwindow.cpp 14
This slot handles requests for changing the color of \c Arrows in
the same manner that \c textColorChanged() does it for \c
DiagramTextItems.
Here is the \c textButtonTriggered() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 15
\c textAction points to the QAction of the currently selected menu item
in the \c fontColorToolButton's color drop-down menu. We have set
the data of the action to the QColor the action represents, so we
can simply fetch this when we set the color of text with \c
setTextColor().
Here is the \c fillButtonTriggered() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 16
\c fillAction points to the selected menu item in the drop-down
menu of \c fillColorToolButton(). We can therefore use the data of
this action when we set the item color with \c setItemColor().
Here is the \c lineButtonTriggered() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 17
\c lineAction point to the selected item in the drop-down menu of
\c lineColorToolButton. We use its data when we set the arrow
color with \c setLineColor().
Here is the \c handleFontChange() function:
\snippet graphicsview/diagramscene/mainwindow.cpp 18
\c handleFontChange() is called when any of the widgets that show
font properties changes. We create a new QFont object and set its
properties based on the widgets. We then call the \c setFont()
function of \c DiagramScene; it is the scene that set the font of
the \c DiagramTextItems it manages.
Here is the \c itemSelected() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 19
This slot is called when an item in the \c DiagramScene is
selected. In the case of this example it is only text items that
emit signals when they are selected, so we do not need to check
what kind of graphics \a item is.
We set the state of the widgets to match the properties of the
font of the selected text item.
This is the \c about() slot:
\snippet graphicsview/diagramscene/mainwindow.cpp 20
This slot displays an about box for the example when the user
selects the about menu item from the help menu.
\section1 DiagramScene Class Definition
The \c DiagramScene class inherits QGraphicsScene and adds
functionality to handle \c DiagramItems, \c Arrows, and \c
DiagramTextItems in addition to the items handled by its super
class.
\snippet graphicsview/diagramscene/diagramscene.h 0
In the \c DiagramScene a mouse click can give three different
actions: the item under the mouse can be moved, an item may be
inserted, or an arrow may be connected between to diagram items.
Which action a mouse click has depends on the mode, given by the
Mode enum, the scene is in. The mode is set with the \c setMode()
function.
The scene also sets the color of its items and the font of its
text items. The colors and font used by the scene can be set with
the \c setLineColor(), \c setTextColor(), \c setItemColor() and \c
setFont() functions. The type of \c DiagramItem, given by the
DiagramItem::DiagramType function, to be created when an item is
inserted is set with the \c setItemType() slot.
The \c MainWindow and \c DiagramScene share responsibility for
the examples functionality. \c MainWindow handles the following
tasks: the deletion of items, text, and arrows; moving diagram
items to the back and front; and setting the scale of the scene.
\section1 DiagramScene Class Implementation
We start with the constructor:
\snippet graphicsview/diagramscene/diagramscene.cpp 0
The scene uses \c myItemMenu to set the context menu when it
creates \c DiagramItems. We set the default mode to \c
DiagramScene::MoveItem as this gives the default behavior of
QGraphicsScene.
Here is the \c setLineColor() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 1
The \c isItemChange function returns true if an \c Arrow item is
selected in the scene in which case we want to change its color.
When the \c DiagramScene creates and adds new arrows to the scene
it will also use the new \a color.
Here is the \c setTextColor() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 2
This function sets the color of \c DiagramTextItems equal to the
way \c setLineColor() sets the color of \c Arrows.
Here is the \c setItemColor() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 3
This function sets the color the scene will use when creating
\c DiagramItems. It also changes the color of a selected \c
DiagramItem.
This is the implementation of \c setFont():
\snippet graphicsview/diagramscene/diagramscene.cpp 4
Set the font to use for new and selected, if a text item is
selected, \c DiagramTextItems.
This is the implementation of \c editorLostFocus() slot:
\snippet graphicsview/diagramscene/diagramscene.cpp 5
\c DiagramTextItems emit a signal when they lose focus, which is
connected to this slot. We remove the item if it has no text.
If not, we would leak memory and confuse the user as the items
will be edited when pressed on by the mouse.
The \c mousePressEvent() function handles mouse press event's
different depending on which mode the \c DiagramScene is in. We
examine its implementation for each mode:
\snippet graphicsview/diagramscene/diagramscene.cpp 6
We simply create a new \c DiagramItem and add it to the scene at
the position the mouse was pressed. Note that the origin of its
local coordinate system will be under the mouse pointer position.
\snippet graphicsview/diagramscene/diagramscene.cpp 7
The user adds \c Arrows to the scene by stretching a line between
the items the arrow should connect. The start of the line is fixed
in the place the user clicked the mouse and the end follows the
mouse pointer as long as the button is held down. When the user
releases the mouse button an \c Arrow will be added to the scene
if there is a \c DiagramItem under the start and end of the line.
We will see how this is implemented later; here we simply add the
line.
\snippet graphicsview/diagramscene/diagramscene.cpp 8
The \c DiagramTextItem is editable when the
Qt::TextEditorInteraction flag is set, else it is movable by the
mouse. We always want the text to be drawn on top of the other
items in the scene, so we set the value to a number higher
than other items in the scene.
\snippet graphicsview/diagramscene/diagramscene.cpp 9
We are in MoveItem mode if we get to the default switch; we
can then call the QGraphicsScene implementation, which
handles movement of items with the mouse. We make this call even
if we are in another mode making it possible to add an item and
then keep the mouse button pressed down and start moving
the item. In the case of text items, this is not possible as they
do not propagate mouse events when they are editable.
This is the \c mouseMoveEvent() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 10
We must draw the line if we are in InsertMode and the mouse button
is pressed down (the line is not 0). As discussed in \c
mousePressEvent() the line is drawn from the position the mouse
was pressed to the current position of the mouse.
If we are in MoveItem mode, we call the QGraphicsScene
implementation, which handles movement of items.
In the \c mouseReleaseEvent() function we need to check if an arrow
should be added to the scene:
\snippet graphicsview/diagramscene/diagramscene.cpp 11
First we need to get the items (if any) under the line's start
and end points. The line itself is the first item at these points,
so we remove it from the lists. As a precaution, we check if the
lists are empty, but this should never happen.
\snippet graphicsview/diagramscene/diagramscene.cpp 12
Now we check if there are two different \c DiagramItems under
the lines start and end points. If there are we can create an \c
Arrow with the two items. The arrow is then added to each item and
finally the scene. The arrow must be updated to adjust its start
and end points to the items. We set the z-value of the arrow to
-1000.0 because we always want it to be drawn under the items.
\snippet graphicsview/diagramscene/diagramscene.cpp 13
Here is the \c isItemChange() function:
\snippet graphicsview/diagramscene/diagramscene.cpp 14
The scene has single selection, i.e., only one item can be
selected at any given time. The for loop will then loop one time
with the selected item or none if no item is selected. \c
isItemChange() is used to check whether a selected item exists
and also is of the specified diagram \a type.
\section1 DiagramItem Class Definition
\snippet graphicsview/diagramscene/diagramitem.h 0
The \c DiagramItem represents a flowchart shape in the \c
DiagramScene. It inherits QGraphicsPolygonItem and has a polygon
for each shape. The enum DiagramType has a value for each of the
flowchart shapes.
The class has a list of the arrows that are connected to it.
This is necessary because only the item knows when it is being
moved (with the \c itemChanged() function) at which time the
arrows must be updated. The item can also draw itself onto a
QPixmap with the \c image() function. This is used for the tool
buttons in \c MainWindow, see \c createColorToolButtonIcon() in
\c MainWindow.
The Type enum is a unique identifier of the class. It is used by
\c qgraphicsitem_cast(), which does dynamic casts of graphics
items. The UserType constant is the minimum value a custom
graphics item type can be.
\section1 DiagramItem Class Implementation
We start with a look at the constructor:
\snippet graphicsview/diagramscene/diagramitem.cpp 0
In the constructor we create the items polygon according to
\a diagramType. \l{QGraphicsItem}s are not movable or selectable
by default, so we must set these properties.
Here is the \c removeArrow() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 1
\c removeArrow() is used to remove \c Arrow items when they
or \c DiagramItems they are connected to are removed from the
scene.
Here is the \c removeArrows() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 2
This function is called when the item is removed from the scene
and removes all arrows that are connected to this item. The arrow
must be removed from the \c arrows list of both its start and end
item. Since either the start or the end item is the object where
this function is currently called, we have to make sure to work on
a copy of arrows since removeArrow() is modifying this container.
Here is the \c addArrow() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 3
This function simply adds the \a arrow to the items \c arrows list.
Here is the \c image() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 4
This function draws the polygon of the item onto a QPixmap. In
this example we use this to create icons for the tool buttons in
the tool box.
Here is the \c contextMenuEvent() function:
\snippet graphicsview/diagramscene/diagramitem.cpp 5
We show the context menu. As right mouse clicks, which shows the
menu, don't select items by default we set the item selected with
\l{QGraphicsItem::}{setSelected()}. This is necessary since an
item must be selected to change its elevation with the
\c bringToFront and \c sendToBack actions.
This is the implementation of \c itemChange():
\snippet graphicsview/diagramscene/diagramitem.cpp 6
If the item has moved, we need to update the positions of the
arrows connected to it. The implementation of QGraphicsItem does
nothing, so we just return \a value.
\section1 DiagramTextItem Class Definition
The \c TextDiagramItem class inherits QGraphicsTextItem and
adds the possibility to move editable text items. Editable
QGraphicsTextItems are designed to be fixed in place and editing
starts when the user single clicks on the item. With \c
DiagramTextItem the editing starts with a double click leaving
single click available to interact with and move it.
\snippet graphicsview/diagramscene/diagramtextitem.h 0
We use \c itemChange() and \c focusOutEvent() to notify the
\c DiagramScene when the text item loses focus and gets selected.
We reimplement the functions that handle mouse events to make it
possible to alter the mouse behavior of QGraphicsTextItem.
\section1 DiagramTextItem Implementation
We start with the constructor:
\snippet graphicsview/diagramscene/diagramtextitem.cpp 0
We simply set the item movable and selectable, as these flags are
off by default.
Here is the \c itemChange() function:
\snippet graphicsview/diagramscene/diagramtextitem.cpp 1
When the item is selected we emit the selectedChanged signal. The
\c MainWindow uses this signal to update the widgets that display
font properties to the font of the selected text item.
Here is the \c focusOutEvent() function:
\snippet graphicsview/diagramscene/diagramtextitem.cpp 2
\c DiagramScene uses the signal emitted when the text item loses
focus to remove the item if it is empty, i.e., it contains no
text.
This is the implementation of \c mouseDoubleClickEvent():
\snippet graphicsview/diagramscene/diagramtextitem.cpp 5
When we receive a double click event, we make the item editable by calling
QGraphicsTextItem::setTextInteractionFlags(). We then forward the
double-click to the item itself.
\section1 Arrow Class Definition
The \c Arrow class is a graphics item that connects two \c
DiagramItems. It draws an arrow head to one of the items. To
achieve this the item needs to paint itself and also re implement
methods used by the graphics scene to check for collisions and
selections. The class inherits QGraphicsLine item, and draws the
arrowhead and moves with the items it connects.
\snippet graphicsview/diagramscene/arrow.h 0
The item's color can be set with \c setColor().
\c boundingRect() and \c shape() are reimplemented
from QGraphicsLineItem and are used by the scene
to check for collisions and selections.
Calling \c updatePosition() causes the arrow to recalculate its
position and arrow head angle. \c paint() is reimplemented so that
we can paint an arrow rather than just a line between items.
\c myStartItem and \c myEndItem are the diagram items that the
arrow connects. The arrow is drawn with its head to the end item.
\c arrowHead is a polygon with three vertices's we use to draw the
arrow head.
\section1 Arrow Class Implementation
The constructor of the \c Arrow class looks like this:
\snippet graphicsview/diagramscene/arrow.cpp 0
We set the start and end diagram items of the arrow. The arrow
head will be drawn where the line intersects the end item.
Here is the \c boundingRect() function:
\snippet graphicsview/diagramscene/arrow.cpp 1
We need to reimplement this function because the arrow is
larger than the bounding rectangle of the QGraphicsLineItem. The
graphics scene uses the bounding rectangle to know which regions
of the scene to update.
Here is the \c shape() function:
\snippet graphicsview/diagramscene/arrow.cpp 2
The shape function returns a QPainterPath that is the exact
shape of the item. The QGraphicsLineItem::shape() returns a path
with a line drawn with the current pen, so we only need to add
the arrow head. This function is used to check for collisions and
selections with the mouse.
Here is the \c updatePosition() slot:
\snippet graphicsview/diagramscene/arrow.cpp 3
This slot updates the arrow by setting the start and end
points of its line to the center of the items it connects.
Here is the \c paint() function:
\snippet graphicsview/diagramscene/arrow.cpp 4
If the start and end items collide we do not draw the arrow; the
algorithm we use to find the point the arrow should be drawn at
may fail if the items collide.
We first set the pen and brush we will use for drawing the arrow.
\snippet graphicsview/diagramscene/arrow.cpp 5
We then need to find the position at which to draw the
arrowhead. The head should be drawn where the line and the end
item intersects. This is done by taking the line between each
point in the polygon and check if it intersects with the line of
the arrow. Since the line start and end points are set to the
center of the items the arrow line should intersect one and only
one of the lines of the polygon. Note that the points in the
polygon are relative to the local coordinate system of the item.
We must therefore add the position of the end item to make the
coordinates relative to the scene.
\snippet graphicsview/diagramscene/arrow.cpp 6
We calculate the angle between the x-axis and the line of the
arrow. We need to turn the arrow head to this angle so that it
follows the direction of the arrow. If the angle is negative we
must turn the direction of the arrow.
We can then calculate the three points of the arrow head polygon.
One of the points is the end of the line, which now is the
intersection between the arrow line and the end polygon. Then we
clear the \c arrowHead polygon from the previous calculated arrow
head and set these new points.
\snippet graphicsview/diagramscene/arrow.cpp 7
If the line is selected, we draw two dotted lines that are
parallel with the line of the arrow. We do not use the default
implementation, which uses \l{QGraphicsItem::}{boundingRect()}
because the QRect bounding rectangle is considerably larger than
the line.
*/

View File

@ -0,0 +1,51 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/digitalclock
\title Digital Clock Example
\ingroup examples-widgets
\brief The Digital Clock example shows how to use QLCDNumber to display a
number with LCD-like digits.
\borderedimage digitalclock-example.png
\caption Screenshot of the Digital Clock example
This example also demonstrates how QTimer can be used to update a widget
at regular intervals.
\section1 DigitalClock Class Definition
The \c DigitalClock class provides a clock widget showing the time with
hours and minutes separated by a blinking colon. We subclass QLCDNumber
and implement a private slot called \c showTime() to update the clock
display:
\snippet widgets/digitalclock/digitalclock.h 0
\section1 DigitalClock Class Implementation
\snippet widgets/digitalclock/digitalclock.cpp 0
In the constructor, we first change the look of the LCD numbers. The
QLCDNumber::Filled style produces raised segments filled with the
foreground color (typically black). We also set up a one-second timer
to keep track of the current time, and we connect
its \l{QTimer::timeout()}{timeout()} signal to the private \c showTime() slot
so that the display is updated every second. Then, we
call the \c showTime() slot; without this call, there would be a one-second
delay at startup before the time is shown.
\snippet widgets/digitalclock/digitalclock.cpp 1
\snippet widgets/digitalclock/digitalclock.cpp 2
The \c showTime() slot is called whenever the clock display needs
to be updated.
The current time is converted into a string with the format "hh:mm".
When QTime::second() is a even number, the colon in the string is
replaced with a space. This makes the colon appear and vanish every
other second.
Finally, we call QLCDNumber::display() to update the widget.
*/

View File

@ -0,0 +1,49 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/dirview
\title Dir View Example
\ingroup examples-itemviews
\brief This example demonstrates the usage of a tree view, and smooth flicking on a touchscreen.
The Dir View example shows a tree view of the local file
system. It uses the QFileSystemModel class to provide file
and directory information.
\borderedimage dirview-example.png
\quotefromfile itemviews/dirview/main.cpp
\skipto QCommandLineParser parser
\printuntil parser.positionalArguments
The example supports a number of command line options.
These options include:
\list
\li Application description
\li -help option
\li -version option
\li if the optionc {-c} is specified, the application will not
use custom directory options
\endlist
\skipto QFileSystemModel
\printuntil tree.setModel
Declares \c model as data model for reading the local filesystem.
\c model.setRootPath("") sets the current folder as the folder from
which \c model will start reading.
QTreeView object \c tree visualizes the filesystem in a tree structure.
\skipto tree.setAnimated(false)
\printuntil tree.setColumnWidth
Sets layout options for animation, indentation, sorting, and sizing of the
filesystem tree.
\skipto QScroller::grabGesture
\printuntil QScroller::grabGesture
Creates a \l QScroller instance to recognize gestures on touchscreens,
so that you can flick the tree view with your finger.
*/

View File

@ -0,0 +1,140 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example mainwindows/dockwidgets
\title Dock Widgets Example
\ingroup examples-mainwindow
\brief The Dock Widgets example shows how to add dock windows to an
application. It also shows how to use Qt's rich text engine.
\image dockwidgets-example.png Screenshot of the Dock Widgets example
The application presents a simple business letter template, and has
a list of customer names and addresses and a list of standard
phrases in two dock windows. The user can click a customer to have
their name and address inserted into the template, and click one or
more of the standard phrases. Errors can be corrected by clicking
the Undo button. Once the letter has been prepared it can be printed
or saved as HTML.
\section1 MainWindow Class Definition
Here's the class definition:
\snippet mainwindows/dockwidgets/mainwindow.h 0
We will now review each function in turn.
\section1 MainWindow Class Implementation
\snippet mainwindows/dockwidgets/mainwindow.cpp 0
We start by including \c <QtWidgets>, a header file that contains the
definition of all classes in the Qt Core, Qt GUI and Qt Widgets
modules. This saves us from having to include
every class individually and is especially convenient if we add new
widgets. We also include \c mainwindow.h.
\snippet mainwindows/dockwidgets/mainwindow.cpp 1
In the constructor, we start by creating a QTextEdit widget. Then we call
QMainWindow::setCentralWidget(). This function passes ownership of
the QTextEdit to the \c MainWindow and tells the \c MainWindow that
the QTextEdit will occupy the \c MainWindow's central area.
Then we call \c createActions(), \c createMenus(), \c
createToolBars(), \c createStatusBar(), and \c createDockWindows()
to set up the user interface. Finally we call \c setWindowTitle() to
give the application a title, and \c newLetter() to create a new
letter template.
We won't quote the \c createActions(), \c createMenus(), \c
createToolBars(), and \c createStatusBar() functions since they
follow the same pattern as all the other Qt examples.
\snippet mainwindows/dockwidgets/mainwindow.cpp 9
We create the customers dock window first, and in addition to a
window title, we also pass it a \c this pointer so that it becomes a
child of \c MainWindow. Normally we don't have to pass a parent
because widgets are parented automatically when they are laid out:
but dock windows aren't laid out using layouts.
We've chosen to restrict the customers dock window to the left and
right dock areas. (So the user cannot drag the dock window to the
top or bottom dock areas.) The user can drag the dock window out of
the dock areas entirely so that it becomes a free floating window.
We can change this (and whether the dock window is moveable or
closable) using QDockWidget::setFeatures().
Once we've created the dock window we create a list widget with the
dock window as parent, then we populate the list and make it the
dock window's widget. Finally we add the dock widget to the \c
MainWindow using \c addDockWidget(), choosing to put it in the right
dock area.
We undertake a similar process for the paragraphs dock window,
except that we don't restrict which dock areas it can be dragged to.
Finally we set up the signal-slot connections. If the user clicks a
customer or a paragraph their \c currentTextChanged() signal will be
emitted and we connect these to \c insertCustomer() and
addParagraph() passing the text that was clicked.
We briefly discuss the rest of the implementation, but have now
covered everything relating to dock windows.
\snippet mainwindows/dockwidgets/mainwindow.cpp 2
In this function we clear the QTextEdit so that it is empty. Next we
create a QTextCursor on the QTextEdit. We move the cursor to the
start of the document and create and format a frame. We then create
some character formats and a table format. We insert a table into
the document and insert the company's name and address into a table
using the table and character formats we created earlier. Then we
insert the skeleton of the letter including two markers \c NAME and
\c ADDRESS. We will also use the \c{Yours sincerely,} text as a marker.
\snippet mainwindows/dockwidgets/mainwindow.cpp 6
If the user clicks a customer we split the customer details into
pieces. We then look for the \c NAME marker using the \c find()
function. This function selects the text it finds, so when we call
\c insertText() with the customer's name the name replaces the marker.
We then look for the \c ADDRESS marker and replace it with each line
of the customer's address. Notice that we wrapped all the insertions
between a \c beginEditBlock() and \c endEditBlock() pair. This means
that the entire name and address insertion is treated as a single
operation by the QTextEdit, so a single undo will revert all the
insertions.
\snippet mainwindows/dockwidgets/mainwindow.cpp 7
This function works in a similar way to \c insertCustomer(). First
we look for the marker, in this case, \c {Yours sincerely,}, and then
replace it with the standard paragraph that the user clicked. Again
we use a \c beginEditBlock() ... \c endEditBlock() pair so that the
insertion can be undone as a single operation.
\snippet mainwindows/dockwidgets/mainwindow.cpp 3
Qt's QTextDocument class makes printing documents easy. We simply
take the QTextEdit's QTextDocument, set up the printer and print the
document.
\snippet mainwindows/dockwidgets/mainwindow.cpp 4
QTextEdit can output its contents in HTML format, so we prompt the
user for the name of an HTML file and if they provide one we simply
write the QTextEdit's contents in HTML format to the file.
\snippet mainwindows/dockwidgets/mainwindow.cpp 5
If the focus is in the QTextEdit, pressing \uicontrol Ctrl+Z undoes as
expected. But for the user's convenience we provide an
application-wide undo function that simply calls the QTextEdit's
undo: this means that the user can undo regardless of where the
focus is in the application.
*/

View File

@ -0,0 +1,18 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example draganddrop/puzzle
\title Drag and Drop Puzzle Example
\brief The Drag and Drop Puzzle example demonstrates a way of using the drag and drop system with
item view widgets.
\image draganddroppuzzle-example.png
This example is an implementation of a simple jigsaw puzzle game using Qt's
drag and drop API.
The \l{Item Views Puzzle Example}{Item View Puzzle} example shows
many of the same features, but takes an alternative approach that uses Qt's
model/view framework to manage drag and drop operations.
*/

View File

@ -0,0 +1,343 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/dragdroprobot
\title Drag and Drop Robot Example
\ingroup examples-graphicsview
\brief Demonstrates how to drag and drop items in a graphics view.
The Drag and Drop Robot example shows how to implement Drag and Drop in a
QGraphicsItem subclass, as well as how to animate items using Qt's
\l{Animation Framework}.
\image dragdroprobot-example.png
Graphics View provides the QGraphicsScene class for managing and
interacting with a large number of custom-made 2D graphical items derived
from the QGraphicsItem class, and a QGraphicsView widget for visualizing
the items, with support for zooming and rotation.
This example consists of a \c Robot class, a \c ColorItem class, and a main
function: the \c Robot class describes a simple robot consisting of several
\c RobotPart derived limbs, including \c RobotHead and \c RobotLimb, the \c
ColorItem class provides a draggable colored ellipse, and the \c main()
function provides the main application window.
We will first review the \c Robot class to see how to assemble the
different parts so that they can be individually rotated and animated using
QPropertyAnimation, and we will then review the \c ColorItem class to
demonstrate how to implement Drag and Drop between items. Finally we will
review the main() function to see how we can put all the pieces together,
to form the final application.
\section1 Robot Class Definition
The robot consists of three main classes: the \c RobotHead, the \c
RobotTorso, and the \c RobotLimb, which is used for the upper and lower
arms and legs. All parts derive from the \c RobotPart class, which in turn
inherits \c QGraphicsObject. The \c Robot class itself has no visual
appearance and serves only as a root node for the robot.
Let's start with the \c RobotPart class declaration.
\snippet graphicsview/dragdroprobot/robot.h 0
This base class inherits QGraphicsObject. QGraphicsObject provides signals
and slots through inheriting QObject, and it also declares QGraphicsItem's
properties using Q_PROPERTY, which makes the properties accessible for
QPropertyAnimation.
RobotPart also implements the three most important event handlers for
accepting drop events:
\l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()},
\l{QGraphicsItem::dragLeaveEvent()}{dragLeaveEvent()}, and
\l{QGraphicsItem::dropEvent()}{dropEvent()}.
The color is stored as a member variable, along with the \c dragOver
variable, which we will use later to indicate visually that the limb can
accept colors that are is dragged onto it.
\snippet graphicsview/dragdroprobot/robot.cpp 0
\c RobotPart's constructor initializes the dragOver member and sets the
color to Qt::lightGray. In the constructor body we enable support for
accepting drop events by calling
\l{QGraphicsItem::setAcceptDrops()}{setAcceptDrops(true)}.
The rest of this class's implementation is to support Drag and Drop.
\snippet graphicsview/dragdroprobot/robot.cpp 1
The \l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()} handler is called
when a Drag and Drop element is dragged into the robot part's area.
The handler implementation determines whether or not this item as a whole
can accept the mime data assiciated with the incoming drag object. \c
RobotPart provides a base behavior for all parts that accepts color drops.
So if the incoming drag object contains a color, the event is accepted, we
set \c dragOver to \c true and call update() to help provide positive
visual feedback to the user; otherwise the event is ignored, which in turn
allows the event to propagate to parent elements.
\snippet graphicsview/dragdroprobot/robot.cpp 2
The \l{QGraphicsItem::dragLeaveEvent()}{dragLeaveEvent()} handler is called
when a Drag and Drop element is dragged away from the robot part's area.
Our implementation simply resets \e dragOver to false and calls
\l{QGraphicsItem::update()}{update()} to help provide visual feedback that
the drag has left this item.
\snippet graphicsview/dragdroprobot/robot.cpp 3
The \l{QGraphicsItem::dropEvent()}{dropEvent()} handler is called when a
Drag and Drop element is dropped onto an item (i.e., when the mouse button
is released over the item while dragging).
We reset \c dragOver to false, assign the item's new color, and call
\l{QGraphicsItem::update()}{update()}.
The declaration and implementation of \c RobotHead, \c RobotTorso, and \c
RobotLimb are practically identical. We will review \c RobotHead in detail,
as this class has one minor difference, and leave the other classes as an
exercise for the reader.
\snippet graphicsview/dragdroprobot/robot.h 1
The \c RobotHead class inherits \c RobotPart and provides the necessary
implementations of \l{QGraphicsItem::boundingRect()}{boundingRect()} and
\l{QGraphicsItem::paint()}{paint()}. It also reimplements
\l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()} and dropEvent() to
provide special handling of image drops.
The class contains a private pixmap member that we can use to implement
support for accepting image drops.
\snippet graphicsview/dragdroprobot/robot.cpp 4
\c RobotHead has a rather plain constructor that simply forwards to
\c RobotPart's constructor.
\snippet graphicsview/dragdroprobot/robot.cpp 5
The \l{QGraphicsItem::boundingRect()}{boundingRect()} reimplementation
returns the extents for the head. Because we want the center of rotation to
be the bottom center of the item, we have chosen a bounding rectangle that
starts at (-15, -50) and extends to 30 units wide and 50 units tall. When
rotating the head, the "neck" will stay still while the top of the head
tilts from side to side.
\snippet graphicsview/dragdroprobot/robot.cpp 6
In \l{QGraphicsItem::paint()}{paint()} we draw the actual head. The
implementation is split into two sections; if an image has been dropped
onto the head, we draw the image, otherwise we draw a round rectangular
robot head with simple vector graphics.
For performance reasons, depending on the complexity of what is painted, it
can often be faster to draw the head as an image rather than using a
sequence of vector operations.
\snippet graphicsview/dragdroprobot/robot.cpp 7
The robot head can accept image drops. In order to support this, its
reimplementation of \l{QGraphicsItem::dragEnterEvent()}{dragEnterEvent()}
checks if the drag object contains image data, and if it does, then the
event is accepted. Otherwise we fall back to the base \c RobotPart
implementation.
\snippet graphicsview/dragdroprobot/robot.cpp 8
To follow up on image support, we must also implement
\l{QGraphicsItem::dropEvent()}{dropEvent()}. We check if the drag object
contains image data, and if it does, we store this data as a member pixmap
and call \l{QGraphicsItem::update()}{update()}. This pixmap is used inside
the \l{QGraphicsItem::paint()}{paint()} implementation that we reviewed
before.
\c RobotTorso and \c RobotLimb are similar to \c RobotHead, so let's
skip directly to the \c Robot class.
\snippet graphicsview/dragdroprobot/robot.h 4
The \c Robot class also inherits \c RobotPart, and like the other parts it
also implements \l{QGraphicsItem::boundingRect()}{boundingRect()} and
\l{QGraphicsItem::paint()}{paint()}. It provides a rather special
implementation, though:
\snippet graphicsview/dragdroprobot/robot.cpp 9
Because the \c Robot class is only used as a base node for the rest of the
robot, it has no visual representation. Its
\l{QGraphicsItem::boundingRect()}{boundingRect()} implementation can
therefore return a null QRectF, and its paint() function does nothing.
\snippet graphicsview/dragdroprobot/robot.cpp 10
The constructor starts by setting the flag
\l{QGraphicsItem::ItemHasNoContents}{ItemHasNoContents}, which is a minor
optimization for items that have no visual appearance.
We then construct all the robot parts (head, torso, and upper/lower arms
and legs). The stacking order is very important, and we use the
parent-child hierarchy to ensure the elements rotate and move properly. We
construct the torso first, as this is the root element. We then construct
the head and pass the torso to \c HeadItem's constructor. This will make
the head a child of the torso; if you rotate the torso, the head will
follow. The same pattern is applied to the rest of the limbs.
\snippet graphicsview/dragdroprobot/robot.cpp 11
Each robot part is carefully positioned. For example, the upper left arm is
moved precisely to the top-left area of the torso, and the upper right arm
is moved to the top-right area.
\snippet graphicsview/dragdroprobot/robot.cpp 12
The next section creates all animation objects. This snippet shows the two
animations that operate on the head's scale and rotation. The two
QPropertyAnimation instances simply set the object, property, and
respective start and end values.
All animations are controlled by one top-level parallel animation group.
The scale and rotation animations are added to this group.
The rest of the animations are defined in a similar way.
\snippet graphicsview/dragdroprobot/robot.cpp 13
Finally we set an easing curve and duration on each animation, ensure the
toplevel animation group loops forever, and start the toplevel animation.
\section1 ColorItem Class Definition
The \c ColorItem class represents a circular item that can be pressed to
drag colors onto robot parts.
\snippet graphicsview/dragdroprobot/coloritem.h 0
This class is very simple. It does not use animations, and has no need for
properties nor signals and slots, so to save resources, it's most natural
that it inherits QGraphicsItem (as opposed to QGraphicsObject).
It declares the mandatory \l{QGraphicsItem::boundingRect()}{boundingRect()}
and \l{QGraphicsItem::paint()}{paint()} functions, and adds
reimplementations of
\l{QGraphicsItem::mousePressEvent()}{mousePressEvent()},
\l{QGraphicsItem::mouseMoveEvent()}{mouseMoveEvent()}, and
\l{QGraphicsItem::mouseReleaseEvent()}{mouseReleaseEvent()}. It contains a
single private color member.
Let's take a look at its implementation.
\snippet graphicsview/dragdroprobot/coloritem.cpp 0
\c ColorItem's constructor assigns an opaque random color to its color
member by making use of \l QRandomGenerator. For improved usability, it assigns a
tooltip that provides a useful hint to the user, and it also sets a
suitable cursor. This ensures that the cursor will chance to
Qt::OpenHandCursor when the mouse pointer hovers over the item.
Finally, we call
\l{QGraphicsItem::setAcceptedMouseButtons()}{setAcceptedMouseButtons()} to
ensure that this item can only process Qt::LeftButton. This simplifies the
mouse event handlers greatly, as we can always assume that only the left
mouse button is pressed and released.
\snippet graphicsview/dragdroprobot/coloritem.cpp 1
The item's bounding rect is a fixed 30x30 units centered around the item's
origin (0, 0), and adjusted by 0.5 units in all directions to allow a
scalable pen to draw its outline. For a final visual touch the bounds
also compensate with a few units down and to the right to make room
for a simple dropshadow.
\snippet graphicsview/dragdroprobot/coloritem.cpp 2
The \l{QGraphicsItem::paint()}{paint()} implementation draws an ellipse
with a 1-unit black outline, a plain color fill, and a dark gray
dropshadow.
\snippet graphicsview/dragdroprobot/coloritem.cpp 3
The \l{QGraphicsItem::mousePressEvent()}{mousePressEvent()} handler is
called when you press the mouse button inside the item's area. Our
implementation simply sets the cursor to Qt::ClosedHandCursor.
\snippet graphicsview/dragdroprobot/coloritem.cpp 4
The \l{QGraphicsItem::mouseReleaseEvent()}{mouseReleaseEvent()} handler is
called when you release the mouse button after having pressed it inside an
item's area. Our implementation sets the cursor back to Qt::OpenHandCursor.
The mouse press and release event handlers together provide useful visual
feedback to the user: when you move the mouse pointer over a \c CircleItem,
the cursor changes to an open hand. Pressing the item will show a closed
hand cursor. Releasing will restore to an open hand cursor again.
\snippet graphicsview/dragdroprobot/coloritem.cpp 5
The \l{QGraphicsItem::mouseMoveEvent()}{mouseMoveEvent()} handler is called
when you move the mouse around after pressing the mouse button inside the
\c ColorItem's area. This implementation provides the most important piece
of logic for \c CircleItem: the code that starts and manages drags.
The implementation starts by checking if the mouse has been dragged far
enough to eliminate mouse jitter noise. We only want to start a drag if the
mouse has been dragged farther than the application start drag distance.
Continuing, we create a QDrag object, passing the event
\l{QGraphicsSceneEvent::widget()}{widget} (i.e., the QGraphicsView
viewport) to its constructor. Qt will ensure that this object is deleted at
the right time. We also create a QMimeData instance that can contain our
color or image data, and assign this to the drag object.
\snippet graphicsview/dragdroprobot/coloritem.cpp 6
This snippet has a somewhat random outcome: once in a while, a special
image is assigned to the drag object's mime data. The pixmap is also
assigned as the drag object's pixmap. This will ensure that you can see the
image that is being dragged as a pixmap under the mouse cursor.
\snippet graphicsview/dragdroprobot/coloritem.cpp 7
Otherwise, and this is the most common outcome, a simple color is assigned
to the drag object's mime data. We render this \c ColorItem into a new
pixmap to give the user visual feedback that the color is being "dragged".
\snippet graphicsview/dragdroprobot/coloritem.cpp 8
Finally we execute the drag. QDrag::exec() will reenter the event loop, and
only exit if the drag has either been dropped, or canceled. In any case we
reset the cursor to Qt::OpenHandCursor.
\section1 The main() Function
Now that the \c Robot and \c ColorItem classes are complete, we can put all
the pieces together inside the main() function.
\snippet graphicsview/dragdroprobot/main.cpp 0
We start off by constructing QApplication, and initializing the random
number generator. This ensures that the color items have different colors
every time the application starts.
\snippet graphicsview/dragdroprobot/main.cpp 1
We construct a fixed size scene, and create 10 \c ColorItem instances
arranged in a circle. Each item is added to the scene.
In the center of this circle we create one \c Robot instance. The
robot is scaled and moved up a few units. It is then added to the scene.
\snippet graphicsview/dragdroprobot/main.cpp 2
Finally we create a QGraphicsView window, and assign the scene to it.
For increased visual quality, we enable antialiasing. We also choose to use
bounding rectangle updates to simplify visual update handling.
The view is given a fixed sand-colored background, and a window title.
We then show the view. The animations start immediately after
control enters the event loop.
*/

View File

@ -0,0 +1,66 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example draganddrop/draggableicons
\title Draggable Icons Example
\brief The Draggable Icons example shows how to drag and drop image data between widgets
in the same application, and between different applications.
\image draggableicons-example.png
In many situations where drag and drop is used, the user starts dragging from
a particular widget and drops the payload onto another widget. In this example,
we subclass QLabel to create labels that we use as drag sources, and place them
inside \l{QWidget}s that serve as both containers and drop sites.
In addition, when a drag and drop operation occurs, we want to send more than
just an image. We also want to send information about where the user clicked in
the image so that the user can place it precisely on the drop target. This level
of detail means that we must create a custom MIME type for our data.
\section1 DragWidget Class Definition
The icon widgets that we use to display icons are subclassed from QLabel:
\snippet draganddrop/draggableicons/dragwidget.h 0
Since the QLabel class provides most of what we require for the icon, we
only need to reimplement the \l QWidget::mousePressEvent() to provide
drag and drop facilities.
\section1 DragWidget Class Implementation
The \c DragWidget constructor sets an attribute on the widget that ensures
that it will be deleted when it is closed:
\snippet draganddrop/draggableicons/dragwidget.cpp 0
To enable dragging from the icon, we need to act on a mouse press event.
We do this by reimplementing \l QWidget::mousePressEvent() and setting up
a QDrag object.
\snippet draganddrop/draggableicons/dragwidget.cpp 1
Since we will be sending pixmap data for the icon and information about the
user's click in the icon widget, we construct a QByteArray and package up the
details using a QDataStream.
For interoperability, drag and drop operations describe the data they contain
using MIME types. In Qt, we describe this data using a QMimeData object:
\snippet draganddrop/draggableicons/dragwidget.cpp 2
We choose an unofficial MIME type for this purpose, and supply the QByteArray
to the MIME data object.
The drag and drop operation itself is handled by a QDrag object:
\snippet draganddrop/draggableicons/dragwidget.cpp 3
Here, we pass the data to the drag object, set a pixmap that will be shown
alongside the cursor during the operation, and define the position of a hot
spot that places the position of this pixmap under the cursor.
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example draganddrop/draggabletext
\title Draggable Text Example
\brief Illustrates how to drag and drop text between widgets.
\brief The Draggable Text example shows how to drag and drop textual data between widgets
in the same application, and between different applications.
\image draggabletext-example.png
*/

View File

@ -0,0 +1,91 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example layouts/dynamiclayouts
\title Dynamic Layouts Example
\brief Shows how to re-orient widgets in running applications.
\e{Dynamic Layouts} implements dynamically placed widgets within running
applications. The widget placement depends on whether \c Horizontal or \c
Vertical is chosen.
\borderedimage dynamiclayouts-example.png
For more information, visit the \l{Layout Management} page.
\section1 Dialog Constructor
To begin with, the application creates the UI components by calling the
following methods:
\list
\li createRotatableGroupBox()
\li createOptionsGroupBox()
\li createButtonBox()
\endlist
It then adds the UI components to a GridLayout (\c mainLayout).
Finally, \c Dialog::rotateWidgets() is called.
\quotefromfile layouts/dynamiclayouts/dialog.cpp
\skipuntil createRotatableGroupBox
\printuntil setWindowTitle
\section1 Creating the Main Widgets
The \c createRotatableGroupBox() method creates a rotatable group box,
then adds a series of widgets:
\list
\li QSpinBox
\li QSlider
\li QDial
\li QProgressBar
\endlist
It goes on to add signals and slots to each widget, and assigns
a QGridLayout called \a rotatableLayout.
\skipto Dialog::createRotatableGroupBox
\printuntil /^\}/
\section1 Adding Options
\c createOptionsGroupBox() creates the following widgets:
\list
\li \c optionsGroupBox
\li \c buttonsOrientationLabel
\li \c buttonsOrientationComboBox. The orientation of the ComboBox is either
\c horizontal (default value) or \c vertical. These two values
are added during the startup of the application. It is not possible
to leave the option empty.
\endlist
\skipto Dialog::createOptionsGroupBox()
\printuntil /^\}/
\section1 Adding Buttons
createButtonBox() constructs a QDialogButtonBox called \c buttonBox
to which are added a \c closeButton, a \c helpButton and a
\c rotateWidgetsButton.
It then assigns a signal and a slot to each button in \c buttonBox.
\skipto Dialog::createButtonBox()
\printuntil /^\}/
\section1 Rotating the Widgets
Removes the current widgets and activates the next widget.
\quotefromfile layouts/dynamiclayouts/dialog.cpp
\skipto Dialog::rotateWidgets()
\printuntil rotatableLayout->addWidget(rotatableWidgets[i]
\printuntil }
\printuntil }
\include examples-run.qdocinc
*/

View File

@ -0,0 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example animation/easing
\title Easing Curves Example
\brief The Easing Curves example shows how to use easing curves to
control the speed of an animation.
\image easing-example.png
See \l QEasingCurve for a detailed description on how to use the class's
functionalities.
*/

View File

@ -0,0 +1,181 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/echoplugin
\title Echo Plugin Example
\ingroup examples-widgets-tools
\ingroup examples-layout
\brief This example shows how to create a Qt plugin.
\image echopluginexample.png
There are two kinds of plugins in Qt: plugins that extend Qt
itself and plugins that extend applications written in Qt. In this
example, we show the procedure of implementing plugins that extend
applications. When you create a plugin you declare an interface,
which is a class with only pure virtual functions. This interface
is inherited by the class that implements the plugin. The class is
stored in a shared library and can therefore be loaded by
applications at run-time. When loaded, the plugin is dynamically
cast to the interface using Qt's \l{Meta-Object
System}{meta-object system}. The plugin \l{How to Create Qt
Plugins}{overview document} gives a high-level introduction to
plugins.
We have implemented a plugin, the \c EchoPlugin, which implements
the \c EchoInterface. The interface consists of \c echo(), which
takes a QString as argument. The \c EchoPlugin returns the string
unaltered (i.e., it works as the familiar echo command found in
both Unix and Windows).
We test the plugin in \c EchoWindow: when you push the QPushButton
(as seen in the image above), the application sends the text in
the QLineEdit to the plugin, which echoes it back to the
application. The answer from the plugin is displayed in the
QLabel.
\section1 EchoWindow Class Definition
The \c EchoWindow class lets us test the \c EchoPlugin through a
GUI.
\snippet tools/echoplugin/echowindow/echowindow.h 0
We load the plugin in \c loadPlugin() and cast it to \c
EchoInterface. When the user clicks the \c button we take the
text in \c lineEdit and call the interface's \c echo() with it.
\section1 EchoWindow Class Implementation
We start with a look at the constructor:
\snippet tools/echoplugin/echowindow/echowindow.cpp 0
We create the widgets and set a title for the window. We then load
the plugin. \c loadPlugin() returns false if the plugin could not
be loaded, in which case we disable the widgets. If you wish a
more detailed error message, you can use
\l{QPluginLoader::}{errorString()}; we will look more closely at
QPluginLoader later.
Here is the implementation of \c sendEcho():
\snippet tools/echoplugin/echowindow/echowindow.cpp 1
This slot is called when the user pushes \c button or presses
enter in \c lineEdit. We call \c echo() of the echo interface. In
our example this is the \c EchoPlugin, but it could be any plugin
that inherit the \c EchoInterface. We take the QString returned
from \c echo() and display it in the \c label.
Here is the implementation of \c createGUI():
\snippet tools/echoplugin/echowindow/echowindow.cpp 2
We create the widgets and lay them out in a grid layout. We
connect the label and line edit to our \c sendEcho() slot.
Here is the \c loadPlugin() function:
\snippet tools/echoplugin/echowindow/echowindow.cpp 3
Access to plugins at run-time is provided by QPluginLoader. You
supply it with the filename of the shared library the plugin is
stored in and call \l{QPluginLoader::}{instance()}, which loads
and returns the root component of the plugin (i.e., it resolves
the type of the plugin and creates a QObject instance of it). If
the plugin was not successfully loaded, it will be null, so we
return false. If it was loaded correctly, we can cast the plugin
to our \c EchoInterface and return true. In the case that the
plugin loaded does not implement the \c EchoInterface, \c
instance() will return null, but this cannot happen in our
example. Notice that the location of the plugin is not the same
for all platforms.
\section1 EchoInterface Class Definition
The \c EchoInterface defines the functions that the plugin will
provide. An interface is a class that only consists of pure
virtual functions. If non virtual functions were present in the
class you would get misleading compile errors in the moc files.
\snippet tools/echoplugin/echowindow/echointerface.h 0
We declare \c echo(). In our \c EchoPlugin we use this method to
return, or echo, \a message.
We use the Q_DECLARE_INTERFACE macro to let \l{Meta-Object
System}{Qt's meta object system} aware of the interface. We do
this so that it will be possible to identify plugins that
implements the interface at run-time. The second argument is a
string that must identify the interface in a unique way.
\section1 EchoPlugin Class Definition
We inherit both QObject and \c EchoInterface to make this class a
plugin. The Q_INTERFACES macro tells Qt which interfaces the class
implements. In our case we only implement the \c EchoInterface.
If a class implements more than one interface, they are given as
a space separated list. The Q_PLUGIN_METADATA macro is included next
to the Q_OBJECT macro. It contains the plugins IID and a filename
pointing to a json file containing the metadata for the plugin.
The json file is compiled into the plugin and does not need to be installed.
\snippet tools/echoplugin/plugin/echoplugin.h 0
\section1 EchoPlugin Class Implementation
Here is the implementation of \c echo():
\snippet tools/echoplugin/plugin/echoplugin.cpp 0
We simply return the functions parameter.
\section1 The \c main() function
\snippet tools/echoplugin/echowindow/main.cpp 0
We create an \c EchoWindow and display it as a top-level window.
\section1 The Profiles
When creating plugins the profiles need to be adjusted.
We show here what changes need to be done.
The profile in the echoplugin directory uses the \c subdirs
template and simply includes includes to directories in which
the echo window and echo plugin lives:
\snippet tools/echoplugin/echoplugin.pro 0
The profile for the echo window does not need any plugin specific
settings. We move on to the plugin profile:
\snippet tools/echoplugin/plugin/plugin.pro 0
We need to set the TEMPLATE as we now want to make a library
instead of an executable. We also need to tell qmake that we are
creating a plugin. The \c EchoInterface that the plugin implements
lives in the \c echowindow directory, so we need to add that
directory to the include path. We set the TARGET of the project,
which is the name of the library file in which the plugin will be
stored; qmake appends the appropriate file extension depending on
the platform. By convention the target should have the same name
as the plugin (set with Q_EXPORT_PLUGIN2)
\section1 Further Reading and Examples
The \l {qtplugin-defining-plugins}{Defining Plugins} page presents an overview of the macros needed to
create plugins.
We give an example of a plugin that extends Qt in the \l{Style
Plugin Example}{style plugin} example. The \l{Plug & Paint
Example}{plug and paint} example shows how to create static
plugins.
*/

View File

@ -0,0 +1,422 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/editabletreemodel
\title Editable Tree Model Example
\ingroup examples-itemviews
\brief This example shows how to implement a simple item-based tree model that can
be used with other classes the model/view framework.
\image itemviews-editabletreemodel.png
The model supports editable items, custom headers, and the ability to
insert and remove rows and columns. With these features, it is also
possible to insert new child items, and this is shown in the supporting
example code.
\section1 Overview
As described in the \l{Model Subclassing Reference}, models must
provide implementations for the standard set of model functions:
\l{QAbstractItemModel::}{flags()}, \l{QAbstractItemModel::}{data()},
\l{QAbstractItemModel::}{headerData()},
\l{QAbstractItemModel::}{columnCount()}, and
\l{QAbstractItemModel::}{rowCount()}. In addition, hierarchical models,
such as this one, need to provide implementations of
\l{QAbstractItemModel::}{index()} and \l{QAbstractItemModel::}{parent()}.
An editable model needs to provide implementations of
\l{QAbstractItemModel::}{setData()} and
\l{QAbstractItemModel::}{setHeaderData()}, and must return a suitable
combination of flags from its \l{QAbstractItemModel::}{flags()} function.
Since this example allows the dimensions of the model to be changed,
we must also implement \l{QAbstractItemModel::}{insertRows()},
\l{QAbstractItemModel::}{insertColumns()},
\l{QAbstractItemModel::}{removeRows()}, and
\l{QAbstractItemModel::}{removeColumns()}.
\section1 Design
As with the \l{itemviews/simpletreemodel}{Simple Tree Model} example,
the model simply acts as a wrapper around a collection
of instances of a \c TreeItem class. Each \c TreeItem is designed to
hold data for a row of items in a tree view, so it contains a list of
values corresponding to the data shown in each column.
Since QTreeView provides a row-oriented view onto a model, it is
natural to choose a row-oriented design for data structures that
will supply data via a model to this kind of view. Although this makes
the tree model less flexible, and possibly less useful for use with
more sophisticated views, it makes it less complex to design and easier
to implement.
\target Relations-between-internal-items
\table
\row \li \inlineimage itemviews-editabletreemodel-items.png
\li \b{Relations between internal items}
When designing a data structure for use with a custom model, it is useful
to expose each item's parent via a function like
\l{TreeItem::parent}{TreeItem::parent()} because it will make
writing the model's own \l{QAbstractItemModel::}{parent()} function easier.
Similarly, a function like \l{TreeItem::child}{TreeItem::child()} is
helpful when implementing the model's \l{QAbstractItemModel::}{index()}
function. As a result, each \c TreeItem maintains information about
its parent and children, making it possible for us to traverse the tree
structure.
The diagram shows how \c TreeItem instances are connected via their
\l{TreeItem::parent}{parent()} and \l{TreeItem::child}{child()}
functions.
In the example shown, two top-level items, \b{A} and
\b{B}, can be obtained from the root item by calling its child()
function, and each of these items return the root node from their
parent() functions, though this is only shown for item \b{A}.
\endtable
Each \c TreeItem stores data for each column in the row it represents
in its \c itemData private member (a list of QVariant objects).
Since there is a one-to-one mapping between each column in the view
and each entry in the list, we provide a simple
\l{TreeItem::data}{data()} function to read entries in the \c itemData
list and a \l{TreeItem::setData}{setData()} function to allow them to
be modified.
As with other functions in the item, this simplifies the implementation
of the model's \l{QAbstractItemModel::}{data()} and
\l{QAbstractItemModel::}{setData()} functions.
We place an item at the root of the tree of items. This root item
corresponds to the null model index, \l{QModelIndex::}{QModelIndex()},
that is used to represent the parent of a top-level item when handling
model indexes.
Although the root item does not have a visible representation in any of
the standard views, we use its internal list of QVariant objects to
store a list of strings that will be passed to views for use as
horizontal header titles.
\table
\row \li \inlineimage itemviews-editabletreemodel-model.png
\li \b{Accessing data via the model}
In the case shown in the diagram, the piece of information represented
by \b{a} can be obtained using the standard model/view API:
\code
QVariant a = model->index(0, 0, QModelIndex()).data();
\endcode
Since each items holds pieces of data for each column in a given row,
there can be many model indexes that map to the same \c TreeItem object.
For example, the information represented by \b{b} can be obtained
using the following code:
\code
QVariant b = model->index(1, 0, QModelIndex()).data();
\endcode
The same underlying \c TreeItem would be accessed to obtain information
for the other model indexes in the same row as \b{b}.
\endtable
In the model class, \c TreeModel, we relate \c TreeItem objects to
model indexes by passing a pointer for each item when we create its
corresponding model index with QAbstractItemModel::createIndex() in
our \l{TreeModel::index}{index()} and \l{TreeModel::parent}{parent()}
implementations.
We can retrieve pointers stored in this way by calling the
\l{QModelIndex::}{internalPointer()} function on the relevant model
index - we create our own \l{TreeModel::getItem}{getItem()} function to
do the work for us, and call it from our \l{TreeModel::data}{data()}
and \l{TreeModel::parent}{parent()} implementations.
Storing pointers to items is convenient when we control how they are
created and destroyed since we can assume that an address obtained from
\l{QModelIndex::}{internalPointer()} is a valid pointer.
However, some models need to handle items that are obtained from other
components in a system, and in many cases it is not possible to fully
control how items are created or destroyed. In such situations, a pure
pointer-based approach needs to be supplemented by safeguards to ensure
that the model does not attempt to access items that have been deleted.
\table
\row \li \b{Storing information in the underlying data structure}
Several pieces of data are stored as QVariant objects in the \c itemData
member of each \c TreeItem instance.
The diagram shows how pieces of information,
represented by the labels \b{a}, \b{b} and \b{c} in the
previous two diagrams, are stored in items \b{A}, \b{B} and
\b{C} in the underlying data structure. Note that pieces of
information from the same row in the model are all obtained from the
same item. Each element in a list corresponds to a piece of information
exposed by each column in a given row in the model.
\li \inlineimage itemviews-editabletreemodel-values.png
\endtable
Since the \c TreeModel implementation has been designed for use with
QTreeView, we have added a restriction on the way it uses \c TreeItem
instances: each item must expose the same number of columns of data.
This makes viewing the model consistent, allowing us to use the root
item to determine the number of columns for any given row, and only
adds the requirement that we create items containing enough data for
the total number of columns. As a result, inserting and removing
columns are time-consuming operations because we need to traverse the
entire tree to modify every item.
An alternative approach would be to design the \c TreeModel class so
that it truncates or expands the list of data in individual \c TreeItem
instances as items of data are modified. However, this "lazy" resizing
approach would only allow us to insert and remove columns at the end of
each row and would not allow columns to be inserted or removed at
arbitrary positions in each row.
\target Relating-items-using-model-indexes
\table
\row
\li \inlineimage itemviews-editabletreemodel-indexes.png
\li \b{Relating items using model indexes}
As with the \l{itemviews/simpletreemodel}{Simple Tree Model} example,
the \c TreeModel needs to be able to take a model index, find the
corresponding \c TreeItem, and return model indexes that correspond to
its parents and children.
In the diagram, we show how the model's \l{TreeModel::parent}{parent()}
implementation obtains the model index corresponding to the parent of
an item supplied by the caller, using the items shown in a
\l{Relations-between-internal-items}{previous diagram}.
A pointer to item \b{C} is obtained from the corresponding model index
using the \l{QModelIndex::internalPointer()} function. The pointer was
stored internally in the index when it was created. Since the child
contains a pointer to its parent, we use its \l{TreeItem::parent}{parent()}
function to obtain a pointer to item \b{B}. The parent model index is
created using the QAbstractItemModel::createIndex() function, passing
the pointer to item \b{B} as the internal pointer.
\endtable
\section1 TreeItem Class Definition
The \c TreeItem class provides simple items that contain several
pieces of data, including information about their parent and
child items:
\snippet itemviews/editabletreemodel/treeitem.h 0
We have designed the API to be similar to that provided by
QAbstractItemModel by giving each item functions to return the number
of columns of information, read and write data, and insert and remove
columns. However, we make the relationship between items explicit by
providing functions to deal with "children" rather than "rows".
Each item contains a list of pointers to child items, a pointer to its
parent item, and a list of QVariant objects that correspond to
information held in columns in a given row in the model.
\section1 TreeItem Class Implementation
Each \c TreeItem is constructed with a list of data and an optional
parent item:
\snippet itemviews/editabletreemodel/treeitem.cpp 0
Initially, each item has no children. These are added to the item's
internal \c childItems member using the \c insertChildren() function
described later.
The destructor ensures that each child added to the item is deleted
when the item itself is deleted:
\snippet itemviews/editabletreemodel/treeitem.cpp 1
\target TreeItem::parent
Since each item stores a pointer to its parent, the \c parent() function
is trivial:
\snippet itemviews/editabletreemodel/treeitem.cpp 9
\target TreeItem::child
Three functions provide information about the children of an item.
\c child() returns a specific child from the internal list of children:
\snippet itemviews/editabletreemodel/treeitem.cpp 2
The \c childCount() function returns the total number of children:
\snippet itemviews/editabletreemodel/treeitem.cpp 3
The \c childNumber() function is used to determine the index of the child
in its parent's list of children. It accesses the parent's \c childItems
member directly to obtain this information:
\snippet itemviews/editabletreemodel/treeitem.cpp 4
The root item has no parent item; for this item, we return zero to be
consistent with the other items.
The \c columnCount() function simply returns the number of elements in
the internal \c itemData list of QVariant objects:
\snippet itemviews/editabletreemodel/treeitem.cpp 5
\target TreeItem::data
Data is retrieved using the \c data() function, which accesses the
appropriate element in the \c itemData list:
\snippet itemviews/editabletreemodel/treeitem.cpp 6
\target TreeItem::setData
Data is set using the \c setData() function, which only stores values
in the \c itemData list for valid list indexes, corresponding to column
values in the model:
\snippet itemviews/editabletreemodel/treeitem.cpp 11
To make implementation of the model easier, we return true to indicate
that the data was set successfully.
Editable models often need to be resizable, enabling rows and columns to
be inserted and removed. The insertion of rows beneath a given model index
in the model leads to the insertion of new child items in the corresponding
item, handled by the \c insertChildren() function:
\snippet itemviews/editabletreemodel/treeitem.cpp 7
This ensures that new items are created with the required number of columns
and inserted at a valid position in the internal \c childItems list.
Items are removed with the \c removeChildren() function:
\snippet itemviews/editabletreemodel/treeitem.cpp 10
As discussed above, the functions for inserting and removing columns are
used differently to those for inserting and removing child items because
they are expected to be called on every item in the tree. We do this by
recursively calling this function on each child of the item:
\snippet itemviews/editabletreemodel/treeitem.cpp 8
\section1 TreeModel Class Definition
The \c TreeModel class provides an implementation of the QAbstractItemModel
class, exposing the necessary interface for a model that can be edited and
resized.
\snippet itemviews/editabletreemodel/treemodel.h 0
The constructor and destructor are specific to this model.
\snippet itemviews/editabletreemodel/treemodel.h 1
Read-only tree models only need to provide the above functions. The
following public functions provide support for editing and resizing:
\snippet itemviews/editabletreemodel/treemodel.h 2
To simplify this example, the data exposed by the model is organized into
a data structure by the model's \l{TreeModel::setupModelData}{setupModelData()}
function. Many real world models will not process the raw data at all, but
simply work with an existing data structure or library API.
\section1 TreeModel Class Implementation
The constructor creates a root item and initializes it with the header
data supplied:
\snippet itemviews/editabletreemodel/treemodel.cpp 0
We call the internal \l{TreeModel::setupModelData}{setupModelData()}
function to convert the textual data supplied to a data structure we can
use with the model. Other models may be initialized with a ready-made
data structure, or use an API from a library that maintains its own data.
The destructor only has to delete the root item, which will cause all child
items to be recursively deleted.
\snippet itemviews/editabletreemodel/treemodel.cpp 1
\target TreeModel::getItem
Since the model's interface to the other model/view components is based
on model indexes, and since the internal data structure is item-based,
many of the functions implemented by the model need to be able to convert
any given model index to its corresponding item. For convenience and
consistency, we have defined a \c getItem() function to perform this
repetitive task:
\snippet itemviews/editabletreemodel/treemodel.cpp 4
Each model index passed to this function should correspond to a valid
item in memory. If the index is invalid, or its internal pointer does
not refer to a valid item, the root item is returned instead.
The model's \c rowCount() implementation is simple: it first uses the
\c getItem() function to obtain the relevant item; then it returns the
number of children it contains:
\snippet itemviews/editabletreemodel/treemodel.cpp 8
By contrast, the \c columnCount() implementation does not need to look
for a particular item because all items are defined to have the same
number of columns associated with them.
\snippet itemviews/editabletreemodel/treemodel.cpp 2
As a result, the number of columns can be obtained directly from the root
item.
To enable items to be edited and selected, the \c flags() function needs
to be implemented so that it returns a combination of flags that includes
the Qt::ItemIsEditable and Qt::ItemIsSelectable flags as well as
Qt::ItemIsEnabled:
\snippet itemviews/editabletreemodel/treemodel.cpp 3
\target TreeModel::index
The model needs to be able to generate model indexes to allow other
components to request data and information about its structure. This task
is performed by the \c index() function, which is used to obtain model
indexes corresponding to children of a given parent item:
\snippet itemviews/editabletreemodel/treemodel.cpp 5
In this model, we only return model indexes for child items if the parent
index is invalid (corresponding to the root item) or if it has a zero
column number.
We use the custom \l{TreeModel::getItem}{getItem()} function to obtain
a \c TreeItem instance that corresponds to the model index supplied, and
request its child item that corresponds to the specified row.
\snippet itemviews/editabletreemodel/treemodel.cpp 6
Since each item contains information for an entire row of data, we create
a model index to uniquely identify it by calling
\l{QAbstractItemModel::}{createIndex()} it with the row and column numbers
and a pointer to the item. In the \l{TreeModel::data}{data()} function,
we will use the item pointer and column number to access the data
associated with the model index; in this model, the row number is not
needed to identify data.
\target TreeModel::parent
The \c parent() function supplies model indexes for parents of items
by finding the corresponding item for a given model index, using its
\l{TreeItem::parent}{parent()} function to obtain its parent item,
then creating a model index to represent the parent. (See
\l{Relating-items-using-model-indexes}{the above diagram}).
\snippet itemviews/editabletreemodel/treemodel.cpp 7
Items without parents, including the root item, are handled by returning
a null model index. Otherwise, a model index is created and returned as
in the \l{TreeModel::index}{index()} function, with a suitable row number,
but with a zero column number to be consistent with the scheme used in
the \l{TreeModel::index}{index()} implementation.
\target TreeModel::data
\target TreeModel::setupModelData
*/

View File

@ -0,0 +1,407 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/elasticnodes
\title Elastic Nodes Example
\ingroup examples-graphicsview
\brief Demonstrates how to interact with graphical items in a scene.
The Elastic Nodes example shows how to implement edges between nodes in a
graph, with basic interaction. You can click to drag a node around, and
zoom in and out using the mouse wheel or the keyboard. Hitting the space
bar will randomize the nodes. The example is also resolution independent;
as you zoom in, the graphics remain crisp.
\image elasticnodes-example.png
Graphics View provides the QGraphicsScene class for managing and
interacting with a large number of custom-made 2D graphical items derived
from the QGraphicsItem class, and a QGraphicsView widget for visualizing
the items, with support for zooming and rotation.
This example consists of a \c Node class, an \c Edge class, a \c
GraphWidget test, and a \c main function: the \c Node class represents
draggable yellow nodes in a grid, the \c Edge class represents the lines
between the nodes, the \c GraphWidget class represents the application
window, and the \c main() function creates and shows this window, and runs
the event loop.
\section1 Node Class Definition
The \c Node class serves three purposes:
\list
\li Painting a yellow gradient "ball" in two states: sunken and raised.
\li Managing connections to other nodes.
\li Calculating forces pulling and pushing the nodes in the grid.
\endlist
Let's start by looking at the \c Node class declaration.
\snippet graphicsview/elasticnodes/node.h 0
The \c Node class inherits QGraphicsItem, and reimplements the two
mandatory functions \l{QGraphicsItem::boundingRect()}{boundingRect()} and
\l{QGraphicsItem::paint()}{paint()} to provide its visual appearance. It
also reimplements \l{QGraphicsItem::shape()}{shape()} to ensure its hit
area has an elliptic shape (as opposed to the default bounding rectangle).
For edge management purposes, the node provides a simple API for adding
edges to a node, and for listing all connected edges.
The \l{QGraphicsItem::advance()}{advance()} reimplementation is called
whenever the scene's state advances by one step. The calculateForces()
function is called to calculate the forces that push and pull on this node
and its neighbors.
The \c Node class also reimplements
\l{QGraphicsItem::itemChange()}{itemChange()} to react to state changes (in
this case, position changes), and
\l{QGraphicsItem::mousePressEvent()}{mousePressEvent()} and
\l{QGraphicsItem::mouseReleaseEvent()}{mouseReleaseEvent()} to update the
item's visual appearance.
We will start reviewing the \c Node implementation by looking at its
constructor:
\snippet graphicsview/elasticnodes/node.cpp 0
In the constructor, we set the
\l{QGraphicsItem::ItemIsMovable}{ItemIsMovable} flag to allow the item to
move in response to mouse dragging, and
\l{QGraphicsItem::ItemSendsGeometryChanges}{ItemSendsGeometryChanges} to
enable \l{QGraphicsItem::itemChange()}{itemChange()} notifications for
position and transformation changes. We also enable
\l{QGraphicsItem::DeviceCoordinateCache}{DeviceCoordinateCache} to speed up
rendering performance. To ensure that the nodes are always stacked on top
of edges, we finally set the item's Z value to -1.
\c Node's constructor takes a \c GraphWidget pointer and stores this as a
member variable. We will revisit this pointer later on.
\snippet graphicsview/elasticnodes/node.cpp 1
The addEdge() function adds the input edge to a list of attached edges. The
edge is then adjusted so that the end points for the edge match the
positions of the source and destination nodes.
The edges() function simply returns the list of attached edges.
\snippet graphicsview/elasticnodes/node.cpp 2
There are two ways to move a node. The \c calculateForces() function
implements the elastic effect that pulls and pushes on nodes in the grid.
In addition, the user can directly move one node around with the mouse.
Because we do not want the two approaches to operate at the same time on
the same node, we start \c calculateForces() by checking if this \c Node is
the current mouse grabber item (i.e., QGraphicsScene::mouseGrabberItem()).
Because we need to find all neighboring (but not necessarily connected)
nodes, we also make sure the item is part of a scene in the first place.
\snippet graphicsview/elasticnodes/node.cpp 3
The "elastic" effect comes from an algorithm that applies pushing and
pulling forces. The effect is impressive, and surprisingly simple to
implement.
The algorithm has two steps: the first is to calculate the forces that push
the nodes apart, and the second is to subtract the forces that pull the
nodes together. First we need to find all the nodes in the graph. We call
QGraphicsScene::items() to find all items in the scene, and then use
qgraphicsitem_cast() to look for \c Node instances.
We make use of \l{QGraphicsItem::mapFromItem()}{mapFromItem()} to create a
temporary vector pointing from this node to each other node, in \l{The
Graphics View Coordinate System}{local coordinates}. We use the decomposed
components of this vector to determine the direction and strength of force
that should apply to the node. The forces accumulate for each node, and are
then adjusted so that the closest nodes are given the strongest force, with
rapid degradation when distance increases. The sum of all forces is stored
in \c xvel (X-velocity) and \c yvel (Y-velocity).
\snippet graphicsview/elasticnodes/node.cpp 4
The edges between the nodes represent forces that pull the nodes together.
By visiting each edge that is connected to this node, we can use a similar
approach as above to find the direction and strength of all pulling forces.
These forces are subtracted from \c xvel and \c yvel.
\snippet graphicsview/elasticnodes/node.cpp 5
In theory, the sum of pushing and pulling forces should stabilize to
precisely 0. In practice, however, they never do. To circumvent errors in
numerical precision, we simply force the sum of forces to be 0 when they
are less than 0.1.
\snippet graphicsview/elasticnodes/node.cpp 6
The final step of \c calculateForces() determines the node's new position.
We add the force to the node's current position. We also make sure the new
position stays inside of our defined boundaries. We don't actually move the
item in this function; that's done in a separate step, from \c advance().
\snippet graphicsview/elasticnodes/node.cpp 7
The \c advance() function updates the item's current position. It is called
from \c GraphWidget::timerEvent(). If the node's position changed, the
function returns true; otherwise false is returned.
\snippet graphicsview/elasticnodes/node.cpp 8
The \c Node's bounding rectangle is a 20x20 sized rectangle centered around
its origin (0, 0), adjusted by 2 units in all directions to compensate for
the node's outline stroke, and by 3 units down and to the right to make
room for a simple drop shadow.
\snippet graphicsview/elasticnodes/node.cpp 9
The shape is a simple ellipse. This ensures that you must click inside the
node's elliptic shape in order to drag it around. You can test this effect
by running the example, and zooming far in so that the nodes are very
large. Without reimplementing \l{QGraphicsItem::shape()}{shape()}, the
item's hit area would be identical to its bounding rectangle (i.e.,
rectangular).
\snippet graphicsview/elasticnodes/node.cpp 10
This function implements the node's painting. We start by drawing a simple
dark gray elliptic drop shadow at (-7, -7), that is, (3, 3) units down and
to the right from the top-left corner (-10, -10) of the ellipse.
We then draw an ellipse with a radial gradient fill. This fill is either
Qt::yellow to Qt::darkYellow when raised, or the opposite when sunken. In
sunken state we also shift the center and focal point by (3, 3) to
emphasize the impression that something has been pushed down.
Drawing filled ellipses with gradients can be quite slow, especially when
using complex gradients such as QRadialGradient. This is why this example
uses \l{QGraphicsItem::DeviceCoordinateCache}{DeviceCoordinateCache}, a
simple yet effective measure that prevents unnecessary redrawing.
\snippet graphicsview/elasticnodes/node.cpp 11
We reimplement \l{QGraphicsItem::itemChange()}{itemChange()} to adjust the
position of all connected edges, and to notify the scene that an item has
moved (i.e., "something has happened"). This will trigger new force
calculations.
This notification is the only reason why the nodes need to keep a pointer
back to the \c GraphWidget. Another approach could be to provide such
notification using a signal; in such case, \c Node would need to inherit
from QGraphicsObject.
\snippet graphicsview/elasticnodes/node.cpp 12
Because we have set the \l{QGraphicsItem::ItemIsMovable}{ItemIsMovable}
flag, we don't need to implement the logic that moves the node according to
mouse input; this is already provided for us. We still need to reimplement
the mouse press and release handlers, though, to update the nodes' visual
appearance (i.e., sunken or raised).
\section1 Edge Class Definition
The \c Edge class represents the arrow-lines between the nodes in this
example. The class is very simple: it maintains a source- and destination
node pointer, and provides an \c adjust() function that makes sure the line
starts at the position of the source, and ends at the position of the
destination. The edges are the only items that change continuously as
forces pull and push on the nodes.
Let's take a look at the class declaration:
\snippet graphicsview/elasticnodes/edge.h 0
\c Edge inherits from QGraphicsItem, as it's a simple class that has no use
for signals, slots, and properties (compare to QGraphicsObject).
The constructor takes two node pointers as input. Both pointers are
mandatory in this example. We also provide get-functions for each node.
The \c adjust() function repositions the edge, and the item also implements
\l{QGraphicsItem::boundingRect()}{boundingRect()} and
\l{QGraphicsItem::paint()}{paint()}.
We will now review its implementation.
\snippet graphicsview/elasticnodes/edge.cpp 0
The \c Edge constructor initializes its \c arrowSize data member to 10 units;
this determines the size of the arrow which is drawn in
\l{QGraphicsItem::paint()}{paint()}.
In the constructor body, we call
\l{QGraphicsItem::setAcceptedMouseButtons()}{setAcceptedMouseButtons(0)}.
This ensures that the edge items are not considered for mouse input at all
(i.e., you cannot click the edges). Then, the source and destination
pointers are updated, this edge is registered with each node, and we call
\c adjust() to update this edge's start end end position.
\snippet graphicsview/elasticnodes/edge.cpp 1
The source and destination get-functions simply return the respective
pointers.
\snippet graphicsview/elasticnodes/edge.cpp 2
In \c adjust(), we define two points: \c sourcePoint, and \c destPoint,
pointing at the source and destination nodes' origins respectively. Each
point is calculated using \l{The Graphics View Coordinate System}{local
coordinates}.
We want the tip of the edge's arrows to point to the exact outline of the
nodes, as opposed to the center of the nodes. To find this point, we first
decompose the vector pointing from the center of the source to the center
of the destination node into X and Y, and then normalize the components by
dividing by the length of the vector. This gives us an X and Y unit delta
that, when multiplied by the radius of the node (which is 10), gives us the
offset that must be added to one point of the edge, and subtracted from the
other.
If the length of the vector is less than 20 (i.e., if two nodes overlap),
then we fix the source and destination pointer at the center of the source
node. In practice this case is very hard to reproduce manually, as the
forces between the two nodes is then at its maximum.
It's important to notice that we call
\l{QGraphicsItem::prepareGeometryChange()}{prepareGeometryChange()} in this
function. The reason is that the variables \c sourcePoint and \c destPoint
are used directly when painting, and they are returned from the
\l{QGraphicsItem::boundingRect()}{boundingRect()} reimplementation. We must
always call
\l{QGraphicsItem::prepareGeometryChange()}{prepareGeometryChange()} before
changing what \l{QGraphicsItem::boundingRect()}{boundingRect()} returns,
and before these variables can be used by
\l{QGraphicsItem::paint()}{paint()}, to keep Graphics View's internal
bookkeeping clean. It's safest to call this function once, immediately
before any such variable is modified.
\snippet graphicsview/elasticnodes/edge.cpp 3
The edge's bounding rectangle is defined as the smallest rectangle that
includes both the start and the end point of the edge. Because we draw an
arrow on each edge, we also need to compensate by adjusting with half the
arrow size and half the pen width in all directions. The pen is used to
draw the outline of the arrow, and we can assume that half of the outline
can be drawn outside of the arrow's area, and half will be drawn inside.
\snippet graphicsview/elasticnodes/edge.cpp 4
We start the reimplementation of \l{QGraphicsItem::paint()}{paint()} by
checking a few preconditions. Firstly, if either the source or destination
node is not set, then we return immediately; there is nothing to draw.
At the same time, we check if the length of the edge is approximately 0,
and if it is, then we also return.
\snippet graphicsview/elasticnodes/edge.cpp 5
We draw the line using a pen that has round joins and caps. If you run the
example, zoom in and study the edge in detail, you will see that there are
no sharp/square edges.
\snippet graphicsview/elasticnodes/edge.cpp 6
We proceed to drawing one arrow at each end of the edge. Each arrow is
drawn as a polygon with a black fill. The coordinates for the arrow are
determined using simple trigonometry.
\section1 GraphWidget Class Definition
\c GraphWidget is a subclass of QGraphicsView, which provides the main
window with scrollbars.
\snippet graphicsview/elasticnodes/graphwidget.h 0
The class provides a basic constructor that initializes the scene, an \c
itemMoved() function to notify changes in the scene's node graph, a few
event handlers, a reimplementation of
\l{QGraphicsView::drawBackground()}{drawBackground()}, and a helper
function for scaling the view by using the mouse wheel or keyboard.
\snippet graphicsview/elasticnodes/graphwidget.cpp 0
\c GraphicsWidget's constructor creates the scene, and because most items
move around most of the time, it sets QGraphicsScene::NoIndex. The scene
then gets a fixed \l{QGraphicsScene::sceneRect}{scene rectangle}, and is
assigned to the \c GraphWidget view.
The view enables QGraphicsView::CacheBackground to cache rendering of its
static, and somewhat complex, background. Because the graph renders a close
collection of small items that all move around, it's unnecessary for
Graphics View to waste time finding accurate update regions, so we set the
QGraphicsView::BoundingRectViewportUpdate viewport update mode. The default
would work fine, but this mode is noticeably faster for this example.
To improve rendering quality, we set QPainter::Antialiasing.
The transformation anchor decides how the view should scroll when you
transform the view, or in our case, when we zoom in or out. We have chosen
QGraphicsView::AnchorUnderMouse, which centers the view on the point under
the mouse cursor. This makes it easy to zoom towards a point in the scene
by moving the mouse over it, and then rolling the mouse wheel.
Finally we give the window a minimum size that matches the scene's default
size, and set a suitable window title.
\snippet graphicsview/elasticnodes/graphwidget.cpp 1
The last part of the constructor creates the grid of nodes and edges, and
gives each node an initial position.
\snippet graphicsview/elasticnodes/graphwidget.cpp 2
\c GraphWidget is notified of node movement through this \c itemMoved()
function. Its job is simply to restart the main timer in case it's not
running already. The timer is designed to stop when the graph stabilizes,
and start once it's unstable again.
\snippet graphicsview/elasticnodes/graphwidget.cpp 3
This is \c GraphWidget's key event handler. The arrow keys move the center
node around, the '+' and '-' keys zoom in and out by calling \c
scaleView(), and the enter and space keys randomize the positions of the
nodes. All other key events (e.g., page up and page down) are handled by
QGraphicsView's default implementation.
\snippet graphicsview/elasticnodes/graphwidget.cpp 4
The timer event handler's job is to run the whole force calculation
machinery as a smooth animation. Each time the timer is triggered, the
handler will find all nodes in the scene, and call \c
Node::calculateForces() on each node, one at a time. Then, in a final step
it will call \c Node::advance() to move all nodes to their new positions.
By checking the return value of \c advance(), we can decide if the grid
stabilized (i.e., no nodes moved). If so, we can stop the timer.
\snippet graphicsview/elasticnodes/graphwidget.cpp 5
In the wheel event handler, we convert the mouse wheel delta to a scale
factor, and pass this factor to \c scaleView(). This approach takes into
account the speed that the wheel is rolled. The faster you roll the mouse
wheel, the faster the view will zoom.
\snippet graphicsview/elasticnodes/graphwidget.cpp 6
The view's background is rendered in a reimplementation of
QGraphicsView::drawBackground(). We draw a large rectangle filled with a
linear gradient, add a drop shadow, and then render text on top. The text
is rendered twice for a simple drop-shadow effect.
This background rendering is quite expensive; this is why the view enables
QGraphicsView::CacheBackground.
\snippet graphicsview/elasticnodes/graphwidget.cpp 7
The \c scaleView() helper function checks that the scale factor stays
within certain limits (i.e., you cannot zoom too far in nor too far out),
and then applies this scale to the view.
\section1 The main() Function
In contrast to the complexity of the rest of this example, the \c main()
function is very simple: We create a QApplication instance, then create and
show an instance of \c GraphWidget. Because all nodes in the grid are moved
initially, the \c GraphWidget timer will start immediately after control
has returned to the event loop.
*/

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/embeddeddialogs
\title Embedded Dialogs
\ingroup examples-graphicsview-layout
\brief Demonstrates how to embed dialogs into a graphics view.
This example shows how to embed standard dialogs into
Graphics View. It also shows how you can customize the
proxy class and add window shadows.
\image embeddeddialogs-demo.png
*/

View File

@ -0,0 +1,121 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example dialogs/extension
\title Extension Example
\ingroup examples-dialogs
\brief The Extension example shows how to add an extension to a QDialog
using the QAbstractButton::toggled() signal and the
QWidget::setVisible() slot.
\image extension-example.png Screenshot of the Extension example
The Extension application lets the user add search parameters in
a dialog and launch a simple or advanced search.
The simple search has two options: \uicontrol {Match case} and \uicontrol
{Search from start}. The advanced search offers search for \uicontrol {Whole words},
\uicontrol {Search backward}, and \uicontrol {Search selection}. The
application starts with simple search as the default. Click the \uicontrol More button
to show the advanced search options:
\image extension_more.png Screenshot of the Extension example
\section1 FindDialog Class Definition
The \c FindDialog class inherits QDialog. QDialog is the
base class for dialog windows. A dialog window is a top-level
window mostly used for short-term tasks and brief communications
with the user.
\snippet dialogs/extension/finddialog.h 0
The \c FindDialog widget is the main application widget, and
displays the application's search options and controlling
buttons.
In addition to the constructor, there are several child widgets:
\list
\li A QLineEdit with an associated QLabel to let the
user type a word to search for.
\li Several \l {QCheckBox}{QCheckBox}es to facilitate the search options.
\li Three \l {QPushButton}{QPushButton}s:
\list
\li the \uicontrol Find button to start a search
\li the \uicontrol More button to enable an advanced search
\li a QWidget representing the application's extension part
\endlist
\endlist
\section1 FindDialog Class Implementation
Create the standard child widgets for the simple search in the constructor:
the QLineEdit with the associated QLabel, two {QCheckBox}es and all the
\l {QPushButton}{QPushButton}s.
\snippet dialogs/extension/finddialog.cpp 0
This snippet illustrates how you can define a shortcut key
for a widget. A shortcut should be defined by putting the ampersand
character (\c &) in front of the letter that should
become the shortcut.
For example, for \uicontrol {Find what}, pressing \uicontrol Alt
and \uicontrol w transfers focus to the QLineEdit widget.
Shortcuts can also be used for checking on or off a checkmark.
For example, pressing \uicontrol Alt and \uicontrol c puts the check mark
on \uicontrol {Match Case} if it was unchecked and vice versa.
It is the QLabel::setBuddy() method that links a widget to the shortcut
character if it has been defined.
Set the \uicontrol Find button's default property to true, using the
QPushButton::setDefault() function. Then the push button will be
pressed if the user presses the Enter (or Return) key. Note that a
QDialog can only have one default button.
\snippet dialogs/extension/finddialog.cpp 2
Create the extension widget, and the \l {QCheckBox}{QCheckBox}es associated
with the advanced search options.
\snippet dialogs/extension/finddialog.cpp 3
Now that the extension widget is created, connect the \uicontrol
More button's \l{QAbstractButton::toggled()}{toggled()} signal to
the extension widget's \l{QWidget::setVisible()}{setVisible()} slot.
The QAbstractButton::toggled() signal is emitted whenever a
checkable button changes its state. The signal's argument is true
if the button is checked, or false if the button is unchecked. The
QWidget::setVisible() slot sets the widget's visible status. If
the status is true the widget is shown, otherwise the widget is
hidden.
Since the \uicontrol More button is checkable, the connection makes
sure that the extension widget is shown depending on the state of
the \uicontrol More button.
Create checkboxes associated with the advanced search options in
a layout installed on the extension widget.
\snippet dialogs/extension/finddialog.cpp 4
Before creating the main layout, create several child layouts
for the widgets. First align the QLabel and its buddy, the
QLineEdit, using a QHBoxLayout. Then align the QLabel and the QLineEdit
vertically with the checkboxes associated with the simple search,
using a QVBoxLayout. Create also a QVBoxLayout for the buttons.
Finally, lay out the two latter layouts and the extension widget
using a QGridLayout.
\snippet dialogs/extension/finddialog.cpp 5
Hide the extension widget using the QWidget::hide()
function, making the application only show the simple search
options when it starts. When the user wants to access the advanced
search options, the dialog only needs to change the visibility of
the extension widget. Qt's layout management takes care of the
dialog's appearance.
*/

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example effects/fademessage
\title Fade Message Effect Example
\ingroup examples-graphicsview-graphicseffects
\brief Demonstrates how to apply effects on items in the view.
\div { style="text-align: left"}
\inlineimage fademessageeffect-example.png
\inlineimage fademessageeffect-example-faded.png
\enddiv
*/

View File

@ -0,0 +1,92 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/fetchmore
\title Fetch More Example
\ingroup examples-itemviews
\brief The Fetch More example shows how to add items to an item view
model on demand.
\image fetchmore-example.png
When you have large - or perhaps even infinite - data sets, you
will need to add items to the model in batches, and preferably only
when the items are needed by the view (i.e., when they become visible
in the view).
In this example, we implement \c FileListModel - an item view
model containing the entries of a directory. We also have \c
Window, which sets up the GUI and feeds the model with
directories.
The UI consists of a dialog with a list showing the contents
of the root directory. Directories can be navigated by double-clicking.
At the bottom, there is a log window displaying messages when the view
asks the model for more data.
To exercise it, navigate to a large directory (say \c /bin), and scroll
to the bottom. Log messages appear showing the data being retrieved.
Let's take a tour of \c {FileListModel}'s code.
\section1 FileListModel Class Definition
The \c FileListModel inherits QAbstractListModel and contains the
contents of a directory. It will add items to itself only when
requested to do so by the view.
\snippet itemviews/fetchmore/filelistmodel.h 0
The secret lies in the reimplementation of
\l{QAbstractItemModel::}{fetchMore()} and
\l{QAbstractItemModel::}{canFetchMore()} from QAbstractItemModel.
These functions are called by the item view when it needs more
items.
The \c setDirPath() function sets the directory the model will
work on. We emit \c numberPopulated() each time we add a batch of
items to the model.
We keep all directory entries in \c fileList. \c fileCount is the
number of items that have been added to the model.
\section1 FileListModel Class Implementation
We start by checking out the \c setDirPath().
\snippet itemviews/fetchmore/filelistmodel.cpp 0
We use a QDir to get the contents of the directory. We need to
inform QAbstractItemModel that we want to remove all items - if
any - from the model.
\snippet itemviews/fetchmore/filelistmodel.cpp 1
The \c canFetchMore() function is called by the view when it needs
more items. We return true if there still are entries that we have
not added to the model; otherwise, we return false.
And now, the \c fetchMore() function itself:
\snippet itemviews/fetchmore/filelistmodel.cpp 2
We first calculate the number of items to fetch.
\l{QAbstractItemModel::}{beginInsertRows()} and
\l{QAbstractItemModel::}{endInsertRows()} are mandatory for
QAbstractItemModel to keep up with the row insertions. Finally, we
emit \c numberPopulated(), which is picked up by \c Window.
To complete the tour, we also look at \c rowCount() and \c data().
\snippet itemviews/fetchmore/filelistmodel.cpp 4
Notice that the row count is only the items we have added so far,
i.e., not the number of entries in the directory.
In \c data(), we return the appropriate entry from the \c
fileList. We also separate the batches with a different background
color.
*/

View File

@ -0,0 +1,123 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example layouts/flowlayout
\title Flow Layout Example
\ingroup examples-layout
\brief Shows how to arrange widgets for different window sizes.
\e{Flow Layout} implements a layout that handles different window sizes. The
widget placement changes depending on the width of the application window.
\image flowlayout-example.png Screenshot of the Flow Layout example
The Flowlayout class mainly uses QLayout and QWidgetItem, while the
Window uses QWidget and QLabel.
For more information, visit the \l{Layout Management} page.
\include examples-run.qdocinc
\section1 FlowLayout Class Definition
The \c FlowLayout class inherits QLayout. It is a custom layout class
that arranges its child widgets horizontally and vertically.
\snippet layouts/flowlayout/flowlayout.h 0
We reimplement functions inherited from QLayout. These functions add items to
the layout and handle their orientation and geometry.
We also declare two private methods, \c doLayout() and \c smartSpacing().
\c doLayout() lays out the layout items, while the \c
smartSpacing() function calculates the spacing between them.
\section1 FlowLayout Class Implementation
We start off by looking at the constructor:
\snippet layouts/flowlayout/flowlayout.cpp 1
In the constructor we call \c setContentsMargins() to set the left, top,
right and bottom margin. By default, QLayout uses values provided by
the current style (see QStyle::PixelMetric).
\snippet layouts/flowlayout/flowlayout.cpp 2
In this example we reimplement \c addItem(), which is a pure virtual
function. When using \c addItem() the ownership of the layout items is
transferred to the layout, and it is therefore the layout's
responsibility to delete them.
\snippet layouts/flowlayout/flowlayout.cpp 3
\c addItem() is implemented to add items to the layout.
\snippet layouts/flowlayout/flowlayout.cpp 4
We implement \c horizontalSpacing() and \c verticalSpacing() to get
hold of the spacing between the widgets inside the layout. If the value
is less than or equal to 0, this value will be used. If not,
\c smartSpacing() will be called to calculate the spacing.
\snippet layouts/flowlayout/flowlayout.cpp 5
We then implement \c count() to return the number of items in the
layout. To navigate the list of items we use \c itemAt() and
takeAt() to remove and return items from the list. If an item is
removed, the remaining items will be renumbered. All three
functions are pure virtual functions from QLayout.
\snippet layouts/flowlayout/flowlayout.cpp 6
\c expandingDirections() returns the \l{Qt::Orientation}s in which the
layout can make use of more space than its \c sizeHint().
\snippet layouts/flowlayout/flowlayout.cpp 7
To adjust to widgets of which height is dependent on width, we implement \c
heightForWidth(). The function \c hasHeightForWidth() is used to test for this
dependency, and \c heightForWidth() passes the width on to \c doLayout() which
in turn uses the width as an argument for the layout rect, i.e., the bounds in
which the items are laid out. This rect does not include the layout margin().
\snippet layouts/flowlayout/flowlayout.cpp 8
\c setGeometry() is normally used to do the actual layout, i.e., calculate
the geometry of the layout's items. In this example, it calls \c doLayout()
and passes the layout rect.
\c sizeHint() returns the preferred size of the layout and \c minimumSize()
returns the minimum size of the layout.
\snippet layouts/flowlayout/flowlayout.cpp 9
\c doLayout() handles the layout if \c horizontalSpacing() or \c
verticalSpacing() don't return the default value. It uses
\c getContentsMargins() to calculate the area available to the
layout items.
\snippet layouts/flowlayout/flowlayout.cpp 10
It then sets the proper amount of spacing for each widget in the
layout, based on the current style.
\snippet layouts/flowlayout/flowlayout.cpp 11
The position of each item in the layout is then calculated by
adding the items width and the line height to the initial x and y
coordinates. This in turn lets us find out whether the next item
will fit on the current line or if it must be moved down to the next.
We also find the height of the current line based on the widgets height.
\snippet layouts/flowlayout/flowlayout.cpp 12
\c smartSpacing() is designed to get the default spacing for either
the top-level layouts or the sublayouts. The default spacing for
top-level layouts, when the parent is a QWidget, will be determined
by querying the style. The default spacing for sublayouts, when
the parent is a QLayout, will be determined by querying the spacing
of the parent layout.
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/fontsampler
\title Font Sampler Example
\ingroup examples-painting
\brief The Font Sampler example shows how to preview and print multi-page documents.
The Font Sampler example shows how to preview and print multi-page documents.
\image fontsampler-example.png
*/

View File

@ -0,0 +1,338 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example draganddrop/fridgemagnets
\title Fridge Magnets Example
\brief The Fridge Magnets example illustrates how to move around several types of
MIME-encoded data with drag and drop.
The Fridge Magnets example shows how to supply more than one type
of MIME-encoded data with a drag and drop operation.
\image fridgemagnets-example.png
With this application the user can play around with a collection
of fridge magnets, using drag and drop to form new sentences from
the words on the magnets. The example consists of two classes:
\list
\li \c DragLabel is a custom widget representing one
single fridge magnet.
\li \c DragWidget provides the main application window.
\endlist
We will first take a look at the \c DragLabel class, then we will
examine the \c DragWidget class.
\section1 DragLabel Class Definition
Each fridge magnet is represented by an instance of the \c
DragLabel class:
\snippet draganddrop/fridgemagnets/draglabel.h 0
Each instance of this QLabel subclass will be used to display an
pixmap generated from a text string. Since we cannot store both
text and a pixmap in a standard label, we declare a private variable
to hold the original text, and we define an additional member
function to allow it to be accessed.
\section1 DragLabel Class Implementation
In the \c DragLabel constructor, we first create a QImage object
on which we will draw the fridge magnet's text and frame:
\snippet draganddrop/fridgemagnets/draglabel.cpp 0
Its size depends on the current font size, and its format is
QImage::Format_ARGB32_Premultiplied; i.e., the image is stored
using a premultiplied 32-bit ARGB format (0xAARRGGBB).
We then construct a font object that uses the application's
default font, and set its style strategy. The style strategy tells
the font matching algorithm what type of fonts should be used to
find an appropriate default family. The QFont::ForceOutline forces
the use of outline fonts.
To draw the text and frame onto the image, we use the QPainter
class. QPainter provides highly optimized methods to do most of
the drawing GUI programs require. It can draw everything from
simple lines to complex shapes like pies and chords. It can also
draw aligned text and pixmaps.
\snippet draganddrop/fridgemagnets/draglabel.cpp 1
A painter can be activated by passing a paint device to the
constructor, or by using the \l{QPainter::}{begin()} method as we
do in this example. The \l{QPainter::}{end()} method deactivates
it. Note that the latter function is called automatically upon
destruction when the painter is activated by its constructor. The
QPainter::Antialiasing render hint ensures that the paint engine
will antialias the edges of primitives if possible.
When the painting is done, we convert our image to a pixmap using
QPixmap's \l {QPixmap::}{fromImage()} method. This method also
takes an optional flags argument, and converts the given image to
a pixmap using the specified flags to control the conversion (the
flags argument is a bitwise-OR of the Qt::ImageConversionFlags;
passing 0 for flags sets all the default options).
\snippet draganddrop/fridgemagnets/draglabel.cpp 2
Finally, we set the label's \l{QLabel::pixmap}{pixmap property}
and store the label's text for later use.
\e{Note that setting the pixmap clears any previous content, including
any text previously set using QLabel::setText(), and disables
the label widget's buddy shortcut, if any.}
\section1 DragWidget Class Definition
The \c DragWidget class inherits QWidget, providing support for
drag and drop operations:
\snippet draganddrop/fridgemagnets/dragwidget.h 0
To make the widget responsive to drag and drop operations, we simply
reimplement the \l{QWidget::}{dragEnterEvent()},
\l{QWidget::}{dragMoveEvent()} and \l{QWidget::}{dropEvent()} event
handlers inherited from QWidget.
We also reimplement \l{QWidget::}{mousePressEvent()} to make the
widget responsive to mouse clicks. This is where we will write code
to start drag and drop operations.
\section1 DragWidget Class Implementation
In the constructor, we first open the file containing the words on
our fridge magnets:
\snippet draganddrop/fridgemagnets/dragwidget.cpp 0
QFile is an I/O device for reading and writing text and binary
files and resources, and may be used by itself or in combination
with QTextStream or QDataStream. We have chosen to read the
contents of the file using the QTextStream class that provides a
convenient interface for reading and writing text.
We then create the fridge magnets. As long as there is data (the
QTextStream::atEnd() method returns true if there is no more data
to be read from the stream), we read one line at a time using
QTextStream's \l {QTextStream::}{readLine()} method.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 1
For each line, we create a \c DragLabel object using the read line
as text, we calculate its position and ensure that it is visible by
calling the QWidget::show() method. We set the Qt::WA_DeleteOnClose
attribute on each label to ensure that any unused labels will be
deleted; we will need to create new labels and delete old ones when
they are dragged around, and this ensures that the example does not
leak memory.
We also set the \c FridgeMagnets widget's palette, minimum size
and window title.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 2
Finally, to enable our user to move the fridge magnets around, we
must also set the \c FridgeMagnets widget's
\l{QWidget::acceptDrops}{acceptDrops} property.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 3
Setting this property to true announces to the system that this
widget \e may be able to accept drop events (events that are sent
when drag and drop actions are completed). Later, we will
implement the functions that ensure that the widget accepts the
drop events it is interested in.
\section2 Dragging
Let's take a look at the \l{QWidget::}{mousePressEvent()} event
handler, where drag and drop operations begin:
\snippet draganddrop/fridgemagnets/dragwidget.cpp 13
\snippet draganddrop/fridgemagnets/dragwidget.cpp 14
Mouse events occur when a mouse button is pressed or released
inside a widget, or when the mouse cursor is moved. By
reimplementing the \l{QWidget::}{mousePressEvent()} method we
ensure that we will receive mouse press events for the widget
containing the fridge magnets.
Whenever we receive such an event, we first check to see if the
position of the click coincides with one of the labels. If not,
we simply return.
If the user clicked a label, we determine the position of the
\e{hot spot} (the position of the click relative to the top-left
corner of the label). We create a byte array to store the label's
text and the hot spot, and we use a QDataStream object to stream
the data into the byte array.
With all the information in place, we create a new QMimeData object.
As mentioned above, QMimeData objects associate the data that they
hold with the corresponding MIME types to ensure that information
can be safely transferred between applications. The
\l{QMimeData::}{setData()} method sets the data associated with a
given MIME type. In our case, we associate our item data with the
custom \c application/x-fridgemagnet type.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 15
Note that we also associate the magnet's text with the
\c text/plain MIME type using QMimeData's \l{QMimeData::}{setText()}
method. Below, we will see how our widget detects both these MIME
types with its event handlers.
Finally, we create a QDrag object. It is the QDrag class that
handles most of the details of a drag and drop operation,
providing support for MIME-based drag and drop data transfer. The
data to be transferred by the drag and drop operation is contained
in a QMimeData object. When we call QDrag's
\l{QDrag::}{setMimeData()} method the ownership of our item data is
transferred to the QDrag object.
We call the \l{QDrag::}{setPixmap()} function to set the pixmap used
to represent the data during the drag and drop operation.
Typically, this pixmap shows an icon that represents the MIME type
of the data being transferred, but any pixmap can be used. In this
example, we simply use the pixmap used by the label itself to make
it look like the fridge magnet itself is being moved.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 16
We also specify the cursor's hot spot, its position relative to the
top-level corner of the drag pixmap, to be the point we calculated
above. This makes the process of dragging the label feel more natural
because the cursor always points to the same place on the label
during the drag operation.
We start the drag operation using QDrag's \l{QDrag::}{exec()} function,
requesting that the magnet is copied when the drag is completed.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 17
The function returns the drop action actually performed by the user
(this can be either a copy or a move action in this case); if this
action is equal to Qt::MoveAction we will close the activated
fridge magnet widget because we will create a new one to replace it
(see the \l{drop}{dropEvent()} implementation). Otherwise, if
the drop is outside our main widget, we simply show the widget in
its original position.
\section2 Dropping
When a a drag and drop action enters our widget, we will receive a
drag enter \e event. QDragEnterEvent inherits most of its
functionality from QDragMoveEvent, which in turn inherits most of
its functionality from QDropEvent. Note that we must accept this
event in order to receive the drag move events that are sent while
the drag and drop action is in progress. The drag enter event is
always immediately followed by a drag move event.
In our \c dragEnterEvent() implementation, we first determine
whether we support the event's MIME type or not:
\snippet draganddrop/fridgemagnets/dragwidget.cpp 4
\snippet draganddrop/fridgemagnets/dragwidget.cpp 5
\snippet draganddrop/fridgemagnets/dragwidget.cpp 6
If the type is \c application/x-fridgemagnet and the event
origins from any of this application's fridge magnet widgets, we
first set the event's drop action using the
QDropEvent::setDropAction() method. An event's drop action is the
action to be performed on the data by the target. Qt::MoveAction
indicates that the data is moved from the source to the target.
Then we call the event's \l {QDragMoveEvent::}{accept()} method to
indicate that we have handled the event. In general, unaccepted
events might be propagated to the parent widget. If the event
origins from any other widget, we simply accept the proposed
action.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 7
We also accept the proposed action if the event's MIME type is \c
text/plain, i.e., if QMimeData::hasText() returns true. If the
event has any other type, on the other hand, we call the event's
\l {QDragMoveEvent::}{ignore()} method allowing the event to be
propagated further.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 8
Drag move events occur when the cursor enters a widget, when it
moves within the widget, and when a modifier key is pressed on the
keyboard while the widget has focus. Our widget will receive drag
move events repeatedly while a drag is within its boundaries. We
reimplement the \l {QWidget::}{dragMoveEvent()} method, and
examine the event in the exact same way as we did with drag enter
events.
Note that the \l{QWidget::}{dropEvent()} event handler behaves
slightly differently: We first get hold of the event's MIME
data.
\target drop
\snippet draganddrop/fridgemagnets/dragwidget.cpp 9
The QMimeData class provides a container for data that
records information about its MIME type. QMimeData objects
associate the data that they hold with the corresponding MIME
types to ensure that information can be safely transferred between
applications, and copied around within the same application.
We retrieve the data associated with the \c application/x-fridgemagnet
MIME type using a data stream in order to create a new \c DragLabel
object.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 10
The QDataStream class provides serialization of binary data to a
QIODevice (a data stream is a binary stream of encoded information
which is completely independent of the host computer's operating
system, CPU or byte order).
Finally, we create a label and move it to the event's position:
\snippet draganddrop/fridgemagnets/dragwidget.cpp 11
If the source of the event is also the widget receiving the
drop event, we set the event's drop action to Qt::MoveAction and
call the event's \l{QDragMoveEvent::}{accept()}
method. Otherwise, we simply accept the proposed action. This
means that labels are moved rather than copied in the same
window. However, if we drag a label to a second instance of the
Fridge Magnets example, the default action is to copy it, leaving
the original in the first instance.
If the event's MIME type is \c text/plain (i.e., if
QMimeData::hasText() returns true) we retrieve its text and split
it into words. For each word we create a new \c DragLabel action,
and show it at the event's position plus an offset depending on
the number of words in the text. In the end we accept the proposed
action. This lets the user drop selected text from a text editor or
Web browser onto the widget to add more fridge magnets.
\snippet draganddrop/fridgemagnets/dragwidget.cpp 12
If the event has any other type, we call the event's
\l{QDragMoveEvent::}{ignore()} method allowing the event to be
propagated further.
\section1 Summary
We set our main widget's \l{QWidget::}{acceptDrops} property
and reimplemented QWidget's \l{QWidget::}{dragEnterEvent()},
\l{QWidget::}{dragMoveEvent()} and \l{QWidget::}{dropEvent()} event
handlers to support content dropped on our widget.
In addition, we reimplemented the \l{QWidget::}{mousePressEvent()}
function to let the user pick up fridge magnets in the first place.
Because data is communicated using drag and drop operations and
encoded using MIME types, you can run more than one instance of this
example, and transfer magnets between them.
*/

View File

@ -0,0 +1,109 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/frozencolumn
\title Frozen Column Example
\ingroup examples-itemviews
\brief This example demonstrates how to freeze a column within a QTableView.
\image frozencolumn-example.png "Screenshot of the example"
We use Qt's model/view framework to implement a table with its first
column frozen. This technique can be applied to several columns or rows,
as long as they are on the edge of the table.
The model/view framework allows for one model to be displayed in different
ways using multiple views. For this example, we use two views on the same
model - two \l {QTableView}{table views} sharing one model. The frozen
column is a child of the main tableview, and we provide the desired visual
effect using an overlay technique which will be described step by step in
the coming sections.
\image frozencolumn-tableview.png
\section1 FreezeTableWidget Class Definition
The \c FreezeTableWidget class has a constructor and a destructor. Also, it
has two private members: the table view that we will use as an overlay, and
the shared model for both table views. Two slots are added to help keep the
section sizes in sync, as well as a function to readjust the frozen
column's geometry. In addition, we reimplement two functions:
\l{QAbstractItemView::}{resizeEvent()} and \l{QTableView::}{moveCursor()}.
\snippet itemviews/frozencolumn/freezetablewidget.h Widget definition
\note QAbstractItemView is \l{QTableView}'s ancestor.
\section1 FreezeTableWidget Class Implementation
The constructor takes \a model as an argument and creates a table view that
we will use to display the frozen column. Then, within the constructor, we
invoke the \c init() function to set up the frozen column. Finally, we
connect the \l{QHeaderView::sectionResized()} signals (for horizontal and
vertical headers) to the appropriate slots. This ensures that our frozen
column's sections are in sync with the headers. We also connect the
vertical scrollbars together so that the frozen column scrolls vertically
with the rest of our table.
\snippet itemviews/frozencolumn/freezetablewidget.cpp constructor
In the \c init() function, we ensure that the overlay table view
responsible for displaying the frozen column, is set up properly. This
means that this table view, \c frozenTableView, has to have the same model
as the main table view. However, the difference here is: \c frozenTableView's
only visible column is its first column; we hide the others using
\l{QTableView::}{setColumnHidden()}
\snippet itemviews/frozencolumn/freezetablewidget.cpp init part1
In terms of the frozen column's z-order, we stack it on top of the
viewport. This is achieved by calling \l{QWidget::}{stackUnder()} on the
viewport. For appearance's sake, we prevent the column from stealing focus
from the main tableview. Also, we make sure that both views share the same
selection model, so only one cell can be selected at a time. A few other
tweaks are done to make our application look good and behave consistently
with the main tableview. Note that we called \c updateFrozenTableGeometry()
to make the column occupy the correct spot.
\snippet itemviews/frozencolumn/freezetablewidget.cpp init part2
When you resize the frozen column, the same column on the main table view
must resize accordingly, to provide seamless integration. This is
accomplished by getting the new size of the column from the \c newSize
value from the \l{QHeaderView::}{sectionResized()} signal, emitted by both
the horizontal and vertical header.
\snippet itemviews/frozencolumn/freezetablewidget.cpp sections
Since the width of the frozen column is modified, we adjust the geometry of
the widget accordingly by invoking \c updateFrozenTableGeometry(). This
function is further explained below.
In our reimplementation of QTableView::resizeEvent(), we call
\c updateFrozenTableGeometry() after invoking the base class
implementation.
\snippet itemviews/frozencolumn/freezetablewidget.cpp resize
When navigating around the table with the keyboard, we need to ensure that
the current selection does not disappear behind the frozen column. To
synchronize this, we reimplement QTableView::moveCursor() and adjust the
scrollbar positions if needed, after calling the base class implementation.
\snippet itemviews/frozencolumn/freezetablewidget.cpp navigate
The frozen column's geometry calculation is based on the geometry of the
table underneath, so it always appears in the right place. Using the
QFrame::frameWidth() function helps to calculate this geometry correctly,
no matter which style is used. We rely on the geometry of the viewport and
headers to set the boundaries for the frozen column.
\snippet itemviews/frozencolumn/freezetablewidget.cpp geometry
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example gallery
\meta {tag} {gallery}
\title Widgets Gallery Example
\ingroup examples-widgets
\brief The Widgets Gallery example shows widgets relevant for designing UIs.
This example demonstrates widgets typically used in dialogs and forms.
It also allows for changing the style.
*/

View File

@ -0,0 +1,38 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/gradients
\title Gradients
\ingroup examples-painting
\brief Shows how gradients can be used with QPainter.
\brief In this example we show the various types of gradients that can
be used in Qt.
\image gradients-demo.png
There are three types of gradients:
\list
\li \b{Linear} gradients interpolate colors between start and end points.
\li \b{Radial} gradients interpolate colors between a focal point and the
points on a circle surrounding it.
\li \b{Conical} gradients interpolate colors around a center point.
\endlist
The panel on the right contains a color table editor that defines
the colors in the gradient. The three topmost controls determine the red,
green and blue components while the last defines the alpha of the
gradient. You can move points, and add new ones, by clicking with the left
mouse button, and remove points by clicking with the right button.
There are three example configurations available at the bottom of
the page that are provided as suggestions on how a color table could be
configured.
Qt also provides a suite of named gradient presets. They are based on the
free WebGradients collection. Click on the name in the Presets box to show
the gradient. Use the arrow buttons to browse through the available
presets.
*/

View File

@ -0,0 +1,28 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/flowlayout
\title Graphics View Flow Layout Example
\ingroup examples-graphicsview-layout
\brief Demonstrates flow layout on a graphics view scene.
The Graphics View Flow Layout example shows the use of a flow layout
in a Graphics View widget.
\image graphicsflowlayout-example.png
This example uses a Graphics View to display the widget, which is a more
customizable approach than displaying the flow layout in the application
window (See \l {Flow Layout Example}).
Graphics View Flow Layout snippet:
\snippet graphicsview/flowlayout/main.cpp 1
Flow Layout Example snippet:
\snippet layouts/flowlayout/main.cpp 1
*/

View File

@ -0,0 +1,58 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example graphicsview/simpleanchorlayout
\title Simple Anchor Layout Example
\ingroup examples-graphicsview-layout
\brief Demonstrates anchor layout on a graphics view scene.
The Simple Anchor Layout example shows the basic use of the
QGraphicsAnchorLayout class.
\image graphicssimpleanchorlayout-example.png
The example starts by creating a QGraphicsScene (\c scene), 3 widgets
(\c a, \c b, and \c c), and a QGraphicsAnchorlayout (\c layout).
\quotefromfile graphicsview/simpleanchorlayout/main.cpp
\skipto QGraphicsScene
\printuntil QGraphicsAnchorLayout
First it anchors the top left corner of item \c a to the top left
corner of \c layout. This can be done in two steps:
\skipto layout->addAnchor(a
\printto adding
Or in one step:
\skipuntil [adding a corner anchor]
\printline layout->addCornerAnchors(a, Qt::T
Then the right anchor of \c a is anchored to the left anchor of
\c b, and the top of item \c b is anchored to the bottom of \c a.
\skipuntil [adding anchors]
\printto adding anchors
Place a third widget \c c under widget \c b:
\skipuntil third widget
\printline AnchorBottom
Items \c b and \c c are anchored to each other horizontally:
\skipto Qt::Horizontal
\printline Qt::Horizontal
Item c is anchored to the bottom right point of \c layout
\skipuntil corner of the layout
\printline Qt::BottomRightCorner
Finally, QGraphicsWidget \c w is displayed in QGraphicsView \c view.
\skipto QGraphicsWidget
\printuntil app.exec()
*/

View File

@ -0,0 +1,116 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/groupbox
\title Group Box Example
\ingroup examples-widgets
\brief The Group Box example shows how to use the different kinds of group
boxes in Qt.
Group boxes are container widgets that organize buttons into groups,
both logically and on screen. They manage the interactions between
the user and the application so that you do not have to enforce
simple constraints.
Group boxes are usually used to organize check boxes and radio
buttons into exclusive groups.
\borderedimage groupbox-example.png
The Group Boxes example consists of a single \c Window class that
is used to show four group boxes: an exclusive radio button group,
a non-exclusive checkbox group, an exclusive radio button group
with an enabling checkbox, and a group box with normal push buttons.
\section1 Window Class Definition
The \c Window class is a subclass of \c QWidget that is used to
display a number of group boxes. The class definition contains
functions to construct each group box and populate it with different
selections of button widgets:
\snippet widgets/groupbox/window.h 0
In the example, the widget will be used as a top-level window, so
the constructor is defined so that we do not have to specify a parent
widget.
\section1 Window Class Implementation
The constructor creates a grid layout and fills it with each of the
group boxes that are to be displayed:
\snippet widgets/groupbox/window.cpp 0
The functions used to create each group box each return a
QGroupBox to be inserted into the grid layout.
\snippet widgets/groupbox/window.cpp 1
The first group box contains and manages three radio buttons. Since
the group box contains only radio buttons, it is exclusive by
default, so only one radio button can be checked at any given time.
We check the first radio button to ensure that the button group
contains one checked button.
\snippet widgets/groupbox/window.cpp 3
We use a vertical layout within the group box to present the
buttons in the form of a vertical list, and return the group
box to the constructor.
The second group box is itself checkable, providing a convenient
way to disable all the buttons inside it. Initially, it is
unchecked, so the group box itself must be checked before any of
the radio buttons inside can be checked.
\snippet widgets/groupbox/window.cpp 4
The group box contains three exclusive radio buttons, and an
independent checkbox. For consistency, one radio button must be
checked at all times, so we ensure that the first one is initially
checked.
\snippet widgets/groupbox/window.cpp 5
The buttons are arranged in the same way as those in the first
group box.
\snippet widgets/groupbox/window.cpp 6
The third group box is constructed with a "flat" style that is
better suited to certain types of dialog.
\snippet widgets/groupbox/window.cpp 7
This group box contains only checkboxes, so it is non-exclusive by
default. This means that each checkbox can be checked independently
of the others.
\snippet widgets/groupbox/window.cpp 8
Again, we use a vertical layout within the group box to present
the buttons in the form of a vertical list.
\snippet widgets/groupbox/window.cpp 9
The final group box contains only push buttons and, like the
second group box, it is checkable.
\snippet widgets/groupbox/window.cpp 10
We create a normal button, a toggle button, and a flat push button:
\snippet widgets/groupbox/window.cpp 11
Push buttons can be used to display popup menus. We create one, and
attach a simple menu to it:
\snippet widgets/groupbox/window.cpp 12
Finally, we lay out the widgets vertically, and return the group box
that we created:
\snippet widgets/groupbox/window.cpp 13
*/

View File

@ -0,0 +1,805 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/icons
\title Icons Example
\ingroup examples-widgets
\brief The Icons example shows how QIcon can generate pixmaps reflecting
an icon's state, mode and size.
These pixmaps are generated from the set of pixmaps made available to the
icon, and are used by Qt widgets to show an icon representing a particular
action.
\image icons-example.png Screenshot of the Icons example
Contents:
\tableofcontents
\section1 QIcon Overview
The QIcon class provides scalable icons in different modes and
states. An icon's state and mode are depending on the intended use
of the icon. Qt currently defines four modes:
\table
\header \li Mode \li Description
\row
\li QIcon::Normal
\li Display the pixmap when the user is not interacting with the
icon, but the functionality represented by the icon is
available.
\row
\li QIcon::Active
\li Display the pixmap when the functionality represented by the
icon is available and the user is interacting with the icon,
for example, moving the mouse over it or clicking it.
\row
\li QIcon::Disabled
\li Display the pixmap when the functionality represented by
the icon is not available.
\row
\li QIcon::Selected
\li Display the pixmap when the icon is selected.
\endtable
QIcon's states are QIcon::On and QIcon::Off, which will display
the pixmap when the widget is in the respective state. The most
common usage of QIcon's states are when displaying checkable tool
buttons or menu entries (see QAbstractButton::setCheckable() and
QAction::setCheckable()). When a tool button or menu entry is
checked, the QIcon's state is \l{QIcon::}{On}, otherwise it's
\l{QIcon::}{Off}. You can, for example, use the QIcon's states to
display differing pixmaps depending on whether the tool button or
menu entry is checked or not.
A QIcon can generate smaller, larger, active, disabled, and
selected pixmaps from the set of pixmaps it is given. Such
pixmaps are used by Qt widgets to show an icon representing a
particular action.
\section1 Overview of the Icons Application
With the Icons application you get a preview of an icon's
generated pixmaps reflecting its different states, modes and size.
When an image is loaded into the application, it is converted into
a pixmap and becomes a part of the set of pixmaps available to the
icon. An image can be excluded from this set by checking off the
related checkbox. The application provides a sub directory
containing sets of images explicitly designed to illustrate how Qt
renders an icon in different modes and states.
The application allows you to manipulate the icon size with some
predefined sizes and a spin box. The predefined sizes are style
dependent, but most of the styles have the same values. Only the
\macos style differs by using 32 pixels instead of 16 pixels
for toolbar buttons. You can navigate between the available styles
using the \uicontrol View menu.
\image icons-view-menu.png Screenshot of the View menu
The \uicontrol View menu also provide the option to make the application
guess the icon state and mode from an image's file name. The \uicontrol
File menu provide the options of adding an image and removing all
images. These last options are also available through a context
menu that appears if you press the right mouse button within the
table of image files. In addition, the \uicontrol File menu provide an
\uicontrol Exit option, and the \uicontrol Help menu provide information about
the example and about Qt.
\image icons_find_normal.png Screenshot of the Find Files
The screenshot above shows the application with one image file
loaded. The \uicontrol {Guess Image Mode/State} is enabled and the
style is Plastique.
When QIcon is provided with only one available pixmap, that
pixmap is used for all the states and modes. In this case the
pixmap's icon mode is set to normal, and the generated pixmaps
for the normal and active modes will look the same. But in
disabled and selected mode, Qt will generate a slightly different
pixmap.
The next screenshot shows the application with an additional file
loaded, providing QIcon with two available pixmaps. Note that the
new image file's mode is set to disabled. When rendering the \uicontrol
Disabled mode pixmaps, Qt will now use the new image. We can see
the difference: The generated disabled pixmap in the first
screenshot is slightly darker than the pixmap with the originally
set disabled mode in the second screenshot.
\image icons_find_normal_disabled.png Screenshot of the Find Files
When Qt renders the icon's pixmaps it searches through the set of
available pixmaps following a particular algorithm. The algorithm
is documented in QIcon, but we will describe some particular cases
below.
\image icons_monkey_active.png Screenshot of the Find Files
In the screenshot above, we have set \c monkey_on_32x32 to be an
Active/On pixmap and \c monkey_off_64x64 to be Normal/Off. To
render the other six mode/state combinations, QIcon uses the
search algorithm described in the table below:
\table 100%
\header \li{2,1} Requested Pixmap \li {8,1} Preferred Alternatives (mode/state)
\header \li Mode \li State \li 1 \li 2 \li 3 \li 4 \li 5 \li 6 \li 7 \li 8
\row \li{1,2} Normal \li Off \li \b N0 \li A0 \li N1 \li A1 \li D0 \li S0 \li D1 \li S1
\row \li On \li N1 \li \b A1 \li N0 \li A0 \li D1 \li S1 \li D0 \li S0
\row \li{1,2} Active \li Off \li A0 \li \b N0 \li A1 \li N1 \li D0 \li S0 \li D1 \li S1
\row \li On \li \b A1 \li N1 \li A0 \li N0 \li D1 \li S1 \li D0 \li S0
\row \li{1,2} Disabled \li Off \li D0 \li \b {N0'} \li A0' \li D1 \li N1' \li A1' \li S0' \li S1'
\row \li On \li D1 \li N1' \li \b {A1'} \li D0 \li N0' \li A0' \li S1' \li S0'
\row \li{1,2} Selected \li Off \li S0 \li \b {N0''} \li A0'' \li S1 \li N1'' \li A1'' \li D0'' \li D1''
\row \li On \li S1 \li N1'' \li \b {A1''} \li S0 \li N0'' \li A0'' \li D1'' \li D0''
\endtable
In the table, "0" and "1" stand for "Off" and "On", respectively.
Single quotes indicates that QIcon generates a disabled ("grayed
out") version of the pixmap; similarly, double quuote indicate
that QIcon generates a selected ("blued out") version of the
pixmap.
The alternatives used in the screenshot above are shown in bold.
For example, the Disabled/Off pixmap is derived by graying out
the Normal/Off pixmap (\c monkey_off_64x64).
In the next screenshots, we loaded the whole set of monkey
images. By checking or unchecking file names from the image list,
we get different results:
\table
\row
\li \inlineimage icons_monkey.png Screenshot of the Monkey Files
\li \inlineimage icons_monkey_mess.png Screenshot of the Monkey Files
\endtable
For any given mode/state combination, it is possible to specify
several images at different resolutions. When rendering an
icon, QIcon will automatically pick the most suitable image
and scale it down if necessary. (QIcon never scales up images,
because this rarely looks good.)
The screenshots below shows what happens when we provide QIcon
with three images (\c qt_extended_16x16.png, \c qt_extended_32x32.png, \c
qt_extended_48x48.png) and try to render the QIcon at various
resolutions:
\table
\row
\li
\li \inlineimage icons_qt_extended_8x8.png Qt Extended icon at 8 x 8
\li \inlineimage icons_qt_extended_16x16.png Qt Extended icon at 16 x 16
\li \inlineimage icons_qt_extended_17x17.png Qt Extended icon at 17 x 17
\row
\li
\li 8 x 8
\li \b {16 x 16}
\li 17 x 17
\row
\li \inlineimage icons_qt_extended_32x32.png Qt Extended icon at 32 x 32
\li \inlineimage icons_qt_extended_33x33.png Qt Extended icon at 33 x 33
\li \inlineimage icons_qt_extended_48x48.png Qt Extended icon at 48 x 48
\li \inlineimage icons_qt_extended_64x64.png Qt Extended icon at 64 x 64
\row
\li \b {32 x 32}
\li 33 x 33
\li \b {48 x 48}
\li 64 x 64
\endtable
For sizes up to 16 x 16, QIcon uses \c qt_extended_16x16.png and
scales it down if necessary. For sizes between 17 x 17 and 32 x
32, it uses \c qt_extended_32x32.png. For sizes above 32 x 32, it uses
\c qt_extended_48x48.png.
\section1 Line-by-Line Walkthrough
The Icons example consists of four classes:
\list
\li \c MainWindow inherits QMainWindow and is the main application
window.
\li \c IconPreviewArea is a custom widget that displays all
combinations of states and modes for a given icon.
\li \c IconSizeSpinBox is a subclass of QSpinBox that lets the
user enter icon sizes (e.g., "48 x 48").
\li \c ImageDelegate is a subclass of QStyledItemDelegate that
provides comboboxes for letting the user set the mode and state
associated with an image.
\endlist
We will start by reviewing the \c IconPreviewArea class before we
take a look at the \c MainWindow class. Finally, we will review the
\c IconSizeSpinBox and \c ImageDelegate classes.
\section2 IconPreviewArea Class Definition
An \c IconPreviewArea widget consists of a group box containing a grid of
QLabel widgets displaying headers and pixmaps.
\image icons_preview_area.png Screenshot of IconPreviewArea.
\snippet widgets/icons/iconpreviewarea.h 0
The \c IconPreviewArea class inherits QWidget. It displays the
generated pixmaps corresponding to an icon's possible states and
modes at a given size.
\snippet widgets/icons/iconpreviewarea.cpp 42
We would like the table columns to be in the order QIcon::Normal,
QIcon::Active, QIcon::Disabled, QIcon::Selected and the rows in the order
QIcon::Off, QIcon::On, which does not match the enumeration. The above code
provides arrays allowing to map from enumeration value to row/column
(by using QList::indexOf()) and back by using the array index and lists
of the matching strings. Qt's containers can be easily populated by
using C++ 11 initializer lists.
We need two public functions to set the current icon and the
icon's size. In addition the class has three private functions: We
use the \c createHeaderLabel() and \c createPixmapLabel()
functions when constructing the preview area, and we need the \c
updatePixmapLabels() function to update the preview area when
the icon or the icon's size has changed.
The \c NumModes and \c NumStates constants reflect \l{QIcon}'s
number of currently defined modes and states.
\section2 IconPreviewArea Class Implementation
\snippet widgets/icons/iconpreviewarea.cpp 0
In the constructor we create the labels displaying the headers and
the icon's generated pixmaps, and add them to a grid layout.
When creating the header labels, we make sure the enums \c
NumModes and \c NumStates defined in the \c .h file, correspond
with the number of labels that we create. Then if the enums at
some point are changed, the \c Q_ASSERT() macro will alert that this
part of the \c .cpp file needs to be updated as well.
If the application is built in debug mode, the \c Q_ASSERT()
macro will expand to
\code
if (!condition)
qFatal("ASSERT: "condition" in file ...");
\endcode
In release mode, the macro simply disappear. The mode can be set
in the application's \c .pro file. One way to do so is to add an
option to \c qmake when building the application:
\code
qmake "CONFIG += debug" icons.pro
\endcode
or
\code
qmake "CONFIG += release" icons.pro
\endcode
Another approach is to add this line directly to the \c .pro
file.
\snippet widgets/icons/iconpreviewarea.cpp 1
\codeline
\snippet widgets/icons/iconpreviewarea.cpp 2
The public \c setIcon() and \c setSize() functions change the icon
or the icon size, and make sure that the generated pixmaps are
updated.
\snippet widgets/icons/iconpreviewarea.cpp 3
\codeline
\snippet widgets/icons/iconpreviewarea.cpp 4
We use the \c createHeaderLabel() and \c createPixmapLabel()
functions to create the preview area's labels displaying the
headers and the icon's generated pixmaps. Both functions return
the QLabel that is created.
\snippet widgets/icons/iconpreviewarea.cpp 5
We use the private \c updatePixmapLabel() function to update the
generated pixmaps displayed in the preview area.
For each mode, and for each state, we retrieve a pixmap using the
QIcon::pixmap() function, which generates a pixmap corresponding
to the given state, mode and size. We pass the QWindows instance
obtained by calling QWidget::windowHandle() on the top level
widget (QWidget::nativeParentWidget()) in order to retrieve
the pixmap that matches best.
We format a tooltip displaying size, actual size and device pixel
ratio.
\section2 MainWindow Class Definition
The \c MainWindow widget consists of three main elements: an
images group box, an icon size group box and a preview area.
\image icons-example.png Screenshot of the Icons example
\snippet widgets/icons/mainwindow.h 0
The MainWindow class inherits from QMainWindow. We reimplement the
constructor, and declare several private slots:
\list
\li The \c about() slot simply provides information about the example.
\li The \c changeStyle() slot changes the application's GUI style and
adjust the style dependent size options.
\li The \c changeSize() slot changes the size of the preview area's icon.
\li The \c changeIcon() slot updates the set of pixmaps available to the
icon displayed in the preview area.
\li The \c addSampleImages() slot allows the user to load a new image
from the samples provided into the application.
\li The \c addOtherImages() slot allows the user to load a new image from
the directory obtained by calling
QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).
\li The \c screenChanged() updates the display in the \uicontrol{High DPI}
group box to correctly display the parameters of the current screen
the window is located on.
\endlist
In addition we declare several private functions to simplify the
constructor.
\section2 MainWindow Class Implementation
\snippet widgets/icons/mainwindow.cpp 0
In the constructor we first create the main window's central
widget and its child widgets, and put them in a grid layout. Then
we create the menus with their associated entries and actions.
We set the window title and determine the current style for the
application. We also enable the icon size spin box by clicking the
associated radio button, making the current value of the spin box
the icon's initial size.
\snippet widgets/icons/mainwindow.cpp 1
The \c about() slot displays a message box using the static
QMessageBox::about() function. In this example it displays a
simple box with information about the example.
The \c about() function looks for a suitable icon in four
locations: It prefers its parent's icon if that exists. If it
doesn't, the function tries the top-level widget containing
parent, and if that fails, it tries the active window. As a last
resort it uses the QMessageBox's Information icon.
\snippet widgets/icons/mainwindow.cpp 2
In the \c changeStyle() slot we first check the slot's
parameter. If it is false we immediately return, otherwise we find
out which style to change to, i.e. which action that triggered the
slot, using the QObject::sender() function.
This function returns the sender as a QObject pointer. Since we
know that the sender is a QAction object, we can safely cast the
QObject. We could have used a C-style cast or a C++ \c
static_cast(), but as a defensive programming technique we use a
\l qobject_cast(). The advantage is that if the object has the
wrong type, a null pointer is returned. Crashes due to null
pointers are much easier to diagnose than crashes due to unsafe
casts.
\snippet widgets/icons/mainwindow.cpp 3
\snippet widgets/icons/mainwindow.cpp 4
Once we have the action, we extract the style name using
QAction::data(). Then we create a QStyle object using the static
QStyleFactory::create() function.
Although we can assume that the style is supported by the
QStyleFactory: To be on the safe side, we use the \c Q_ASSERT()
macro to check if the created style is valid before we use the
QApplication::setStyle() function to set the application's GUI
style to the new style. QApplication will automatically delete
the style object when a new style is set or when the application
exits.
The predefined icon size options provided in the application are
style dependent, so we need to update the labels in the icon size
group box and in the end call the \c changeSize() slot to update
the icon's size.
\snippet widgets/icons/mainwindow.cpp 5
The \c changeSize() slot sets the size for the preview area's
icon.
It is invoked by the QButtonGroup whose members are radio buttons for
controlling the icon size. In \c createIconSizeGroupBox(), each button is
assigned a QStyle::PixelMetric value as an id, which is passed as a
parameter to the slot.
The special value \c OtherSize indicates that the spin box is
enabled. If it is, we extract the extent of the new size from the
box. If it's not, we query the style for the metric. Then we create
a QSize object based on the extent, and use that object to set the
size of the preview area's icon.
\snippet widgets/icons/mainwindow.cpp 12
The function \c addImages() is called by the slot addSampleImages()
passing the samples directory, or by the slot addOtherImages()
passing the directory obtained by querying
QStandardPaths::standardLocations().
The first thing we do is to show a file dialog to the user.
We initialize it to show the filters returned by
QImageReader::supportedMimeTypes().
For each of the files the file dialog returns, we add a row to the
table widget. The table widget is listing the images the user has
loaded into the application.
\snippet widgets/icons/mainwindow.cpp 13
We retrieve the image name using the QFileInfo::baseName()
function that returns the base name of the file without the path,
and create the first table widget item in the row.
We check if a high resolution version of the image exists (identified by
the suffix \c @2x on the base name) and display that along with the size
in the tooltip.
We add the file's complete name to the item's data. Since an item can
hold several information pieces, we need to assign the file name a role
that will distinguish it from other data. This role can be Qt::UserRole
or any value above it.
We also make sure that the item is not editable by removing the
Qt::ItemIsEditable flag. Table items are editable by default.
\snippet widgets/icons/mainwindow.cpp 15
Then we create the second and third items in the row making the
default mode Normal and the default state Off. But if the \uicontrol
{Guess Image Mode/State} option is checked, and the file name
contains "_act", "_dis", or "_sel", the modes are changed to
Active, Disabled, or Selected. And if the file name contains
"_on", the state is changed to On. The sample files in the
example's \c images subdirectory respect this naming convention.
\snippet widgets/icons/mainwindow.cpp 18
In the end we add the items to the associated row, and use the
QTableWidget::openPersistentEditor() function to create
comboboxes for the mode and state columns of the items.
Due to the connection between the table widget's \l
{QTableWidget::itemChanged()}{itemChanged()} signal and the \c
changeIcon() slot, the new image is automatically converted into a
pixmap and made part of the set of pixmaps available to the icon
in the preview area. So, corresponding to this fact, we need to
make sure that the new image's check box is enabled.
\snippet widgets/icons/mainwindow.cpp 6
The \c changeIcon() slot is called when the user alters the set
of images listed in the QTableWidget, to update the QIcon object
rendered by the \c IconPreviewArea.
We first create a QIcon object, and then we run through the
QTableWidget, which lists the images the user has loaded into the
application.
\snippet widgets/icons/mainwindow.cpp 8
We also extract the image file's name using the
QTableWidgetItem::data() function. This function takes a
Qt::DataItemRole as an argument to retrieve the right data
(remember that an item can hold several pieces of information)
and returns it as a QVariant. Then we use the
QVariant::toString() function to get the file name as a QString.
To create a pixmap from the file, we need to first create an
image and then convert this image into a pixmap using
QPixmap::fromImage(). Once we have the final pixmap, we add it,
with its associated mode and state, to the QIcon's set of
available pixmaps.
\snippet widgets/icons/mainwindow.cpp 11
After running through the entire list of images, we change the
icon of the preview area to the one we just created.
\snippet widgets/icons/mainwindow.cpp 20
In the \c removeAllImages() slot, we simply set the table widget's
row count to zero, automatically removing all the images the user
has loaded into the application. Then we update the set of pixmaps
available to the preview area's icon using the \c changeIcon()
slot.
\image icons_images_groupbox.png Screenshot of the images group box
The \c createImagesGroupBox() function is implemented to simplify
the constructor. The main purpose of the function is to create a
QTableWidget that will keep track of the images the user has
loaded into the application.
\snippet widgets/icons/mainwindow.cpp 21
First we create a group box that will contain the table widget.
Then we create a QTableWidget and customize it to suit our
purposes.
We call QAbstractItemView::setSelectionMode() to prevent the user
from selecting items.
The QAbstractItemView::setItemDelegate() call sets the item
delegate for the table widget. We create a \c ImageDelegate that
we make the item delegate for our view.
The QStyledItemDelegate class can be used to provide an editor for an item view
class that is subclassed from QAbstractItemView. Using a delegate
for this purpose allows the editing mechanism to be customized and
developed independently from the model and view.
In this example we derive \c ImageDelegate from QStyledItemDelegate.
QStyledItemDelegate usually provides line editors, while our subclass
\c ImageDelegate, provides comboboxes for the mode and state
fields.
\snippet widgets/icons/mainwindow.cpp 22
Then we customize the QTableWidget's horizontal header, and hide
the vertical header.
\snippet widgets/icons/mainwindow.cpp 24
At the end, we connect the QTableWidget::itemChanged() signal to
the \c changeIcon() slot to ensure that the preview area is in
sync with the image table.
\image icons_size_groupbox.png Screenshot of the icon size group box
The \c createIconSizeGroupBox() function is called from the
constructor. It creates the widgets controlling the size of the
preview area's icon.
\snippet widgets/icons/mainwindow.cpp 26
First we create a group box that will contain all the widgets;
then we create the radio buttons and the spin box. We add the
radio buttons to an instance of QButtonGroup, using the value
of the QStyle::PixelMetric they represent as an integer id.
\snippet widgets/icons/mainwindow.cpp 40
We introduce an enumeration constant \c OtherSize to represent
a custom size.
The spin box is not a regular QSpinBox but an \c IconSizeSpinBox.
The \c IconSizeSpinBox class inherits QSpinBox and reimplements
two functions: QSpinBox::textFromValue() and
QSpinBox::valueFromText(). The \c IconSizeSpinBox is designed to
handle icon sizes, e.g., "32 x 32", instead of plain integer
values.
\snippet widgets/icons/mainwindow.cpp 27
Then we connect all of the radio buttons
\l{QRadioButton::toggled()}{toggled()} signals and the spin box's
\l {QSpinBox::valueChanged()}{valueChanged()} signal to the \c
changeSize() slot to make sure that the size of the preview
area's icon is updated whenever the user changes the icon size.
In the end we put the widgets in a layout that we install on the
group box.
\snippet widgets/icons/mainwindow.cpp 28
In the \c createActions() function we create and customize all the
actions needed to implement the functionality associated with the
menu entries in the application.
In particular we create the \c styleActionGroup based on the
currently available GUI styles using
QStyleFactory. QStyleFactory::keys() returns a list of valid keys,
typically including "windows" and "fusion". Depending on the platform,
"windowsvista" and "macos" may be available.
We create one action for each key, and adds the action to the
action group. Also, for each action, we call QAction::setData()
with the style name. We will retrieve it later using
QAction::data().
As we go along, we create the \uicontrol File, \uicontrol View and
\uicontrol Help menus and add the actions to them.
The QMenu class provides a menu widget for use in menu bars,
context menus, and other popup menus. We put each menu in the
application's menu bar, which we retrieve using
QMainWindow::menuBar().
\snippet widgets/icons/mainwindow.cpp 30
QWidgets have a \l{QWidget::contextMenuPolicy}{contextMenuPolicy}
property that controls how the widget should behave when the user
requests a context menu (e.g., by right-clicking). We set the
QTableWidget's context menu policy to Qt::ActionsContextMenu,
meaning that the \l{QAction}s associated with the widget should
appear in its context menu.
Then we add the \uicontrol{Add Image} and \uicontrol{Remove All Images}
actions to the table widget. They will then appear in the table
widget's context menu.
\snippet widgets/icons/mainwindow.cpp 31
In the \c checkCurrentStyle() function we go through the group of
style actions, looking for the current GUI style.
For each action, we first extract the style name using
QAction::data(). Since this is only a QStyleFactory key (e.g.,
"macos"), we cannot compare it directly to the current
style's class name. We need to create a QStyle object using the
static QStyleFactory::create() function and compare the class
name of the created QStyle object with that of the current style.
As soon as we are done with a QStyle candidate, we delete it.
For all QObject subclasses that use the \c Q_OBJECT macro, the
class name of an object is available through its
\l{QObject::metaObject()}{meta-object}.
We can assume that the style is supported by
QStyleFactory, but to be on the safe side we use the \c
Q_ASSERT() macro to make sure that QStyleFactory::create()
returned a valid pointer.
\snippet widgets/icons/mainwindow.cpp 44
We overload the show() function to set up the updating of the
current screen in \c screenChanged(). After calling QWidget::show(),
the QWindow associated with the QWidget is created and we can
connect to its QWindow::screenChanged() signal.
\section2 IconSizeSpinBox Class Definition
\snippet widgets/icons/iconsizespinbox.h 0
The \c IconSizeSpinBox class is a subclass of QSpinBox. A plain
QSpinBox can only handle integers. But since we want to display
the spin box's values in a more sophisticated way, we need to
subclass QSpinBox and reimplement the QSpinBox::textFromValue()
and QSpinBox::valueFromText() functions.
\image icons_size_spinbox.png Screenshot of the icon size spinbox
\section2 IconSizeSpinBox Class Implementation
\snippet widgets/icons/iconsizespinbox.cpp 0
The constructor is trivial.
\snippet widgets/icons/iconsizespinbox.cpp 2
QSpinBox::textFromValue() is used by the spin box whenever it
needs to display a value. The default implementation returns a
base 10 representation of the \c value parameter.
Our reimplementation returns a QString of the form "32 x 32".
\snippet widgets/icons/iconsizespinbox.cpp 1
The QSpinBox::valueFromText() function is used by the spin box
whenever it needs to interpret text typed in by the user. Since
we reimplement the \c textFromValue() function we also need to
reimplement the \c valueFromText() function to interpret the
parameter text and return the associated int value.
We parse the text using a regular expression (a QRegularExpression). We
define an expression that matches one or several digits,
optionally followed by whitespace, an "x" or the times symbol,
whitespace and one or several digits again.
The first digits of the regular expression are captured using
parentheses. This enables us to use the QRegularExpressionMatch::captured()
or QRegularExpressionMatch::capturedTexts() functions to extract the matched
characters. If the first and second numbers of the spin box value
differ (e.g., "16 x 24"), we use the first number.
When the user presses \uicontrol Enter, QSpinBox first calls
QSpinBox::valueFromText() to interpret the text typed by the
user, then QSpinBox::textFromValue() to present it in a canonical
format (e.g., "16 x 16").
\section2 ImageDelegate Class Definition
\snippet widgets/icons/imagedelegate.h 0
The \c ImageDelegate class is a subclass of QStyledItemDelegate. The
QStyledItemDelegate class provides display and editing facilities for
data items from a model. A single QStyledItemDelegate object is
responsible for all items displayed in a item view (in our case,
a QTableWidget).
A QStyledItemDelegate can be used to provide an editor for an item view
class that is subclassed from QAbstractItemView. Using a delegate
for this purpose allows the editing mechanism to be customized and
developed independently from the model and view.
\snippet widgets/icons/imagedelegate.h 1
The default implementation of QStyledItemDelegate creates a QLineEdit.
Since we want the editor to be a QComboBox, we need to subclass
QStyledItemDelegate and reimplement the QStyledItemDelegate::createEditor(),
QStyledItemDelegate::setEditorData() and QStyledItemDelegate::setModelData()
functions.
\snippet widgets/icons/imagedelegate.h 2
The \c emitCommitData() slot is used to emit the
QImageDelegate::commitData() signal with the appropriate
argument.
\section2 ImageDelegate Class Implementation
\snippet widgets/icons/imagedelegate.cpp 0
The constructor is trivial.
\snippet widgets/icons/imagedelegate.cpp 1
The default QStyledItemDelegate::createEditor() implementation returns
the widget used to edit the item specified by the model and item
index for editing. The parent widget and style option are used to
control the appearance of the editor widget.
Our reimplementation creates and populates a combobox instead of
the default line edit. The contents of the combobox depends on
the column in the table for which the editor is requested. Column
1 contains the QIcon modes, whereas column 2 contains the QIcon
states.
In addition, we connect the combobox's \l
{QComboBox::activated()}{activated()} signal to the \c
emitCommitData() slot to emit the
QAbstractItemDelegate::commitData() signal whenever the user
chooses an item using the combobox. This ensures that the rest of
the application notices the change and updates itself.
\snippet widgets/icons/imagedelegate.cpp 2
The QStyledItemDelegate::setEditorData() function is used by
QTableWidget to transfer data from a QTableWidgetItem to the
editor. The data is stored as a string; we use
QComboBox::findText() to locate it in the combobox.
Delegates work in terms of models, not items. This makes it
possible to use them with any item view class (e.g., QListView,
QListWidget, QTreeView, etc.). The transition between model and
items is done implicitly by QTableWidget; we don't need to worry
about it.
\snippet widgets/icons/imagedelegate.cpp 3
The QStyledItemDelegate::setEditorData() function is used by QTableWidget
to transfer data back from the editor to the \l{QTableWidgetItem}.
\snippet widgets/icons/imagedelegate.cpp 4
The \c emitCommitData() slot simply emit the
QAbstractItemDelegate::commitData() signal for the editor that
triggered the slot. This signal must be emitted when the editor
widget has completed editing the data, and wants to write it back
into the model.
\section2 The Implementation of the Function main()
\snippet widgets/icons/main.cpp 45
We use QCommandLineParser to handle any command line options or parameters
passed to the application. Then, we resize the main window according
to the available screen geometry and show it.
*/

View File

@ -0,0 +1,144 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/imagecomposition
\title Image Composition Example
\ingroup examples-painting
\ingroup examples-layout
\brief Shows how composition modes work in QPainter.
\brief The Image Composition example lets the user combine images
together using any composition mode supported by QPainter, described
in detail in \l{QPainter#Composition Modes}{Composition Modes}.
\image imagecomposition-example.png
\section1 Setting Up The Resource File
The Image Composition example requires two source images,
\e butterfly.png and \e checker.png that are embedded within
\e imagecomposition.qrc. The file contains the following code:
\quotefile painting/imagecomposition/imagecomposition.qrc
For more information on resource files, see \l{The Qt Resource System}.
\section1 ImageComposer Class Definition
The \c ImageComposer class is a subclass of QWidget that implements three
private slots, \c chooseSource(), \c chooseDestination(), and
\c recalculateResult().
\snippet painting/imagecomposition/imagecomposer.h 0
In addition, \c ImageComposer consists of five private functions,
\c addOp(), \c chooseImage(), \c loadImage(), \c currentMode(), and
\c imagePos(), as well as private instances of QToolButton, QComboBox,
QLabel, and QImage.
\snippet painting/imagecomposition/imagecomposer.h 1
\section1 ImageComposer Class Implementation
We declare a QSize object, \c resultSize, as a static constant with width
and height equal to 200.
\snippet painting/imagecomposition/imagecomposer.cpp 0
Within the constructor, we instantiate a QToolButton object,
\c sourceButton and set its \l{QAbstractButton::setIconSize()}{iconSize}
property to \c resultSize. The \c operatorComboBox is instantiated and
then populated using the \c addOp() function. This function accepts a
QPainter::CompositionMode, \a mode, and a QString, \a name, representing
the name of the composition mode.
\snippet painting/imagecomposition/imagecomposer.cpp 1
The \c destinationButton is instantiated and its
\l{QAbstractButton::setIconSize()}{iconSize} property is set to
\c resultSize as well. The \l{QLabel}s \c equalLabel and \c resultLabel
are created and \c{resultLabel}'s \l{QWidget::setMinimumWidth()}
{minimumWidth} is set.
\snippet painting/imagecomposition/imagecomposer.cpp 2
We connect the following signals to their corresponding slots:
\list
\li \c{sourceButton}'s \l{QPushButton::clicked()}{clicked()} signal is
connected to \c chooseSource(),
\li \c{operatorComboBox}'s \l{QComboBox::activated()}{activated()}
signal is connected to \c recalculateResult(), and
\li \c{destinationButton}'s \l{QToolButton::clicked()}{clicked()} signal
is connected to \c chooseDestination().
\endlist
\snippet painting/imagecomposition/imagecomposer.cpp 3
A QGridLayout, \c mainLayout, is used to place all the widgets. Note
that \c{mainLayout}'s \l{QLayout::setSizeConstraint()}{sizeConstraint}
property is set to QLayout::SetFixedSize, which means that
\c{ImageComposer}'s size cannot be resized at all.
\snippet painting/imagecomposition/imagecomposer.cpp 4
We create a QImage, \c resultImage, and we invoke \c loadImage() twice
to load both the image files in our \e imagecomposition.qrc file. Then,
we set the \l{QWidget::setWindowTitle()}{windowTitle} property to
"Image Composition".
\snippet painting/imagecomposition/imagecomposer.cpp 5
The \c chooseSource() and \c chooseDestination() functions are
convenience functions that invoke \c chooseImage() with specific
parameters.
\snippet painting/imagecomposition/imagecomposer.cpp 6
\codeline
\snippet painting/imagecomposition/imagecomposer.cpp 7
The \c chooseImage() function loads an image of the user's choice,
depending on the \a title, \a image, and \a button.
\snippet painting/imagecomposition/imagecomposer.cpp 10
The \c recalculateResult() function is used to calculate amd display the
result of combining the two images together with the user's choice of
composition mode.
\snippet painting/imagecomposition/imagecomposer.cpp 8
The \c addOp() function adds an item to the \c operatorComboBox using
\l{QComboBox}'s \l{QComboBox::addItem()}{addItem} function. This function
accepts a QPainter::CompositionMode, \a mode, and a QString, \a name. The
rectangle is filled with Qt::Transparent and both the \c sourceImage and
\c destinationImage are painted, before displaying it on \c resultLabel.
\snippet painting/imagecomposition/imagecomposer.cpp 9
The \c loadImage() function paints a transparent background using
\l{QPainter::fillRect()}{fillRect()} and draws \c image in a
centralized position using \l{QPainter::drawImage()}{drawImage()}.
This \c image is then set as the \c{button}'s icon.
\snippet painting/imagecomposition/imagecomposer.cpp 11
The \c currentMode() function returns the composition mode currently
selected in \c operatorComboBox.
\snippet painting/imagecomposition/imagecomposer.cpp 12
We use the \c imagePos() function to ensure that images loaded onto the
QToolButton objects, \c sourceButton and \c destinationButton, are
centralized.
\snippet painting/imagecomposition/imagecomposer.cpp 13
\section1 The \c main() Function
The \c main() function instantiates QApplication and \c ImageComposer
and invokes its \l{QWidget::show()}{show()} function.
\snippet painting/imagecomposition/main.cpp 0
*/

View File

@ -0,0 +1,320 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/imageviewer
\title Image Viewer Example
\ingroup examples-widgets
\brief The example shows how to combine QLabel and QScrollArea to
display an image.
QLabel is typically used for displaying text,
but it can also display an image. QScrollArea provides a
scrolling view around another widget. If the child widget exceeds
the size of the frame, QScrollArea automatically provides scroll
bars.
The example demonstrates how QLabel's ability to scale its
contents (QLabel::scaledContents), and QScrollArea's ability to
automatically resize its contents (QScrollArea::widgetResizable),
can be used to implement zooming and scaling features. In
addition the example shows how to use QPainter to print an image.
\borderedimage imageviewer-example.png
\caption Screenshot of the Image Viewer example
With the Image Viewer application, the users can view an image of
their choice. The \uicontrol File menu gives the user the possibility
to:
\list
\li \uicontrol{Open...} - Open an image file
\li \uicontrol{Print...} - Print an image
\li \uicontrol{Exit} - Exit the application
\endlist
Once an image is loaded, the \uicontrol View menu allows the users to:
\list
\li \uicontrol{Zoom In} - Scale the image up by 25%
\li \uicontrol{Zoom Out} - Scale the image down by 25%
\li \uicontrol{Normal Size} - Show the image at its original size
\li \uicontrol{Fit to Window} - Stretch the image to occupy the entire window
\endlist
In addition the \uicontrol Help menu provides the users with information
about the Image Viewer example in particular, and about Qt in
general.
\section1 ImageViewer Class Definition
\snippet widgets/imageviewer/imageviewer.h 0
The \c ImageViewer class inherits from QMainWindow. We reimplement
the constructor, and create several private slots to facilitate
the menu entries. In addition we create four private functions.
We use \c createActions() and \c createMenus() when constructing
the \c ImageViewer widget. We use the \c updateActions() function
to update the menu entries when a new image is loaded, or when
the \uicontrol {Fit to Window} option is toggled. The zoom slots use \c
scaleImage() to perform the zooming. In turn, \c
scaleImage() uses \c adjustScrollBar() to preserve the focal point after
scaling an image.
\section1 ImageViewer Class Implementation
\snippet widgets/imageviewer/imageviewer.cpp 0
In the constructor we first create the label and the scroll area.
We set \c {imageLabel}'s size policy to \l
{QSizePolicy::Ignored}{ignored}, making the users able to scale
the image to whatever size they want when the \uicontrol {Fit to Window}
option is turned on. Otherwise, the default size polizy (\l
{QSizePolicy::Preferred}{preferred}) will make scroll bars appear
when the scroll area becomes smaller than the label's minimum size
hint.
We ensure that the label will scale its contents to fill all
available space, to enable the image to scale properly when
zooming. If we omitted to set the \c {imageLabel}'s \l
{QLabel::scaledContents}{scaledContents} property, zooming in
would enlarge the QLabel, but leave the pixmap at
its original size, exposing the QLabel's background.
We make \c imageLabel the scroll area's child widget, and we make
\c scrollArea the central widget of the QMainWindow. At the end
we create the associated actions and menus, and customize the \c
{ImageViewer}'s appearance.
\snippet widgets/imageviewer/imageviewer.cpp 1
In the \c open() slot, we show a file dialog to the user. We compile
a list of mime types for use as a filter by querying QImageReader
for the available mime type names.
We show the file dialog until a valid file name is entered or
the user cancels.
The function \c loadFile() is used to load the image.
\snippet widgets/imageviewer/imageviewer.cpp 2
In the \c loadFile() function, we instantiate a QImageReader
and enable automatic transformations by calling
QImageReader::setAutoTransform(). For files in JPEG format,
this ensures that portrait mode images of digital cameras are shown
correctly by applying the appropriate orientation read from the
EXIF meta data stored in the image file.
We then load the image using QImageReader::read(). If this returns
a null image, indicating that the file is not an image file,
we use a QMessageBox to alert the user.
The QMessageBox class provides a modal dialog with a short
message, an icon, and some buttons. As with QFileDialog the
easiest way to create a QMessageBox is to use its static
convenience functions. QMessageBox provides a range of different
messages arranged along two axes: severity (question,
information, warning and critical) and complexity (the number of
necessary response buttons). In this particular example an
information message with an \uicontrol OK button (the default) is
sufficient, since the message is part of a normal operation.
\snippet widgets/imageviewer/imageviewer.cpp 4
If the format is supported, we display the image in \c imageLabel
by setting the label's \l {QLabel::pixmap}{pixmap}. Then we enable
the \uicontrol Print and \uicontrol {Fit to Window} menu entries and update
the rest of the view menu entries. The \uicontrol Open and \uicontrol Exit
entries are enabled by default.
If the \uicontrol {Fit to Window} option is turned off, the
QScrollArea::widgetResizable property is \c false and it is
our responsibility (not QScrollArea's) to give the QLabel a
reasonable size based on its contents. We call
\{QWidget::adjustSize()}{adjustSize()} to achieve this, which is
essentially the same as
\code
imageLabel->resize(imageLabel->pixmap()->size());
\endcode
In the \c print() slot, we first make sure that an image has been
loaded into the application:
\snippet widgets/imageviewer/imageviewer.cpp 5
\snippet widgets/imageviewer/imageviewer.cpp 6
If the application is built in debug mode, the \c Q_ASSERT() macro
will expand to
\code
if (imageLabel->pixmap().isNull())
qFatal("ASSERT: "imageLabel->pixmap().isNull()" in file ...");
\endcode
In release mode, the macro simply disappear. The mode can be set
in the application's \c .pro file. One way to do so is to add an
option to \uicontrol qmake when building the application:
\code
qmake "CONFIG += debug" foo.pro
\endcode
or
\code
qmake "CONFIG += release" foo.pro
\endcode
Another approach is to add this line directly to the \c .pro
file.
\snippet widgets/imageviewer/imageviewer.cpp 7
\snippet widgets/imageviewer/imageviewer.cpp 8
Then we present a print dialog allowing the user to choose a
printer and to set a few options. We construct a painter with a
QPrinter as the paint device. We set the painter's window
and viewport in such a way that the image is as large as possible
on the paper, but without altering its
\l{Qt::KeepAspectRatio}{aspect ratio}.
In the end we draw the pixmap at position (0, 0).
\snippet widgets/imageviewer/imageviewer.cpp 9
\snippet widgets/imageviewer/imageviewer.cpp 10
We implement the zooming slots using the private \c scaleImage()
function. We set the scaling factors to 1.25 and 0.8,
respectively. These factor values ensure that a \uicontrol {Zoom In}
action and a \uicontrol {Zoom Out} action will cancel each other (since
1.25 * 0.8 == 1), and in that way the normal image size can be
restored using the zooming features.
The screenshots below show an image in its normal size, and the
same image after zooming in:
\table
\row
\li \inlineimage imageviewer-original_size.png
\li \inlineimage imageviewer-zoom_in_1.png
\li \inlineimage imageviewer-zoom_in_2.png
\endtable
\snippet widgets/imageviewer/imageviewer.cpp 11
\snippet widgets/imageviewer/imageviewer.cpp 12
When zooming, we use the QLabel's ability to scale its contents.
Such scaling doesn't change the actual size hint of the contents.
And since the \l {QLabel::adjustSize()}{adjustSize()} function
use those size hint, the only thing we need to do to restore the
normal size of the currently displayed image is to call \c
adjustSize() and reset the scale factor to 1.0.
\snippet widgets/imageviewer/imageviewer.cpp 13
\snippet widgets/imageviewer/imageviewer.cpp 14
The \c fitToWindow() slot is called each time the user toggled
the \uicontrol {Fit to Window} option. If the slot is called to turn on
the option, we tell the scroll area to resize its child widget
with the QScrollArea::setWidgetResizable() function. Then we
disable the \uicontrol {Zoom In}, \uicontrol {Zoom Out} and \uicontrol {Normal
Size} menu entries using the private \c updateActions() function.
If the \l {QScrollArea::widgetResizable} property is set to \c
false (the default), the scroll area honors the size of its child
widget. If this property is set to \c true, the scroll area will
automatically resize the widget in order to avoid scroll bars
where they can be avoided, or to take advantage of extra space.
But the scroll area will honor the minimum size hint of its child
widget independent of the widget resizable property. So in this
example we set \c {imageLabel}'s size policy to \l
{QSizePolicy::Ignored}{ignored} in the constructor, to avoid that
scroll bars appear when the scroll area becomes smaller than the
label's minimum size hint.
The screenshots below shows an image in its normal size, and the
same image with the \uicontrol {Fit to window} option turned on.
Enlarging the window will stretch the image further, as shown in
the third screenshot.
\table
\row
\li \inlineimage imageviewer-original_size.png
\li \inlineimage imageviewer-fit_to_window_1.png
\li \inlineimage imageviewer-fit_to_window_2.png
\endtable
If the slot is called to turn off the option, the
{QScrollArea::setWidgetResizable} property is set to \c false. We
also restore the image pixmap to its normal size by adjusting the
label's size to its content. And in the end we update the view
menu entries.
\snippet widgets/imageviewer/imageviewer.cpp 15
\snippet widgets/imageviewer/imageviewer.cpp 16
We implement the \c about() slot to create a message box
describing what the example is designed to show.
\snippet widgets/imageviewer/imageviewer.cpp 17
\snippet widgets/imageviewer/imageviewer.cpp 18
In the private \c createAction() function, we create the
actions providing the application features and populate
a menu with them.
We assign a short-cut key to each action and connect them to the
appropriate slots. We only enable the \c openAct and \c exitAct at
the time of creation, the others are updated once an image has
been loaded into the application. In addition we make the \c
fitToWindowAct \l {QAction::checkable}{checkable}.
The QMenu class provides a menu widget for use in menu bars,
context menus, and other popup menus. The QMenuBar class provides
a horizontal menu bar that consists of a list of pull-down menu
items. So we put the menus in the \c {ImageViewer}'s
menu bar which we retrieve with the QMainWindow::menuBar()
function.
\snippet widgets/imageviewer/imageviewer.cpp 21
\snippet widgets/imageviewer/imageviewer.cpp 22
The private \c updateActions() function enables or disables the
\uicontrol {Zoom In}, \uicontrol {Zoom Out} and \uicontrol {Normal Size} menu
entries depending on whether the \uicontrol {Fit to Window} option is
turned on or off.
\snippet widgets/imageviewer/imageviewer.cpp 23
\snippet widgets/imageviewer/imageviewer.cpp 24
In \c scaleImage(), we use the \c factor parameter to calculate
the new scaling factor for the displayed image, and resize \c
imageLabel. Since we set the
\l{QLabel::scaledContents}{scaledContents} property to \c true in
the constructor, the call to QWidget::resize() will scale the
image displayed in the label. We also adjust the scroll bars to
preserve the focal point of the image.
At the end, if the scale factor is less than 33.3% or greater
than 300%, we disable the respective menu entry to prevent the
image pixmap from becoming too large, consuming too much
resources in the window system.
\snippet widgets/imageviewer/imageviewer.cpp 25
\snippet widgets/imageviewer/imageviewer.cpp 26
Whenever we zoom in or out, we need to adjust the scroll bars in
consequence. It would have been tempting to simply call
\code
scrollBar->setValue(int(factor * scrollBar->value()));
\endcode
but this would make the top-left corner the focal point, not the
center. Therefore we need to take into account the scroll bar
handle's size (the \l{QScrollBar::pageStep}{page step}).
*/

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/interview
\title Interview
\ingroup examples-itemviews
\brief This example demonstrates the usage of the model/view framework.
\brief The Interview example explores the flexibility and scalability of the
model/view framework by presenting an infinitely deep data structure using a model
and three different types of view.
\image interview-demo.png
*/

View File

@ -0,0 +1,19 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/puzzle
\title Item Views Puzzle Example
\ingroup examples-itemviews
\brief The Puzzle example shows how to enable drag and drop with a custom model
to allow items to be transferred between a view and another widget.
\image itemviewspuzzle-example.png
This example is an implementation of a simple jigsaw puzzle game using the
built-in support for drag and drop provided by Qt's model/view framework.
The \l{Drag and Drop Puzzle Example}{Drag and Drop Puzzle} example shows
many of the same features, but takes an alternative approach that uses Qt's
drag and drop API at the application level to handle drag and drop
operations.
*/

View File

@ -0,0 +1,195 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example dialogs/licensewizard
\title License Wizard Example
\ingroup examples-dialogs
\brief The License Wizard example shows how to implement complex wizards in
Qt.
\image licensewizard-example.png Screenshot of the License Wizard example
Most wizards have a linear structure, with page 1 followed by
page 2 and so on until the last page. The
\l{dialogs/classwizard}{Class Wizard} example shows how to create
such wizards.
Some wizards are more complex in that they allow different
traversal paths based on the information provided by the user.
The License Wizard example illustrates this. It provides five
wizard pages; depending on which options are selected, the user
can reach different pages.
\image licensewizard-flow.png The License Wizard pages
The example consists of the following classes:
\list
\li \c LicenseWizard inherits QWizard and implements a non-linear
five-page wizard that leads the user through the process of
choosing a license agreement.
\li \c IntroPage, \c EvaluatePage, \c RegisterPage, \c
DetailsPage, and \c ConclusionPage are QWizardPage subclasses
that implement the wizard pages.
\endlist
\section1 The LicenseWizard Class
The \c LicenseWizard class derives from QWizard and provides a
five-page wizard that guides the user through the process of
registering their copy of a fictitious software product. Here's
the class definition:
\snippet dialogs/licensewizard/licensewizard.h 1
The class's public API is limited to a constructor and an enum.
The enum defines the IDs associated with the various pages:
\table
\header \li Class name \li Enum value \li Page ID
\row \li \c IntroPage \li \c Page_Intro \li 0
\row \li \c EvaluatePage \li \c Page_Evaluate \li 1
\row \li \c RegisterPage \li \c Page_Register \li 2
\row \li \c DetailsPage \li \c Page_Details \li 3
\row \li \c ConclusionPage \li \c Page_Conclusion \li 4
\endtable
For this example, the IDs are arbitrary. The only constraints are
that they must be unique and different from -1. IDs allow us to
refer to pages.
\snippet dialogs/licensewizard/licensewizard.cpp 2
In the constructor, we create the five pages, insert them into
the wizard using QWizard::setPage(), and set \c Page_Intro to be
the first page.
\snippet dialogs/licensewizard/licensewizard.cpp 3
\snippet dialogs/licensewizard/licensewizard.cpp 4
We set the style to \l{QWizard::}{ModernStyle} on all platforms
except \macos,
\snippet dialogs/licensewizard/licensewizard.cpp 5
\snippet dialogs/licensewizard/licensewizard.cpp 6
We configure the QWizard to show a \uicontrol Help button, which is
connected to our \c showHelp() slot. We also set the
\l{QWizard::}{LogoPixmap} for all pages that have a header (i.e.,
\c EvaluatePage, \c RegisterPage, and \c DetailsPage).
\snippet dialogs/licensewizard/licensewizard.cpp 9
\snippet dialogs/licensewizard/licensewizard.cpp 11
\dots
\snippet dialogs/licensewizard/licensewizard.cpp 13
In \c showHelp(), we display help texts that are appropriate for
the current page. If the user clicks \uicontrol Help twice for the same
page, we say, "Sorry, I already gave what help I could. Maybe you
should try asking a human?"
\section1 The IntroPage Class
The pages are defined in \c licensewizard.h and implemented in \c
licensewizard.cpp, together with \c LicenseWizard.
Here's the definition and implementation of \c{IntroPage}:
\snippet dialogs/licensewizard/licensewizard.h 4
\codeline
\snippet dialogs/licensewizard/licensewizard.cpp 16
A page inherits from QWizardPage. We set a
\l{QWizardPage::}{title} and a
\l{QWizard::WatermarkPixmap}{watermark pixmap}. By not setting
any \l{QWizardPage::}{subTitle}, we ensure that no header is
displayed for this page. (On Windows, it is customary for wizards
to display a watermark pixmap on the first and last pages, and to
have a header on the other pages.)
\snippet dialogs/licensewizard/licensewizard.cpp 17
\snippet dialogs/licensewizard/licensewizard.cpp 19
The \c nextId() function returns the ID for \c EvaluatePage if
the \uicontrol{Evaluate the product for 30 days} option is checked;
otherwise it returns the ID for \c RegisterPage.
\section1 The EvaluatePage Class
The \c EvaluatePage is slightly more involved:
\snippet dialogs/licensewizard/licensewizard.h 5
\codeline
\snippet dialogs/licensewizard/licensewizard.cpp 20
\dots
\snippet dialogs/licensewizard/licensewizard.cpp 21
\dots
\snippet dialogs/licensewizard/licensewizard.cpp 22
First, we set the page's \l{QWizardPage::}{title}
and \l{QWizardPage::}{subTitle}.
Then we create the child widgets, create \l{Registering and Using
Fields}{wizard fields} associated with them, and put them into
layouts. The fields are created with an asterisk (\c
*) next to their name. This makes them \l{mandatory fields}, that
is, fields that must be filled before the user can press the
\uicontrol Next button (\uicontrol Continue on \macos). The fields' values
can be accessed from any other page using QWizardPage::field().
Resetting the page amounts to clearing the two text fields.
\snippet dialogs/licensewizard/licensewizard.cpp 23
The next page is always the \c ConclusionPage.
\section1 The ConclusionPage Class
The \c RegisterPage and \c DetailsPage are very similar to \c
EvaluatePage. Let's go directly to the \c ConclusionPage:
\snippet dialogs/licensewizard/licensewizard.h 6
This time, we reimplement QWizardPage::initializePage() and
QWidget::setVisible(), in addition to
\l{QWizardPage::}{nextId()}. We also declare a private slot:
\c printButtonClicked().
\snippet dialogs/licensewizard/licensewizard.cpp 18
The default implementation of QWizardPage::nextId() returns
the page with the next ID, or -1 if the current page has the
highest ID. This behavior would work here, because \c
Page_Conclusion equals 5 and there is no page with a higher ID,
but to avoid relying on such subtle behavior, we reimplement
\l{QWizardPage::}{nextId()} to return -1.
\snippet dialogs/licensewizard/licensewizard.cpp 27
We use QWizard::hasVisitedPage() to determine the type of
license agreement the user has chosen. If the user filled the \c
EvaluatePage, the license text refers to an Evaluation License
Agreement. If the user filled the \c DetailsPage, the license
text is a First-Time License Agreement. If the user provided an
upgrade key and skipped the \c DetailsPage, the license text is
an Update License Agreement.
\snippet dialogs/licensewizard/licensewizard.cpp 28
We want to display a \uicontrol Print button in the wizard when the \c
ConclusionPage is up. One way to accomplish this is to reimplement
QWidget::setVisible():
\list
\li If the page is shown, we set the \l{QWizard::}{CustomButton1} button's
text to \uicontrol{\underline{P}rint}, we enable the \l{QWizard::}{HaveCustomButton1}
option, and we connect the QWizard's \l{QWizard::}{customButtonClicked()}
signal to our \c printButtonClicked() slot.
\li If the page is hidden, we disable the \l{QWizard::}{HaveCustomButton1}
option and disconnect the \c printButtonClicked() slot.
\endlist
\sa QWizard, {Class Wizard Example}, {Trivial Wizard Example}
*/

View File

@ -0,0 +1,138 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/lineedits
\title Line Edits Example
\ingroup examples-widgets
\brief The Line Edits example demonstrates the many ways that QLineEdit
can be used, and shows the effects of various properties and validators
on the input and output supplied by the user.
\borderedimage lineedits-example.png
The example consists of a single \c Window class, containing a selection of
line edits with different input constraints and display properties that can be
changed by selecting items from comboboxes. Presenting these together helps
developers choose suitable properties to use with line edits, and makes it easy
to compare the effects of each validator on user input.
\section1 Window Class Definition
The \c Window class inherits QWidget and contains a constructor and several
slots:
\snippet widgets/lineedits/window.h 0
The slots are used to update the type of validator used for a given line edit when
a new validator has been selected in the associated combobox. The line edits
are stored in the window for use in these slots.
\section1 Window Class Implementation
The \c Window constructor is used to set up the line edits, validators,
and comboboxes, connect signals from the comboboxes to slots in the \c Window
class, and arrange the child widgets in layouts.
We begin by constructing a \l{QGroupBox}{group box} to hold a label, combobox,
and line edit so that we can demonstrate the QLineEdit::echoMode property:
\snippet widgets/lineedits/window.cpp 0
At this point, none of these widgets have been arranged in layouts. Eventually,
the \c echoLabel, \c echoComboBox, and \c echoLineEdit will be placed in a
vertical layout inside the \c echoGroup group box.
Similarly, we construct group boxes and collections of widgets to show the
effects of QIntValidator and QDoubleValidator on a line edit's contents:
\snippet widgets/lineedits/window.cpp 1
Text alignment is demonstrated by another group of widgets:
\snippet widgets/lineedits/window.cpp 2
QLineEdit supports the use of \l{QLineEdit::inputMask}{input masks}.
These only allow the user to type characters into the line edit that
follow a simple specification. We construct a group of widgets to
demonstrate a selection of predefined masks:
\snippet widgets/lineedits/window.cpp 3
Another useful feature of QLineEdit is its ability to make its contents
read-only. This property is used to control access to a line edit in the
following group of widgets:
\snippet widgets/lineedits/window.cpp 4
Now that all the child widgets have been constructed, we connect signals
from the comboboxes to slots in the \c Window object:
\snippet widgets/lineedits/window.cpp 5
Each of these connections use the QComboBox::activated() signal that
supplies an integer to the slot. This will be used to efficiently
make changes to the appropriate line edit in each slot.
We place each combobox, line edit, and label in a layout for each group
box, beginning with the layout for the \c echoGroup group box:
\snippet widgets/lineedits/window.cpp 6
The other layouts are constructed in the same way:
\snippet widgets/lineedits/window.cpp 7
Finally, we place each group box in a grid layout for the \c Window object
and set the window title:
\snippet widgets/lineedits/window.cpp 8
The slots respond to signals emitted when the comboboxes are changed by the
user.
When the combobox for the \uicontrol{Echo} group box is changed, the \c echoChanged()
slot is called:
\snippet widgets/lineedits/window.cpp 9
The slot updates the line edit in the same group box to use an echo mode that
corresponds to the entry described in the combobox.
When the combobox for the \uicontrol{Validator} group box is changed, the
\c validatorChanged() slot is called:
\snippet widgets/lineedits/window.cpp 10
The slot either creates a new validator for the line edit to use, or it removes
the validator in use by calling QLineEdit::setValidator() with a zero pointer.
We clear the line edit in this case to ensure that the new validator is
initially given valid input to work with.
When the combobox for the \uicontrol{Alignment} group box is changed, the
\c alignmentChanged() slot is called:
\snippet widgets/lineedits/window.cpp 11
This changes the way that text is displayed in the line edit to correspond with
the description selected in the combobox.
The \c inputMaskChanged() slot handles changes to the combobox in the
\uicontrol{Input Mask} group box:
\snippet widgets/lineedits/window.cpp 12
Each entry in the relevant combobox is associated with an input mask. We set
a new mask by calling the QLineEdit::setInputMask() function with a suitable string;
the mask is disabled if an empty string is used.
The \c accessChanged() slot handles changes to the combobox in the
\uicontrol{Access} group box:
\snippet widgets/lineedits/window.cpp 13
Here, we simply associate the \uicontrol{False} and \uicontrol{True} entries in the combobox
with \c false and \c true values to be passed to QLineEdit::setReadOnly(). This
allows the user to enable and disable input to the line edit.
*/

View File

@ -0,0 +1,13 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example mainwindows/mainwindow
\title Main Window
\ingroup examples-mainwindow
\brief The Main Window example shows Qt's extensive support for tool bars,
dock windows, menus, and other standard application features.
\image mainwindow-demo.png
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example mainwindows/mdi
\title MDI Example
\ingroup examples-mainwindow
\brief The MDI example shows how to implement a Multiple Document Interface using Qt's
QMdiArea class.
\image mdi-example.png
*/

View File

@ -0,0 +1,196 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example mainwindows/menus
\title Menus Example
\ingroup examples-mainwindow
\ingroup examples-layout
\brief The Menus example demonstrates how menus can be used in a main
window application.
A menu widget can be either a pull-down menu in a menu bar or a
standalone context menu. Pull-down menus are shown by the menu bar
when the user clicks on the respective item or presses the
specified shortcut key. Context menus are usually invoked by some
special keyboard key or by right-clicking.
\image menus-example.png
A menu consists of a list of \e action items. In applications,
many common commands can be invoked via menus, toolbar buttons as
well as keyboard shortcuts. Since the user expects the commands to
be performed in the same way, regardless of the user interface
used, it is useful to represent each command as an action.
The Menus example consists of one single class, \c MainWindow, derived
from the QMainWindow class. When choosing one of the
action items in our application, it will display the item's path
in its central widget.
\section1 MainWindow Class Definition
QMainWindow provides a main application window, with a menu bar,
tool bars, dock widgets and a status bar around a large central
widget.
\snippet mainwindows/menus/mainwindow.h 0
In this example, we will see how to implement pull-down menus as
well as a context menu. In order to implement a custom context
menu we must reimplement QWidget's \l
{QWidget::}{contextMenuEvent()} function to receive the context
menu events for our main window.
\snippet mainwindows/menus/mainwindow.h 1
We must also implement a collection of private slots to respond to
the user activating any of our menu entries. Note that these
slots are left out of this documentation since they are trivial,
i.e., most of them are only displaying the action's path in the
main window's central widget.
\snippet mainwindows/menus/mainwindow.h 2
We have chosen to simplify the constructor by implementing two
private convenience functions to create the various actions, to
add them to menus and to insert the menus into our main window's
menu bar.
\snippet mainwindows/menus/mainwindow.h 3
Finally, we declare the various menus and actions as well as a
simple information label in the application wide scope.
The QMenu class provides a menu widget for use in menu bars,
context menus, and other popup menus while the QAction class
provides an abstract user interface action that can be inserted
into widgets.
In some situations it is useful to group actions together, e.g.,
we have a \uicontrol {Left Align} action, a \uicontrol {Right Align} action, a
\uicontrol {Justify} action, and a \uicontrol {Center} action, and we want
only one of these actions to be active at any one time. One simple
way of achieving this is to group the actions together in an
action group using the QActionGroup class.
\section1 MainWindow Class Implementation
In the constructor, we start off by creating a regular QWidget and
make it our main window's central widget. Note that the main
window takes ownership of the widget pointer and deletes it at the
appropriate time.
\snippet mainwindows/menus/mainwindow.cpp 0
\codeline
\snippet mainwindows/menus/mainwindow.cpp 1
Then we create the information label as well as a top and bottom
filler that we add to a layout which we install on the central
widget. QMainWindow objects come with their own customized layout
and setting a layout on a the actual main window, or creating a
layout with a main window as a parent, is considered an error. You
should always set your own layout on the central widget instead.
\snippet mainwindows/menus/mainwindow.cpp 2
To create the actions and menus we call our two convenience
functions: \c createActions() and \c createMenus(). We will get
back to these shortly.
QMainWindow's \l {QMainWindow::statusBar()}{statusBar()} function
returns the status bar for the main window (if the status bar does
not exist, this function will create and return an empty status
bar). We initialize the status bar and window title, resize the
window to an appropriate size as well as ensure that the main
window cannot be resized to a smaller size than the given
one.
Now, let's take a closer look at the \c createActions() convenience
function that creates the various actions:
\snippet mainwindows/menus/mainwindow.cpp 4
\dots
A QAction object may contain an icon, a text, a shortcut, a status
tip, a "What's This?" text, and a tooltip. Most of these can be
set in the constructor, but they can also be set independently
using the provided convenience functions.
In the \c createActions() function, we first create a \c newAct
action. We make \uicontrol Ctrl+N its shortcut using the
QAction::setShortcut() function, and we set its status tip using the
QAction::setStatusTip() function (the status tip is displayed on all
status bars provided by the action's top-level parent widget). We
also connect its \l {QAction::}{triggered()} signal to the \c
newFile() slot.
The rest of the actions are created in a similar manner. Please
see the source code for details.
\snippet mainwindows/menus/mainwindow.cpp 7
Once we have created the \uicontrol {Left Align}, \uicontrol {Right Align},
\uicontrol {Justify}, and a \uicontrol {Center} actions, we can also create
the previously mentioned action group.
Each action is added to the group using QActionGroup's \l
{QActionGroup::}{addAction()} function. Note that an action also
can be added to a group by creating it with the group as its
parent. Since an action group is exclusive by default, only one of
the actions in the group is checked at any one time (this can be
altered using the QActionGroup::setExclusive() function).
When all the actions are created, we use the \c createMenus()
function to add the actions to the menus and to insert the menus
into the menu bar:
\snippet mainwindows/menus/mainwindow.cpp 8
QMenuBar's \l {QMenuBar::addMenu()}{addMenu()} function appends a
new QMenu with the given title, to the menu bar (note that the
menu bar takes ownership of the menu). We use QWidget's \l
{QWidget::addAction()}{addAction()} function to add each action to
the corresponding menu.
Alternatively, the QMenu class provides several \l
{QMenu::addAction()}{addAction()} convenience functions that create
and add new actions from given texts and/or icons. You can also
provide a member that will automatically connect to the new
action's \l {QAction::triggered()}{triggered()} signal, and a
shortcut represented by a QKeySequence instance.
The QMenu::addSeparator() function creates and returns a new
separator action, i.e. an action for which QAction::isSeparator()
returns true, and adds the new action to the menu's list of
actions.
\snippet mainwindows/menus/mainwindow.cpp 12
Note the \uicontrol Format menu. First of all, it is added as a submenu
to the \uicontrol Edit Menu using QMenu's \l
{QMenu::addMenu()}{addMenu()} function. Secondly, take a look at the
alignment actions: In the \c createActions() function we added the
\c leftAlignAct, \c rightAlignAct, \c justifyAct and \c centerAct
actions to an action group. Nevertheless, we must add each action
to the menu separately while the action group does its magic
behind the scene.
\snippet mainwindows/menus/mainwindow.cpp 3
To provide a custom context menu, we must reimplement QWidget's \l
{QWidget::}{contextMenuEvent()} function to receive the widget's
context menu events (note that the default implementation simply
ignores these events).
Whenever we receive such an event, we create a menu containing the
\uicontrol Cut, \uicontrol Copy and \uicontrol Paste actions. Context menus can be
executed either asynchronously using the \l {QMenu::}{popup()}
function or synchronously using the \l {QMenu::}{exec()}
function. In this example, we have chosen to show the menu using
its \l {QMenu::}{exec()} function. By passing the event's position
as argument we ensure that the context menu appears at the
expected position.
*/

View File

@ -0,0 +1,16 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/movie
\title Movie Example
\ingroup examples-widgets
\brief The Movie example demonstrates how to use QMovie and QLabel to
display animations.
QMovie is mostly useful if one wants to play
a simple animation without the added complexity of a multimedia
framework to install and deploy.
\borderedimage movie-example.png
*/

View File

@ -0,0 +1,344 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example richtext/orderform
\title Order Form Example
\ingroup examples-richtext
\brief The Order Form example shows how to generate rich text
documents by combining a simple template with data input by the
user in a dialog.
\brief The Order Form example shows how to generate rich text documents by
combining a simple template with data input by the user in a dialog. Data
is extracted from a \c DetailsDialog object and displayed on a QTextEdit
with a QTextCursor, using various formats. Each form generated is added
to a QTabWidget for easy access.
\image orderform-example.png
\section1 DetailsDialog Definition
The \c DetailsDialog class is a subclass of QDialog, implementing a slot
\c verify() to allow contents of the \c DetailsDialog to be verified later.
This is further explained in \c DetailsDialog Implementation.
\snippet richtext/orderform/detailsdialog.h 0
The constructor of \c DetailsDialog accepts parameters \a title and
\a parent. The class defines four \e{getter} functions: \c orderItems(),
\c senderName(), \c senderAddress(), and \c sendOffers() to allow data
to be accessed externally.
The class definition includes input widgets for the required
fields, \c nameEdit and \c addressEdit. Also, a QCheckBox and a
QDialogButtonBox are defined; the former to provide the user with the
option to receive information on products and offers, and the latter
to ensure that buttons used are arranged according to the user's native
platform. In addition, a QTableWidget, \c itemsTable, is used to hold
order details.
The screenshot below shows the \c DetailsDialog we intend to create.
\image orderform-example-detailsdialog.png
\section1 DetailsDialog Implementation
The constructor of \c DetailsDialog instantiates the earlier defined fields
and their respective labels. The label for \c offersCheckBox is set and the
\c setupItemsTable() function is invoked to setup and populate
\c itemsTable. The QDialogButtonBox object, \c buttonBox, is instantiated
with \uicontrol OK and \uicontrol Cancel buttons. This \c buttonBox's \c accepted() and
\c rejected() signals are connected to the \c verify() and \c reject()
slots in \c DetailsDialog.
\snippet richtext/orderform/detailsdialog.cpp 0
A QGridLayout is used to place all the objects on the \c DetailsDialog.
\snippet richtext/orderform/detailsdialog.cpp 1
The \c setupItemsTable() function instantiates the QTableWidget object,
\c itemsTable, and sets the number of rows based on the QStringList
object, \c items, which holds the type of items ordered. The number of
columns is set to 2, providing a "name" and "quantity" layout. A \c for
loop is used to populate the \c itemsTable and the \c name item's flag
is set to Qt::ItemIsEnabled or Qt::ItemIsSelectable. For demonstration
purposes, the \c quantity item is set to a 1 and all items in the
\c itemsTable have this value for quantity; but this can be modified by
editing the contents of the cells at run time.
\snippet richtext/orderform/detailsdialog.cpp 2
The \c orderItems() function extracts data from the \c itemsTable and
returns it in the form of a QList<QPair<QString,int>> where each QPair
corresponds to an item and the quantity ordered.
\snippet richtext/orderform/detailsdialog.cpp 3
The \c senderName() function is used to return the value of the QLineEdit
used to store the name field for the order form.
\snippet richtext/orderform/detailsdialog.cpp 4
The \c senderAddress() function is used to return the value of the
QTextEdit containing the address for the order form.
\snippet richtext/orderform/detailsdialog.cpp 5
The \c sendOffers() function is used to return a \c true or \c false
value that is used to determine if the customer in the order form
wishes to receive more information on the company's offers and promotions.
\snippet richtext/orderform/detailsdialog.cpp 6
The \c verify() function is an additionally implemented slot used to
verify the details entered by the user into the \c DetailsDialog. If
the details entered are incomplete, a QMessageBox is displayed
providing the user the option to discard the \c DetailsDialog. Otherwise,
the details are accepted and the \c accept() function is invoked.
\snippet richtext/orderform/detailsdialog.cpp 7
\section1 MainWindow Definition
The \c MainWindow class is a subclass of QMainWindow, implementing two
slots - \c openDialog() and \c printFile(). It also contains a private
instance of QTabWidget, \c letters.
\snippet richtext/orderform/mainwindow.h 0
\section1 MainWindow Implementation
The \c MainWindow constructor sets up the \c fileMenu and the required
actions, \c newAction and \c printAction. These actions' \c triggered()
signals are connected to the additionally implemented openDialog() slot
and the default close() slot. The QTabWidget, \c letters, is
instantiated and set as the window's central widget.
\snippet richtext/orderform/mainwindow.cpp 0
The \c createLetter() function creates a new QTabWidget with a QTextEdit,
\c editor, as the parent. This function accepts four parameters that
correspond to we obtained through \c DetailsDialog, in order to "fill"
the \c editor.
\snippet richtext/orderform/mainwindow.cpp 1
We then obtain the cursor for the \c editor using QTextEdit::textCursor().
The \c cursor is then moved to the start of the document using
QTextCursor::Start.
\snippet richtext/orderform/mainwindow.cpp 2
Recall the structure of a \l{Rich Text Document Structure}
{Rich Text Document}, where sequences of frames and
tables are always separated by text blocks, some of which may contain no
information.
In the case of the Order Form Example, the document structure for this portion
is described by the table below:
\table
\row
\li {1, 8} frame with \e{referenceFrameFormat}
\row
\li block \li \c{A company}
\row
\li block
\row
\li block \li \c{321 City Street}
\row
\li block
\row
\li block \li \c{Industry Park}
\row
\li block
\row
\li block \li \c{Another country}
\endtable
This is accomplished with the following code:
\snippet richtext/orderform/mainwindow.cpp 3
Note that \c topFrame is the \c {editor}'s top-level frame and is not shown
in the document structure.
We then set the \c{cursor}'s position back to its last position in
\c topFrame and fill in the customer's name (provided by the constructor)
and address - using a range-based for loop to traverse the QString, \c address.
\snippet richtext/orderform/mainwindow.cpp 4
The \c cursor is now back in \c topFrame and the document structure for
the above portion of code is:
\table
\row
\li block \li \c{Donald}
\row
\li block \li \c{47338 Park Avenue}
\row
\li block \li \c{Big City}
\endtable
For spacing purposes, we invoke \l{QTextCursor::insertBlock()}
{insertBlock()} twice. The \l{QDate::currentDate()}{currentDate()} is
obtained and displayed. We use \l{QTextFrameFormat::setWidth()}
{setWidth()} to increase the width of \c bodyFrameFormat and we insert
a new frame with that width.
\snippet richtext/orderform/mainwindow.cpp 5
The following code inserts standard text into the order form.
\snippet richtext/orderform/mainwindow.cpp 6
\snippet richtext/orderform/mainwindow.cpp 7
This part of the document structure now contains the date, a frame with
\c bodyFrameFormat, as well as the standard text.
\table
\row
\li block
\row
\li block
\row
\li block \li \c{Date: 25 May 2007}
\row
\li block
\row
\li {1, 4} frame with \e{bodyFrameFormat}
\row
\li block \li \c{I would like to place an order for the following items:}
\row
\li block
\row
\li block
\endtable
A QTextTableFormat object, \c orderTableFormat, is used to hold the type
of item and the quantity ordered.
\snippet richtext/orderform/mainwindow.cpp 8
We use \l{QTextTable::cellAt()}{cellAt()} to set the headers for the
\c orderTable.
\snippet richtext/orderform/mainwindow.cpp 9
Then, we iterate through the QList of QPair objects to populate
\c orderTable.
\snippet richtext/orderform/mainwindow.cpp 10
The resulting document structure for this section is:
\table
\row
\li {1, 11} \c{orderTable} with \e{orderTableFormat}
\row
\li block \li \c{Product}
\row
\li block \li \c{Quantity}
\row
\li block \li \c{T-shirt}
\row
\li block \li \c{4}
\row
\li block \li \c{Badge}
\row
\li block \li \c{3}
\row
\li block \li \c{Reference book}
\row
\li block \li \c{2}
\row
\li block \li \c{Coffee cup}
\row
\li block \li \c{5}
\endtable
The \c cursor is then moved back to \c{topFrame}'s
\l{QTextFrame::lastPosition()}{lastPosition()} and more standard text
is inserted.
\snippet richtext/orderform/mainwindow.cpp 11
\snippet richtext/orderform/mainwindow.cpp 12
Another QTextTable is inserted, to display the customer's
preference regarding offers.
\snippet richtext/orderform/mainwindow.cpp 13
The document structure for this portion is:
\table
\row
\li block
\row
\li block\li \c{Please update my...}
\row
\li {1, 5} block
\row
\li {1, 4} \c{offersTable}
\row
\li block \li \c{I want to receive...}
\row
\li block \li \c{I do not want to receive...}
\row
\li block \li \c{X}
\endtable
The \c cursor is moved to insert "Sincerely" along with the customer's
name. More blocks are inserted for spacing purposes. The \c printAction
is enabled to indicate that an order form can now be printed.
\snippet richtext/orderform/mainwindow.cpp 14
The bottom portion of the document structure is:
\table
\row
\li block
\row
\li {1, 5} block\li \c{Sincerely,}
\row
\li block
\row
\li block
\row
\li block
\row
\li block \li \c{Donald}
\endtable
The \c createSample() function is used for illustration purposes, to create
a sample order form.
\snippet richtext/orderform/mainwindow.cpp 15
The \c openDialog() function opens a \c DetailsDialog object. If the
details in \c dialog are accepted, the \c createLetter() function is
invoked using the parameters extracted from \c dialog.
\snippet richtext/orderform/mainwindow.cpp 16
In order to print out the order form, a \c printFile() function is
included, as shown below:
\snippet richtext/orderform/mainwindow.cpp 17
This function also allows the user to print a selected area with
QTextCursor::hasSelection(), instead of printing the entire document.
\section1 \c main() Function
The \c main() function instantiates \c MainWindow and sets its size to
640x480 pixels before invoking the \c show() function and
\c createSample() function.
\snippet richtext/orderform/main.cpp 0
*/

View File

@ -0,0 +1,392 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/painterpaths
\title Painter Paths Example
\ingroup examples-painting
\brief The Painter Paths example shows how painter paths can be
used to beuild complex shapes for rendering.
\brief The Painter Paths example shows how painter paths can be used to
build complex shapes for rendering.
\image painterpaths-example.png
The QPainterPath class provides a container for painting
operations, enabling graphical shapes to be constructed and
reused.
A painter path is an object composed of a number of graphical
building blocks (such as rectangles, ellipses, lines, and curves),
and can be used for filling, outlining, and clipping. The main
advantage of painter paths over normal drawing operations is that
complex shapes only need to be created once, but they can be drawn
many times using only calls to QPainter::drawPath().
The example consists of two classes:
\list
\li The \c RenderArea class which is a custom widget displaying
a single painter path.
\li The \c Window class which is the applications main window
displaying several \c RenderArea widgets, and allowing the user
to manipulate the painter paths' filling, pen, color
and rotation angle.
\endlist
First we will review the \c Window class, then we will take a look
at the \c RenderArea class.
\section1 Window Class Definition
The \c Window class inherits QWidget, and is the applications main
window displaying several \c RenderArea widgets, and allowing the
user to manipulate the painter paths' filling, pen, color and
rotation angle.
\snippet painting/painterpaths/window.h 0
We declare three private slots to respond to user input regarding
filling and color: \c fillRuleChanged(), \c fillGradientChanged()
and \c penColorChanged().
When the user changes the pen width and the rotation angle, the
new value is passed directly on to the \c RenderArea widgets using
the QSpinBox::valueChanged() signal. The reason why we must
implement slots to update the filling and color, is that QComboBox
doesn't provide a similar signal passing the new value as
argument; so we need to retrieve the new value, or values, before
we can update the \c RenderArea widgets.
\snippet painting/painterpaths/window.h 1
We also declare a couple of private convenience functions: \c
populateWithColors() populates a given QComboBox with items
corresponding to the color names Qt knows about, and \c
currentItemData() returns the current item for a given QComboBox.
\snippet painting/painterpaths/window.h 2
Then we declare the various components of the main window
widget. We also declare a convenience constant specifying the
number of \c RenderArea widgets.
\section1 Window Class Implementation
In the \c Window constructor, we define the various painter paths
and create corresponding \c RenderArea widgets which will render
the graphical shapes:
\snippet painting/painterpaths/window.cpp 1
We construct a rectangle with sharp corners using the
QPainterPath::moveTo() and QPainterPath::lineTo()
functions.
QPainterPath::moveTo() moves the current point to the point passed
as argument. A painter path is an object composed of a number of
graphical building blocks, i.e. subpaths. Moving the current point
will also start a new subpath (implicitly closing the previously
current path when the new one is started). The
QPainterPath::lineTo() function adds a straight line from the
current point to the given end point. After the line is drawn, the
current point is updated to be at the end point of the line.
We first move the current point starting a new subpath, and we
draw three of the rectangle's sides. Then we call the
QPainterPath::closeSubpath() function which draws a line to the
beginning of the current subpath. A new subpath is automatically
begun when the current subpath is closed. The current point of the
new path is (0, 0). We could also have called
QPainterPath::lineTo() to draw the last line as well, and then
explicitly start a new subpath using the QPainterPath::moveTo()
function.
QPainterPath also provide the QPainterPath::addRect() convenience
function, which adds a given rectangle to the path as a closed
subpath. The rectangle is added as a clockwise set of lines. The
painter path's current position after the rect has been added is
at the top-left corner of the rectangle.
\snippet painting/painterpaths/window.cpp 2
Then we construct a rectangle with rounded corners. As before, we
use the QPainterPath::moveTo() and QPainterPath::lineTo()
functions to draw the rectangle's sides. To create the rounded
corners we use the QPainterPath::arcTo() function.
QPainterPath::arcTo() creates an arc that occupies the given
rectangle (specified by a QRect or the rectangle's coordinates),
beginning at the given start angle and extending the given degrees
counter-clockwise. Angles are specified in degrees. Clockwise arcs
can be specified using negative angles. The function connects the
current point to the starting point of the arc if they are not
already connected.
\snippet painting/painterpaths/window.cpp 3
We also use the QPainterPath::arcTo() function to construct the
ellipse path. First we move the current point starting a new
path. Then we call QPainterPath::arcTo() with starting angle 0.0
and 360.0 degrees as the last argument, creating an ellipse.
Again, QPainterPath provides a convenience function (
QPainterPath::addEllipse()) which creates an ellipse within a
given bounding rectangle and adds it to the painter path. If the
current subpath is closed, a new subpath is started. The ellipse
is composed of a clockwise curve, starting and finishing at zero
degrees (the 3 o'clock position).
\snippet painting/painterpaths/window.cpp 4
When constructing the pie chart path we continue to use a
combination of the mentioned functions: First we move the current
point, starting a new subpath. Then we create a line from the
center of the chart to the arc, and the arc itself. When we close
the subpath, we implicitly construct the last line back to the
center of the chart.
\snippet painting/painterpaths/window.cpp 5
Constructing a polygon is equivalent to constructing a rectangle.
QPainterPath also provide the QPainterPath::addPolygon()
convenience function which adds the given polygon to the path as a
new subpath. Current position after the polygon has been added is
the last point in polygon.
\snippet painting/painterpaths/window.cpp 6
Then we create a path consisting of a group of subpaths: First we
move the current point, and create a circle using the
QPainterPath::arcTo() function with starting angle 0.0, and 360
degrees as the last argument, as we did when we created the
ellipse path. Then we move the current point again, starting a
new subpath, and construct three sides of a square using the
QPainterPath::lineTo() function.
Now, when we call the QPainterPath::closeSubpath() function the
last side is created. Remember that the
QPainterPath::closeSubpath() function draws a line to the
beginning of the \e current subpath, i.e the square.
QPainterPath provide a convenience function,
QPainterPath::addPath() which adds a given path to the path that
calls the function.
\snippet painting/painterpaths/window.cpp 7
When creating the text path, we first create the font. Then we set
the font's style strategy which tells the font matching algorithm
what type of fonts should be used to find an appropriate default
family. QFont::ForceOutline forces the use of outline fonts.
To construct the text, we use the QPainterPath::addText() function
which adds the given text to the path as a set of closed subpaths
created from the supplied font. The subpaths are positioned so
that the left end of the text's baseline lies at the specified
point.
\snippet painting/painterpaths/window.cpp 8
To create the Bezier path, we use the QPainterPath::cubicTo()
function which adds a Bezier curve between the current point and
the given end point with the given control point. After the curve
is added, the current point is updated to be at the end point of
the curve.
In this case we omit to close the subpath so that we only have a
simple curve. But there is still a logical line from the curve's
endpoint back to the beginning of the subpath; it becomes visible
when filling the path as can be seen in the applications main
window.
\snippet painting/painterpaths/window.cpp 9
The final path that we construct shows that you can use
QPainterPath to construct rather complex shapes using only the
previous mentioned QPainterPath::moveTo(), QPainterPath::lineTo()
and QPainterPath::closeSubpath() functions.
\snippet painting/painterpaths/window.cpp 10
Now that we have created all the painter paths that we need, we
create a corresponding \c RenderArea widget for each. In the end,
we make sure that the number of render areas is correct using the
Q_ASSERT() macro.
\snippet painting/painterpaths/window.cpp 11
Then we create the widgets associated with the painter paths' fill
rule.
There are two available fill rules in Qt: The Qt::OddEvenFill rule
determine whether a point is inside the shape by drawing a
horizontal line from the point to a location outside the shape,
and count the number of intersections. If the number of
intersections is an odd number, the point is inside the
shape. This rule is the default.
The Qt::WindingFill rule determine whether a point is inside the
shape by drawing a horizontal line from the point to a location
outside the shape. Then it determines whether the direction of the
line at each intersection point is up or down. The winding number
is determined by summing the direction of each intersection. If
the number is non zero, the point is inside the shape.
The Qt::WindingFill rule can in most cases be considered as the
intersection of closed shapes.
\snippet painting/painterpaths/window.cpp 12
We also create the other widgets associated with the filling, the
pen and the rotation angle.
\snippet painting/painterpaths/window.cpp 16
We connect the comboboxes \l {QComboBox::activated()}{activated()}
signals to the associated slots in the \c Window class, while we
connect the spin boxes \l
{QSpinBox::valueChanged()}{valueChanged()} signal directly to the
\c RenderArea widget's respective slots.
\snippet painting/painterpaths/window.cpp 17
We add the \c RenderArea widgets to a separate layout which we
then add to the main layout along with the rest of the widgets.
\snippet painting/painterpaths/window.cpp 18
Finally, we initialize the \c RenderArea widgets by calling the \c
fillRuleChanged(), \c fillGradientChanged() and \c
penColorChanged() slots, and we set the initial pen width and
window title.
\snippet painting/painterpaths/window.cpp 19
\codeline
\snippet painting/painterpaths/window.cpp 20
\codeline
\snippet painting/painterpaths/window.cpp 21
The private slots are implemented to retrieve the new value, or
values, from the associated comboboxes and update the RenderArea
widgets.
First we determine the new value, or values, using the private \c
currentItemData() function and the qvariant_cast() template
function. Then we call the associated slot for each of the \c
RenderArea widgets to update the painter paths.
\snippet painting/painterpaths/window.cpp 22
The \c populateWithColors() function populates the given combobox
with items corresponding to the color names Qt knows about
provided by the static QColor::colorNames() function.
\snippet painting/painterpaths/window.cpp 23
The \c currentItemData() function simply return the current item
of the given combobox.
\section1 RenderArea Class Definition
The \c RenderArea class inherits QWidget, and is a custom widget
displaying a single painter path.
\snippet painting/painterpaths/renderarea.h 0
We declare several public slots updating the \c RenderArea
widget's associated painter path. In addition we reimplement the
QWidget::minimumSizeHint() and QWidget::sizeHint() functions to
give the \c RenderArea widget a reasonable size within our
application, and we reimplement the QWidget::paintEvent() event
handler to draw its painter path.
\snippet painting/painterpaths/renderarea.h 1
Each instance of the \c RenderArea class has a QPainterPath, a
couple of fill colors, a pen width, a pen color and a rotation
angle.
\section1 RenderArea Class Implementation
The constructor takes a QPainterPath as argument (in addition to
the optional QWidget parent):
\snippet painting/painterpaths/renderarea.cpp 0
In the constructor we initialize the \c RenderArea widget with the
QPainterPath parameter as well as initializing the pen width and
rotation angle. We also set the widgets \l
{QWidget::backgroundRole()}{background role}; QPalette::Base is
typically white.
\snippet painting/painterpaths/renderarea.cpp 1
\codeline
\snippet painting/painterpaths/renderarea.cpp 2
Then we reimplement the QWidget::minimumSizeHint() and
QWidget::sizeHint() functions to give the \c RenderArea widget a
reasonable size within our application.
\snippet painting/painterpaths/renderarea.cpp 3
\codeline
\snippet painting/painterpaths/renderarea.cpp 4
\codeline
\snippet painting/painterpaths/renderarea.cpp 5
\codeline
\snippet painting/painterpaths/renderarea.cpp 6
\codeline
\snippet painting/painterpaths/renderarea.cpp 7
The various public slots updates the \c RenderArea widget's
painter path by setting the associated property and make a call to
the QWidget::update() function, forcing a repaint of the widget
with the new rendering preferences.
The QWidget::update() slot does not cause an immediate repaint;
instead it schedules a paint event for processing when Qt returns
to the main event loop.
\snippet painting/painterpaths/renderarea.cpp 8
A paint event is a request to repaint all or parts of the
widget. The paintEvent() function is an event handler that can be
reimplemented to receive the widget's paint events. We reimplement
the event handler to render the \c RenderArea widget's painter
path.
First, we create a QPainter for the \c RenderArea instance, and
set the painter's render hints. The QPainter::RenderHints are used
to specify flags to QPainter that may, or may not, be respected by
any given engine. QPainter::Antialiasing indicates that the engine
should anti-alias the edges of primitives if possible, i.e. put
additional pixels around the original ones to smooth the edges.
\snippet painting/painterpaths/renderarea.cpp 9
Then we scale the QPainter's coordinate system to ensure that the
painter path is rendered in the right size, i.e that it grows with
the \c RenderArea widget when the application is resized. When we
constructed the various painter paths, they were all rnedered
within a square with a 100 pixel width which is equivalent to \c
RenderArea::sizeHint(). The QPainter::scale() function scales the
coordinate system by the \c RenderArea widget's \e current width
and height divided by 100.
Now, when we are sure that the painter path has the right size, we
can translate the coordinate system to make the painter path
rotate around the \c RenderArea widget's center. After we have
performed the rotation, we must remember to translate the
coordinate system back again.
\snippet painting/painterpaths/renderarea.cpp 10
Then we set the QPainter's pen with the instance's rendering
preferences. We create a QLinearGradient and set its colors
corresponding to the \c RenderArea widget's fill colors. Finally,
we set the QPainter's brush (the gradient is automatically
converted into a QBrush), and draw the \c RenderArea widget's
painter path using the QPainter::drawPath() function.
*/

View File

@ -0,0 +1,26 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/pathstroke
\title Path Stroking
\ingroup examples-painting
\brief The Path Stroking example shows various types of pens that
can be used with QPainter.
\brief In this example we show some of the various types of pens that can be
used in Qt.
\image pathstroke-demo.png
Qt defines cap styles for how the end points are treated and join
styles for how path segments are joined together. A standard set of
predefined dash patterns are also included that can be used with
QPen.
In addition to the predefined patterns available in
QPen we also demonstrate direct use of the
QPainterPathStroker class which can be used to define
custom dash patterns. You can see this by enabling the
\e{Custom Pattern} option.
*/

View File

@ -0,0 +1,231 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/pixelator
\title Pixelator Example
\ingroup examples-itemviews
\brief The Pixelator example shows how delegates can be used to customize the way that
items are rendered in standard item views.
\image pixelator-example.png
By default, QTreeView, QTableView, and QListView use a standard item delegate
to display and edit a set of common data types that are sufficient for many
applications. However, an application may need to represent items of data in a
particular way, or provide support for rendering more specialized data types,
and this often requires the use of a custom delegate.
In this example, we show how to use custom delegates to modify the appearance
of standard views. To do this, we implement the following components:
\list
\li A model which represents each pixel in an image as an item of data, where each
item contains a value for the brightness of the corresponding pixel.
\li A custom delegate that uses the information supplied by the model to represent
each pixel as a black circle on a white background, where the radius of the
circle corresponds to the darkness of the pixel.
\endlist
This example may be useful for developers who want to implement their own table
models or custom delegates. The process of creating custom delegates for editing
item data is covered in the \l{Spin Box Delegate Example}{Spin Box Delegate}
example.
\section1 ImageModel Class Definition
The \c ImageModel class is defined as follows:
\snippet itemviews/pixelator/imagemodel.h 0
Since we only require a simple, read-only table model, we only need to implement
functions to indicate the dimensions of the image and supply data to other
components.
\section1 ImageModel Class Implementation
The constructor is trivial:
\snippet itemviews/pixelator/imagemodel.cpp 0
The \c setImage() function sets the image that will be used by the model:
\snippet itemviews/pixelator/imagemodel.cpp 1
The QAbstractItemModel::reset() call tells the view(s) that the model
has changed.
The \c rowCount() and \c columnCount() functions return the height and width of
the image respectively:
\snippet itemviews/pixelator/imagemodel.cpp 2
\snippet itemviews/pixelator/imagemodel.cpp 3
Since the image is a simple two-dimensional structure, the \c parent arguments
to these functions are unused. They both simply return the relevant size from
the underlying image object.
The \c data() function returns data for the item that corresponds to a given
model index in a format that is suitable for a particular role:
\snippet itemviews/pixelator/imagemodel.cpp 4
In this implementation, we only check that the model index is valid, and that
the role requested is the \l{Qt::ItemDataRole}{DisplayRole}. If so, the function
returns the grayscale value of the relevant pixel in the image; otherwise, a null
model index is returned.
This model can be used with QTableView to display the integer brightness values
for the pixels in the image. However, we will implement a custom delegate to
display this information in a more artistic way.
The \c headerData() function is also reimplemented:
\snippet itemviews/pixelator/imagemodel.cpp 5
We return (1, 1) as the size hint for a header item. If we
didn't, the headers would default to a larger size, preventing
us from displaying really small items (which can be specified
using the \uicontrol{Pixel size} combobox).
\section1 PixelDelegate Class Definition
The \c PixelDelegate class is defined as follows:
\snippet itemviews/pixelator/pixeldelegate.h 0
This class provides only basic features for a delegate so, unlike the
\l{Spin Box Delegate Example}{Spin Box Delegate} example, we subclass
QAbstractItemDelegate instead of QItemDelegate.
We only need to reimplement \l{QAbstractItemDelegate::paint()}{paint()} and
\l{QAbstractItemDelegate::sizeHint()}{sizeHint()} in this class.
However, we also provide a delegate-specific \c setPixelSize() function so
that we can change the delegate's behavior via the signals and slots mechanism.
\section1 PixelDelegate Class Implementation
The \c PixelDelegate constructor is used to set up a default value for
the size of each "pixel" that it renders. The base class constructor is
also called to ensure that the delegate is set up with a parent object,
if one is supplied:
\snippet itemviews/pixelator/pixeldelegate.cpp 0
Each item is rendered by the delegate's
\l{QAbstractItemDelegate::paint()}{paint()} function. The view calls this
function with a ready-to-use QPainter object, style information that the
delegate should use to correctly draw the item, and an index to the item in
the model:
\snippet itemviews/pixelator/pixeldelegate.cpp 1
The first task the delegate has to perform is to draw the item's background
correctly. Usually, selected items appear differently to non-selected items,
so we begin by testing the state passed in the style option and filling the
background if necessary.
The radius of each circle is calculated in the following lines of code:
\snippet itemviews/pixelator/pixeldelegate.cpp 3
\snippet itemviews/pixelator/pixeldelegate.cpp 4
First, the largest possible radius of the circle is determined by taking the
smallest dimension of the style option's \c rect attribute.
Using the model index supplied, we obtain a value for the brightness of the
relevant pixel in the image. The radius of the circle is calculated by
scaling the brightness to fit within the item and subtracting it from the
largest possible radius.
\snippet itemviews/pixelator/pixeldelegate.cpp 5
\snippet itemviews/pixelator/pixeldelegate.cpp 6
\snippet itemviews/pixelator/pixeldelegate.cpp 7
We save the painter's state, turn on antialiasing (to obtain smoother
curves), and turn off the pen.
\snippet itemviews/pixelator/pixeldelegate.cpp 8
\snippet itemviews/pixelator/pixeldelegate.cpp 9
The foreground of the item (the circle representing a pixel) must be
rendered using an appropriate brush. For unselected items, we will use a
solid black brush; selected items are drawn using a predefined brush from
the style option's palette.
\snippet itemviews/pixelator/pixeldelegate.cpp 10
Finally, we paint the circle within the rectangle specified by the style
option and we call \l{QPainter::}{restore()} on the painter.
The \c paint() function does not have to be particularly complicated; it is
only necessary to ensure that the state of the painter when the function
returns is the same as it was when it was called. This usually
means that any transformations applied to the painter must be preceded by
a call to QPainter::save() and followed by a call to QPainter::restore().
The delegate's \l{QAbstractItemDelegate::}{sizeHint()} function
returns a size for the item based on the predefined pixel size, initially set
up in the constructor:
\snippet itemviews/pixelator/pixeldelegate.cpp 11
The delegate's size is updated whenever the pixel size is changed.
We provide a custom slot to do this:
\snippet itemviews/pixelator/pixeldelegate.cpp 12
\section1 Using The Custom Delegate
In this example, we use a main window to display a table of data, using the
custom delegate to render each cell in a particular way. Much of the
\c MainWindow class performs tasks that are not related to item views. Here,
we only quote the parts that are relevant. You can look at the rest of the
implementation by following the links to the code at the top of this
document.
In the constructor, we set up a table view, turn off its grid, and hide its
headers:
\snippet itemviews/pixelator/mainwindow.cpp 0
\dots
\snippet itemviews/pixelator/mainwindow.cpp 1
This enables the items to be drawn without any gaps between them. Removing
the headers also prevents the user from adjusting the sizes of individual
rows and columns.
We also set the minimum section size to 1 on the headers. If we
didn't, the headers would default to a larger size, preventing
us from displaying really small items (which can be specified
using the \uicontrol{Pixel size} combobox).
The custom delegate is constructed with the main window as its parent, so
that it will be deleted correctly later, and we set it on the table view.
\snippet itemviews/pixelator/mainwindow.cpp 2
Each item in the table view will be rendered by the \c PixelDelegate
instance.
We construct a spin box to allow the user to change the size of each "pixel"
drawn by the delegate:
\snippet itemviews/pixelator/mainwindow.cpp 3
This spin box is connected to the custom slot we implemented in the
\c PixelDelegate class. This ensures that the delegate always draws each
pixel at the currently specified size:
\snippet itemviews/pixelator/mainwindow.cpp 4
\dots
\snippet itemviews/pixelator/mainwindow.cpp 5
We also connect the spin box to a slot in the \c MainWindow class. This
forces the view to take into account the new size hints for each item;
these are provided by the delegate in its \c sizeHint() function.
\snippet itemviews/pixelator/mainwindow.cpp 6
We explicitly resize the columns and rows to match the
\uicontrol{Pixel size} combobox.
*/

View File

@ -0,0 +1,527 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/plugandpaint/app
\title Plug & Paint Example
\ingroup examples-widgets-tools
\brief Demonstrates how to extend Qt applications using plugins.
\image plugandpaint.png Screenshot of the Plug & Paint example
A plugin is a dynamic library that can be loaded at run-time to
extend an application. Qt makes it possible to create custom
plugins and to load them using QPluginLoader. To ensure that
plugins don't get lost, it is also possible to link them
statically to the executable. The Plug & Paint example uses
plugins to support custom brushes, shapes, and image filters. A
single plugin can provide multiple brushes, shapes, and/or
filters.
If you want to learn how to make your own application extensible
through plugins, we recommend that you start by reading this
overview, which explains how to make an application use plugins.
Afterwards, you can read the
\l{tools/plugandpaint/plugins/basictools}{Basic Tools} and
\l{tools/plugandpaint/plugins/extrafilters}{Extra Filters}
overviews, which show how to implement static and dynamic
plugins, respectively.
Plug & Paint consists of the following classes:
\list
\li \c MainWindow is a QMainWindow subclass that provides the menu
system and that contains a \c PaintArea as the central widget.
\li \c PaintArea is a QWidget that allows the user to draw using a
brush and to insert shapes.
\li \c PluginDialog is a dialog that shows information about the
plugins detected by the application.
\li \c BrushInterface, \c ShapeInterface, and \c FilterInterface are
abstract base classes that can be implemented by plugins to
provide custom brushes, shapes, and image filters.
\endlist
\section1 The Plugin Interfaces
We will start by reviewing the interfaces defined in \c
interfaces.h. These interfaces are used by the Plug & Paint
application to access extra functionality. They are implemented
in the plugins.
\snippet tools/plugandpaint/app/interfaces.h 0
The \c BrushInterface class declares four pure virtual functions.
The first pure virtual function, \c brushes(), returns a list of
strings that identify the brushes provided by the plugin. By
returning a QStringList instead of a QString, we make it possible
for a single plugin to provide multiple brushes. The other
functions have a \c brush parameter to identify which brush
(among those returned by \c brushes()) is used.
\c mousePress(), \c mouseMove(), and \c mouseRelease() take a
QPainter and one or two \l{QPoint}s, and return a QRect
identifying which portion of the image was altered by the brush.
The class also has a virtual destructor. Interface classes
usually don't need such a destructor (because it would make
little sense to \c delete the object that implements the
interface through a pointer to the interface), but some compilers
emit a warning for classes that declare virtual functions but no
virtual destructor. We provide the destructor to keep these
compilers happy.
\snippet tools/plugandpaint/app/interfaces.h 1
The \c ShapeInterface class declares a \c shapes() function that
works the same as \c{BrushInterface}'s \c brushes() function, and
a \c generateShape() function that has a \c shape parameter.
Shapes are represented by a QPainterPath, a data type that can
represent arbitrary 2D shapes or combinations of shapes. The \c
parent parameter can be used by the plugin to pop up a dialog
asking the user to specify more information.
\snippet tools/plugandpaint/app/interfaces.h 2
The \c FilterInterface class declares a \c filters() function
that returns a list of filter names, and a \c filterImage()
function that applies a filter to an image.
\snippet tools/plugandpaint/app/interfaces.h 4
To make it possible to query at run-time whether a plugin
implements a given interface, we must use the \c
Q_DECLARE_INTERFACE() macro. The first argument is the name of
the interface. The second argument is a string identifying the
interface in a unique way. By convention, we use a "Java package
name" syntax to identify interfaces. If we later change the
interfaces, we must use a different string to identify the new
interface; otherwise, the application might crash. It is therefore
a good idea to include a version number in the string, as we did
above.
The \l{tools/plugandpaint/plugins/basictools}{Basic Tools} plugin
and the \l{tools/plugandpaint/plugins/extrafilters}{Extra Filters}
plugin shows how to derive from \c BrushInterface, \c
ShapeInterface, and \c FilterInterface.
A note on naming: It might have been tempting to give the \c
brushes(), \c shapes(), and \c filters() functions a more generic
name, such as \c keys() or \c features(). However, that would
have made multiple inheritance impractical. When creating
interfaces, we should always try to give unique names to the pure
virtual functions.
\section1 The MainWindow Class
The \c MainWindow class is a standard QMainWindow subclass, as
found in many of the other examples (e.g.,
\l{mainwindows/application}{Application}). Here, we'll
concentrate on the parts of the code that are related to plugins.
\snippet tools/plugandpaint/app/mainwindow.cpp 4
The \c loadPlugins() function is called from the \c MainWindow
constructor to detect plugins and update the \uicontrol{Brush},
\uicontrol{Shapes}, and \uicontrol{Filters} menus. We start by handling static
plugins (available through QPluginLoader::staticInstances())
To the application that uses the plugin, a Qt plugin is simply a
QObject. That QObject implements plugin interfaces using multiple
inheritance.
\snippet tools/plugandpaint/app/mainwindow.cpp 5
The next step is to load dynamic plugins. We initialize the \c
pluginsDir member variable to refer to the \c plugins
subdirectory of the Plug & Paint example. On Unix, this is just a
matter of initializing the QDir variable with
QApplication::applicationDirPath(), the path of the executable
file, and to do a \l{QDir::cd()}{cd()}. On Windows and \macos,
this file is usually located in a subdirectory, so we need to
take this into account.
\snippet tools/plugandpaint/app/mainwindow.cpp 6
\snippet tools/plugandpaint/app/mainwindow.cpp 7
\snippet tools/plugandpaint/app/mainwindow.cpp 8
We use QDir::entryList() to get a list of all files in that
directory. Then we iterate over the result using a range-based for loop
and try to load the plugin using QPluginLoader.
The QObject provided by the plugin is accessible through
QPluginLoader::instance(). If the dynamic library isn't a Qt
plugin, or if it was compiled against an incompatible version of
the Qt library, QPluginLoader::instance() returns a null pointer.
If QPluginLoader::instance() is non-null, we add it to the menus.
\snippet tools/plugandpaint/app/mainwindow.cpp 9
At the end, we enable or disable the \uicontrol{Brush}, \uicontrol{Shapes},
and \uicontrol{Filters} menus based on whether they contain any items.
\snippet tools/plugandpaint/app/mainwindow.cpp 10
For each plugin (static or dynamic), we check which interfaces it
implements using \l qobject_cast(). First, we try to cast the
plugin instance to a \c BrushInterface; if it works, we call the
private function \c addToMenu() with the list of brushes returned
by \c brushes(). Then we do the same with the \c ShapeInterface
and the \c FilterInterface.
\snippet tools/plugandpaint/app/mainwindow.cpp 3
The \c aboutPlugins() slot is called on startup and can be
invoked at any time through the \uicontrol{About Plugins} action. It
pops up a \c PluginDialog, providing information about the loaded
plugins.
\image plugandpaint-plugindialog.png Screenshot of the Plugin dialog
The \c addToMenu() function is called from \c loadPlugin() to
create \l{QAction}s for custom brushes, shapes, or filters and
add them to the relevant menu. The QAction is created with the
plugin from which it comes from as the parent; this makes it
convenient to get access to the plugin later.
\snippet tools/plugandpaint/app/mainwindow.cpp 0
The \c changeBrush() slot is invoked when the user chooses one of
the brushes from the \uicontrol{Brush} menu. We start by finding out
which action invoked the slot using QObject::sender(). Then we
get the \c BrushInterface out of the plugin (which we
conveniently passed as the QAction's parent) and we call \c
PaintArea::setBrush() with the \c BrushInterface and the string
identifying the brush. Next time the user draws on the paint
area, \c PaintArea will use this brush.
\snippet tools/plugandpaint/app/mainwindow.cpp 1
The \c insertShape() is invoked when the use chooses one of the
shapes from the \uicontrol{Shapes} menu. We retrieve the QAction that
invoked the slot, then the \c ShapeInterface associated with that
QAction, and finally we call \c ShapeInterface::generateShape()
to obtain a QPainterPath.
\snippet tools/plugandpaint/app/mainwindow.cpp 2
The \c applyFilter() slot is similar: We retrieve the QAction
that invoked the slot, then the \c FilterInterface associated to
that QAction, and finally we call \c
FilterInterface::filterImage() to apply the filter onto the
current image.
\section1 The PaintArea Class
The \c PaintArea class contains some code that deals with \c
BrushInterface, so we'll review it briefly.
\snippet tools/plugandpaint/app/paintarea.cpp 0
In \c setBrush(), we simply store the \c BrushInterface and the
brush that are given to us by \c MainWindow.
\snippet tools/plugandpaint/app/paintarea.cpp 1
In the \l{QWidget::mouseMoveEvent()}{mouse move event handler},
we call the \c BrushInterface::mouseMove() function on the
current \c BrushInterface, with the current brush. The mouse
press and mouse release handlers are very similar.
\section1 The PluginDialog Class
The \c PluginDialog class provides information about the loaded
plugins to the user. Its constructor takes a path to the plugins
and a list of plugin file names. It calls \c findPlugins()
to fill the QTreeWdiget with information about the plugins:
\snippet tools/plugandpaint/app/plugindialog.cpp 0
The \c findPlugins() is very similar to \c
MainWindow::loadPlugins(). It uses QPluginLoader to access the
static and dynamic plugins. Its helper function \c
populateTreeWidget() uses \l qobject_cast() to find out which
interfaces are implemented by the plugins:
\snippet tools/plugandpaint/app/plugindialog.cpp 1
\section1 Importing Static Plugins
The \l{tools/plugandpaint/plugins/basictools}{Basic Tools} plugin
is built as a static plugin, to ensure that it is always
available to the application. This requires using the
Q_IMPORT_PLUGIN() macro somewhere in the application (in a \c
.cpp file) and specifying the plugin in the \c .pro file.
For Plug & Paint, we have chosen to put Q_IMPORT_PLUGIN() in \c
main.cpp:
\snippet tools/plugandpaint/app/main.cpp 0
The argument to Q_IMPORT_PLUGIN() is the plugin name, which corresponds
with the name of the class that declares metadata for the plugin with
Q_PLUGIN_METADATA().
In the \c .pro file, we need to specify the static library.
Here's the project file for building Plug & Paint:
\snippet tools/plugandpaint/app/app.pro 0
The \c LIBS line variable specifies the library \c pnp_basictools
located in the \c ../plugandpaint/plugins/basictools directory.
(Although the \c LIBS syntax has a distinct Unix flavor, \c qmake
supports it on all platforms.)
The \c CONFIG() code at the end is necessary for this example
because the example is part of the Qt distribution and Qt can be
configured to be built simultaneously in debug and in release
modes. You don't need to for your own plugin applications.
This completes our review of the Plug & Paint application. At
this point, you might want to take a look at the
\l{tools/plugandpaint/plugins/basictools}{Basic Tools} example
plugin.
*/
/*!
\example tools/plugandpaint/plugins/basictools
\title Plug & Paint Basic Tools Example
\brief A plugin providing the basic tools for painting functionality.
\image plugandpaint.png Screenshot of the Plug & Paint example
The Basic Tools example is a static plugin for the
\l{tools/plugandpaint/app}{Plug & Paint} example. It provides a set
of basic brushes, shapes, and filters. Through the Basic Tools
example, we will review the four steps involved in writing a Qt
plugin:
\list 1
\li Declare a plugin class.
\li Implement the interfaces provided by the plugin.
\li Export the plugin using the Q_PLUGIN_METADATA() macro.
\li Build the plugin using an adequate \c .pro file.
\endlist
\section1 Declaration of the Plugin Class
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.h 0
We start by including \c interfaces.h, which defines the plugin
interfaces for the \l{tools/plugandpaint/app}{Plug & Paint}
application. For the \c #include to work, we need to add an \c
INCLUDEPATH entry to the \c .pro file with the path to the
header file.
The \c BasicToolsPlugin class is a QObject subclass that
implements the \c BrushInterface, the \c ShapeInterface, and the
\c FilterInterface. This is done through multiple inheritance.
The \c Q_INTERFACES() macro is necessary to tell \l{moc}, Qt's
meta-object compiler, that the base classes are plugin
interfaces. Without the \c Q_INTERFACES() macro, we couldn't use
\l qobject_cast() in the \l{tools/plugandpaint/app}{Plug & Paint}
application to detect interfaces.
For an explanation for the \c Q_PLUGIN_METADATA() macro see
\l {Exporting the Plugin}.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.h 2
In the \c public section of the class, we declare all the
functions from the three interfaces.
\section1 Implementation of the Brush Interface
Let's now review the implementation of the \c BasicToolsPlugin
member functions inherited from \c BrushInterface.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 0
The \c brushes() function returns a list of brushes provided by
this plugin. We provide three brushes: \uicontrol{Pencil}, \uicontrol{Air
Brush}, and \uicontrol{Random Letters}.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 1
On a mouse press event, we just call \c mouseMove() to draw the
spot where the event occurred.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 2
In \c mouseMove(), we start by saving the state of the QPainter
and we compute a few variables that we'll need later.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 3
Then comes the brush-dependent part of the code:
\list
\li If the brush is \uicontrol{Pencil}, we just call
QPainter::drawLine() with the current QPen.
\li If the brush is \uicontrol{Air Brush}, we start by setting the
painter's QBrush to Qt::Dense6Pattern to obtain a dotted
pattern. Then we draw a circle filled with that QBrush several
times, resulting in a thick line.
\li If the brush is \uicontrol{Random Letters}, we draw a random letter
at the new cursor position. Most of the code is for setting
the font to be bold and larger than the default font and for
computing an appropriate bounding rect.
\endlist
At the end, we restore the painter state to what it was upon
entering the function and we return the bounding rectangle.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 4
When the user releases the mouse, we do nothing and return an
empty QRect.
\section1 Implementation of the Shape Interface
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 5
The plugin provides three shapes: \uicontrol{Circle}, \uicontrol{Star}, and
\uicontrol{Text...}. The three dots after \uicontrol{Text} are there because
the shape pops up a dialog asking for more information. We know
that the shape names will end up in a menu, so we include the
three dots in the shape name.
A cleaner but more complicated design would have been to
distinguish between the internal shape name and the name used in
the user interface.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 6
The \c generateShape() creates a QPainterPath for the specified
shape. If the shape is \uicontrol{Text}, we pop up a QInputDialog to
let the user enter some text.
\section1 Implementation of the Filter Interface
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 7
The plugin provides three filters: \uicontrol{Invert Pixels}, \uicontrol{Swap
RGB}, and \uicontrol{Grayscale}.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.cpp 8
The \c filterImage() function takes a filter name and a QImage as
parameters and returns an altered QImage. The first thing we do
is to convert the image to a 32-bit RGB format, to ensure that
the algorithms will work as expected. For example,
QImage::invertPixels(), which is used to implement the
\uicontrol{Invert Pixels} filter, gives counterintuitive results for
8-bit images, because they invert the indices into the color
table instead of inverting the color table's entries.
\section1 Exporting the Plugin
To finally export your plugin you just have to add the
\c Q_PLUGIN_METADATA() macro right next to the \c Q_OBJECT() macro
into the header file of the plugin.
It must contain the plugins IID and optionally a filename pointing
to a json file containing the metadata for the plugin.
\snippet tools/plugandpaint/plugins/basictools/basictoolsplugin.h 4
Within this example the json file does not need to export any metadata,
so it just contains an empty json object.
\code
{}
\endcode
\section1 The .pro File
Here's the project file for building the Basic Tools plugin:
\snippet tools/plugandpaint/plugins/basictools/basictools.pro 0
The \c .pro file differs from typical \c .pro files in many
respects. First, it starts with a \c TEMPLATE entry specifying \c
lib. (The default template is \c app.) It also adds \c plugin to
the \c CONFIG variable. This is necessary on some platforms to
avoid generating symbolic links with version numbers in the file
name, which is appropriate for most dynamic libraries but not for
plugins.
To make the plugin a static plugin, all that is required is to
specify \c static in addition to \c plugin. The
\l{tools/plugandpaint/plugins/extrafilters}{Extra Filters} plugin,
which is compiled as a dynamic plugin, doesn't specify \c static
in its \c .pro file.
The \c INCLUDEPATH variable sets the search paths for global
headers (i.e., header files included using \c{#include <...>}).
We add \c ../../app to the list, so that we can include
\c <interfaces.h>.
The \c TARGET variable specifies which name we want to give the
target library. We use \c pnp_ as the prefix to show that the
plugin is designed to work with Plug & Paint. On Unix, \c lib is
also prepended to that name. On all platforms, a
platform-specific suffix is appended (e.g., \c .dll on Windows,
\c .a on Linux).
The \c CONFIG() code at the end is necessary for this example
because the example is part of the Qt distribution and Qt can be
configured to be built simultaneously in debug and in release
modes. You don't need to for your own plugins.
*/
/*!
\example tools/plugandpaint/plugins/extrafilters
\title Plug & Paint Extra Filters Example
\brief A plugin providing the extra filters.
\image plugandpaint.png Screenshot of the Plug & Paint example
The Extra Filters example is a plugin for the
\l{tools/plugandpaint/app}{Plug & Paint} example. It provides a set
of filters in addition to those provided by the
\l{tools/plugandpaint/plugins/basictools}{Basic Tools} plugin.
Since the approach is identical to
\l{tools/plugandpaint/plugins/basictools}{Basic Tools}, we won't
review the code here. The only part of interest is the
\c .pro file, since Extra Filters is a dynamic plugin
(\l{tools/plugandpaint/plugins/basictools}{Basic Tools} is
linked statically into the Plug & Paint executable).
Here's the project file for building the Extra Filters plugin:
\snippet tools/plugandpaint/plugins/extrafilters/extrafilters.pro 0
The \c .pro file differs from typical \c .pro files in many
respects. First, it starts with a \c TEMPLATE entry specifying \c
lib. (The default template is \c app.) It also adds \c plugin to
the \c CONFIG variable. This is necessary on some platforms to
avoid generating symbolic links with version numbers in the file
name, which is appropriate for most dynamic libraries but not for
plugins.
The \c INCLUDEPATH variable sets the search paths for global
headers (i.e., header files included using \c{#include <...>}).
We add \c ../../app to the list, so that we can include
\c <interfaces.h>.
The \c TARGET variable specifies which name we want to give the
target library. We use \c pnp_ as the prefix to show that the
plugin is designed to work with Plug & Paint. On Unix, \c lib is
also prepended to that name. On all platforms, a
platform-specific suffix is appended (e.g., \c .dll on Windows,
\c .so on Linux).
The \c DESTDIR variable specifies where we want to install the
plugin. We put it in Plug & Paint's \c plugins subdirectory,
since that's where the application looks for dynamic plugins.
The \c CONFIG() code at the end is necessary for this example
because the example is part of the Qt distribution and Qt can be
configured to be built simultaneously in debug and in release
modes. You don't need to for your own plugins.
*/

View File

@ -0,0 +1,21 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/regularexpression
\title QRegularExpression Example
\ingroup examples-widgets-tools
\brief The QRegularExpression example shows how regular expressions in Qt are
applied to text by providing an environment in which new regular expressions can be
created and tested on custom text strings.
The example makes usage of the QRegularExpression class, which has been
introduced in Qt 5.0. QRegularExpression implements Perl-compatible regular
expressions, supporting a number of advanced matching features, such as
case insensitive matching, multiline matching, Unicode properties selectors
and partial/incremental matching.
\image regularexpression-example.png
*/

View File

@ -0,0 +1,202 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example desktop/screenshot
\title Screenshot Example
\ingroup examples-desktop
\brief The Screenshot example shows how to take a screenshot of the
desktop.
\brief The Screenshot example shows how to take a screenshot of the
desktop using QScreen. It also shows how
to use QTimer to provide a single-shot timer, and how to
reimplement the QWidget::resizeEvent() event handler to make sure
that an application resizes smoothly and without data loss.
\image screenshot-example.png
With the application the users can take a screenshot of their
desktop. They are provided with a couple of options:
\list
\li Delaying the screenshot, giving them time to rearrange
their desktop.
\li Hiding the application's window while the screenshot is taken.
\endlist
In addition the application allows the users to save their
screenshot if they want to.
\section1 Screenshot Class Definition
\snippet desktop/screenshot/screenshot.h 0
The \c Screenshot class inherits QWidget and is the application's
main widget. It displays the application options and a preview of
the screenshot.
We reimplement the QWidget::resizeEvent() function to make sure
that the preview of the screenshot scales properly when the user
resizes the application widget. We also need several private slots
to facilitate the options:
\list
\li The \c newScreenshot() slot prepares a new screenshot.
\li The \c saveScreenshot() slot saves the last screenshot.
\li The \c shootScreen() slot takes the screenshot.
\li The \c updateCheckBox() slot enables or disables the
\uicontrol {Hide This Window} option.
\endlist
We also declare the private function \c updateScreenshotLabel() which
is called whenever a new screenshot is taken or when a resize event
changes the size of the screenshot preview label.
In addition we need to store the screenshot's original pixmap. The
reason is that when we display the preview of the screenshot, we
need to scale its pixmap, storing the original we make sure that
no data are lost in that process.
\section1 Screenshot Class Implementation
\snippet desktop/screenshot/screenshot.cpp 0
In the constructor we first create the QLabel displaying the
screenshot preview.
We set the QLabel's size policy to be QSizePolicy::Expanding both
horizontally and vertically. This means that the QLabel's size
hint is a sensible size, but the widget can be shrunk and still be
useful. Also, the widget can make use of extra space, so it should
get as much space as possible. Then we make sure the QLabel is
aligned in the center of the \c Screenshot widget, and set its
minimum size.
Next, we create a group box that will contain all of the options'
widgets. Then we create a QSpinBox and a QLabel for the \uicontrol
{Screenshot Delay} option, and connect the spinbox to the \c
updateCheckBox() slot. Finally, we create a QCheckBox for the \uicontrol
{Hide This Window} option, add all the options' widgets to a
QGridLayout installed on the group box.
We create the applications's buttons and the group box containing
the application's options, and put it all into a main
layout. Finally we take the initial screenshot, and set the initial
delay and the window title, before we resize the widget to a
suitable size depending on the screen geometry.
\snippet desktop/screenshot/screenshot.cpp 1
The \c resizeEvent() function is reimplemented to receive the
resize events dispatched to the widget. The purpose is to scale
the preview screenshot pixmap without deformation of its content,
and also make sure that the application can be resized smoothly.
To achieve the first goal, we scale the screenshot pixmap using
Qt::KeepAspectRatio. We scale the pixmap to a rectangle as large
as possible inside the current size of the screenshot preview
label, preserving the aspect ratio. This means that if the user
resizes the application window in only one direction, the preview
screenshot keeps the same size.
To reach our second goal, we make sure that the preview screenshot
only is repainted (using the private \c updateScreenshotLabel()
function) when it actually changes its size.
\snippet desktop/screenshot/screenshot.cpp 2
The private \c newScreenshot() slot is called when the user
requests a new screenshot; but the slot only prepares a new
screenshot.
First we see if the \uicontrol {Hide This Window} option is checked, if
it is we hide the \c Screenshot widget. Then we disable the \uicontrol
{New Screenshot} button, to make sure the user only can request
one screenshot at a time.
We create a timer using the QTimer class which provides repetitive
and single-shot timers. We set the timer to time out only once,
using the static QTimer::singleShot() function. This function
calls the private \c shootScreen() slot after the time interval
specified by the \uicontrol {Screenshot Delay} option. It is \c
shootScreen() that actually performs the screenshot.
\snippet desktop/screenshot/screenshot.cpp 3
The \c saveScreenshot() slot is called when the user push the \uicontrol
Save button, and it presents a file dialog using the QFileDialog
class.
QFileDialog enables a user to traverse the file system in order to
select one or many files or a directory. The easiest way to create
a QFileDialog is to use the convenience static
functions. Here, we instantiate the dialog on the stack in order
to be able to set up the supported mime types of QImageWriter,
allowing the user to save in a variety of formats.
We define the default file format to be png, and we make the file
dialog's initial path the location of pictures as obtained from
QStandardPaths, defaulting to the path the application is run from.
We run the dialog by invoking QDialog::exec() and return if the
user canceled the dialog. If the dialog has been accepted, we
obtain a file name by calling QFileDialog::selectedFiles().
The file does not have to exist. If the file
name is valid, we use the QPixmap::save() function to save the
screenshot's original pixmap in that file.
\snippet desktop/screenshot/screenshot.cpp 4
The \c shootScreen() slot is called to take the screenshot.
First, we find the instance of QScreen the window is located
by retrieving the QWindow and its QScreen, defaulting
to the primary screen. If no screen can be found, we return.
Although this is unlikely to happen, applications should check
for null pointers since there might be situations in which no
screen is connected.
If the user has chosen to delay the screenshot, we make the application
beep when the screenshot is taken using the static
QApplication::beep() function.
We then take the screenshot using the QScreen::grabWindow()
function. The function grabs the contents of the window passed as
an argument, makes a pixmap out of it and returns that pixmap.
The window id can be obtained with QWidget::winId() or QWindow::winId().
Here, however, we just pass 0 as the window id, indicating that we
want to grab the entire screen.
We update the screenshot preview label using the private \c
updateScreenshotLabel() function. Then we enable the \uicontrol {New
Screenshot} button, and finally we make the \c Screenshot widget
visible if it was hidden during the screenshot.
\snippet desktop/screenshot/screenshot.cpp 6
The \uicontrol {Hide This Window} option is enabled or disabled
depending on the delay of the screenshot. If there is no delay,
the application window cannot be hidden and the option's checkbox
is disabled.
The \c updateCheckBox() slot is called whenever the user changes
the delay using the \uicontrol {Screenshot Delay} option.
\snippet desktop/screenshot/screenshot.cpp 10
The private \c updateScreenshotLabel() function is called whenever
the screenshot changes, or when a resize event changes the size of
the screenshot preview label. It updates the screenshot preview's
label using the QLabel::setPixmap() and QPixmap::scaled()
functions.
QPixmap::scaled() returns a copy of the given pixmap scaled to a
rectangle of the given size according to the given
Qt::AspectRatioMode and Qt::TransformationMode.
We scale the original pixmap to fit the current screenshot label's
size, preserving the aspect ratio and giving the resulting pixmap
smoothed edges.
*/

View File

@ -0,0 +1,393 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/scribble
\title Scribble Example
\ingroup examples-widgets
\brief The Scribble example shows how to reimplement some of QWidget's
event handlers to receive the events generated for the
application's widgets.
We reimplement the mouse event handlers to implement drawing, the
paint event handler to update the application and the resize event
handler to optimize the application's appearance. In addition we
reimplement the close event handler to intercept the close events
before terminating the application.
The example also demonstrates how to use QPainter to draw an image
in real time, as well as to repaint widgets.
\image scribble-example.png Screenshot of the Scribble example
With the Scribble application the users can draw an image. The
\uicontrol File menu gives the users the possibility to open and edit an
existing image file, save an image and exit the application. While
drawing, the \uicontrol Options menu allows the users to choose the
pen color and pen width, as well as clear the screen. In addition
the \uicontrol Help menu provides the users with information about the
Scribble example in particular, and about Qt in general.
The example consists of two classes:
\list
\li \c ScribbleArea is a custom widget that displays a QImage and
allows to the user to draw on it.
\li \c MainWindow provides a menu above the \c ScribbleArea.
\endlist
We will start by reviewing the \c ScribbleArea class. Then we will
review the \c MainWindow class, which uses \c ScribbleArea.
\section1 ScribbleArea Class Definition
\snippet widgets/scribble/scribblearea.h 0
The \c ScribbleArea class inherits from QWidget. We reimplement
the \c mousePressEvent(), \c mouseMoveEvent() and \c
mouseReleaseEvent() functions to implement the drawing. We
reimplement the \c paintEvent() function to update the scribble
area, and the \c resizeEvent() function to ensure that the QImage
on which we draw is at least as large as the widget at any time.
We need several public functions: \c openImage() loads an image
from a file into the scribble area, allowing the user to edit the
image; \c save() writes the currently displayed image to file; \c
clearImage() slot clears the image displayed in the scribble
area. We need the private \c drawLineTo() function to actually do
the drawing, and \c resizeImage() to change the size of a
QImage. The \c print() slot handles printing.
We also need the following private variables:
\list
\li \c modified is \c true if there are unsaved
changes to the image displayed in the scribble area.
\li \c scribbling is \c true while the user is pressing
the left mouse button within the scribble area.
\li \c penWidth and \c penColor hold the currently
set width and color for the pen used in the application.
\li \c image stores the image drawn by the user.
\li \c lastPoint holds the position of the cursor at the last
mouse press or mouse move event.
\endlist
\section1 ScribbleArea Class Implementation
\snippet widgets/scribble/scribblearea.cpp 0
In the constructor, we set the Qt::WA_StaticContents
attribute for the widget, indicating that the widget contents are
rooted to the top-left corner and don't change when the widget is
resized. Qt uses this attribute to optimize paint events on
resizes. This is purely an optimization and should only be used
for widgets whose contents are static and rooted to the top-left
corner.
\snippet widgets/scribble/scribblearea.cpp 1
\snippet widgets/scribble/scribblearea.cpp 2
In the \c openImage() function, we load the given image. Then we
resize the loaded QImage to be at least as large as the widget in
both directions using the private \c resizeImage() function and
we set the \c image member variable to be the loaded image. At
the end, we call QWidget::update() to schedule a repaint.
\snippet widgets/scribble/scribblearea.cpp 3
\snippet widgets/scribble/scribblearea.cpp 4
The \c saveImage() function creates a QImage object that covers
only the visible section of the actual \c image and saves it using
QImage::save(). If the image is successfully saved, we set the
scribble area's \c modified variable to \c false, because there is
no unsaved data.
\snippet widgets/scribble/scribblearea.cpp 5
\snippet widgets/scribble/scribblearea.cpp 6
\codeline
\snippet widgets/scribble/scribblearea.cpp 7
\snippet widgets/scribble/scribblearea.cpp 8
The \c setPenColor() and \c setPenWidth() functions set the
current pen color and width. These values will be used for future
drawing operations.
\snippet widgets/scribble/scribblearea.cpp 9
\snippet widgets/scribble/scribblearea.cpp 10
The public \c clearImage() slot clears the image displayed in the
scribble area. We simply fill the entire image with white, which
corresponds to RGB value (255, 255, 255). As usual when we modify
the image, we set \c modified to \c true and schedule a repaint.
\snippet widgets/scribble/scribblearea.cpp 11
\snippet widgets/scribble/scribblearea.cpp 12
For mouse press and mouse release events, we use the
QMouseEvent::button() function to find out which button caused
the event. For mouse move events, we use QMouseEvent::buttons()
to find which buttons are currently held down (as an OR-combination).
If the users press the left mouse button, we store the position
of the mouse cursor in \c lastPoint. We also make a note that the
user is currently scribbling. (The \c scribbling variable is
necessary because we can't assume that a mouse move and mouse
release event is always preceded by a mouse press event on the
same widget.)
If the user moves the mouse with the left button pressed down or
releases the button, we call the private \c drawLineTo() function
to draw.
\snippet widgets/scribble/scribblearea.cpp 13
\snippet widgets/scribble/scribblearea.cpp 14
In the reimplementation of the \l
{QWidget::paintEvent()}{paintEvent()} function, we simply create
a QPainter for the scribble area, and draw the image.
At this point, you might wonder why we don't just draw directly
onto the widget instead of drawing in a QImage and copying the
QImage onto screen in \c paintEvent(). There are at least three
good reasons for this:
\list
\li The window system requires us to be able to redraw the widget
\e{at any time}. For example, if the window is minimized and
restored, the window system might have forgotten the contents
of the widget and send us a paint event. In other words, we
can't rely on the window system to remember our image.
\li Qt normally doesn't allow us to paint outside of \c
paintEvent(). In particular, we can't paint from the mouse
event handlers. (This behavior can be changed using the
Qt::WA_PaintOnScreen widget attribute, though.)
\li If initialized properly, a QImage is guaranteed to use 8-bit
for each color channel (red, green, blue, and alpha), whereas
a QWidget might have a lower color depth, depending on the
monitor configuration. This means that if we load a 24-bit or
32-bit image and paint it onto a QWidget, then copy the
QWidget into a QImage again, we might lose some information.
\endlist
\snippet widgets/scribble/scribblearea.cpp 15
\snippet widgets/scribble/scribblearea.cpp 16
When the user starts the Scribble application, a resize event is
generated and an image is created and displayed in the scribble
area. We make this initial image slightly larger than the
application's main window and scribble area, to avoid always
resizing the image when the user resizes the main window (which
would be very inefficient). But when the main window becomes
larger than this initial size, the image needs to be resized.
\snippet widgets/scribble/scribblearea.cpp 17
\snippet widgets/scribble/scribblearea.cpp 18
In \c drawLineTo(), we draw a line from the point where the mouse
was located when the last mouse press or mouse move occurred, we
set \c modified to true, we generate a repaint event, and we
update \c lastPoint so that next time \c drawLineTo() is called,
we continue drawing from where we left.
We could call the \c update() function with no parameter, but as
an easy optimization we pass a QRect that specifies the rectangle
inside the scribble are needs updating, to avoid a complete
repaint of the widget.
\snippet widgets/scribble/scribblearea.cpp 19
\snippet widgets/scribble/scribblearea.cpp 20
QImage has no nice API for resizing an image. There's a
QImage::copy() function that could do the trick, but when used to
expand an image, it fills the new areas with black, whereas we
want white.
So the trick is to create a brand new QImage with the right size,
to fill it with white, and to draw the old image onto it using
QPainter. The new image is given the QImage::Format_RGB32
format, which means that each pixel is stored as 0xffRRGGBB
(where RR, GG, and BB are the red, green and blue
color channels, ff is the hexadecimal value 255).
Printing is handled by the \c print() slot:
\snippet widgets/scribble/scribblearea.cpp 21
We construct a high resolution QPrinter object for the required
output format, using a QPrintDialog to ask the user to specify a
page size and indicate how the output should be formatted on the page.
If the dialog is accepted, we perform the task of printing to the paint
device:
\snippet widgets/scribble/scribblearea.cpp 22
Printing an image to a file in this way is simply a matter of
painting onto the QPrinter. We scale the image to fit within the
available space on the page before painting it onto the paint
device.
\section1 MainWindow Class Definition
\snippet widgets/scribble/mainwindow.h 0
The \c MainWindow class inherits from QMainWindow. We reimplement
the \l{QWidget::closeEvent()}{closeEvent()} handler from QWidget.
The \c open(), \c save(), \c penColor() and \c penWidth()
slots correspond to menu entries. In addition we create four
private functions.
We use the boolean \c maybeSave() function to check if there are
any unsaved changes. If there are unsaved changes, we give the
user the opportunity to save these changes. The function returns
\c false if the user clicks \uicontrol Cancel. We use the \c saveFile()
function to let the user save the image currently displayed in
the scribble area.
\section1 MainWindow Class Implementation
\snippet widgets/scribble/mainwindow.cpp 0
In the constructor, we create a scribble area which we make the
central widget of the \c MainWindow widget. Then we create the
associated actions and menus.
\snippet widgets/scribble/mainwindow.cpp 1
\snippet widgets/scribble/mainwindow.cpp 2
Close events are sent to widgets that the users want to close,
usually by clicking \uicontrol{File|Exit} or by clicking the \uicontrol X
title bar button. By reimplementing the event handler, we can
intercept attempts to close the application.
In this example, we use the close event to ask the user to save
any unsaved changes. The logic for that is located in the \c
maybeSave() function. If \c maybeSave() returns true, there are
no modifications or the users successfully saved them, and we
accept the event. The application can then terminate normally. If
\c maybeSave() returns false, the user clicked \uicontrol Cancel, so we
"ignore" the event, leaving the application unaffected by it.
\snippet widgets/scribble/mainwindow.cpp 3
\snippet widgets/scribble/mainwindow.cpp 4
In the \c open() slot we first give the user the opportunity to
save any modifications to the currently displayed image, before a
new image is loaded into the scribble area. Then we ask the user
to choose a file and we load the file in the \c ScribbleArea.
\snippet widgets/scribble/mainwindow.cpp 5
\snippet widgets/scribble/mainwindow.cpp 6
The \c save() slot is called when the users choose the \uicontrol {Save
As} menu entry, and then choose an entry from the format menu. The
first thing we need to do is to find out which action sent the
signal using QObject::sender(). This function returns the sender
as a QObject pointer. Since we know that the sender is an action
object, we can safely cast the QObject. We could have used a
C-style cast or a C++ \c static_cast<>(), but as a defensive
programming technique we use a qobject_cast(). The advantage is
that if the object has the wrong type, a null pointer is
returned. Crashes due to null pointers are much easier to diagnose
than crashes due to unsafe casts.
Once we have the action, we extract the chosen format using
QAction::data(). (When the actions are created, we use
QAction::setData() to set our own custom data attached to the
action, as a QVariant. More on this when we review \c
createActions().)
Now that we know the format, we call the private \c saveFile()
function to save the currently displayed image.
\snippet widgets/scribble/mainwindow.cpp 7
\snippet widgets/scribble/mainwindow.cpp 8
We use the \c penColor() slot to retrieve a new color from the
user with a QColorDialog. If the user chooses a new color, we
make it the scribble area's color.
\snippet widgets/scribble/mainwindow.cpp 9
\snippet widgets/scribble/mainwindow.cpp 10
To retrieve a new pen width in the \c penWidth() slot, we use
QInputDialog. The QInputDialog class provides a simple
convenience dialog to get a single value from the user. We use
the static QInputDialog::getInt() function, which combines a
QLabel and a QSpinBox. The QSpinBox is initialized with the
scribble area's pen width, allows a range from 1 to 50, a step of
1 (meaning that the up and down arrow increment or decrement the
value by 1).
The boolean \c ok variable will be set to \c true if the user
clicked \uicontrol OK and to \c false if the user pressed \uicontrol Cancel.
\snippet widgets/scribble/mainwindow.cpp 11
\snippet widgets/scribble/mainwindow.cpp 12
We implement the \c about() slot to create a message box
describing what the example is designed to show.
\snippet widgets/scribble/mainwindow.cpp 13
\snippet widgets/scribble/mainwindow.cpp 14
In the \c createAction() function we create the actions
representing the menu entries and connect them to the appropriate
slots. In particular we create the actions found in the \uicontrol
{Save As} sub-menu. We use QImageWriter::supportedImageFormats()
to get a list of the supported formats (as a QList<QByteArray>).
Then we iterate through the list, creating an action for each
format. We call QAction::setData() with the file format, so we
can retrieve it later as QAction::data(). We could also have
deduced the file format from the action's text, by truncating the
"...", but that would have been inelegant.
\snippet widgets/scribble/mainwindow.cpp 15
\snippet widgets/scribble/mainwindow.cpp 16
In the \c createMenu() function, we add the previously created
format actions to the \c saveAsMenu. Then we add the rest of the
actions as well as the \c saveAsMenu sub-menu to the \uicontrol File,
\uicontrol Options and \uicontrol Help menus.
The QMenu class provides a menu widget for use in menu bars,
context menus, and other popup menus. The QMenuBar class provides
a horizontal menu bar with a list of pull-down \l{QMenu}s. At the
end we put the \uicontrol File and \uicontrol Options menus in the \c
{MainWindow}'s menu bar, which we retrieve using the
QMainWindow::menuBar() function.
\snippet widgets/scribble/mainwindow.cpp 17
\snippet widgets/scribble/mainwindow.cpp 18
In \c mayBeSave(), we check if there are any unsaved changes. If
there are any, we use QMessageBox to give the user a warning that
the image has been modified and the opportunity to save the
modifications.
As with QColorDialog and QFileDialog, the easiest way to create a
QMessageBox is to use its static functions. QMessageBox provides
a range of different messages arranged along two axes: severity
(question, information, warning and critical) and complexity (the
number of necessary response buttons). Here we use the \c
warning() function sice the message is rather important.
If the user chooses to save, we call the private \c saveFile()
function. For simplicitly, we use PNG as the file format; the
user can always press \uicontrol Cancel and save the file using another
format.
The \c maybeSave() function returns \c false if the user clicks
\uicontrol Cancel; otherwise it returns \c true.
\snippet widgets/scribble/mainwindow.cpp 19
\snippet widgets/scribble/mainwindow.cpp 20
In \c saveFile(), we pop up a file dialog with a file name
suggestion. The static QFileDialog::getSaveFileName() function
returns a file name selected by the user. The file does not have
to exist.
*/

View File

@ -0,0 +1,14 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/settingseditor
\title Settings Editor Example
\ingroup examples-widgets-tools
\brief The Settings Editor example shows how Qt's standard settings support is used in an
application by providing an editor that enables the user to view the settings for
installed applications, and modify those that can be edited.
\image settingseditor-example.png
*/

View File

@ -0,0 +1,125 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/shapedclock
\title Shaped Clock Example
\ingroup examples-widgets
\brief The Shaped Clock example shows how to apply a translucent background
and a widget mask to a top-level widget to produce a shaped window.
\borderedimage shapedclock-example.png
Widget masks are used to customize the shapes of top-level widgets by
restricting the area available for painting and mouse input. Using a
translucent background facilitates partially transparent windows and smooth
edges. On most window systems, setting certain window flags will cause the
window decoration (title bar, window frame, buttons) to be disabled,
allowing specially-shaped windows to be created. In this example, we use
this feature to create a circular window containing an analog clock.
Since this example's window does not provide a \uicontrol File menu or a close
button, we provide a context menu with an \uicontrol Exit entry so that the example
can be closed. Click the right mouse button over the window to open this menu.
\section1 ShapedClock Class Definition
The \c ShapedClock class is based on the \c AnalogClock class defined in the
\l{Analog Clock} example. The whole class definition is
presented below:
\snippet widgets/shapedclock/shapedclock.h 0
The \l{QWidget::paintEvent()}{paintEvent()} implementation is the same as
that found in the \c AnalogClock class, with one important exception: we
now must also draw background (the clock face) ourselves, since the widget
background is just transparent. We implement \l{QWidget::sizeHint()}{sizeHint()}
so that we don't have to resize the widget explicitly. We also provide an event
handler for resize events. This allows us to update the mask if the clock is resized.
Since the window containing the clock widget will have no title bar, we provide
implementations for \l{QWidget::mouseMoveEvent()}{mouseMoveEvent()} and
\l{QWidget::mousePressEvent()}{mousePressEvent()} to allow the clock to be dragged
around the screen. The \c dragPosition variable lets us keep track of where the user
last clicked on the widget.
\section1 ShapedClock Class Implementation
The \c ShapedClock constructor performs many of the same tasks as the \c AnalogClock
constructor. We set up a timer and connect it to the widget's update() slot:
\snippet widgets/shapedclock/shapedclock.cpp 0
We request a transparent window by setting the Qt::WA_TranslucentBackground
widget attribute. We inform the window manager that the widget is not to be
decorated with a window frame by setting the Qt::FramelessWindowHint flag
on the widget. As a result, we need to provide a way for the user to move
the clock around the screen.
Mouse button events are delivered to the \c mousePressEvent() handler:
\snippet widgets/shapedclock/shapedclock.cpp 1
If the left mouse button is pressed over the widget, we record the displacement in
global (screen) coordinates between the top-left position of the widget's frame (even
when hidden) and the point where the mouse click occurred. This displacement will be
used if the user moves the mouse while holding down the left button. Since we acted
on the event, we accept it by calling its \l{QEvent::accept()}{accept()} function.
\image shapedclock-dragging.png
The \c mouseMoveEvent() handler is called if the mouse is moved over the widget.
\snippet widgets/shapedclock/shapedclock.cpp 2
If the left button is held down while the mouse is moved, the top-left corner of the
widget is moved to the point given by subtracting the \c dragPosition from the current
cursor position in global coordinates. If we drag the widget, we also accept the event.
The \c paintEvent() function is mainly the same as described in the
\l{Analog Clock} example. The one addition is that we
use QPainter::drawEllipse() to draw a round clock face with the current
palette's default background color. We make the clock face a bit smaller
than the widget mask, so that the anti-aliased, semi-transparent pixels on
the edge are not clipped away by the widget mask. This gives the shaped
window smooth edges on the screen.
\snippet widgets/shapedclock/shapedclock.cpp 3
In the \c resizeEvent() handler, we re-use some of the code from the \c
paintEvent() to determine the region of the widget that is visible to the
user. This tells the system the area where mouse clicks should go to us,
and not to whatever window is behind us:
\snippet widgets/shapedclock/shapedclock.cpp 4
Since the clock face is a circle drawn in the center of the widget, this is the region
we use as the mask.
Although the lack of a window frame may make it difficult for the user to resize the
widget on some platforms, it will not necessarily be impossible. The \c resizeEvent()
function ensures that the widget mask will always be updated if the widget's dimensions
change, and additionally ensures that it will be set up correctly when the widget is
first displayed.
Finally, we implement the \c sizeHint() for the widget so that it is given a reasonable
default size when it is first shown:
\snippet widgets/shapedclock/shapedclock.cpp 5
\section1 Notes on Widget Masks
Widget masks are used to hint to the window system that the application
does not want mouse events for areas outside the mask. On most systems,
they also result in coarse visual clipping. To get smooth window edges, one
should use translucent background and anti-aliased painting, as shown in
this example.
Since QRegion allows arbitrarily complex regions to be created, widget masks can be
made to suit the most unconventionally-shaped windows, and even allow widgets to be
displayed with holes in them.
Widget masks can also be constructed by using the contents of pixmap to define the
opaque part of the widget. For a pixmap with an alpha channel, a suitable mask can be
obtained with QPixmap::mask().
*/

View File

@ -0,0 +1,230 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/shortcuteditor
\title Shortcut Editor Example
\ingroup examples-widgets
\brief The Shortcut Editor example shows how to create a basic, read-write
hierarchical model to use with Qt's standard view and QKeySequenceEdit
classes. For a description of Model/View Programming, see the \l{Model/View
Programming} overview.
\image shortcuteditor-example.png
Qt's model/view architecture provides a standard way for views to
manipulate information in a data source, using an abstract model
of the data to simplify and standardize the way it is accessed.
The shortcut editor model represents the actions as a tree of items, and
allow views to access this data via an
\l{Model/View Programming#Models}{index-based} system. More generally,
models can be used to represent data in the form of a tree structure
by allowing each item to act as a parent to a table of child items.
\section1 Design and Concepts
The data structure that we use to represent the structure of the data takes
the form of a tree built from ShortcutEditorModelItem objects. Each
ShortcutEditorModelItem represents an item in a tree view, and contains
two columns of data.
\table
\row \li \inlineimage treemodel-structure.png
\li \b{Shortcut Editor Structure}
The data is stored internally in the model using ShortcutEditorModelItem
objects that are linked together in a pointer-based tree structure.
Generally, each ShortcutEditorModelItem has a parent item, and can have a
number of child items. However, the root item in the tree structure has no
parent item and it is never referenced outside the model.
Each ShortcutEditorModelItem contains information about its place in the
tree structure; it can return its parent item and its row number. Having
this information readily available makes implementing the model easier.
Since each item in a tree view usually contains several columns of data
(a name and a shortcut in this example), it is natural to store this
information in each item. For simplicity, we will use a list of QVariant
objects to store the data for each column in the item.
\endtable
The use of a pointer-based tree structure means that, when passing a
model index to a view, we can record the address of the corresponding
item in the index (see QAbstractItemModel::createIndex()) and retrieve
it later with QModelIndex::internalPointer(). This makes writing the
model easier and ensures that all model indexes that refer to the same
item have the same internal data pointer.
With the appropriate data structure in place, we can create a tree model
with a minimal amount of extra code to supply model indexes and data to
other components.
\section1 ShortcutEditorModelItem Class Definition
The ShortcutEditorModelItem class is defined as follows:
The class is a basic C++ class. It does not inherit from QObject or
provide signals and slots. It is used to hold a list of QVariants,
containing column data, and information about its position in the tree
structure. The functions provide the following features:
\list
\li The \c appendChildItem() is used to add data when the model is first
constructed and is not used during normal use.
\li The \c child() and \c childCount() functions allow the model to obtain
information about any child items.
\li Information about the number of columns associated with the item is
provided by \c columnCount(), and the data in each column can be
obtained with the data() function.
\li The \c row() and \c parent() functions are used to obtain the item's
row number and parent item.
\endlist
The parent item and column data are stored in the \c parentItem and
\c itemData private member variables. The \c childItems variable contains
a list of pointers to the item's own child items.
\section1 ShortcutEditorModel Class Definition
The \c ShortcutEditorModel class is defined as follows:
\snippet widgets/shortcuteditor/shortcuteditormodel.h 0
This class is similar to most other subclasses of QAbstractItemModel that
provide read-write models. Only the form of the constructor and the
\c setupModelData() function are specific to this model. In addition, we
provide a destructor to clean up when the model is destroyed.
\section1 ShortcutEditorModel Class Implementation
The constructor takes an argument containing the data that the model will
share with views and delegates:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 0
It is up to the constructor to create a root item for the model. This
item only contains vertical header data for convenience. We also use it
to reference the internal data structure that contains the model data,
and it is used to represent an imaginary parent of top-level items in
the model.
The model's internal data structure is populated with items by the
\c setupModelData() function. We will examine this function separately
at the end of this document.
The destructor ensures that the root item and all of its descendants
are deleted when the model is destroyed:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 1
Since we cannot add data to the model after it is constructed and set
up, this simplifies the way that the internal tree of items is managed.
Models must implement an \c index() function to provide indexes for
views and delegates to use when accessing data. Indexes are created
for other components when they are referenced by their row and column
numbers, and their parent model index. If an invalid model
index is specified as the parent, it is up to the model to return an
index that corresponds to a top-level item in the model.
When supplied with a model index, we first check whether it is valid.
If it is not, we assume that a top-level item is being referred to;
otherwise, we obtain the data pointer from the model index with its
\l{QModelIndex::internalPointer()}{internalPointer()} function and use
it to reference a \c TreeItem object. Note that all the model indexes
that we construct will contain a pointer to an existing \c TreeItem,
so we can guarantee that any valid model indexes that we receive will
contain a valid data pointer.
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 2
Since the row and column arguments to this function refer to a
child item of the corresponding parent item, we obtain the item using
the \c TreeItem::child() function. The
\l{QAbstractItemModel::createIndex()}{createIndex()} function is used
to create a model index to be returned. We specify the row and column
numbers, and a pointer to the item itself. The model index can be used
later to obtain the item's data.
The way that the \c TreeItem objects are defined makes writing the
\c parent() function easy:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 3
We only need to ensure that we never return a model index corresponding
to the root item. To be consistent with the way that the \c index()
function is implemented, we return an invalid model index for the
parent of any top-level items in the model.
When creating a model index to return, we must specify the row and
column numbers of the parent item within its own parent. We can
easily discover the row number with the \c TreeItem::row() function,
but we follow a convention of specifying 0 as the column number of
the parent. The model index is created with
\l{QAbstractItemModel::createIndex()}{createIndex()} in the same way
as in the \c index() function.
The \c rowCount() function simply returns the number of child items
for the \c TreeItem that corresponds to a given model index, or the
number of top-level items if an invalid index is specified:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 4
Since each item manages its own column data, the \c columnCount()
function has to call the item's own \c columnCount() function to
determine how many columns are present for a given model index.
As with the \c rowCount() function, if an invalid model index is
specified, the number of columns returned is determined from the
root item:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 5
Data is obtained from the model via \c data(). Since the item manages
its own columns, we need to use the column number to retrieve the data
with the \c TreeItem::data() function:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 6
Note that we only support the \l{Qt::ItemDataRole}{DisplayRole}
in this implementation, and we also return invalid QVariant objects for
invalid model indexes.
We use the \c flags() function to ensure that views know that the
model is read-only:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 7
The \c headerData() function returns data that we conveniently stored
in the root item:
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 8
This information could have been supplied in a different way: either
specified in the constructor, or hard coded into the \c headerData()
function.
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 9
TODO
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 10
TODO
\snippet widgets/shortcuteditor/shortcuteditormodel.cpp 11
TODO
\section1 Setting Up the Data in the Model
We use the \c setupModelData() function to set up the initial data in
the model. This function retrieves the registered actions text and creates
item objects that record both the data and the overall model structure.
Naturally, this function works in a way that is very specific to
this model. We provide the following description of its behavior,
and refer the reader to the example code itself for more information.
To ensure that the model works correctly, it is only necessary to
create instances of ShortcutEditorModelItem with the correct data and
parent item.
*/

View File

@ -0,0 +1,256 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/simpledommodel
\title Simple DOM Model Example
\ingroup examples-itemviews
\brief The Simple DOM Model example shows how an existing class can be adapted for use with
the model/view framework.
\image simpledommodel-example.png
Qt provides two complementary sets of classes for reading XML files: The classes based
around QXmlReader provide a SAX-style API for incremental reading of large files, and
the classes based around QDomDocument enable developers to access the contents of XML
files using a Document Object Model (DOM) API.
In this example, we create a model that uses the DOM API to expose the structure and
contents of XML documents to views via the standard QAbstractModel interface.
\section1 Design and Concepts
Reading an XML document with Qt's DOM classes is a straightforward process. Typically,
the contents of a file are supplied to QDomDocument, and nodes are accessed using the
functions provided by QDomNode and its subclasses.
\omit
For example, the following code
snippet reads the contents of a file into a QDomDocument object and traverses the
document, reading all the plain text that can be found:
\snippet doc/src/snippets/code/doc_src_examples_simpledommodel.cpp 0
In principle, the functions provided by QDomNode can be used to navigate from any
given starting point in a document to the piece of data requested by another component.
Since QDomDocument maintains information about the structure of a document, we can
use this to implement the required virtual functions in a QAbstractItemModel subclass.
\endomit
The aim is to use the structure provided by QDomDocument by wrapping QDomNode objects
in item objects similar to the \c TreeItem objects used in the
\l{Simple Tree Model Example}{Simple Tree Model} example.
\section1 DomModel Class Definition
Let us begin by examining the \c DomModel class:
\snippet itemviews/simpledommodel/dommodel.h 0
The class definition contains all the basic functions that are needed for a
read-only model. Only the constructor and \c document() function are specific to
this model. The private \c domDocument variable is used to hold the document
that is exposed by the model; the \c rootItem variable contains a pointer to
the root item in the model.
\section1 DomItem Class Definition
The \c DomItem class is used to hold information about a specific QDomNode in
the document:
\snippet itemviews/simpledommodel/domitem.h 0
Each \c DomItem provides a wrapper for a QDomNode obtained from the underlying
document which contains a reference to the node, it's location in the parent node's
list of child nodes, and a pointer to a parent wrapper item.
The \c parent(), \c child(), and \c row() functions are convenience functions for
the \c DomModel to use that provide basic information about the item to be discovered
quickly. The node() function provides access to the underlying QDomNode object.
As well as the information supplied in the constructor, the class maintains a cache
of information about any child items. This is used to provide a collection of
persistent item objects that the model can identify consistently and improve the
performance of the model when accessing child items.
\section1 DomItem Class Implementation
Since the \c DomItem class is only a thin wrapper around QDomNode objects, with a
few additional features to help improve performance and memory usage, we can provide
a brief outline of the class before discussing the model itself.
The constructor simply records details of the QDomNode that needs to be wrapped:
\snippet itemviews/simpledommodel/domitem.cpp 0
\snippet itemviews/simpledommodel/domitem.cpp 1
As a result, functions to provide the parent wrapper, the row number occupied by
the item in its parent's list of children, and the underlying QDomNode for each item
are straightforward to write:
\snippet itemviews/simpledommodel/domitem.cpp 4
\codeline
\snippet itemviews/simpledommodel/domitem.cpp 6
\codeline
\snippet itemviews/simpledommodel/domitem.cpp 3
It is necessary to maintain a collection of items which can be consistently identified
by the model. For that reason, we maintain a hash of child wrapper items that, to
minimize memory usage, is initially empty. The model uses the item's \c child()
function to help create model indexes, and this constructs wrappers for the children
of the item's QDomNode, relating the row number of each child to the newly-constructed
wrapper:
\snippet itemviews/simpledommodel/domitem.cpp 5
If a QDomNode was previously wrapped, the cached wrapper is returned; otherwise, a
new wrapper is constructed and stored for valid children, and zero is returned for
invalid ones.
The class's destructor deletes all the child items of the wrapper:
\snippet itemviews/simpledommodel/domitem.cpp 2
These, in turn, will delete their children and free any QDomNode objects in use.
\section1 DomModel Class Implementation
The structure provided by the \c DomItem class makes the implementation of \c DomModel
similar to the \c TreeModel shown in the
\l{Simple Tree Model Example}{Simple Tree Model} example.
The constructor accepts an existing document and a parent object for the model:
\snippet itemviews/simpledommodel/dommodel.cpp 0
A shallow copy of the document is stored for future reference, and a root item is
created to provide a wrapper around the document. We assign the root item a row
number of zero only to be consistent since the root item will have no siblings.
Since the model only contains information about the root item, the destructor only
needs to delete this one item:
\snippet itemviews/simpledommodel/dommodel.cpp 1
All of the child items in the tree will be deleted by the \c DomItem destructor as
their parent items are deleted.
\section2 Basic Properties of The Model
Some aspects of the model do not depend on the structure of the underlying document,
and these are simple to implement.
The number of columns exposed by the model is returned by the \c columnCount()
function:
\snippet itemviews/simpledommodel/dommodel.cpp 2
This value is fixed, and does not depend on the location or type of the underlying
node in the document. We will use these three columns to display different kinds of
data from the underlying document.
Since we only implement a read-only model, the \c flags() function is straightforward
to write:
\snippet itemviews/simpledommodel/dommodel.cpp 5
Since the model is intended for use in a tree view, the \c headerData() function only
provides a horizontal header:
\snippet itemviews/simpledommodel/dommodel.cpp 6
The model presents the names of nodes in the first column, element attributes in the
second, and any node values in the third.
\section2 Navigating The Document
The index() function creates a model index for the item with the given row, column,
and parent in the model:
\snippet itemviews/simpledommodel/dommodel.cpp 7
The function first has to relate the parent index to an item that contains a node
from the underlying document. If the parent index is invalid, it refers to the root
node in the document, so we retrieve the root item that wraps it; otherwise, we
obtain a pointer to the relevant item using the QModelIndex::internalPointer()
function. We are able to extract a pointer in this way because any valid model index
will have been created by this function, and we store pointers to item objects in
any new indexes that we create with QAbstractItemModel::createIndex():
\snippet itemviews/simpledommodel/dommodel.cpp 8
A child item for the given row is provided by the parent item's \c child() function.
If a suitable child item was found then we call
\l{QAbstractItemModel::createIndex()}{createIndex()} to produce a model index for the
requested row and column, passing a pointer to the child item for it to store
internally. If no suitable child item is found, an invalid model index is returned.
Note that the items themselves maintain ownership of their child items. This means
that the model does not need to keep track of the child items that have been created,
and can let the items themselves tidy up when they are deleted.
The number of rows beneath a given item in the model is returned by the \c rowCount()
function, and is the number of child nodes contained by the node that corresponds to
the specified model index:
\snippet itemviews/simpledommodel/dommodel.cpp 10
To obtain the relevant node in the underlying document, we access the item via the
internal pointer stored in the model index. If an invalid index is supplied, the
root item is used instead. We use the item's \c node() function to access the node
itself, and simply count the number of child nodes it contains.
Since the model is used to represent a hierarchical data structure, it needs to
provide an implementation for the \c parent() function. This returns a model index
that corresponds to the parent of a child model index supplied as its argument:
\snippet itemviews/simpledommodel/dommodel.cpp 9
For valid indexes other than the index corresponding to the root item, we obtain
a pointer to the relevant item using the method described in the \c index() function,
and use the item's \c parent() function to obtain a pointer to the parent item.
If no valid parent item exists, or if the parent item is the root item, we can simply
follow convention and return an invalid model index. For all other parent items, we
create a model index containing the appropriate row and column numbers, and a pointer
to the parent item we just obtained.
Data is provided by the \c data() function. For simplicity, we only provide data for
the \l{Qt::DisplayRole}{display role}, returning an invalid variant for all other
requests:
\snippet itemviews/simpledommodel/dommodel.cpp 3
As before, we obtain an item pointer for the index supplied, and use it to obtain
the underlying document node. Depending on the column specified, the data we return
is obtained in different ways:
\snippet itemviews/simpledommodel/dommodel.cpp 4
For the first column, we return the node's name. For the second column, we read any
attributes that the node may have, and return a string that contains a space-separated
list of attribute-value assignments. For the third column, we return any value that
the node may have; this allows the contents of text nodes to be displayed in a view.
If data from any other column is requested, an invalid variant is returned.
\section1 Implementation Notes
Ideally, we would rely on the structure provided by QDomDocument to help us write
the \l{QAbstractItemModel::parent()}{parent()} and
\l{QAbstractItemModel::index()}{index()} functions that are required when subclassing
QAbstractItemModel. However, since Qt's DOM classes use their own system for
dynamically allocating memory for DOM nodes, we cannot guarantee that the QDomNode
objects returned for a given piece of information will be the same for subsequent
accesses to the document.
We use item wrappers for each QDomNode to provide consistent pointers that the model
can use to navigate the document structure.
\omit
Since these items contain value references to the QDomNode objects themselves, this
has the side effect that the DOM nodes themselves can be used to reliably navigate
the document [not sure about this - QDom* may return different QDomNode objects for
the same piece of information]. However, this advantage is redundant since we need to
use wrapper items to obtain it. [Possible use of QDomNode cache in the model itself.]
\endomit
*/

View File

@ -0,0 +1,320 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/simpletreemodel
\title Simple Tree Model Example
\ingroup examples-itemviews
\ingroup examples-layout
\brief The Simple Tree Model example shows how to use a hierarchical model
with Qt's standard view classes.
\brief The Simple Tree Model example shows how to create a basic, read-only
hierarchical model to use with Qt's standard view classes. For a
description of simple non-hierarchical list and table models, see the
\l{Model/View Programming} overview.
\image simpletreemodel-example.png
Qt's model/view architecture provides a standard way for views to
manipulate information in a data source, using an abstract model
of the data to simplify and standardize the way it is accessed.
Simple models represent data as a table of items, and allow views
to access this data via an
\l{Model/View Programming#Models}{index-based} system. More generally,
models can be used to represent data in the form of a tree structure
by allowing each item to act as a parent to a table of child items.
Before attempting to implement a tree model, it is worth considering whether
the data is supplied by an external source, or whether it is going to be
maintained within the model itself. In this example, we will implement an
internal structure to hold data rather than discuss how to package data from
an external source.
\section1 Design and Concepts
The data structure that we use to represent the structure of the data takes
the form of a tree built from \c TreeItem objects. Each \c TreeItem
represents an item in a tree view, and contains several columns of data.
\target SimpleTreeModelStructure
\table
\row \li \inlineimage treemodel-structure.png
\li \b{Simple Tree Model Structure}
The data is stored internally in the model using \c TreeItem objects that
are linked together in a pointer-based tree structure. Generally, each
\c TreeItem has a parent item, and can have a number of child items.
However, the root item in the tree structure has no parent item and it
is never referenced outside the model.
Each \c TreeItem contains information about its place in the tree
structure; it can return its parent item and its row number. Having
this information readily available makes implementing the model easier.
Since each item in a tree view usually contains several columns of data
(a title and a summary in this example), it is natural to store this
information in each item. For simplicity, we will use a list of QVariant
objects to store the data for each column in the item.
\endtable
The use of a pointer-based tree structure means that, when passing a
model index to a view, we can record the address of the corresponding
item in the index (see QAbstractItemModel::createIndex()) and retrieve
it later with QModelIndex::internalPointer(). This makes writing the
model easier and ensures that all model indexes that refer to the same
item have the same internal data pointer.
With the appropriate data structure in place, we can create a tree model
with a minimal amount of extra code to supply model indexes and data to
other components.
\section1 TreeItem Class Definition
The \c TreeItem class is defined as follows:
\snippet itemviews/simpletreemodel/treeitem.h 0
The class is a basic C++ class. It does not inherit from QObject or
provide signals and slots. It is used to hold a list of QVariants,
containing column data, and information about its position in the tree
structure. The functions provide the following features:
\list
\li The \c appendChildItem() is used to add data when the model is first
constructed and is not used during normal use.
\li The \c child() and \c childCount() functions allow the model to obtain
information about any child items.
\li Information about the number of columns associated with the item is
provided by \c columnCount(), and the data in each column can be
obtained with the data() function.
\li The \c row() and \c parent() functions are used to obtain the item's
row number and parent item.
\endlist
The parent item and column data are stored in the \c parentItem and
\c itemData private member variables. The \c childItems variable contains
a list of pointers to the item's own child items.
\section1 TreeItem Class Implementation
The constructor is only used to record the item's parent and the data
associated with each column.
\snippet itemviews/simpletreemodel/treeitem.cpp 0
A pointer to each of the child items belonging to this item will be
stored in the \c childItems private member variable. When the class's
destructor is called, it must delete each of these to ensure that
their memory is reused:
\snippet itemviews/simpletreemodel/treeitem.cpp 1
Since each of the child items are constructed when the model is initially
populated with data, the function to add child items is straightforward:
\snippet itemviews/simpletreemodel/treeitem.cpp 2
Each item is able to return any of its child items when given a suitable
row number. For example, in the \l{#SimpleTreeModelStructure}{above diagram},
the item marked with the letter "A" corresponds to the child of the root item
with \c{row = 0}, the "B" item is a child of the "A" item with \c{row = 1},
and the "C" item is a child of the root item with \c{row = 1}.
The \c child() function returns the child that corresponds to
the specified row number in the item's list of child items:
\snippet itemviews/simpletreemodel/treeitem.cpp 3
The number of child items held can be found with \c childCount():
\snippet itemviews/simpletreemodel/treeitem.cpp 4
The \c TreeModel uses this function to determine the number of rows that
exist for a given parent item.
The \c row() function reports the item's location within its parent's
list of items:
\snippet itemviews/simpletreemodel/treeitem.cpp 8
Note that, although the root item (with no parent item) is automatically
assigned a row number of 0, this information is never used by the model.
The number of columns of data in the item is trivially returned by the
\c columnCount() function.
\snippet itemviews/simpletreemodel/treeitem.cpp 5
Column data is returned by the \c data() function. The bounds are checked
before accessing the container with the data:
\snippet itemviews/simpletreemodel/treeitem.cpp 6
The item's parent is found with \c parent():
\snippet itemviews/simpletreemodel/treeitem.cpp 7
Note that, since the root item in the model will not have a parent, this
function will return zero in that case. We need to ensure that the model
handles this case correctly when we implement the \c TreeModel::parent()
function.
\section1 TreeModel Class Definition
The \c TreeModel class is defined as follows:
\snippet itemviews/simpletreemodel/treemodel.h 0
This class is similar to most other subclasses of QAbstractItemModel that
provide read-only models. Only the form of the constructor and the
\c setupModelData() function are specific to this model. In addition, we
provide a destructor to clean up when the model is destroyed.
\section1 TreeModel Class Implementation
For simplicity, the model does not allow its data to be edited. As a
result, the constructor takes an argument containing the data that the
model will share with views and delegates:
\snippet itemviews/simpletreemodel/treemodel.cpp 0
It is up to the constructor to create a root item for the model. This
item only contains vertical header data for convenience. We also use it
to reference the internal data structure that contains the model data,
and it is used to represent an imaginary parent of top-level items in
the model.
The model's internal data structure is populated with items by the
\c setupModelData() function. We will examine this function separately
at the end of this document.
The destructor ensures that the root item and all of its descendants
are deleted when the model is destroyed:
\snippet itemviews/simpletreemodel/treemodel.cpp 1
Since we cannot add data to the model after it is constructed and set
up, this simplifies the way that the internal tree of items is managed.
Models must implement an \c index() function to provide indexes for
views and delegates to use when accessing data. Indexes are created
for other components when they are referenced by their row and column
numbers, and their parent model index. If an invalid model
index is specified as the parent, it is up to the model to return an
index that corresponds to a top-level item in the model.
When supplied with a model index, we first check whether it is valid.
If it is not, we assume that a top-level item is being referred to;
otherwise, we obtain the data pointer from the model index with its
\l{QModelIndex::internalPointer()}{internalPointer()} function and use
it to reference a \c TreeItem object. Note that all the model indexes
that we construct will contain a pointer to an existing \c TreeItem,
so we can guarantee that any valid model indexes that we receive will
contain a valid data pointer.
\snippet itemviews/simpletreemodel/treemodel.cpp 6
Since the row and column arguments to this function refer to a
child item of the corresponding parent item, we obtain the item using
the \c TreeItem::child() function. The
\l{QAbstractItemModel::createIndex()}{createIndex()} function is used
to create a model index to be returned. We specify the row and column
numbers, and a pointer to the item itself. The model index can be used
later to obtain the item's data.
The way that the \c TreeItem objects are defined makes writing the
\c parent() function easy:
\snippet itemviews/simpletreemodel/treemodel.cpp 7
We only need to ensure that we never return a model index corresponding
to the root item. To be consistent with the way that the \c index()
function is implemented, we return an invalid model index for the
parent of any top-level items in the model.
When creating a model index to return, we must specify the row and
column numbers of the parent item within its own parent. We can
easily discover the row number with the \c TreeItem::row() function,
but we follow a convention of specifying 0 as the column number of
the parent. The model index is created with
\l{QAbstractItemModel::createIndex()}{createIndex()} in the same way
as in the \c index() function.
The \c rowCount() function simply returns the number of child items
for the \c TreeItem that corresponds to a given model index, or the
number of top-level items if an invalid index is specified:
\snippet itemviews/simpletreemodel/treemodel.cpp 8
Since each item manages its own column data, the \c columnCount()
function has to call the item's own \c columnCount() function to
determine how many columns are present for a given model index.
As with the \c rowCount() function, if an invalid model index is
specified, the number of columns returned is determined from the
root item:
\snippet itemviews/simpletreemodel/treemodel.cpp 2
Data is obtained from the model via \c data(). Since the item manages
its own columns, we need to use the column number to retrieve the data
with the \c TreeItem::data() function:
\snippet itemviews/simpletreemodel/treemodel.cpp 3
Note that we only support the \l{Qt::ItemDataRole}{DisplayRole}
in this implementation, and we also return invalid QVariant objects for
invalid model indexes.
We use the \c flags() function to ensure that views know that the
model is read-only:
\snippet itemviews/simpletreemodel/treemodel.cpp 4
The \c headerData() function returns data that we conveniently stored
in the root item:
\snippet itemviews/simpletreemodel/treemodel.cpp 5
This information could have been supplied in a different way: either
specified in the constructor, or hard coded into the \c headerData()
function.
\section1 Setting Up the Data in the Model
We use the \c setupModelData() function to set up the initial data in
the model. This function parses a text file, extracting strings of
text to use in the model, and creates item objects that record both
the data and the overall model structure.
Naturally, this function works in a way that is very specific to
this model. We provide the following description of its behavior,
and refer the reader to the example code itself for more information.
We begin with a text file in the following format:
\code
Getting Started How to familiarize yourself with Qt Designer
Launching Designer Running the Qt Designer application
The User Interface How to interact with Qt Designer
\endcode
\dots
\code
Connection Editing Mode Connecting widgets together with signals and slots
Connecting Objects Making connections in Qt Designer
Editing Connections Changing existing connections
\endcode
We process the text file with the following two rules:
\list
\li For each pair of strings on each line, create an item (or node)
in a tree structure, and place each string in a column of data
in the item.
\li When the first string on a line is indented with respect to the
first string on the previous line, make the item a child of the
previous item created.
\endlist
To ensure that the model works correctly, it is only necessary to
create instances of \c TreeItem with the correct data and parent item.
*/

View File

@ -0,0 +1,101 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/simplewidgetmapper
\title Simple Widget Mapper Example
\ingroup examples-itemviews
\brief The Simple Widget Mapper example shows how to use a widget mapper to display
data from a model in a collection of widgets.
\image simplewidgetmapper-example.png
The QDataWidgetMapper class allows information obtained from a
\l{Model Classes}{model} to be viewed and edited in a collection of
widgets instead of in an \l{View Classes}{item view}.
Any model derived from QAbstractItemModel can be used as the source of
data and almost any input widget can be used to display it.
The example itself is very simple: we create \c Window, a QWidget subclass
that we use to hold the widgets used to present the data, and show it. The
\c Window class will provide buttons that the user can click to show
different records from the model.
\section1 Window Class Definition
The class provides a constructor, a slot to keep the buttons up to date,
and a private function to set up the model:
\snippet itemviews/simplewidgetmapper/window.h Window definition
In addition to the QDataWidgetMapper object and the controls used to make
up the user interface, we use a QStandardItemModel to hold our data.
We could use a custom model, but this standard implementation is sufficient
for our purposes.
\section1 Window Class Implementation
The constructor of the \c Window class can be explained in three parts.
In the first part, we set up the widgets used for the user interface:
\snippet itemviews/simplewidgetmapper/window.cpp Set up widgets
We also set up the buddy relationships between various labels and the
corresponding input widgets.
Next, we set up the widget mapper, relating each input widget to a column
in the model specified by the call to \l{QDataWidgetMapper::}{setModel()}:
\snippet itemviews/simplewidgetmapper/window.cpp Set up the mapper
We also connect the mapper to the \uicontrol{Next} and \uicontrol{Previous} buttons
via its \l{QDataWidgetMapper::}{toNext()} and
\l{QDataWidgetMapper::}{toPrevious()} slots. The mapper's
\l{QDataWidgetMapper::}{currentIndexChanged()} signal is connected to the
\c{updateButtons()} slot in the window which we'll show later.
In the final part of the constructor, we set up the layout, placing each
of the widgets in a grid (we could also use a QFormLayout for this):
\snippet itemviews/simplewidgetmapper/window.cpp Set up the layout
Lastly, we set the window title and initialize the mapper by setting it to
refer to the first row in the model.
The model is initialized in the window's \c{setupModel()} function. Here,
we create a standard model with 5 rows and 3 columns, and we insert some
sample names, addresses and ages into each row:
\snippet itemviews/simplewidgetmapper/window.cpp Set up the model
As a result, each row can be treated like a record in a database, and the
widget mapper will read the data from each row, using the column numbers
specified earlier to access the correct data for each widget. This is
shown in the following diagram:
\image widgetmapper-simple-mapping.png
Since the user can navigate using the buttons in the user interface, the
example is fully-functional at this point, but to make it a bit more
user-friendly, we implement the \c{updateButtons()} slot to show when the
user is viewing the first or last records:
\snippet itemviews/simplewidgetmapper/window.cpp Slot for updating the buttons
If the mapper is referring to the first row in the model, the \uicontrol{Previous}
button is disabled. Similarly, the \uicontrol{Next} button is disabled if the
mapper reaches the last row in the model.
\section1 More Complex Mappings
The QDataWidgetMapper class makes it easy to relate information from a
model to widgets in a user interface. However, it is sometimes necessary
to use input widgets which offer choices to the user, such as QComboBox,
in conjunction with a widget mapper.
In these situations, although the mapping to input widgets remains simple,
more work needs to be done to expose additional data to the widget mapper.
This is covered by the \l{Combo Widget Mapper Example}{Combo Widget Mapper}
and \l{SQL Widget Mapper Example}{SQL Widget Mapper}
examples.
*/

View File

@ -0,0 +1,235 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/sliders
\title Sliders Example
\ingroup examples-widgets
\brief The Sliders example shows how to use the different types of sliders
available in Qt: QSlider, QScrollBar and QDial.
Qt provides three types of slider-like widgets: QSlider,
QScrollBar and QDial. They all inherit most of their
functionality from QAbstractSlider, and can in theory replace
each other in an application since the differences only concern
their look and feel. This example shows what they look like, how
they work and how their behavior and appearance can be
manipulated through their properties.
The example also demonstrates how signals and slots can be used to
synchronize the behavior of two or more widgets.
\borderedimage sliders-example.png
\caption Screenshot of the Sliders example
The Sliders example consists of two classes:
\list
\li \c SlidersGroup is a custom widget. It combines a QSlider, a
QScrollBar and a QDial.
\li \c Window is the main widget combining a QGroupBox and a
QStackedWidget. In this example, the QStackedWidget provides a
stack of two \c SlidersGroup widgets. The QGroupBox contain
several widgets that control the behavior of the slider-like
widgets.
\endlist
First we will review the \c Window class, then we
will take a look at the \c SlidersGroup class.
\section1 Window Class Definition
\snippet widgets/sliders/window.h 0
The \c Window class inherits from QWidget. It displays the slider
widgets and allows the user to set their minimum, maximum and
current values and to customize their appearance, key bindings
and orientation. We use a private \c createControls() function to
create the widgets that provide these controlling mechanisms and
to connect them to the slider widgets.
\section1 Window Class Implementation
\snippet widgets/sliders/window.cpp 0
In the constructor we first create the two \c SlidersGroup
widgets that display the slider widgets horizontally and
vertically, and add them to the QStackedWidget. QStackedWidget
provides a stack of widgets where only the top widget is visible.
With \c createControls() we create a connection from a
controlling widget to the QStackedWidget, making the user able to
choose between horizontal and vertical orientation of the slider
widgets. The rest of the controlling mechanisms is implemented by
the same function call.
\snippet widgets/sliders/window.cpp 1
\snippet widgets/sliders/window.cpp 2
Then we connect the \c horizontalSliders, \c verticalSliders and
\c valueSpinBox to each other, so that the slider widgets and the
control widget will behave synchronized when the current value of
one of them changes. The \c valueChanged() signal is emitted with
the new value as argument. The \c setValue() slot sets the
current value of the widget to the new value, and emits \c
valueChanged() if the new value is different from the old one.
We put the group of control widgets and the stacked widget in a
horizontal layout before we initialize the minimum, maximum and
current values. The initialization of the current value will
propagate to the slider widgets through the connection we made
between \c valueSpinBox and the \c SlidersGroup widgets. The
minimum and maximum values propagate through the connections we
created with \c createControls().
\snippet widgets/sliders/window.cpp 3
\snippet widgets/sliders/window.cpp 4
In the private \c createControls() function, we let a QGroupBox
(\c controlsGroup) display the control widgets. A group box can
provide a frame, a title and a keyboard shortcut, and displays
various other widgets inside itself. The group of control widgets
is composed by two checkboxes, three spin boxes (with labels) and
one combobox.
After creating the labels, we create the two checkboxes.
Checkboxes are typically used to represent features in an
application that can be enabled or disabled. When \c
invertedAppearance is enabled, the slider values are inverted.
The table below shows the appearance for the different
slider-like widgets:
\table
\header \li \li{2,1} QSlider \li{2,1} QScrollBar \li{2,1} QDial
\header \li \li Normal \li Inverted \li Normal \li Inverted \li Normal \li Inverted
\row \li Qt::Horizontal \li Left to right \li Right to left \li Left to right \li Right to left \li Clockwise \li Counterclockwise
\row \li Qt::Vertical \li Bottom to top \li Top to bottom \li Top to bottom \li Bottom to top \li Clockwise \li Counterclockwise
\endtable
It is common to invert the appearance of a vertical QSlider. A
vertical slider that controls volume, for example, will typically
go from bottom to top (the non-inverted appearance), whereas a
vertical slider that controls the position of an object on screen
might go from top to bottom, because screen coordinates go from
top to bottom.
When the \c invertedKeyBindings option is enabled (corresponding
to the QAbstractSlider::invertedControls property), the slider's
wheel and key events are inverted. The normal key bindings mean
that scrolling the mouse wheel "up" or using keys like page up
will increase the slider's current value towards its maximum.
Inverted, the same wheel and key events will move the value
toward the slider's minimum. This can be useful if the \e
appearance of a slider is inverted: Some users might expect the
keys to still work the same way on the value, whereas others
might expect \uicontrol PageUp to mean "up" on the screen.
Note that for horizontal and vertical scroll bars, the key
bindings are inverted by default: \uicontrol PageDown increases the
current value, and \uicontrol PageUp decreases it.
\snippet widgets/sliders/window.cpp 5
\snippet widgets/sliders/window.cpp 6
Then we create the spin boxes. QSpinBox allows the user to choose
a value by clicking the up and down buttons or pressing the \uicontrol
Up and \uicontrol Down keys on the keyboard to modify the value
currently displayed. The user can also type in the value
manually. The spin boxes control the minimum, maximum and current
values for the QSlider, QScrollBar, and QDial widgets.
We create a QComboBox that allows the user to choose the
orientation of the slider widgets. The QComboBox widget is a
combined button and popup list. It provides a means of presenting
a list of options to the user in a way that takes up the minimum
amount of screen space.
\snippet widgets/sliders/window.cpp 7
\snippet widgets/sliders/window.cpp 8
We synchronize the behavior of the control widgets and the slider
widgets through their signals and slots. We connect each control
widget to both the horizontal and vertical group of slider
widgets. We also connect \c orientationCombo to the
QStackedWidget, so that the correct "page" is shown. Finally, we
lay out the control widgets in a QGridLayout within the \c
controlsGroup group box.
\section1 SlidersGroup Class Definition
\snippet widgets/sliders/slidersgroup.h 0
The \c SlidersGroup class inherits from QGroupBox. It provides a
frame and a title, and contains a QSlider, a QScrollBar and a
QDial.
We provide a \c valueChanged() signal and a public \c setValue()
slot with equivalent functionality to the ones in QAbstractSlider
and QSpinBox. In addition, we implement several other public
slots to set the minimum and maximum value, and invert the slider
widgets' appearance as well as key bindings.
\section1 SlidersGroup Class Implementation
\snippet widgets/sliders/slidersgroup.cpp 0
First we create the slider-like widgets with the appropriate
properties. In particular we set the focus policy for each
widget. Qt::FocusPolicy is an enum type that defines the various
policies a widget can have with respect to acquiring keyboard
focus. The Qt::StrongFocus policy means that the widget accepts
focus by both tabbing and clicking.
Then we connect the widgets with each other, so that they will
stay synchronized when the current value of one of them changes.
\snippet widgets/sliders/slidersgroup.cpp 1
\snippet widgets/sliders/slidersgroup.cpp 2
We connect \c {dial}'s \c valueChanged() signal to the
\c{SlidersGroup}'s \c valueChanged() signal, to notify the other
widgets in the application (i.e., the control widgets) of the
changed value.
\snippet widgets/sliders/slidersgroup.cpp 3
\codeline
\snippet widgets/sliders/slidersgroup.cpp 4
Finally, depending on the \l {Qt::Orientation}{orientation} given
at the time of construction, we choose and create the layout for
the slider widgets within the group box.
\snippet widgets/sliders/slidersgroup.cpp 5
\snippet widgets/sliders/slidersgroup.cpp 6
The \c setValue() slot sets the value of the QSlider. We don't
need to explicitly call
\l{QAbstractSlider::setValue()}{setValue()} on the QScrollBar and
QDial widgets, since QSlider will emit the
\l{QAbstractSlider::valueChanged()}{valueChanged()} signal when
its value changes, triggering a domino effect.
\snippet widgets/sliders/slidersgroup.cpp 7
\snippet widgets/sliders/slidersgroup.cpp 8
\codeline
\snippet widgets/sliders/slidersgroup.cpp 9
\snippet widgets/sliders/slidersgroup.cpp 10
The \c setMinimum() and \c setMaximum() slots are used by the \c
Window class to set the range of the QSlider, QScrollBar, and
QDial widgets.
\snippet widgets/sliders/slidersgroup.cpp 11
\snippet widgets/sliders/slidersgroup.cpp 12
\codeline
\snippet widgets/sliders/slidersgroup.cpp 13
\snippet widgets/sliders/slidersgroup.cpp 14
The \c invertAppearance() and \c invertKeyBindings() slots
control the child widgets'
\l{QAbstractSlider::invertedAppearance}{invertedAppearance} and
\l{QAbstractSlider::invertedControls}{invertedControls}
properties.
*/

View File

@ -0,0 +1,117 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/spinboxdelegate
\title Spin Box Delegate Example
\ingroup examples-itemviews
\brief The Spin Box Delegate example shows how to create an editor for a custom delegate in
the model/view framework by reusing a standard Qt editor widget.
The model/view framework provides a standard delegate that is used by default
with the standard view classes. For most purposes, the selection of editor
widgets available through this delegate is sufficient for editing text, boolean
values, and other simple data types. However, for specific data types, it is
sometimes necessary to use a custom delegate to either display the data in a
specific way, or allow the user to edit it with a custom control.
\image spinboxdelegate-example.png
This concepts behind this example are covered in the
\l{Model/View Programming#Delegate Classes}{Delegate Classes} chapter
of the \l{Model/View Programming} overview.
\section1 SpinBoxDelegate Class Definition
The definition of the delegate is as follows:
\snippet itemviews/spinboxdelegate/delegate.h 0
The delegate class declares only those functions that are needed to
create an editor widget, display it at the correct location in a view,
and communicate with a model. Custom delegates can also provide their
own painting code by reimplementing the \c paintEvent() function.
Furthermore it is also possible to reuse (and avoid deleting) the editor
widget by reimplementing the \a destroyEditor() function. A reused widget
could be a mutable member created in the constructor and deleted in
the destructor.
\section1 SpinBoxDelegate Class Implementation
Delegates are often stateless. The constructor only needs to
call the base class's constructor with the parent QObject as its
argument:
\snippet itemviews/spinboxdelegate/delegate.cpp 0
Since the delegate is a subclass of QStyledItemDelegate, the data it retrieves
from the model is displayed in a default style, and we do not need to
provide a custom \c paintEvent().
The \c createEditor() function returns an editor widget, in this case a
spin box that restricts values from the model to integers from 0 to 100
inclusive.
\snippet itemviews/spinboxdelegate/delegate.cpp 1
We install an event filter on the spin box to ensure that it behaves in
a way that is consistent with other delegates. The implementation for
the event filter is provided by the base class.
The \c setEditorData() function reads data from the model, converts it
to an integer value, and writes it to the editor widget.
\snippet itemviews/spinboxdelegate/delegate.cpp 2
Since the view treats delegates as ordinary QWidget instances, we have
to use a static cast before we can set the value in the spin box.
The \c setModelData() function reads the contents of the spin box, and
writes it to the model.
\snippet itemviews/spinboxdelegate/delegate.cpp 3
We call \l{QSpinBox::interpretText()}{interpretText()} to make sure that
we obtain the most up-to-date value in the spin box.
The \c updateEditorGeometry() function updates the editor widget's
geometry using the information supplied in the style option. This is the
minimum that the delegate must do in this case.
\snippet itemviews/spinboxdelegate/delegate.cpp 4
More complex editor widgets may divide the rectangle available in
\c{option.rect} between different child widgets if required.
\section1 The Main Function
This example is written in a slightly different way to many of the
other examples supplied with Qt. To demonstrate the use of a custom
editor widget in a standard view, it is necessary to set up a model
containing some arbitrary data and a view to display it.
We set up the application in the normal way, construct a standard item
model to hold some data, set up a table view to use the data in the
model, and construct a custom delegate to use for editing:
\snippet itemviews/spinboxdelegate/main.cpp 0
The table view is informed about the delegate, and will use it to
display each of the items. Since the delegate is a subclass of
QStyledItemDelegate, each cell in the table will be rendered using standard
painting operations.
We insert some arbitrary data into the model for demonstration purposes:
\snippet itemviews/spinboxdelegate/main.cpp 1
\snippet itemviews/spinboxdelegate/main.cpp 2
Finally, the table view is displayed with a window title, and we start
the application's event loop:
\snippet itemviews/spinboxdelegate/main.cpp 3
Each of the cells in the table can now be edited in the usual way, but
the spin box ensures that the data returned to the model is always
constrained by the values allowed by the spin box delegate.
*/

View File

@ -0,0 +1,167 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/spinboxes
\title Spin Boxes Example
\ingroup examples-widgets
\brief The Spin Boxes example shows how to use the many different types of
spin boxes available in Qt, from a simple QSpinBox widget to more complex
editors like the QDateTimeEdit widget.
\borderedimage spinboxes-example.png
The example consists of a single \c Window class that is used to display the
different spin box-based widgets available with Qt.
\section1 Window Class Definition
The \c Window class inherits QWidget and contains two slots that are used
to provide interactive features:
\snippet widgets/spinboxes/window.h 0
The private functions are used to set up each type of spin box in the window.
We use member variables to keep track of various widgets so that they can
be reconfigured when required.
\section1 Window Class Implementation
The constructor simply calls private functions to set up the different types
of spin box used in the example, and places each group in a layout:
\snippet widgets/spinboxes/window.cpp 0
We use the layout to manage the arrangement of the window's child widgets,
and change the window title.
The \c createSpinBoxes() function constructs a QGroupBox and places three
QSpinBox widgets inside it with descriptive labels to indicate the types of
input they expect.
\snippet widgets/spinboxes/window.cpp 1
The first spin box shows the simplest way to use QSpinBox. It accepts values
from -20 to 20, the current value can be increased or decreased by 1 with
either the arrow buttons or \uicontrol{Up} and \uicontrol{Down} keys, and the default
value is 0.
The second spin box uses a larger step size and displays a suffix to
provide more information about the type of data the number represents:
\snippet widgets/spinboxes/window.cpp 2
This spin box also displays a
\l{QAbstractSpinBox::specialValueText}{special value} instead of the minimum
value defined for it. This means that it will never show \uicontrol{0%}, but will
display \uicontrol{Automatic} when the minimum value is selected.
The third spin box shows how a prefix can be used:
\snippet widgets/spinboxes/window.cpp 4
For simplicity, we show a spin box with a prefix and no suffix. It is also
possible to use both at the same time.
\snippet widgets/spinboxes/window.cpp 5
The rest of the function sets up a layout for the group box and places each
of the widgets inside it.
The \c createDateTimeEdits() function constructs another group box with a
selection of spin boxes used for editing dates and times.
\snippet widgets/spinboxes/window.cpp 6
The first spin box is a QDateEdit widget that is able to accept dates
within a given range specified using QDate values. The arrow buttons and
\uicontrol{Up} and \uicontrol{Down} keys can be used to increase and decrease the
values for year, month, and day when the cursor is in the relevant section.
The second spin box is a QTimeEdit widget:
\snippet widgets/spinboxes/window.cpp 7
Acceptable values for the time are defined using QTime values.
The third spin box is a QDateTimeEdit widget that can display both date and
time values, and we place a label above it to indicate the range of allowed
times for a meeting. These widgets will be updated when the user changes a
format string.
\snippet widgets/spinboxes/window.cpp 8
The format string used for the date time editor, which is also shown in the
string displayed by the label, is chosen from a set of strings in a combobox:
\snippet widgets/spinboxes/window.cpp 9
\codeline
\snippet widgets/spinboxes/window.cpp 10
A signal from this combobox is connected to a slot in the \c Window class
(shown later).
\snippet widgets/spinboxes/window.cpp 11
Each child widget of the group box in placed in a layout.
The \c setFormatString() slot is called whenever the user selects a new
format string in the combobox. The display format for the QDateTimeEdit
widget is set using the raw string passed by the signal:
\snippet widgets/spinboxes/window.cpp 12
Depending on the visible sections in the widget, we set a new date or time
range, and update the associated label to provide relevant information for
the user:
\snippet widgets/spinboxes/window.cpp 13
When the format string is changed, there will be an appropriate label and
entry widget for dates, times, or both types of input.
The \c createDoubleSpinBoxes() function constructs three spin boxes that are
used to input double-precision floating point numbers:
\snippet widgets/spinboxes/window.cpp 14
Before the QDoubleSpinBox widgets are constructed, we create a spin box to
control how many decimal places they show. By default, only two decimal places
are shown in the following spin boxes, each of which is the equivalent of a
spin box in the group created by the \c createSpinBoxes() function.
The first double spin box shows a basic double-precision spin box with the
same range, step size, and default value as the first spin box in the
\c createSpinBoxes() function:
\snippet widgets/spinboxes/window.cpp 15
However, this spin box also allows non-integer values to be entered.
The second spin box displays a suffix and shows a special value instead
of the minimum value:
\snippet widgets/spinboxes/window.cpp 16
The third spin box displays a prefix instead of a suffix:
\snippet widgets/spinboxes/window.cpp 17
We connect the QSpinBox widget that specifies the precision to a slot in
the \c Window class.
\snippet widgets/spinboxes/window.cpp 18
The rest of the function places each of the widgets into a layout for the
group box.
The \c changePrecision() slot is called when the user changes the value in
the precision spin box:
\snippet widgets/spinboxes/window.cpp 19
This function simply uses the integer supplied by the signal to specify the
number of decimal places in each of the QDoubleSpinBox widgets. Each one
of these will be updated automatically when their
\l{QDoubleSpinBox::decimals}{decimals} property is changed.
*/

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/spreadsheet
\title Spreadsheet
\ingroup examples-itemviews
\brief The Spreadsheet example shows how to create a simple spreadsheet application.
\brief The Spreadsheet example shows how a table view can be used to create a
simple spreadsheet application. Custom delegates are used to render different
types of data in distinctive colors.
\image spreadsheet-demo.png
*/

View File

@ -0,0 +1,12 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example dialogs/standarddialogs
\title Standard Dialogs Example
\ingroup examples-dialogs
\brief The Standard Dialogs example shows the standard dialogs that are provided by Qt.
\image standarddialogs-example.png
*/

View File

@ -0,0 +1,272 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example itemviews/stardelegate
\title Star Delegate Example
\ingroup examples-itemviews
\brief The Star Delegate example shows how to create a delegate that
can paint itself and that supports editing.
\image stardelegate.png The Star Delegate Example
When displaying data in a QListView, QTableView, or QTreeView,
the individual items are drawn by a
\l{Delegate Classes}{delegate}. Also, when the user starts
editing an item (for example, by double-clicking the item), the delegate
provides an editor widget that is placed on top of the item while
editing takes place.
Delegates are subclasses of QAbstractItemDelegate. Qt provides
QStyledItemDelegate, which inherits QAbstractItemDelegate and handles
the most common data types (notably \c int and QString). If we
need to support custom data types, or want to customize the
rendering or the editing for existing data types, we can subclass
QAbstractItemDelegate or QStyledItemDelegate. See \l{Delegate Classes}
for more information about delegates, and \l{Model/View
Programming} if you need a high-level introduction to Qt's
model/view architecture (including delegates).
In this example, we will see how to implement a custom delegate
to render and edit a "star rating" data type, which can store
values such as "1 out of 5 stars".
The example consists of the following classes:
\list
\li \c StarRating is the custom data type. It stores a rating
expressed as stars, such as "2 out of 5 stars" or "5 out of
6 stars".
\li \c StarDelegate inherits QStyledItemDelegate and provides support
for \c StarRating (in addition to the data types already
handled by QStyledItemDelegate).
\li \c StarEditor inherits QWidget and is used by \c StarDelegate
to let the user edit a star rating using the mouse.
\endlist
To show the \c StarDelegate in action, we will fill a
QTableWidget with some data and install the delegate on it.
\section1 StarDelegate Class Definition
Here's the definition of the \c StarDelegate class:
\snippet itemviews/stardelegate/stardelegate.h 0
All public functions are reimplemented virtual functions from
QStyledItemDelegate to provide custom rendering and editing.
\section1 StarDelegate Class Implementation
The \l{QAbstractItemDelegate::}{paint()} function is
reimplemented from QStyledItemDelegate and is called whenever the view
needs to repaint an item:
\snippet itemviews/stardelegate/stardelegate.cpp 0
The function is invoked once for each item, represented by a
QModelIndex object from the model. If the data stored in the item
is a \c StarRating, we paint it ourselves; otherwise, we let
QStyledItemDelegate paint it for us. This ensures that the \c
StarDelegate can handle the most common data types.
If the item is a \c StarRating, we draw the background if the
item is selected, and we draw the item using \c StarRating::paint(),
which we will review later.
\c{StartRating}s can be stored in a QVariant thanks to the
Q_DECLARE_METATYPE() macro appearing in \c starrating.h. More on
this later.
The \l{QAbstractItemDelegate::}{createEditor()} function is
called when the user starts editing an item:
\snippet itemviews/stardelegate/stardelegate.cpp 2
If the item is a \c StarRating, we create a \c StarEditor and
connect its \c editingFinished() signal to our \c
commitAndCloseEditor() slot, so we can update the model when the
editor closes.
Here's the implementation of \c commitAndCloseEditor():
\snippet itemviews/stardelegate/stardelegate.cpp 5
When the user is done editing, we emit
\l{QAbstractItemDelegate::}{commitData()} and
\l{QAbstractItemDelegate::}{closeEditor()} (both declared in
QAbstractItemDelegate), to tell the model that there is edited
data and to inform the view that the editor is no longer needed.
The \l{QAbstractItemDelegate::}{setEditorData()} function is
called when an editor is created to initialize it with data
from the model:
\snippet itemviews/stardelegate/stardelegate.cpp 3
We simply call \c setStarRating() on the editor.
The \l{QAbstractItemDelegate::}{setModelData()} function is
called to commit data from the editor to the model when editing
is finished:
\snippet itemviews/stardelegate/stardelegate.cpp 4
The \c sizeHint() function returns an item's preferred size:
\snippet itemviews/stardelegate/stardelegate.cpp 1
We simply forward the call to \c StarRating.
\section1 StarEditor Class Definition
The \c StarEditor class was used when implementing \c
StarDelegate. Here's the class definition:
\snippet itemviews/stardelegate/stareditor.h 0
The class lets the user edit a \c StarRating by moving the mouse
over the editor. It emits the \c editingFinished() signal when
the user clicks on the editor.
The protected functions are reimplemented from QWidget to handle
mouse and paint events. The private function \c starAtPosition()
is a helper function that returns the number of the star under
the mouse pointer.
\section1 StarEditor Class Implementation
Let's start with the constructor:
\snippet itemviews/stardelegate/stareditor.cpp 0
We enable \l{QWidget::setMouseTracking()}{mouse tracking} on the
widget so we can follow the cursor even when the user doesn't
hold down any mouse button. We also turn on QWidget's
\l{QWidget::autoFillBackground}{auto-fill background} feature to
obtain an opaque background. (Without the call, the view's
background would shine through the editor.)
The \l{QWidget::}{paintEvent()} function is reimplemented from
QWidget:
\snippet itemviews/stardelegate/stareditor.cpp 1
We simply call \c StarRating::paint() to draw the stars, just
like we did when implementing \c StarDelegate.
\snippet itemviews/stardelegate/stareditor.cpp 2
In the mouse event handler, we call \c setStarCount() on the
private data member \c myStarRating to reflect the current cursor
position, and we call QWidget::update() to force a repaint.
\snippet itemviews/stardelegate/stareditor.cpp 3
When the user releases a mouse button, we simply emit the \c
editingFinished() signal.
\snippet itemviews/stardelegate/stareditor.cpp 4
The \c starAtPosition() function uses basic linear algebra to
find out which star is under the cursor.
\section1 StarRating Class Definition
\snippet itemviews/stardelegate/starrating.h 0
\codeline
\snippet itemviews/stardelegate/starrating.h 1
The \c StarRating class represents a rating as a number of stars.
In addition to holding the data, it is also capable of painting
the stars on a QPaintDevice, which in this example is either a
view or an editor. The \c myStarCount member variable stores the
current rating, and \c myMaxStarCount stores the highest possible
rating (typically 5).
The \c Q_DECLARE_METATYPE() macro makes the type \c StarRating known
to QVariant, making it possible to store \c StarRating values in
QVariant.
\section1 StarRating Class Implementation
The constructor initializes \c myStarCount and \c myMaxStarCount,
and sets up the polygons used to draw stars and diamonds:
\snippet itemviews/stardelegate/starrating.cpp 0
The \c paint() function paints the stars in this \c StarRating
object on a paint device:
\snippet itemviews/stardelegate/starrating.cpp 2
We first set the pen and brush we will use for painting. The \c
mode parameter can be either \c Editable or \c ReadOnly. If \c
mode is editable, we use the \l{QPalette::}{Highlight} color
instead of the \l{QPalette::}{WindowText} color to draw the
stars.
Then we draw the stars. If we are in \c Edit mode, we paint
diamonds in place of stars if the rating is less than the highest
rating.
The \c sizeHint() function returns the preferred size for an area
to paint the stars on:
\snippet itemviews/stardelegate/starrating.cpp 1
The preferred size is just enough to paint the maximum number of
stars. The function is called by both \c StarDelegate::sizeHint()
and \c StarEditor::sizeHint().
\section1 The \c main() Function
Here's the program's \c main() function:
\snippet itemviews/stardelegate/main.cpp 5
The \c main() function creates a QTableWidget and sets a \c
StarDelegate on it. \l{QAbstractItemView::}{DoubleClicked} and
\l{QAbstractItemView::}{SelectedClicked} are set as
\l{QAbstractItemView::editTriggers()}{edit triggers}, so that the
editor is opened with a single click when the star rating item is
selected.
The \c populateTableWidget() function fills the QTableWidget with
data:
\snippet itemviews/stardelegate/main.cpp 0
\snippet itemviews/stardelegate/main.cpp 1
\dots
\snippet itemviews/stardelegate/main.cpp 2
\snippet itemviews/stardelegate/main.cpp 3
\codeline
\snippet itemviews/stardelegate/main.cpp 4
Notice the call to QVariant::fromValue to convert a \c
StarRating to a QVariant.
\section1 Possible Extensions and Suggestions
There are many ways to customize Qt's \l{Model/View
Programming}{model/view framework}. The approach used in this
example is appropriate for most custom delegates and editors.
Examples of possibilities not used by the star delegate and star
editor are:
\list
\li It is possible to open editors programmatically by calling
QAbstractItemView::edit(), instead of relying on edit
triggers. This could be used to support other edit triggers
than those offered by the QAbstractItemView::EditTrigger enum.
For example, in the Star Delegate example, hovering over an
item with the mouse might make sense as a way to pop up an
editor.
\li By reimplementing QAbstractItemDelegate::editorEvent(), it is
possible to implement the editor directly in the delegate,
instead of creating a separate QWidget subclass.
\endlist
*/

View File

@ -0,0 +1,139 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/styleplugin
\title Style Plugin Example
\ingroup examples-widgets-tools
\brief This example shows how to create a plugin that extends Qt with a new
GUI look and feel.
\image stylepluginexample.png
A plugin in Qt is a class stored in a shared library that can be
loaded by a QPluginLoader at run-time. When you create plugins in
Qt, they either extend a Qt application or Qt itself. Writing a
plugin that extends Qt itself is achieved by inheriting one of the
plugin \l{Plugin Classes}{base classes}, reimplementing functions
from that class, and adding a macro. In this example we extend Qt
by adding a new GUI look and feel (i.e., making a new QStyle
available). A high-level introduction to plugins is given in the
plugin \l{How to Create Qt Plugins}{overview document}.
Plugins that provide new styles inherit the QStylePlugin base
class. Style plugins are loaded by Qt and made available through
QStyleFactory; we will look at this later. We have implemented \c
SimpleStylePlugin, which provides \c SimpleStyle. The new style
contributes to widget styling by changing the text color of the
text edit widget to red - not a major contribution, but it still
makes a new style.
The new style is platform agnostic in the sense that it is not
based on any specific style implementation, but uses QProxyStyle
to merely tweak the looks in the current application style that
defaults to the native system style.
\note On some platforms, the native style may overwrite some custom
stylings, e.g., button background color. In that case, try to run
your application in another style (e.g., fusion). You may do this
by passing \c{-style fusion} as a command line argument to your
application.
We test the plugin with \c StyleWindow, in which we display a
QTextEdit. The \c SimpleStyle and \c StyleWindow classes do not
contain any plugin specific functionality and their implementations
are trivial; we will therefore leap past them and head on to the \c
SimpleStylePlugin and the \c main() function. After we have looked
at that, we examine the plugin's \c{.pro} file.
\section1 SimpleStylePlugin Class Definition
\c SimpleStylePlugin inherits QStylePlugin and is the plugin
class.
\snippet tools/styleplugin/plugin/simplestyleplugin.h 0
\c keys() returns a list of style names that this plugin can
create, while \c create() takes such a string and returns the
QStyle corresponding to the key. Both functions are pure virtual
functions reimplemented from QStylePlugin. When an application
requests an instance of the \c SimpleStyle style, which this
plugin creates, Qt will create it with this plugin.
\section1 SimpleStylePlugin Class Implementation
Here is the implementation of \c keys():
\snippet tools/styleplugin/plugin/simplestyleplugin.cpp 0
Since this plugin only supports one style, we return a QStringList
with the class name of that style.
Here is the \c create() function:
\snippet tools/styleplugin/plugin/simplestyleplugin.cpp 1
Note that the key for style plugins are case insensitive.
The case sensitivity varies from plugin to plugin, so you need to
check this when implementing new plugins.
\section1 The \c main() function
\snippet tools/styleplugin/stylewindow/main.cpp 0
Qt loads the available style plugins when the QApplication object
is initialized. The QStyleFactory class knows about all styles and
produces them with \l{QStyleFactory::}{create()} (it is a
wrapper around all the style plugins).
\section1 The Simple Style Plugin's QMake Project File
The \c SimpleStylePlugin lives in its own directory and has
its own \c{.pro} file:
\snippet tools/styleplugin/plugin/plugin.pro 0
In the plugin \c{.pro} file we need to set the lib template as we are
building a shared library instead of an executable. We must also
set the config to plugin. We set the library to be stored in the
\c{styles} folder next to the main executable because this is a path
in which Qt will search for style plugins.
\section2 Using CMake to Set up the Simple Style Plugin
When using CMake, we use \l{qt6_add_plugin}{qt_add_plugin}
to create the \c simplestyleplugin plugin:
\snippet tools/styleplugin/plugin/CMakeLists.txt 0
On Windows and Linux, we place the plugin into the \c{styles} folder
next to the main executable, i.e., \c{styleplugin.exe}:
\snippet tools/styleplugin/plugin/CMakeLists.txt 2
And on macOS, we store the \c simplestyleplugin into the
\c{Contents/PlugIns/styles} folder of the App Bundle.
\snippet tools/styleplugin/plugin/CMakeLists.txt 1
\note On macOS, when creating an App Bundle, store the plugins in
the \c PlugIns folder and not next to the main executable in
the \c MacOS folder as the latter will cause issues during signing
and distribution of the app.
\section1 Related Articles and Examples
In addition to the plugin \l{How to Create Qt Plugins}{overview
document}, we have other examples and articles that concern
plugins.
In the \l{Echo Plugin Example}{echo plugin example} we show how to
implement plugins that extends Qt applications rather than Qt
itself, which is the case with the style plugin of this example.
The \l{Plug & Paint Example}{plug & paint} example shows how to
implement a static plugin as well as being a more involved example
on plugins that extend applications.
*/

View File

@ -0,0 +1,448 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/styles
\title Styles Example
\ingroup examples-widgets
\brief The Styles example illustrates how to create custom widget
drawing styles using Qt, and demonstrates Qt's predefined styles.
\borderedimage styles-enabledwood.png
\caption Screenshot of the Styles example
A style in Qt is a subclass of QStyle or of one of its
subclasses. Styles perform drawing on behalf of widgets. Qt
provides a whole range of predefined styles, either built into
the Qt Widgets module or found in plugins. Styles are usually
customized by subclassing QProxyStyle and reimplementing a few
virtual functions. While QProxyStyle provides a transparent way
to customize either a specific style or the appropriate platform's
default style, Qt also provides QCommonStyle as a convenient base
for full custom style implementations.
In this example, the custom style is called \c NorwegianWoodStyle
and derives from QProxyStyle. Its main features are the wooden
textures used for filling most of the widgets and its round
buttons and comboboxes.
To implement the style, we use some advanced features provided by
QPainter, such as \l{QPainter::Antialiasing}{antialiasing} (to
obtain smoother button edges), \l{QColor::alpha()}{alpha blending}
(to make the buttons appeared raised or sunken), and
\l{QPainterPath}{painter paths} (to fill the buttons and draw the
outline). We also use many features of QBrush and QPalette.
The example consists of the following classes:
\list
\li \c NorwegianWoodStyle inherits from QProxyStyle and implements
the Norwegian Wood style.
\li \c WidgetGallery is a \c QDialog subclass that shows the most
common widgets and allows the user to switch style
dynamically.
\endlist
\section1 NorwegianWoodStyle Class Definition
Here's the definition of the \c NorwegianWoodStyle class:
\snippet widgets/styles/norwegianwoodstyle.h 0
The public functions are all declared in QStyle (QProxyStyle's
grandparent class) and reimplemented here to override the Windows
look and feel. The private functions are helper functions.
\section1 NorwegianWoodStyle Class Implementation
We will now review the implementation of the \c
NorwegianWoodStyle class.
\snippet widgets/styles/norwegianwoodstyle.cpp 0
The \c standardPalette() function is reimplemented from QStyle.
It returns a QPalette with the style's preferred colors and textures.
Most styles don't need to reimplement that function. The
Norwegian Wood style reimplements it to set a "wooden" palette.
We start by defining a few \l{QColor}s that we'll need. Then we
load two PNG images. The \c : prefix in the file path indicates
that the PNG files are \l{The Qt Resource System}{embedded
resources}.
\table
\row \li \inlineimage widgets/styles/images/woodbackground.png
\li \b{woodbackground.png}
This texture is used as the background of most widgets.
The wood pattern is horizontal.
\row \li \inlineimage widgets/styles/images/woodbutton.png
\li \b{woodbutton.png}
This texture is used for filling push buttons and
comboboxes. The wood pattern is vertical and more reddish
than the texture used for the background.
\endtable
The \c midImage variable is initialized to be the same as \c
buttonImage, but then we use a QPainter and fill it with a 25%
opaque black color (a black with an \l{QColor::alpha()}{alpha
channel} of 63). The result is a somewhat darker image than \c
buttonImage. This image will be used for filling buttons that the
user is holding down.
\snippet widgets/styles/norwegianwoodstyle.cpp 1
We initialize the palette. Palettes have various
\l{QPalette::ColorRole}{color roles}, such as QPalette::Base
(used for filling text editors, item views, etc.), QPalette::Text
(used for foreground text), and QPalette::Window (used for
the background of most widgets). Each role has its own QBrush,
which usually is a plain color but can also be a brush pattern or
even a texture (a QPixmap).
In addition to the roles, palettes have several
\l{QPalette::ColorGroup}{color groups}: active, disabled, and
inactive. The active color group is used for painting widgets in
the active window. The disabled group is used for disabled
widgets. The inactive group is used for all other widgets. Most
palettes have identical active and inactive groups, while the
disabled group uses darker shades.
We initialize the QPalette object with a brown color. Qt
automatically derivates all color roles for all color groups from
that single color. We then override some of the default values. For
example, we use Qt::darkGreen instead of the default
(Qt::darkBlue) for the QPalette::Highlight role. The
QPalette::setBrush() overload that we use here sets the same
color or brush for all three color groups.
The \c setTexture() function is a private function that sets the
texture for a certain color role, while preserving the existing
color in the QBrush. A QBrush can hold both a solid color and a
texture at the same time. The solid color is used for drawing
text and other graphical elements where textures don't look good.
At the end, we set the brush for the disabled color group of the
palette. We use \c woodbackground.png as the texture for all
disabled widgets, including buttons, and use a darker color to
accompany the texture.
\image styles-disabledwood.png The Norwegian Wood style with disabled widgets
Let's move on to the other functions reimplemented from
QProxyStyle:
\snippet widgets/styles/norwegianwoodstyle.cpp 3
\snippet widgets/styles/norwegianwoodstyle.cpp 4
This QStyle::polish() overload is called once on every widget
drawn using the style. We reimplement it to set the Qt::WA_Hover
attribute on \l{QPushButton}s and \l{QComboBox}es. When this
attribute is set, Qt generates paint events when the mouse
pointer enters or leaves the widget. This makes it possible to
render push buttons and comboboxes differently when the mouse
pointer is over them.
\snippet widgets/styles/norwegianwoodstyle.cpp 5
\snippet widgets/styles/norwegianwoodstyle.cpp 6
This QStyle::unpolish() overload is called to undo any
modification done to the widget in \c polish(). For simplicity,
we assume that the flag wasn't set before \c polish() was called.
In an ideal world, we would remember the original state for each
widgets (e.g., using a QMap<QWidget *, bool>) and restore it in
\c unpolish().
\snippet widgets/styles/norwegianwoodstyle.cpp 7
\snippet widgets/styles/norwegianwoodstyle.cpp 8
The \l{QStyle::pixelMetric()}{pixelMetric()} function returns the
size in pixels for a certain user interface element. By
reimplementing this function, we can affect the way certain
widgets are drawn and their size hint. Here, we return 8 as the
width around a shown in a QComboBox, ensuring that there is
enough place around the text and the arrow for the Norwegian Wood
round corners. The default value for this setting in the Windows
style is 2.
We also change the extent of \l{QScrollBar}s, i.e., the height
for a horizontal scroll bar and the width for a vertical scroll
bar, to be 4 pixels more than in the Windows style. This makes the
style a bit more distinctive.
For all other QStyle::PixelMetric elements, we use the Windows
settings.
\snippet widgets/styles/norwegianwoodstyle.cpp 9
\snippet widgets/styles/norwegianwoodstyle.cpp 10
The \l{QStyle::styleHint()}{styleHint()} function returns some
hints to widgets or to the base style (in our case QProxyStyle)
about how to draw the widgets. The Windows style returns \c true
for the QStyle::SH_DitherDisabledText hint, resulting in a most
unpleasing visual effect. We override this behavior and return \c
false instead. We also return \c true for the
QStyle::SH_EtchDisabledText hint, meaning that disabled text is
rendered with an embossed look.
\snippet widgets/styles/norwegianwoodstyle.cpp 11
\snippet widgets/styles/norwegianwoodstyle.cpp 12
The \l{QStyle::drawPrimitive()}{drawPrimitive()} function is
called by Qt widgets to draw various fundamental graphical
elements. Here we reimplement it to draw QPushButton and
QComboBox with round corners. The button part of these widgets is
drawn using the QStyle::PE_PanelButtonCommand primitive element.
The \c option parameter, of type QStyleOption, contains
everything we need to know about the widget we want to draw on.
In particular, \c option->rect gives the rectangle within which
to draw the primitive element. The \c painter parameter is a
QPainter object that we can use to draw on the widget.
The \c widget parameter is the widget itself. Normally, all the
information we need is available in \c option and \c painter, so
we don't need \c widget. We can use it to perform special
effects; for example, QMacStyle uses it to animate default
buttons. If you use it, be aware that the caller is allowed to
pass a null pointer.
We start by defining three \l{QColor}s that we'll need later on.
We also put the x, y, width, and height components of the
widget's rectangle in local variables. The value used for the \c
semiTransparentWhite and for the \c semiTransparentBlack color's
alpha channel depends on whether the mouse cursor is over the
widget or not. Since we set the Qt::WA_Hover attribute on
\l{QPushButton}s and \l{QComboBox}es, we can rely on the
QStyle::State_MouseOver flag to be set when the mouse is over the
widget.
\snippet widgets/styles/norwegianwoodstyle.cpp 13
\snippet widgets/styles/norwegianwoodstyle.cpp 14
The \c roundRect variable is a QPainterPath. A QPainterPath is is
a vectorial specification of a shape. Any shape (rectangle,
ellipse, spline, etc.) or combination of shapes can be expressed
as a path. We will use \c roundRect both for filling the button
background with a wooden texture and for drawing the outline. The
\c roundRectPath() function is a private function; we will come
back to it later.
\snippet widgets/styles/norwegianwoodstyle.cpp 15
\snippet widgets/styles/norwegianwoodstyle.cpp 16
\snippet widgets/styles/norwegianwoodstyle.cpp 17
\snippet widgets/styles/norwegianwoodstyle.cpp 18
We define two variables, \c brush and \c darker, and initialize
them based on the state of the button:
\list
\li If the button is a \l{QPushButton::flat}{flat button}, we use
the \l{QPalette::Window}{Window} brush. We set \c
darker to \c true if the button is
\l{QAbstractButton::down}{down} or
\l{QAbstractButton::checked}{checked}.
\li If the button is currently held down by the user or in the
\l{QAbstractButton::checked}{checked} state, we use the
\l{QPalette::Mid}{Mid} component of the palette. We set
\c darker to \c true if the button is
\l{QAbstractButton::checked}{checked}.
\li Otherwise, we use the \l{QPalette::Button}{Button} component
of the palette.
\endlist
The screenshot below illustrates how \l{QPushButton}s are
rendered based on their state:
\image styles-woodbuttons.png Norwegian Wood buttons in different states
To discover whether the button is flat or not, we need to cast
the \c option parameter to QStyleOptionButton and check if the
\l{QStyleOptionButton::features}{features} member specifies the
QStyleOptionButton::Flat flag. The qstyleoption_cast() function
performs a dynamic cast; if \c option is not a
QStyleOptionButton, qstyleoption_cast() returns a null pointer.
\snippet widgets/styles/norwegianwoodstyle.cpp 19
\snippet widgets/styles/norwegianwoodstyle.cpp 20
\snippet widgets/styles/norwegianwoodstyle.cpp 21
\snippet widgets/styles/norwegianwoodstyle.cpp 22
\snippet widgets/styles/norwegianwoodstyle.cpp 23
We turn on antialiasing on QPainter. Antialiasing is a technique
that reduces the visual distortion that occurs when the edges of
a shape are converted into pixels. For the Norwegian Wood style,
we use it to obtain smoother edges for the round buttons.
\image styles-aliasing.png Norwegian wood buttons with and without antialiasing
The first call to QPainter::fillPath() draws the background of
the button with a wooden texture. The second call to
\l{QPainter::fillPath()}{fillPath()} paints the same area with a
semi-transparent black color (a black color with an alpha channel
of 63) to make the area darker if \c darker is true.
\snippet widgets/styles/norwegianwoodstyle.cpp 24
\snippet widgets/styles/norwegianwoodstyle.cpp 25
Next, we draw the outline. The top-left half of the outline and
the bottom-right half of the outline are drawn using different
\l{QPen}s to produce a 3D effect. Normally, the top-left half of
the outline is drawn lighter whereas the bottom-right half is
drawn darker, but if the button is
\l{QAbstractButton::down}{down} or
\l{QAbstractButton::checked}{checked}, we invert the two
\l{QPen}s to give a sunken look to the button.
\snippet widgets/styles/norwegianwoodstyle.cpp 26
We draw the top-left part of the outline by calling
QPainter::drawPath() with an appropriate
\l{QPainter::setClipRegion()}{clip region}. If the
\l{QStyleOption::direction}{layout direction} is right-to-left
instead of left-to-right, we swap the \c x1, \c x2, \c x3, and \c
x4 variables to obtain correct results. On right-to-left desktop,
the "light" comes from the top-right corner of the screen instead
of the top-left corner; raised and sunken widgets must be drawn
accordingly.
The diagram below illustrates how 3D effects are drawn according
to the layout direction. The area in red on the diagram
corresponds to the \c topHalf polygon:
\image styles-3d.png
An easy way to test how a style looks in right-to-left mode is to
pass the \c -reverse command-line option to the application. This
option is recognized by the QApplication constructor.
\snippet widgets/styles/norwegianwoodstyle.cpp 32
\snippet widgets/styles/norwegianwoodstyle.cpp 33
\snippet widgets/styles/norwegianwoodstyle.cpp 34
The bottom-right part of the outline is drawn in a similar
fashion. Then we draw a one-pixel wide outline around the entire
button, using the \l{QPalette::WindowText}{WindowText} component
of the QPalette.
This completes the QStyle::PE_PanelButtonCommand case of the \c
switch statement. Other primitive elements are handled by the
base style. Let's now turn to the other \c NorwegianWoodStyle
member functions:
\snippet widgets/styles/norwegianwoodstyle.cpp 35
\snippet widgets/styles/norwegianwoodstyle.cpp 36
We reimplement QStyle::drawControl() to draw the text on a
QPushButton in a bright color when the button is
\l{QAbstractButton::down}{down} or
\l{QAbstractButton::checked}{checked}.
If the \c option parameter points to a QStyleOptionButton object
(it normally should), we take a copy of the object and modify its
\l{QStyleOption::palette}{palette} member to make the
QPalette::ButtonText be the same as the QPalette::BrightText
component (unless the widget is disabled).
\snippet widgets/styles/norwegianwoodstyle.cpp 37
\snippet widgets/styles/norwegianwoodstyle.cpp 38
The \c setTexture() function is a private function that sets the
\l{QBrush::texture()}{texture} component of the \l{QBrush}es for
a certain \l{QPalette::ColorRole}{color role}, for all three
\l{QPalette::ColorGroup}{color groups} (active, disabled,
inactive). We used it to initialize the Norwegian Wood palette in
\c standardPalette.
\snippet widgets/styles/norwegianwoodstyle.cpp 39
\snippet widgets/styles/norwegianwoodstyle.cpp 40
The \c roundRectPath() function is a private function that
constructs a QPainterPath object for round buttons. The path
consists of eight segments: four arc segments for the corners and
four lines for the sides.
With around 250 lines of code, we have a fully functional custom
style based on one of the predefined styles. Custom styles can be
used to provide a distinct look to an application or family of
applications.
\section1 WidgetGallery Class
For completeness, we will quickly review the \c WidgetGallery
class, which contains the most common Qt widgets and allows the
user to change style dynamically. Here's the class definition:
\snippet widgets/styles/widgetgallery.h 0
\dots
\snippet widgets/styles/widgetgallery.h 1
Here's the \c WidgetGallery constructor:
\snippet widgets/styles/widgetgallery.cpp 0
We start by creating child widgets. The \uicontrol Style combobox is
initialized with all the styles known to QStyleFactory, in
addition to \c NorwegianWood. The \c create...() functions are
private functions that set up the various parts of the \c
WidgetGallery.
\snippet widgets/styles/widgetgallery.cpp 1
\snippet widgets/styles/widgetgallery.cpp 2
We connect the \uicontrol Style combobox to the \c changeStyle()
private slot, the \uicontrol{Use style's standard palette} check box to
the \c changePalette() slot, and the \uicontrol{Disable widgets} check
box to the child widgets'
\l{QWidget::setDisabled()}{setDisabled()} slot.
\snippet widgets/styles/widgetgallery.cpp 3
\snippet widgets/styles/widgetgallery.cpp 4
Finally, we put the child widgets in layouts.
\snippet widgets/styles/widgetgallery.cpp 5
\snippet widgets/styles/widgetgallery.cpp 6
When the user changes the style in the combobox, we call
QApplication::setStyle() to dynamically change the style of the
application.
\snippet widgets/styles/widgetgallery.cpp 7
\snippet widgets/styles/widgetgallery.cpp 8
If the user turns the \uicontrol{Use style's standard palette} on, the
current style's \l{QStyle::standardPalette()}{standard palette}
is used; otherwise, the system's default palette is honored.
\snippet widgets/styles/widgetgallery.cpp 9
\snippet widgets/styles/widgetgallery.cpp 10
The \c advanceProgressBar() slot is called at regular intervals
to advance the progress bar. Since we don't know how long the
user will keep the Styles application running, we use a
logarithmic formula: The closer the progress bar gets to 100%,
the slower it advances.
We will review \c createProgressBar() in a moment.
\snippet widgets/styles/widgetgallery.cpp 11
\snippet widgets/styles/widgetgallery.cpp 12
The \c createTopLeftGroupBox() function creates the QGroupBox
that occupies the top-left corner of the \c WidgetGallery. We
skip the \c createTopRightGroupBox(), \c
createBottomLeftTabWidget(), and \c createBottomRightGroupBox()
functions, which are very similar.
\snippet widgets/styles/widgetgallery.cpp 13
In \c createProgressBar(), we create a QProgressBar at the bottom
of the \c WidgetGallery and connect its
\l{QTimer::timeout()}{timeout()} signal to the \c
advanceProgressBar() slot.
*/

View File

@ -0,0 +1,64 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/stylesheet
\title Style Sheet Example
\ingroup examples-widgets
\brief The Style Sheet Example shows how to use style sheets.
\borderedimage stylesheet-pagefold.png
\caption Screen Shot of the Pagefold style sheet
The Style Sheet example shows how widgets can be styled using Qt Style Sheets.
You can open the style editor by selecting \uicontrol File > \uicontrol Edit Style Sheet,
to select an existing style sheet or design your own style and load it.
The Style Sheet example consists of 2 classes:
\list
\li \c MainWindow
\li \c StyleSheetEditor
\endlist
\section1 MainWindow Class
\c MainWindow inherits QWidget, and is the application's main window defined in
\c mainwindow.ui. The style of \c MainWindow can be modified with \c StyleSheetEditor.
\section1 StyleSheetEditor Class
\c StyleSheetEditor enables you to open an editor where you can load an existing style sheet.
It is also possible to define a new stylesheet and load it. Its layout is defined in
\c stylesheeteditor.ui.
\quotefromfile widgets/stylesheet/stylesheeteditor.cpp
\skipto setStyleName
\printline setStyleName
Sets the specified \a styleName and grays the \c applyButton.
\skipto setStyleSheetName
\printline setStyleSheetName
Loads the stylesheet from \c styleSheetName.
\skipto setModified()
\printline setModified()
Enables the \c applyButton when the text in the buffer has changed.
\skipto apply()
\printline apply()
Sets the stylesheet properties in \l qApp and disables the \c applyButton.
\skipto loadStyleSheet(const QString &sheetName)
\printline loadStyleSheet(const QString &sheetName)
Loads the specified \a sheetName, and sets its properties in
\l qApp.
*/

View File

@ -0,0 +1,222 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example richtext/syntaxhighlighter
\title Syntax Highlighter Example
\ingroup examples-richtext
\brief The Syntax Highlighter example shows how to perform
simple syntax highlighting.
\brief The Syntax Highlighter example shows how to perform simple syntax
highlighting by subclassing the QSyntaxHighlighter class.
\image syntaxhighlighter-example.png
The Syntax Highlighter application displays C++ files with custom
syntax highlighting.
The example consists of two classes:
\list
\li The \c Highlighter class defines and applies the
highlighting rules.
\li The \c MainWindow widget is the application's main window.
\endlist
We will first review the \c Highlighter class to see how you can
customize the QSyntaxHighlighter class to fit your preferences,
then we will take a look at the relevant parts of the \c
MainWindow class to see how you can use your custom highlighter
class in an application.
\section1 Highlighter Class Definition
\snippet richtext/syntaxhighlighter/highlighter.h 0
To provide your own syntax highlighting, you must subclass
QSyntaxHighlighter, reimplement the \l
{QSyntaxHighlighter::highlightBlock()}{highlightBlock()} function,
and define your own highlighting rules.
We have chosen to store our highlighting rules using a private
struct: A rule consists of a QRegularExpression pattern and a
QTextCharFormat instance. The various rules are then stored using a
QList.
The QTextCharFormat class provides formatting information for
characters in a QTextDocument specifying the visual properties of
the text, as well as information about its role in a hypertext
document. In this example, we will only define the font weight and
color using the QTextCharFormat::setFontWeight() and
QTextCharFormat::setForeground() functions.
\section1 Highlighter Class Implementation
When subclassing the QSyntaxHighlighter class you must pass the
parent parameter to the base class constructor. The parent is the
text document upon which the syntax highlighting will be
applied. In this example, we have also chosen to define our
highlighting rules in the constructor:
\snippet richtext/syntaxhighlighter/highlighter.cpp 0
\snippet richtext/syntaxhighlighter/highlighter.cpp 1
First we define a keyword rule which recognizes the most common
C++ keywords. We give the \c keywordFormat a bold, dark blue
font. For each keyword, we assign the keyword and the specified
format to a HighlightingRule object and append the object to our
list of rules.
\snippet richtext/syntaxhighlighter/highlighter.cpp 2
\codeline
\snippet richtext/syntaxhighlighter/highlighter.cpp 4
\codeline
\snippet richtext/syntaxhighlighter/highlighter.cpp 5
Then we create a format that we will apply to Qt class names. The
class names will be rendered with a dark magenta color and a bold
style. We specify a string pattern that is actually a regular
expression capturing all Qt class names. Then we assign the
regular expression and the specified format to a HighlightingRule
object and append the object to our list of rules.
We also define highlighting rules for quotations and functions
using the same approach: The patterns have the form of regular
expressions and are stored in HighlightingRule objects with the
associated format.
\snippet richtext/syntaxhighlighter/highlighter.cpp 3
\codeline
\snippet richtext/syntaxhighlighter/highlighter.cpp 6
The C++ language has two variations of comments: The single line
comment (\c //) and the multiline comment (\c{/*...*}\c{/}). The single
line comment can easily be defined through a highlighting rule
similar to the previous ones. But the multiline comment needs
special care due to the design of the QSyntaxHighlighter class.
After a QSyntaxHighlighter object is created, its \l
{QSyntaxHighlighter::highlightBlock()}{highlightBlock()} function
will be called automatically whenever it is necessary by the rich
text engine, highlighting the given text block. The problem
appears when a comment spans several text blocks. We will take a
closer look at how this problem can be solved when reviewing the
implementation of the \c Highlighter::highlightBlock()
function. At this point we only specify the multiline comment's
color.
\snippet richtext/syntaxhighlighter/highlighter.cpp 7
The highlightBlock() function is called automatically whenever it
is necessary by the rich text engine, i.e. when there are text
blocks that have changed.
First we apply the syntax highlighting rules that we stored in the
\c highlightingRules list. For each rule (i.e. for each
HighlightingRule object) we search for the pattern in the given
text block using the QString::indexOf() function. When the first
occurrence of the pattern is found, we use the
QRegularExpressionMatch::capturedLength() function to determine the string
that will be formatted. QRegularExpressionMatch::capturedLength() returns
the length of the last matched string, or 0 if there was no match.
To perform the actual formatting the QSyntaxHighlighter class
provides the \l {QSyntaxHighlighter::setFormat()}{setFormat()}
function. This function operates on the text block that is passed
as argument to the \c highlightBlock() function. The specified
format is applied to the text from the given start position for
the given length. The formatting properties set in the given
format are merged at display time with the formatting information
stored directly in the document. Note that the document itself
remains unmodified by the format set through this function.
This process is repeated until the last occurrence of the pattern
in the current text block is found.
\snippet richtext/syntaxhighlighter/highlighter.cpp 8
To deal with constructs that can span several text blocks (like
the C++ multiline comment), it is necessary to know the end state
of the previous text block (e.g. "in comment"). Inside your \c
highlightBlock() implementation you can query the end state of the
previous text block using the
QSyntaxHighlighter::previousBlockState() function. After parsing
the block you can save the last state using
QSyntaxHighlighter::setCurrentBlockState().
The \l
{QSyntaxHighlighter::previousBlockState()}{previousBlockState()}
function return an int value. If no state is set, the returned
value is -1. You can designate any other value to identify any
given state using the \l
{QSyntaxHighlighter::setCurrentBlockState()}{setCurrentBlockState()}
function. Once the state is set, the QTextBlock keeps that value
until it is set again or until the corresponding paragraph of text
is deleted.
In this example we have chosen to use 0 to represent the "not in
comment" state, and 1 for the "in comment" state. When the stored
syntax highlighting rules are applied we initialize the current
block state to 0.
\snippet richtext/syntaxhighlighter/highlighter.cpp 9
If the previous block state was "in comment" (\c
{previousBlockState() == 1}), we start the search for an end
expression at the beginning of the text block. If the
previousBlockState() returns 0, we start the search at the
location of the first occurrence of a start expression.
\snippet richtext/syntaxhighlighter/highlighter.cpp 10
\snippet richtext/syntaxhighlighter/highlighter.cpp 11
When an end expression is found, we calculate the length of the
comment and apply the multiline comment format. Then we search for
the next occurrence of the start expression and repeat the
process. If no end expression can be found in the current text
block we set the current block state to 1, i.e. "in comment".
This completes the \c Highlighter class implementation; it is now
ready for use.
\section1 MainWindow Class Definition
Using a QSyntaxHighlighter subclass is simple; just provide your
application with an instance of the class and pass it the document
upon which you want the highlighting to be applied.
\snippet richtext/syntaxhighlighter/mainwindow.h 0
In this example we declare a pointer to a \c Highlighter instance
which we later will initialize in the private \c setupEditor()
function.
\section1 MainWindow Class Implementation
The constructor of the main window is straight forward. We first
set up the menus, then we initialize the editor and make it the
central widget of the application. Finally we set the main
window's title.
\snippet richtext/syntaxhighlighter/mainwindow.cpp 0
We initialize and install the \c Highlighter object in the private
setupEditor() convenience function:
\snippet richtext/syntaxhighlighter/mainwindow.cpp 1
First we create the font we want to use in the editor, then we
create the editor itself which is an instance of the QTextEdit
class. Before we initialize the editor with the \c MainWindow
class definition file, we create a \c Highlighter instance passing
the editor's document as argument. This is the document that the
highlighting will be applied to. Then we are done.
A QSyntaxHighlighter object can only be installed on one document
at the time, but you can easily reinstall the highlighter on
another document using the QSyntaxHighlighter::setDocument()
function. The QSyntaxHighlighter class also provides the \l
{QSyntaxHighlighter::document()}{document()} function which
returns the currently set document.
*/

View File

@ -0,0 +1,111 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example dialogs/tabdialog
\title Tab Dialog Example
\ingroup examples-dialogs
\brief The Tab Dialog example shows how to construct a tab dialog using the
QTabWidget class.
Dialogs provide an efficient way for the application to communicate
with the user, but complex dialogs suffer from the problem that they
often take up too much screen area. By using a number of tabs in a
dialog, information can be split into different categories, while
remaining accessible.
\image tabdialog-example.png
The Tab Dialog example consists of a single \c TabDialog class that
provides three tabs, each containing information about a particular
file, and two standard push buttons that are used to accept or reject
the contents of the dialog.
\section1 TabDialog Class Definition
The \c TabDialog class is a subclass of QDialog that displays a
QTabWidget and two standard dialog buttons. The class definition
only contain the class constructor and a private data member for
the QTabWidget:
\snippet dialogs/tabdialog/tabdialog.h 3
In the example, the widget will be used as a top-level window, but
we define the constructor so that it can take a parent widget. This
allows the dialog to be centered on top of an application's main
window.
\section1 TabDialog Class Implementation
The constructor calls the QDialog constructor and creates a QFileInfo
object for the specified filename.
\snippet dialogs/tabdialog/tabdialog.cpp 0
The tab widget is populated with three custom widgets that each
contain information about the file. We construct each of these
without a parent widget because the tab widget will reparent
them as they are added to it.
We create two standard push buttons, and connect each of them to
the appropriate slots in the dialog:
\snippet dialogs/tabdialog/tabdialog.cpp 1
\snippet dialogs/tabdialog/tabdialog.cpp 3
We arrange the tab widget above the buttons in the dialog:
\snippet dialogs/tabdialog/tabdialog.cpp 4
Finally, we set the dialog's title:
\snippet dialogs/tabdialog/tabdialog.cpp 5
Each of the tabs are subclassed from QWidget, and only provide
constructors.
\section1 GeneralTab Class Definition
The GeneralTab widget definition is simple because we are only interested
in displaying the contents of a widget within a tab:
\snippet dialogs/tabdialog/tabdialog.h 0
\section1 GeneralTab Class Implementation
The GeneralTab widget simply displays some information about the file
passed by the TabDialog. Various widgets for this purpose, and these
are arranged within a vertical layout:
\snippet dialogs/tabdialog/tabdialog.cpp 6
\section1 PermissionsTab Class Definition
Like the GeneralTab, the PermissionsTab is just used as a placeholder
widget for its children:
\snippet dialogs/tabdialog/tabdialog.h 1
\section1 PermissionsTab Class Implementation
The PermissionsTab shows information about the file's access information,
displaying details of the file permissions and owner in widgets that are
arranged in nested layouts:
\snippet dialogs/tabdialog/tabdialog.cpp 7
\section1 ApplicationsTab Class Definition
The ApplicationsTab is another placeholder widget that is mostly
cosmetic:
\snippet dialogs/tabdialog/tabdialog.h 2
\section1 ApplicationsTab Class Implementation
The ApplicationsTab does not show any useful information, but could be
used as a template for a more complicated example:
\snippet dialogs/tabdialog/tabdialog.cpp 8
*/

View File

@ -0,0 +1,337 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/tablet
\title Tablet Example
\ingroup examples-widgets
\brief This example shows how to use a Wacom tablet in Qt applications.
\image tabletexample.png
When you use a tablet with Qt applications, \l{QTabletEvent}s are
generated. You need to reimplement the \l{QWidget::}{tabletEvent()} event
handler if you want to handle tablet events. Events are generated when the
tool (stylus) used for drawing enters and leaves the proximity of the
tablet (i.e., when it is close but not pressed down on it), when the tool
is pressed down and released from it, when the tool is moved across the
tablet, and when one of the buttons on the tool is pressed or released.
The information available in QTabletEvent depends on the device used.
This example can handle a tablet with up to three different drawing tools:
a stylus, an airbrush, and an art pen. For any of these the event will
contain the position of the tool, pressure on the tablet, button status,
vertical tilt, and horizontal tilt (i.e, the angle between the device and
the perpendicular of the tablet, if the tablet hardware can provide it).
The airbrush has a finger wheel; the position of this is also available
in the tablet event. The art pen provides rotation around the axis
perpendicular to the tablet surface, so that it can be used for calligraphy.
In this example we implement a drawing program. You can use the stylus to
draw on the tablet as you use a pencil on paper. When you draw with the
airbrush you get a spray of virtual paint; the finger wheel is used to
change the density of the spray. When you draw with the art pen, you get a
a line whose width and endpoint angle depend on the rotation of the pen.
The pressure and tilt can also be assigned to change the alpha and
saturation values of the color and the width of the stroke.
The example consists of the following:
\list
\li The \c MainWindow class inherits QMainWindow, creates
the menus, and connects their slots and signals.
\li The \c TabletCanvas class inherits QWidget and
receives tablet events. It uses the events to paint onto an
offscreen pixmap, and then renders it.
\li The \c TabletApplication class inherits QApplication. This
class handles tablet proximity events.
\li The \c main() function creates a \c MainWindow and shows it
as a top level window.
\endlist
\section1 MainWindow Class Definition
The \c MainWindow creates a \c TabletCanvas and sets it as its
center widget.
\snippet widgets/tablet/mainwindow.h 0
\c createMenus() sets up the menus with the actions. We have one
QActionGroup for the actions that alter the alpha channel, color saturation
and line width respectively. The action groups are connected to the
\c setAlphaValuator(), \c setSaturationValuator(), and
\c setLineWidthValuator() slots, which call functions in \c TabletCanvas.
\section1 MainWindow Class Implementation
We start with a look at the constructor \c MainWindow():
\snippet widgets/tablet/mainwindow.cpp 0
In the constructor we call \c createMenus() to create all the actions and
menus, and set the canvas as the center widget.
\snippet widgets/tablet/mainwindow.cpp 8
At the beginning of \c createMenus() we populate the \b File menu.
We use an overload of \l{QMenu::}{addAction()}, introduced in Qt 5.6, to create
a menu item with a shortcut (and optionally an icon), add it to its menu,
and connect it to a slot, all with one line of code. We use QKeySequence to
get the platform-specific standard key shortcuts for these common menu items.
We also populate the \b Brush menu. The command to change a brush does not
normally have a standard shortcut, so we use \l{QObject::}{tr()} to enable
translating the shortcut along with the language translation of the application.
Now we will look at the creation of one group of mutually-exclusive actions
in a submenu of the \b Tablet menu, for selecting which property of each
QTabletEvent will be used to vary the translucency (alpha channel) of the
line being drawn or color being airbrushed.
(See the \l{Qt Widgets - Application Example}{application example} if you want
a high-level introduction to QActions.)
\snippet widgets/tablet/mainwindow.cpp 9
We want the user to be able to choose whether the drawing color's alpha
component should be modulated by the tablet pressure, tilt, or the position
of the thumbwheel on the airbrush tool. We have one action for each choice,
and an additional action to choose not to change the alpha, that is, to keep
the color opaque. We make the actions checkable; the \c alphaChannelGroup
will then ensure that only one of the actions are checked at any time. The
\c triggered() signal is emitted from the group when an action is checked,
so we connect that to \c MainWindow::setAlphaValuator(). It will need to know
which property (valuator) of the QTabletEvent to pay attention to from now
on, so we use the QAction::data property to pass this information along.
(In order for this to be possible, the enum \c Valuator must be a registered
metatype, so that it can be inserted into a QVariant. That is accomplished
by the \c Q_ENUM declaration in tabletcanvas.h.)
Here is the implementation of \c setAlphaValuator():
\snippet widgets/tablet/mainwindow.cpp 2
It simply needs to retrieve the \c Valuator enum from QAction::data(), and
pass that to \c TabletCanvas::setAlphaChannelValuator(). If we were not
using the \c data property, we would instead need to compare the QAction
pointer itself, for example in a switch statement. But that would require
keeping pointers to each QAction in class variables, for comparison purposes.
Here is the implementation of \c setBrushColor():
\snippet widgets/tablet/mainwindow.cpp 1
We do lazy initialization of a QColorDialog the first time the user
chooses \b {Brush color...} from the menu or via the action shortcut.
While the dialog is open, each time the user chooses a different color,
\c TabletCanvas::setColor() will be called to change the drawing color.
Because it is a non-modal dialog, the user is free to leave the color
dialog open, so as to be able to conveniently and frequently change colors,
or close it and re-open it later.
Here is the implementation of \c save():
\snippet widgets/tablet/mainwindow.cpp 5
We use the QFileDialog to let the user select a file to save the drawing,
and then call \c TabletCanvas::saveImage() to actually write it to the
file.
Here is the implementation of \c load():
\snippet widgets/tablet/mainwindow.cpp 6
We let the user select the image file to be opened with a QFileDialog; we
then ask the canvas to load the image with \c loadImage().
Here is the implementation of \c about():
\snippet widgets/tablet/mainwindow.cpp 7
We show a message box with a short description of the example.
\section1 TabletCanvas Class Definition
The \c TabletCanvas class provides a surface on which the
user can draw with a tablet.
\snippet widgets/tablet/tabletcanvas.h 0
The canvas can change the alpha channel, color saturation, and line width
of the stroke. We have an enum listing the QTabletEvent properties with
which it is possible to modulate them. We keep a private variable for each:
\c m_alphaChannelValuator, \c m_colorSaturationValuator and
\c m_lineWidthValuator, and we provide accessor functions for them.
We draw on a QPixmap with \c m_pen and \c m_brush using \c m_color.
Each time a QTabletEvent is received, the stroke is drawn from
\c lastPoint to the point given in the current QTabletEvent,
and then the position and rotation are saved in \c lastPoint for next time.
The \c saveImage() and \c loadImage() functions save and load the QPixmap to disk.
The pixmap is drawn on the widget in \c paintEvent().
The interpretation of events from the tablet is done in \c tabletEvent(), and
\c paintPixmap(), \c updateBrush(), and \c updateCursor() are helper
functions used by \c tabletEvent().
\section1 TabletCanvas Class Implementation
We start with a look at the constructor:
\snippet widgets/tablet/tabletcanvas.cpp 0
In the constructor we initialize most of our class variables.
Here is the implementation of \c saveImage():
\snippet widgets/tablet/tabletcanvas.cpp 1
QPixmap implements functionality to save itself to disk, so we
simply call \l{QPixmap::}{save()}.
Here is the implementation of \c loadImage():
\snippet widgets/tablet/tabletcanvas.cpp 2
We simply call \l{QPixmap::}{load()}, which loads the image from \a file.
Here is the implementation of \c tabletEvent():
\snippet widgets/tablet/tabletcanvas.cpp 3
We get three kind of events to this function: \c TabletPress, \c TabletRelease,
and \c TabletMove, which are generated when a drawing tool is pressed down on,
lifed up from, or moved across the tablet. We set \c m_deviceDown to \c true
when a device is pressed down on the tablet; we then know that we should
draw when we receive move events. We have implemented \c updateBrush()
to update \c m_brush and \c m_pen depending on which of the tablet event
properties the user has chosen to pay attention to. The \c updateCursor()
function selects a cursor to represent the drawing tool in use, so that
as you hover with the tool in proximity of the tablet, you can see what
kind of stroke you are about to make.
\snippet widgets/tablet/tabletcanvas.cpp 12
If an art pen (\c RotationStylus) is in use, \c updateCursor()
is also called for each \c TabletMove event, and renders a rotated cursor
so that you can see the angle of the pen tip.
Here is the implementation of \c paintEvent():
\snippet widgets/tablet/tabletcanvas.cpp 4
The first time Qt calls paintEvent(), m_pixmap is default-constructed, so
QPixmap::isNull() returns \c true. Now that we know which screen we will be
rendering to, we can create a pixmap with the appropriate resolution.
The size of the pixmap with which we fill the window depends on the screen
resolution, as the example does not support zoom; and it may be that one
screen is \l {High DPI}{high DPI} while another is not. We need to
draw the background too, as the default is gray.
After that, we simply draw the pixmap to the top left of the widget.
Here is the implementation of \c paintPixmap():
\snippet widgets/tablet/tabletcanvas.cpp 5
In this function we draw on the pixmap based on the movement of the tool.
If the tool used on the tablet is a stylus, we want to draw a line from
the last-known position to the current position. We also assume that this
is a reasonable handling of any unknown device, but update the status bar
with a warning. If it is an airbrush, we want to draw a circle filled with
a soft gradient, whose density can depend on various event parameters.
By default it depends on the tangential pressure, which is the position of
the finger wheel on the airbrush. If the tool is a rotation stylus, we
simulate a felt marker by drawing trapezoidal stroke segments.
\snippet widgets/tablet/tabletcanvas.cpp 6
In \c updateBrush() we set the pen and brush used for drawing to match
\c m_alphaChannelValuator, \c m_lineWidthValuator, \c m_colorSaturationValuator,
and \c m_color. We will examine the code to set up \c m_brush and
\c m_pen for each of these variables:
\snippet widgets/tablet/tabletcanvas.cpp 7
We fetch the current drawingcolor's hue, saturation, value,
and alpha values. \c hValue and \c vValue are set to the
horizontal and vertical tilt as a number from 0 to 255. The
original values are in degrees from -60 to 60, i.e., 0 equals
-60, 127 equals 0, and 255 equals 60 degrees. The angle measured
is between the device and the perpendicular of the tablet (see
QTabletEvent for an illustration).
\snippet widgets/tablet/tabletcanvas.cpp 8
The alpha channel of QColor is given as a number between 0 and 255 where 0
is transparent and 255 is opaque, or as a floating-point number where 0 is
transparent and 1.0 is opaque. \l{QTabletEvent::}{pressure()} returns the
pressure as a qreal between 0.0 and 1.0. We get the smallest alpha values
(i.e., the color is most transparent) when the pen is perpendicular to the
tablet. We select the largest of the vertical and horizontal tilt values.
\snippet widgets/tablet/tabletcanvas.cpp 9
The color saturation in the HSV color model can be given as an integer
between 0 and 255 or as a floating-point value between 0 and 1. We chose to
represent alpha as an integer, so we call \l{QColor::}{setHsv()} with
integer values. That means we need to multiply the pressure to a number
between 0 and 255.
\snippet widgets/tablet/tabletcanvas.cpp 10
The width of the pen stroke can increase with pressure, if so chosen.
But when the pen width is controlled by tilt, we let the width increase
with the angle between the tool and the perpendicular of the tablet.
\snippet widgets/tablet/tabletcanvas.cpp 11
We finally check whether the pointer is the stylus or the eraser.
If it is the eraser, we set the color to the background color of
the pixmap and let the pressure decide the pen width, else we set
the colors we have decided previously in the function.
\section1 TabletApplication Class Definition
We inherit QApplication in this class because we want to
reimplement the \l{QApplication::}{event()} function.
\snippet widgets/tablet/tabletapplication.h 0
\c TabletApplication exists as a subclass of QApplication in order to
receive tablet proximity events and forward them to \c TabletCanvas.
The \c TabletEnterProximity and \c TabletLeaveProximity events are sent to
the QApplication object, while other tablet events are sent to the QWidget's
\c event() handler, which sends them on to \l{QWidget::}{tabletEvent()}.
\section1 TabletApplication Class Implementation
Here is the implementation of \c event():
\snippet widgets/tablet/tabletapplication.cpp 0
We use this function to handle the \c TabletEnterProximity and
\c TabletLeaveProximity events, which are generated when a drawing
tool enters or leaves the proximity of the tablet. Here we call
\c TabletCanvas::setTabletDevice(), which then calls \c updateCursor(),
which will set an appropriate cursor. This is the only reason we
need the proximity events; for the purpose of correct drawing, it is
enough for \c TabletCanvas to observe the \l{QTabletEvent::}{device()} and
\l{QTabletEvent::}{pointerType()} in each event that it receives.
\section1 The \c main() function
Here is the example's \c main() function:
\snippet widgets/tablet/main.cpp 0
Here we create a \c MainWindow and display it as a top level window. We use
the \c TabletApplication class. We need to set the canvas after the
application is created. We cannot use classes that implement event handling
before an QApplication object is instantiated.
*/

View File

@ -0,0 +1,407 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/tetrix
\title Tetrix Example
\ingroup examples-widgets
\brief The Tetrix example is a Qt version of the classic Tetrix game.
\borderedimage tetrix-example.png
The object of the game is to stack pieces dropped from the top of the
playing area so that they fill entire rows at the bottom of the playing area.
When a row is filled, all the blocks on that row are removed, the player earns
a number of points, and the pieces above are moved down to occupy that row.
If more than one row is filled, the blocks on each row are removed, and the
player earns extra points.
The \uicontrol{Left} cursor key moves the current piece one space to the left, the
\uicontrol{Right} cursor key moves it one space to the right, the \uicontrol{Up} cursor
key rotates the piece counter-clockwise by 90 degrees, and the \uicontrol{Down}
cursor key rotates the piece clockwise by 90 degrees.
To avoid waiting for a piece to fall to the bottom of the board, press \uicontrol{D}
to immediately move the piece down by one row, or press the \uicontrol{Space} key to
drop it as close to the bottom of the board as possible.
This example shows how a simple game can be created using only three classes:
\list
\li The \c TetrixWindow class is used to display the player's score, number of
lives, and information about the next piece to appear.
\li The \c TetrixBoard class contains the game logic, handles keyboard input, and
displays the pieces on the playing area.
\li The \c TetrixPiece class contains information about each piece.
\endlist
In this approach, the \c TetrixBoard class is the most complex class, since it
handles the game logic and rendering. One benefit of this is that the
\c TetrixWindow and \c TetrixPiece classes are very simple and contain only a
minimum of code.
\section1 TetrixWindow Class Definition
The \c TetrixWindow class is used to display the game information and contains
the playing area:
\snippet widgets/tetrix/tetrixwindow.h 0
We use private member variables for the board, various display widgets, and
buttons to allow the user to start a new game, pause the current game, and quit.
Although the window inherits QWidget, the constructor does not provide an
argument to allow a parent widget to be specified. This is because the window
will always be used as a top-level widget.
\section1 TetrixWindow Class Implementation
The constructor sets up the user interface elements for the game:
\snippet widgets/tetrix/tetrixwindow.cpp 0
We begin by constructing a \c TetrixBoard instance for the playing area and a
label that shows the next piece to be dropped into the playing area; the label
is initially empty.
Three QLCDNumber objects are used to display the score, number of lives, and
lines removed. These initially show default values, and will be filled in
when a game begins:
\snippet widgets/tetrix/tetrixwindow.cpp 1
Three buttons with shortcuts are constructed so that the user can start a
new game, pause the current game, and quit the application:
\snippet widgets/tetrix/tetrixwindow.cpp 2
\snippet widgets/tetrix/tetrixwindow.cpp 3
These buttons are configured so that they never receive the keyboard focus;
we want the keyboard focus to remain with the \c TetrixBoard instance so that
it receives all the keyboard events. Nonetheless, the buttons will still respond
to \uicontrol{Alt} key shortcuts.
We connect \l{QAbstractButton::}{clicked()} signals from the \uicontrol{Start}
and \uicontrol{Pause} buttons to the board, and from the \uicontrol{Quit} button to the
application's \l{QCoreApplication::quit()} slot.
\snippet widgets/tetrix/tetrixwindow.cpp 4
\snippet widgets/tetrix/tetrixwindow.cpp 5
Signals from the board are also connected to the LCD widgets for the purpose of
updating the score, number of lives, and lines removed from the playing area.
We place the label, LCD widgets, and the board into a QGridLayout
along with some labels that we create with the \c createLabel() convenience
function:
\snippet widgets/tetrix/tetrixwindow.cpp 6
Finally, we set the grid layout on the widget, give the window a title, and
resize it to an appropriate size.
The \c createLabel() convenience function simply creates a new label on the
heap, gives it an appropriate alignment, and returns it to the caller:
\snippet widgets/tetrix/tetrixwindow.cpp 7
Since each label will be used in the widget's layout, it will become a child
of the \c TetrixWindow widget and, as a result, it will be deleted when the
window is deleted.
\section1 TetrixPiece Class Definition
The \c TetrixPiece class holds information about a piece in the game's
playing area, including its shape, position, and the range of positions it can
occupy on the board:
\snippet widgets/tetrix/tetrixpiece.h 0
Each shape contains four blocks, and these are defined by the \c coords private
member variable. Additionally, each piece has a high-level description that is
stored internally in the \c pieceShape variable.
The constructor is written inline in the definition, and simply ensures that
each piece is initially created with no shape. The \c shape() function simply
returns the contents of the \c pieceShape variable, and the \c x() and \c y()
functions return the x and y-coordinates of any given block in the shape.
\section1 TetrixPiece Class Implementation
The \c setRandomShape() function is used to select a random shape for a piece:
\snippet widgets/tetrix/tetrixpiece.cpp 0
For convenience, it simply chooses a random shape from the \c TetrixShape enum
and calls the \c setShape() function to perform the task of positioning the
blocks.
The \c setShape() function uses a look-up table of pieces to associate each
shape with an array of block positions:
\snippet widgets/tetrix/tetrixpiece.cpp 1
\snippet widgets/tetrix/tetrixpiece.cpp 2
These positions are read from the table into the piece's own array of positions,
and the piece's internal shape information is updated to use the new shape.
The \c x() and \c y() functions are implemented inline in the class definition,
returning positions defined on a grid that extends horizontally and vertically
with coordinates from -2 to 2. Although the predefined coordinates for each
piece only vary horizontally from -1 to 1 and vertically from -1 to 2, each
piece can be rotated by 90, 180, and 270 degrees.
The \c minX() and \c maxX() functions return the minimum and maximum horizontal
coordinates occupied by the blocks that make up the piece:
\snippet widgets/tetrix/tetrixpiece.cpp 3
\snippet widgets/tetrix/tetrixpiece.cpp 4
Similarly, the \c minY() and \c maxY() functions return the minimum and maximum
vertical coordinates occupied by the blocks:
\snippet widgets/tetrix/tetrixpiece.cpp 5
\snippet widgets/tetrix/tetrixpiece.cpp 6
The \c rotatedLeft() function returns a new piece with the same shape as an
existing piece, but rotated counter-clockwise by 90 degrees:
\snippet widgets/tetrix/tetrixpiece.cpp 7
Similarly, the \c rotatedRight() function returns a new piece with the same
shape as an existing piece, but rotated clockwise by 90 degrees:
\snippet widgets/tetrix/tetrixpiece.cpp 9
These last two functions enable each piece to create rotated copies of itself.
\section1 TetrixBoard Class Definition
The \c TetrixBoard class inherits from QFrame and contains the game logic and display features:
\snippet widgets/tetrix/tetrixboard.h 0
Apart from the \c setNextPieceLabel() function and the \c start() and \c pause()
public slots, we only provide public functions to reimplement QWidget::sizeHint()
and QWidget::minimumSizeHint(). The signals are used to communicate changes to
the player's information to the \c TetrixWindow instance.
The rest of the functionality is provided by reimplementations of protected event
handlers and private functions:
\snippet widgets/tetrix/tetrixboard.h 1
The board is composed of a fixed-size array whose elements correspond to
spaces for individual blocks. Each element in the array contains a \c TetrixShape
value corresponding to the type of shape that occupies that element.
Each shape on the board will occupy four elements in the array, and these will
all contain the enum value that corresponds to the type of the shape.
We use a QBasicTimer to control the rate at which pieces fall toward the bottom
of the playing area. This allows us to provide an implementation of
\l{QObject::}{timerEvent()} that we can use to update the widget.
\section1 TetrixBoard Class Implementation
In the constructor, we customize the frame style of the widget, ensure that
keyboard input will be received by the widget by using Qt::StrongFocus for the
focus policy, and initialize the game state:
\snippet widgets/tetrix/tetrixboard.cpp 0
The first (next) piece is also set up with a random shape.
The \c setNextPieceLabel() function is used to pass in an externally-constructed
label to the board, so that it can be shown alongside the playing area:
\snippet widgets/tetrix/tetrixboard.cpp 1
We provide a reasonable size hint and minimum size hint for the board, based on
the size of the space for each block in the playing area:
\snippet widgets/tetrix/tetrixboard.cpp 2
\snippet widgets/tetrix/tetrixboard.cpp 3
By using a minimum size hint, we indicate to the layout in the parent widget
that the board should not shrink below a minimum size.
A new game is started when the \c start() slot is called. This resets the
game's state, the player's score and level, and the contents of the board:
\snippet widgets/tetrix/tetrixboard.cpp 4
We also emit signals to inform other components of these changes before creating
a new piece that is ready to be dropped into the playing area. We start the
timer that determines how often the piece drops down one row on the board.
The \c pause() slot is used to temporarily stop the current game by stopping the
internal timer:
\snippet widgets/tetrix/tetrixboard.cpp 5
\snippet widgets/tetrix/tetrixboard.cpp 6
We perform checks to ensure that the game can only be paused if it is already
running and not already paused.
The \c paintEvent() function is straightforward to implement. We begin by
calling the base class's implementation of \l{QWidget::}{paintEvent()} before
constructing a QPainter for use on the board:
\snippet widgets/tetrix/tetrixboard.cpp 7
Since the board is a subclass of QFrame, we obtain a QRect that covers the area
\e inside the frame decoration before drawing our own content.
If the game is paused, we want to hide the existing state of the board and
show some text. We achieve this by painting text onto the widget and returning
early from the function. The rest of the painting is performed after this point.
The position of the top of the board is found by subtracting the total height
of each space on the board from the bottom of the frame's internal rectangle.
For each space on the board that is occupied by a piece, we call the
\c drawSquare() function to draw a block at that position.
\snippet widgets/tetrix/tetrixboard.cpp 8
\snippet widgets/tetrix/tetrixboard.cpp 9
Spaces that are not occupied by blocks are left blank.
Unlike the existing pieces on the board, the current piece is drawn
block-by-block at its current position:
\snippet widgets/tetrix/tetrixboard.cpp 10
\snippet widgets/tetrix/tetrixboard.cpp 11
\snippet widgets/tetrix/tetrixboard.cpp 12
The \c keyPressEvent() handler is called whenever the player presses a key while
the \c TetrixBoard widget has the keyboard focus.
\snippet widgets/tetrix/tetrixboard.cpp 13
If there is no current game, the game is running but paused, or if there is no
current shape to control, we simply pass on the event to the base class.
We check whether the event is about any of the keys that the player uses to
control the current piece and, if so, we call the relevant function to handle
the input:
\snippet widgets/tetrix/tetrixboard.cpp 14
In the case where the player presses a key that we are not interested in, we
again pass on the event to the base class's implementation of
\l{QWidget::}{keyPressEvent()}.
The \c timerEvent() handler is called every time the class's QBasicTimer
instance times out. We need to check that the event we receive corresponds to
our timer. If it does, we can update the board:
\snippet widgets/tetrix/tetrixboard.cpp 15
\snippet widgets/tetrix/tetrixboard.cpp 16
\snippet widgets/tetrix/tetrixboard.cpp 17
If a row (or line) has just been filled, we create a new piece and reset the
timer; otherwise we move the current piece down by one row. We let the base
class handle other timer events that we receive.
The \c clearBoard() function simply fills the board with the
\c TetrixShape::NoShape value:
\snippet widgets/tetrix/tetrixboard.cpp 18
The \c dropDown() function moves the current piece down as far as possible on
the board, either until it is touching the bottom of the playing area or it is
stacked on top of another piece:
\snippet widgets/tetrix/tetrixboard.cpp 19
\snippet widgets/tetrix/tetrixboard.cpp 20
The number of rows the piece has dropped is recorded and passed to the
\c pieceDropped() function so that the player's score can be updated.
The \c oneLineDown() function is used to move the current piece down by one row
(line), either when the user presses the \uicontrol{D} key or when the piece is
scheduled to move:
\snippet widgets/tetrix/tetrixboard.cpp 21
If the piece cannot drop down by one line, we call the \c pieceDropped() function
with zero as the argument to indicate that it cannot fall any further, and that
the player should receive no extra points for the fall.
The \c pieceDropped() function itself is responsible for awarding points to the
player for positioning the current piece, checking for full rows on the board
and, if no lines have been removed, creating a new piece to replace the current
one:
\snippet widgets/tetrix/tetrixboard.cpp 22
\snippet widgets/tetrix/tetrixboard.cpp 23
We call \c removeFullLines() each time a piece has been dropped. This scans
the board from bottom to top, looking for blank spaces on each row.
\snippet widgets/tetrix/tetrixboard.cpp 24
\snippet widgets/tetrix/tetrixboard.cpp 25
\snippet widgets/tetrix/tetrixboard.cpp 26
\snippet widgets/tetrix/tetrixboard.cpp 27
If a row contains no blank spaces, the rows above it are copied down by one row
to compress the stack of pieces, the top row on the board is cleared, and the
number of full lines found is incremented.
\snippet widgets/tetrix/tetrixboard.cpp 28
\snippet widgets/tetrix/tetrixboard.cpp 29
If some lines have been removed, the player's score and the total number of lines
removed are updated. The \c linesRemoved() and \c scoreChanged() signals are
emitted to send these new values to other widgets in the window.
Additionally, we set the timer to elapse after half a second, set the
\c isWaitingAfterLine flag to indicate that lines have been removed, unset
the piece's shape to ensure that it is not drawn, and update the widget.
The next time that the \c timerEvent() handler is called, a new piece will be
created and the game will continue.
The \c newPiece() function places the next available piece at the top of the
board, and creates a new piece with a random shape:
\snippet widgets/tetrix/tetrixboard.cpp 30
\snippet widgets/tetrix/tetrixboard.cpp 31
We place a new piece in the middle of the board at the top. The game is over if
the piece can't move, so we unset its shape to prevent it from being drawn, stop
the timer, and unset the \c isStarted flag.
The \c showNextPiece() function updates the label that shows the next piece to
be dropped:
\snippet widgets/tetrix/tetrixboard.cpp 32
\snippet widgets/tetrix/tetrixboard.cpp 33
We draw the piece's component blocks onto a pixmap that is then set on the label.
The \c tryMove() function is used to determine whether a piece can be positioned
at the specified coordinates:
\snippet widgets/tetrix/tetrixboard.cpp 34
We examine the spaces on the board that the piece needs to occupy and, if they
are already occupied by other pieces, we return \c false to indicate that the
move has failed.
\snippet widgets/tetrix/tetrixboard.cpp 35
If the piece could be placed on the board at the desired location, we update the
current piece and its position, update the widget, and return \c true to indicate
success.
The \c drawSquare() function draws the blocks (normally squares) that make up
each piece using different colors for pieces with different shapes:
\snippet widgets/tetrix/tetrixboard.cpp 36
We obtain the color to use from a look-up table that relates each shape to an
RGB value, and use the painter provided to draw the block at the specified
coordinates.
*/

View File

@ -0,0 +1,15 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example richtext/textedit
\title Text Edit
\ingroup examples-richtext
\brief The Text Edit example shows Qt's rich text editing facilities
in action.
\brief The Text Edit example shows Qt's rich text editing facilities in action,
providing an example document for you to experiment with.
\image textedit-demo.png
*/

View File

@ -0,0 +1,374 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/tooltips
\title Tool Tips Example
\ingroup examples-widgets
\brief The Tool Tips example shows how to provide static and dynamic tool
tips for an application's widgets.
The simplest and most common way to set a widget's tool tip is by
calling its QWidget::setToolTip() function (static tool
tips). Then the tool tip is shown whenever the cursor points at
the widget. We show how to do this with our application's tool
buttons. But it is also possible to show different tool tips
depending on the cursor's position (dynamic tooltips). This
approach uses mouse tracking and event handling to determine what
widgets are located under the cursor at any point in time, and
displays their tool tips. The tool tips for the shape items in our
application are implemented using the latter approach.
\image tooltips-example.png
With the \c Tooltips application the user can create new shape
items with the provided tool buttons, and move the items around
using the mouse. Tooltips are provided whenever the cursor is
pointing to a shape item or one of the buttons.
The Tooltips example consists of two classes:
\list
\li \c ShapeItem is a custom widget representing one single shape item.
\li \c SortingBox inherits from QWidget and is the application's main
widget.
\endlist
First we will review the \c SortingBox class, then we will take a
look at the \c ShapeItem class.
\section1 SortingBox Class Definition
\snippet widgets/tooltips/sortingbox.h 0
The \c SortingBox class inherits QWidget, and it is the Tooltips
application's main widget. We reimplement several of the event
handlers.
The \c event() function provides tooltips, the \c resize()
function makes sure the application appears consistently when the
user resizes the main widget, and the \c paintEvent() function
displays the shape items within the \c SortingBox widget. The
mouse event handlers are reimplemented to make the user able to
move the items around.
In addition we need three private slots to make the user able to
create new shape items.
\snippet widgets/tooltips/sortingbox.h 1
We also create several private functions: We use the \c
initialItemPosition(), \c initialItemColor() and \c
createToolButton() functions when we are constructing the widget,
and we use the \c updateButtonGeometry() function whenever the
user is resizing the application's main widget.
The \c itemAt() function determines if there is a shape item at a
particular position, and the \c moveItemTo() function moves an
item to a new position. We use the \c createShapeItem(), \c
randomItemPosition() and \c randomItemColor() functions to create
new shape items.
\snippet widgets/tooltips/sortingbox.h 2
We keep all the shape items in a QList, and we keep three
QPainterPath objects holding the shapes of a circle, a square and
a triangle. We also need to have a pointer to an item when it is
moving, and we need to know its previous position.
\section1 SortingBox Class Implementation
\snippet widgets/tooltips/sortingbox.cpp 0
In the constructor, we first set the Qt::WA_StaticContents
attribute on the widget. This attribute indicates that the widget
contents are north-west aligned and static. On resize, such a
widget will receive paint events only for the newly visible part
of itself.
\snippet widgets/tooltips/sortingbox.cpp 1
To be able to show the appropriate tooltips while the user is
moving the cursor around, we need to enable mouse tracking for the
widget.
If mouse tracking is disabled (the default), the widget only
receives mouse move events when at least one mouse button is
pressed while the mouse is being moved. If mouse tracking is
enabled, the widget receives mouse move events even if no buttons
are pressed.
\snippet widgets/tooltips/sortingbox.cpp 2
A widget's background role defines the brush from the widget's
palette that is used to render the background, and QPalette::Base
is typically white.
\snippet widgets/tooltips/sortingbox.cpp 3
After creating the application's tool buttons using the private \c
createToolButton() function, we construct the shapes of a circle,
a square and a triangle using QPainterPath.
The QPainterPath class provides a container for painting
operations, enabling graphical shapes to be constructed and
reused. The main advantage of painter paths over normal drawing
operations is that complex shapes only need to be created once,
but they can be drawn many times using only calls to
QPainter::drawPath().
\snippet widgets/tooltips/sortingbox.cpp 4
Then we set the window title, resize the widget to a suitable
size, and finally create three initial shape items using the
private \c createShapeItem(), \c initialItemPosition() and \c
initialItemColor() functions.
\snippet widgets/tooltips/sortingbox.cpp 27
In the destructor, we delete all shape items.
\snippet widgets/tooltips/sortingbox.cpp 5
QWidget::event() is the main event handler and receives all the
widget's events. Normally, we recommend reimplementing one of the
specialized event handlers instead of this function. But here we
want to catch the QEvent::ToolTip events, and since these are
rather rare, there exists no specific event handler. For that
reason we reimplement the main event handler, and the first thing
we need to do is to determine the event's type:
\snippet widgets/tooltips/sortingbox.cpp 6
If the type is QEvent::ToolTip, we cast the event to a QHelpEvent,
otherwise we propagate the event using the QWidget::event()
function.
The QHelpEvent class provides an event that is used to request
helpful information about a particular point in a widget.
For example, the QHelpEvent::pos() function returns the event's
position relative to the widget to which the event is dispatched.
Here we use this information to determine if the position of the
event is contained within the area of any of the shape items. If
it is, we display the shape item's tooltip at the position of the
event. If not, we hide the tooltip and explicitly ignore the event.
This makes sure that the calling code does not start any tooltip
specific modes as a result of the event. Note that the
QToolTip::showText() function needs the event's position in global
coordinates provided by QHelpEvent::globalPos().
\snippet widgets/tooltips/sortingbox.cpp 7
The \c resizeEvent() function is reimplemented to receive the
resize events dispatched to the widget. It makes sure that the
tool buttons keep their position relative to the main widget when
the widget is resized. We want the buttons to always be vertically
aligned in the application's bottom right corner, so each time the
main widget is resized we update the buttons geometry.
\snippet widgets/tooltips/sortingbox.cpp 8
The \c paintEvent() function is reimplemented to receive paint
events for the widget. We create a QPainter for the \c SortingBox
widget, and run through the list of created shape items, drawing
each item at its defined position.
\snippet widgets/tooltips/sortingbox.cpp 9
The painter will by default draw all the shape items at position
(0,0) in the \c SortingBox widget. The QPainter::translate()
function translates the coordinate system by the given offset,
making each shape item appear at its defined position. But
remember to translate the coordinate system back when the item is
drawn, otherwise the next shape item will appear at a position
relative to the item drawn last.
\snippet widgets/tooltips/sortingbox.cpp 10
The QPainter::setBrush() function sets the current brush used by
the painter. When the provided argument is a QColor, the function
calls the appropriate QBrush constructor which creates a brush with
the specified color and Qt::SolidPattern style. The
QPainter::drawPath() function draws the given path using the
current pen for outline and the current brush for filling.
\snippet widgets/tooltips/sortingbox.cpp 11
The \c mousePressEvent() function is reimplemented to receive the
mouse press events dispatched to the widget. It determines if an
event's position is contained within the area of any of the shape
items, using the private \c itemAt() function.
If an item covers the position, we store a pointer to that item
and the event's position. If several of the shape items cover the
position, we store the pointer to the uppermost item. Finally, we
move the shape item's pointer to the end of the list, and make
a call to the QWidget::update() function to make the item appear
on top.
The QWidget::update() function does not cause an immediate
repaint; instead it schedules a paint event for processing when Qt
returns to the main event loop.
\snippet widgets/tooltips/sortingbox.cpp 12
The \c mouseMoveEvent() function is reimplemented to receive mouse
move events for the widget. If the left mouse button is pressed
and there exists a shape item in motion, we use the private \c
moveItemTo() function to move the item with an offset
corresponding to the offset between the positions of the current
mouse event and the previous one.
\snippet widgets/tooltips/sortingbox.cpp 13
The \c mouseReleaseEvent() function is reimplemented to receive
the mouse release events dispatched to the widget. If the left
mouse button is pressed and there exists a shape item in motion,
we use the private \c moveItemTo() function to move the item like
we did in \c mouseMoveEvent(). But then we remove the pointer to
the item in motion, making the shape item's position final for
now. To move the item further, the user will need to press the
left mouse button again.
\snippet widgets/tooltips/sortingbox.cpp 14
\codeline
\snippet widgets/tooltips/sortingbox.cpp 15
\codeline
\snippet widgets/tooltips/sortingbox.cpp 16
The \c createNewCircle(), \c createNewSquare() and \c
createNewTriangle() slots simply create new shape items, using the
private \c createShapeItem(), \c randomItemPosition() and \c
randomItemColor() functions.
\snippet widgets/tooltips/sortingbox.cpp 17
In the \c itemAt() function, we run through the list of created
shape items to check if the given position is contained within the
area of any of the shape items.
For each shape item we use the QPainterPath::contains() function
to find out if the item's painter path contains the position. If
it does we return the index of the item, otherwise we return
-1. We run through the list backwards to get the index of the
uppermost shape item in case several items cover the position.
\snippet widgets/tooltips/sortingbox.cpp 18
The \c moveItemTo() function moves the shape item in motion, and
the parameter \c pos is the position of a mouse event. First we
calculate the offset between the parameter \c pos and the previous
mouse event position. Then we add the offset to the current
position of the item in motion.
It is tempting to simply set the position of the item to be the
parameter \c pos. But an item's position defines the top left
corner of the item's bounding rectangle, and the parameter \c pos
can be any point; The suggested shortcut would cause the item to
jump to a position where the cursor is pointing to the bounding
rectangle's top left corner, regardless of the item's previous
position.
\snippet widgets/tooltips/sortingbox.cpp 19
Finally, we update the previous mouse event position, and make a
call to the QWidget::update() function to make the item appear at
its new position.
\snippet widgets/tooltips/sortingbox.cpp 20
In the \c updateButtonGeometry() function we set the geometry for
the given button. The parameter coordinates define the bottom
right corner of the button. We use these coordinates and the
button's size hint to determine the position of the upper left
corner. This position, and the button's width and height, are the
arguments required by the QWidget::setGeometry() function.
In the end, we calculate and return the y-coordinate of the bottom
right corner of the next button. We use the QWidget::style()
function to retrieve the widget's GUI style, and then
QStyle::pixelMetric() to determine the widget's preferred default
spacing between its child widgets.
\snippet widgets/tooltips/sortingbox.cpp 21
The \c createShapeItem() function creates a single shape item. It
sets the path, tooltip, position and color, using the item's own
functions. In the end, the function appends the new item's pointer
to the list of shape items, and calls QWidget::update() to make
it appear with the other items within the \c SortingBox widget.
\snippet widgets/tooltips/sortingbox.cpp 22
The \c createToolButton() function is called from the \c
SortingBox constructor. We create a tool button with the given
tooltip and icon. The button's parent is the \c SortingBox widget,
and its size is 32 x 32 pixels. Before we return the button, we
connect it to the given slot.
\snippet widgets/tooltips/sortingbox.cpp 23
The \c initialItemPosition() function is also called from the
constructor. We want the three first items to initially be
centered in the middle of the \c SortingBox widget, and we use
this function to calculate their positions.
\snippet widgets/tooltips/sortingbox.cpp 24
Whenever the user creates a new shape item, we want the new item
to appear at a random position, and we use the \c
randomItemPosition() function to calculate such a position. We
make sure that the item appears within the visible area of the
\c SortingBox widget, using the widget's current width and height
when calculating the random coordinates.
\snippet widgets/tooltips/sortingbox.cpp 25
As with \c initialItemPosition(), the \c initialItemColor()
function is called from the constructor. The purposes of both
functions are purely cosmetic: We want to control the initial
position and color of the three first items.
\snippet widgets/tooltips/sortingbox.cpp 26
Finally the \c randomItemColor() function is implemented to give
the shape items the user creates, a random color.
\section1 ShapeItem Class Definition
\snippet widgets/tooltips/shapeitem.h 0
The \c ShapeItem class is a custom widget representing one single
shape item. The widget has a path, a position, a color and a
tooltip. We need functions to set or modify these objects, as well
as functions that return them. We make the latter functions \c
const to prohibit any modifications of the objects,
i.e. prohibiting unauthorized manipulation of the shape items
appearance.
\section1 ShapeItem Class Implementation
\snippet widgets/tooltips/shapeitem.cpp 0
\codeline
\snippet widgets/tooltips/shapeitem.cpp 1
\codeline
\snippet widgets/tooltips/shapeitem.cpp 2
\codeline
\snippet widgets/tooltips/shapeitem.cpp 3
This first group of functions simply return the objects that are
requested. The objects are returned as constants, i.e. they cannot
be modified.
\snippet widgets/tooltips/shapeitem.cpp 4
\codeline
\snippet widgets/tooltips/shapeitem.cpp 5
\codeline
\snippet widgets/tooltips/shapeitem.cpp 6
\codeline
\snippet widgets/tooltips/shapeitem.cpp 7
The last group of functions set or modify the shape item's path,
position, color and tooltip, respectively.
*/

View File

@ -0,0 +1,350 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example painting/transformations
\title Transformations Example
\ingroup examples-painting
\brief The Transformations example shows how transformations
influence the way that QPainter renders graphics primitives.
\brief The Transformations example shows how transformations influence
the way that QPainter renders graphics primitives. In particular
it shows how the order of transformations affect the result.
\image transformations-example.png
The application allows the user to manipulate the rendering of a
shape by changing the translation, rotation and scale of
QPainter's coordinate system.
The example consists of two classes and a global enum:
\list
\li The \c RenderArea class controls the rendering of a given shape.
\li The \c Window class is the application's main window.
\li The \c Operation enum describes the various transformation
operations available in the application.
\endlist
First we will take a quick look at the \c Operation enum, then we
will review the \c RenderArea class to see how a shape is
rendered. Finally, we will take a look at the Transformations
application's features implemented in the \c Window class.
\section1 Transformation Operations
Normally, the QPainter operates on the associated device's own
coordinate system, but it also has good support for coordinate
transformations.
The default coordinate system of a paint device has its origin at
the top-left corner. The x values increase to the right and the y
values increase downwards. You can scale the coordinate system by
a given offset using the QPainter::scale() function, you can
rotate it clockwise using the QPainter::rotate() function and you
can translate it (i.e. adding a given offset to the points) using
the QPainter::translate() function. You can also twist the
coordinate system around the origin (called shearing) using the
QPainter::shear() function.
All the transformation operations operate on QPainter's
transformation matrix that you can retrieve using the
QPainter::worldTransform() function. A matrix transforms a point in the
plane to another point. For more information about the
transformation matrix, see the \l {Coordinate System} and
QTransform documentation.
\snippet painting/transformations/renderarea.h 0
The global \c Operation enum is declared in the \c renderarea.h
file and describes the various transformation operations available
in the Transformations application.
\section1 RenderArea Class Definition
The \c RenderArea class inherits QWidget, and controls the
rendering of a given shape.
\snippet painting/transformations/renderarea.h 1
We declare two public functions, \c setOperations() and
\c setShape(), to be able to specify the \c RenderArea widget's shape
and to transform the coordinate system the shape is rendered
within.
We reimplement the QWidget's \l
{QWidget::minimumSizeHint()}{minimumSizeHint()} and \l
{QWidget::sizeHint()}{sizeHint()} functions to give the \c
RenderArea widget a reasonable size within our application, and we
reimplement the QWidget::paintEvent() event handler to draw the
render area's shape applying the user's transformation choices.
\snippet painting/transformations/renderarea.h 2
We also declare several convenience functions to draw the shape,
the coordinate system's outline and the coordinates, and to
transform the painter according to the chosen transformations.
In addition, the \c RenderArea widget keeps a list of the
currently applied transformation operations, a reference to its
shape, and a couple of convenience variables that we will use when
rendering the coordinates.
\section1 RenderArea Class Implementation
The \c RenderArea widget controls the rendering of a given shape,
including the transformations of the coordinate system, by
reimplementing the QWidget::paintEvent() event handler. But first
we will take a quick look at the constructor and at the functions
that provides access to the \c RenderArea widget:
\snippet painting/transformations/renderarea.cpp 0
In the constructor we pass the parent parameter on to the base
class, and customize the font that we will use to render the
coordinates. The QWidget::font() function returns the font
currently set for the widget. As long as no special font has been
set, or after QWidget::setFont() is called, this is either a
special font for the widget class, the parent's font or (if this
widget is a top level widget) the default application font.
After ensuring that the font's size is 12 points, we extract the
rectangles enclosing the coordinate letters, 'x' and 'y', using the
QFontMetrics class.
QFontMetrics provides functions to access the individual metrics
of the font, its characters, and for strings rendered in the
font. The QFontMetrics::boundingRect() function returns the
bounding rectangle of the given character relative to the
left-most point on the base line.
\snippet painting/transformations/renderarea.cpp 1
\codeline
\snippet painting/transformations/renderarea.cpp 2
In the \c setShape() and \c setOperations() functions we update
the \c RenderArea widget by storing the new value or values
followed by a call to the QWidget::update() slot which schedules a
paint event for processing when Qt returns to the main event loop.
\snippet painting/transformations/renderarea.cpp 3
\codeline
\snippet painting/transformations/renderarea.cpp 4
We reimplement the QWidget's \l
{QWidget::minimumSizeHint()}{minimumSizeHint()} and \l
{QWidget::sizeHint()}{sizeHint()} functions to give the \c
RenderArea widget a reasonable size within our application. The
default implementations of these functions returns an invalid size
if there is no layout for this widget, and returns the layout's
minimum size or preferred size, respectively, otherwise.
\snippet painting/transformations/renderarea.cpp 5
The \c paintEvent() event handler receives the \c RenderArea
widget's paint events. A paint event is a request to repaint all
or part of the widget. It can happen as a result of
QWidget::repaint() or QWidget::update(), or because the widget was
obscured and has now been uncovered, or for many other reasons.
First we create a QPainter for the \c RenderArea widget. The \l
{QPainter::RenderHint}{QPainter::Antialiasing} render hint
indicates that the engine should antialias edges of primitives if
possible. Then we erase the area that needs to be repainted using
the QPainter::fillRect() function.
We also translate the coordinate system with an constant offset to
ensure that the original shape is renderend with a suitable
margin.
\snippet painting/transformations/renderarea.cpp 6
Before we start to render the shape, we call the QPainter::save()
function.
QPainter::save() saves the current painter state (i.e. pushes the
state onto a stack) including the current coordinate system. The
rationale for saving the painter state is that the following call
to the \c transformPainter() function will transform the
coordinate system depending on the currently chosen transformation
operations, and we need a way to get back to the original state to
draw the outline.
After transforming the coordinate system, we draw the \c
RenderArea's shape, and then we restore the painter state using
the QPainter::restore() function (i.e. popping the saved state off
the stack).
\snippet painting/transformations/renderarea.cpp 7
Then we draw the square outline.
\snippet painting/transformations/renderarea.cpp 8
Since we want the coordinates to correspond with the coordinate
system the shape is rendered within, we must make another call to
the \c transformPainter() function.
The order of the painting operations is essential with respect to
the shared pixels. The reason why we don't render the coordinates
when the coordinate system already is transformed to render the
shape, but instead defer their rendering to the end, is that we
want the coordinates to appear on top of the shape and its
outline.
There is no need to save the QPainter state this time since
drawing the coordinates is the last painting operation.
\snippet painting/transformations/renderarea.cpp 9
\codeline
\snippet painting/transformations/renderarea.cpp 10
\codeline
\snippet painting/transformations/renderarea.cpp 11
The \c drawCoordinates(), \c drawOutline() and \c drawShape() are
convenience functions called from the \c paintEvent() event
handler. For more information about QPainter's basic drawing
operations and how to display basic graphics primitives, see the
\l {painting/basicdrawing}{Basic Drawing} example.
\snippet painting/transformations/renderarea.cpp 12
The \c transformPainter() convenience function is also called from
the \c paintEvent() event handler, and transforms the given
QPainter's coordinate system according to the user's
transformation choices.
\section1 Window Class Definition
The \c Window class is the Transformations application's main
window.
The application displays four \c RenderArea widgets. The left-most
widget renders the shape in QPainter's default coordinate system,
the others render the shape with the chosen transformation in
addition to all the transformations applied to the \c RenderArea
widgets to their left.
\snippet painting/transformations/window.h 0
We declare two public slots to make the application able to
respond to user interaction, updating the displayed \c RenderArea
widgets according to the user's transformation choices.
The \c operationChanged() slot updates each of the \c RenderArea
widgets applying the currently chosen transformation operations, and
is called whenever the user changes the selected operations. The
\c shapeSelected() slot updates the \c RenderArea widgets' shapes
whenever the user changes the preferred shape.
\snippet painting/transformations/window.h 1
We also declare a private convenience function, \c setupShapes(),
that is used when constructing the \c Window widget, and we
declare pointers to the various components of the widget. We
choose to keep the available shapes in a QList of \l
{QPainterPath}s. In addition we declare a private enum counting
the number of displayed \c RenderArea widgets except the widget
that renders the shape in QPainter's default coordinate system.
\section1 Window Class Implementation
In the constructor we create and initialize the application's
components:
\snippet painting/transformations/window.cpp 0
First we create the \c RenderArea widget that will render the
shape in the default coordinate system. We also create the
associated QComboBox that allows the user to choose among four
different shapes: A clock, a house, a text and a truck. The shapes
themselves are created at the end of the constructor, using the
\c setupShapes() convenience function.
\snippet painting/transformations/window.cpp 1
Then we create the \c RenderArea widgets that will render their
shapes with coordinate transformations. By default the applied
operation is \uicontrol {No Transformation}, i.e. the shapes are
rendered within the default coordinate system. We create and
initialize the associated \l {QComboBox}es with items
corresponding to the various transformation operations described by
the global \c Operation enum.
We also connect the \l {QComboBox}es' \l
{QComboBox::activated()}{activated()} signal to the \c
operationChanged() slot to update the application whenever the
user changes the selected transformation operations.
\snippet painting/transformations/window.cpp 2
Finally, we set the layout for the application window using the
QWidget::setLayout() function, construct the available shapes
using the private \c setupShapes() convenience function, and make
the application show the clock shape on startup using the public
\c shapeSelected() slot before we set the window title.
\snippet painting/transformations/window.cpp 3
\snippet painting/transformations/window.cpp 4
\snippet painting/transformations/window.cpp 5
\snippet painting/transformations/window.cpp 6
\dots
\snippet painting/transformations/window.cpp 7
The \c setupShapes() function is called from the constructor and
create the QPainterPath objects representing the shapes that are
used in the application. For construction details, see the \c
{painting/transformations/window.cpp} example
file. The shapes are stored in a QList. The QList::append()
function inserts the given shape at the end of the list.
We also connect the associated QComboBox's \l
{QComboBox::activated()}{activated()} signal to the \c
shapeSelected() slot to update the application when the user
changes the preferred shape.
\snippet painting/transformations/window.cpp 8
The public \c operationChanged() slot is called whenever the user
changes the selected operations.
We retrieve the chosen transformation operation for each of the
transformed \c RenderArea widgets by querying the associated \l
{QComboBox}{QComboBoxes}. The transformed \c RenderArea widgets
are supposed to render the shape with the transformation specified
by its associated combobox \e {in addition to} all the
transformations applied to the \c RenderArea widgets to its
left. For that reason, for each widget we query, we append the
associated operation to a QList of transformations which we apply
to the widget before proceeding to the next.
\snippet painting/transformations/window.cpp 9
The \c shapeSelected() slot is called whenever the user changes
the preferred shape, updating the \c RenderArea widgets using
their public \c setShape() function.
\section1 Summary
The Transformations example shows how transformations influence
the way that QPainter renders graphics primitives. Normally, the
QPainter operates on the device's own coordinate system, but it
also has good support for coordinate transformations. With the
Transformations application you can scale, rotate and translate
QPainter's coordinate system. The order in which these
transformations are applied is essential for the result.
All the transformation operations operate on QPainter's
transformation matrix. For more information about the
transformation matrix, see the \l {Coordinate System} and
QTransform documentation.
The Qt reference documentation provides several painting
examples. Among these is the \l {painting/affine}{Affine
Transformations} example that shows Qt's ability to perform
transformations on painting operations. The example also allows the
user to experiment with the various transformation operations.
*/

View File

@ -0,0 +1,148 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/treemodelcompleter
\title Tree Model Completer Example
\ingroup examples-widgets-tools
\brief The Tree Model Completer example shows how to provide completion
facilities for a hierarchical model, using a period as the separator
to access Child, GrandChild and GrandGrandChild level objects.
\image treemodelcompleter-example.png
Similar to the \l{Completer Example}, we provide QComboBox objects to
enable selection for completion mode and case sensitivity, as well as
a QCheckBox for wrap completions.
\section1 The Resource File
The contents of the TreeModelCompleter is read from \e treemodel.txt.
This file is embedded within the \e treemodelcompleter.qrc resource file,
which contains the following:
\quotefile tools/treemodelcompleter/treemodelcompleter.qrc
\section1 TreeModelCompleter Class Definition
The \c TreeModelCompleter is a subclass of QCompleter with two
constructors - one with \a parent as an argument and another with
\a parent and \a model as arguments.
\snippet tools/treemodelcompleter/treemodelcompleter.h 0
The class reimplements the protected functions
\l{QCompleter::splitPath()}{splitPath()} and
\l{QCompleter::pathFromIndex()}{pathFromIndex()} to suit a tree model.
For more information on customizing QCompleter to suit tree models, refer
to \l{QCompleter#Handling Tree Models}{Handling Tree Models}.
\c TreeModelCompleter also has a separator property which is declared
using the Q_PROPERTY() macro. The separator has READ and WRITE attributes
and the corresponding functions \c separator() and \c setSeparator(). For
more information on Q_PROPERTY(), refer to \l{Qt's Property System}.
\section1 TreeModelCompleter Class Implementation
The first constructor constructs a \c TreeModelCompleter object with a
parent while the second constructor constructs an object with a parent
and a QAbstractItemModel, \a model.
\snippet tools/treemodelcompleter/treemodelcompleter.cpp 0
\codeline
\snippet tools/treemodelcompleter/treemodelcompleter.cpp 1
The \c separator() function is a getter function that returns the
separator string.
\snippet tools/treemodelcompleter/treemodelcompleter.cpp 2
As mentioned earlier, the \c splitPath() function is reimplemented because
the default implementation is more suited to QFileSystemModel or list models. In
order for QCompleter to split the path into a list of strings that are
matched at each level, we split it using QString::split() with \c sep as its
separator.
\snippet tools/treemodelcompleter/treemodelcompleter.cpp 3
The \c pathFromIndex() function returns data for the completionRole() for a
tree model. This function is reimplemented as its default implementation is
more suitable for list models. If there is no separator, we use
\l{QCompleter}'s default implementation, otherwise we use the
\l{QStringList::prepend()}{prepend()} function to navigate upwards and
accumulate the data. The function then returns a QStringList, \c dataList,
using a separator to join objects of different levels.
\snippet tools/treemodelcompleter/treemodelcompleter.cpp 4
\section1 MainWindow Class Definition
The \c MainWindow class is a subclass of QMainWindow and implements five
custom slots: \c about(), \c changeCase(), \c changeMode(),
\c highlight(), and \c updateContentsLabel().
\snippet tools/treemodelcompleter/mainwindow.h 0
In addition, the class has two private functions, \c createMenu() and
\c modelFromFile(), as well as private instances of QTreeView, QComboBox,
QLabel, \c TreeModelCompleter and QLineEdit.
\snippet tools/treemodelcompleter/mainwindow.h 1
\section1 MainWindow Class Implementation
The \c{MainWindow}'s constructor creates a \c MainWindow object with a
parent and initializes the \c completer and \c lineEdit. The
\c createMenu() function is invoked to set up the "File" menu and "Help"
menu. The \c{completer}'s model is set to the QAbstractItemModel obtained
from \c modelFromFile(), and the \l{QCompleter::highlighted()}
{highlighted()} signal is connected to \c{MainWindow}'s \c highlight()
slot.
\snippet tools/treemodelcompleter/mainwindow.cpp 0
The QLabel objects \c modelLabel, \c modeLabel and \c caseLabel are
instantiated. Also, the QComboBox objects, \c modeCombo and \c caseCombo,
are instantiated and populated. By default, the \c{completer}'s mode is
"Filtered Popup" and the case is insensitive.
\snippet tools/treemodelcompleter/mainwindow.cpp 1
\codeline
\snippet tools/treemodelcompleter/mainwindow.cpp 2
We use a QGridLayout to place all the objects in the \c MainWindow.
\snippet tools/treemodelcompleter/mainwindow.cpp 3
The \c createMenu() function sets up the QAction objects required and
adds them to the "File" menu and "Help" menu. The
\l{QAction::triggered()}{triggered()} signals from these actions are
connected to their respective slots.
\snippet tools/treemodelcompleter/mainwindow.cpp 4
The \c changeMode() function accepts an \a index corresponding to the
user's choice of completion mode and changes the \c{completer}'s mode
accordingly.
\snippet tools/treemodelcompleter/mainwindow.cpp 5
The \c about() function provides a brief description on the Tree Model
Completer example.
\snippet tools/treemodelcompleter/mainwindow.cpp 6
The \c changeCase() function alternates between \l{Qt::CaseSensitive}
{Case Sensitive} and \l{Qt::CaseInsensitive}{Case Insensitive} modes,
depending on the value of \a cs.
\snippet tools/treemodelcompleter/mainwindow.cpp 7
\section1 \c main() Function
The \c main() function instantiates \c MainWindow and invokes the
\l{QWidget::show()}{show()} function to display it.
\snippet tools/treemodelcompleter/main.cpp 0
*/

View File

@ -0,0 +1,59 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example dialogs/trivialwizard
\title Trivial Wizard Example
\ingroup examples-dialogs
\brief The Trivial Wizard example illustrates how to create a linear three-page
registration wizard using three instances of QWizardPage and one instance
of QWizard.
\image trivialwizard-example-flow.png
\section1 Introduction Page
\image trivialwizard-example-introduction.png
The introduction page is created with the \c createIntroPage()
function where a QWizardPage is created and its title is set to
"Introduction". A QLabel is used to hold the description of \c page.
A QVBoxLayout is used to hold the \c label. This \c page is returned
when the \c createIntroPage() function is called.
\snippet dialogs/trivialwizard/trivialwizard.cpp 0
\section1 Registration Page
\image trivialwizard-example-registration.png
The registration page is created with the \c createRegistrationPage()
function. QLineEdit objects are used to allow the user to input a name
and an e-mail address. A QGridLayout is used to hold the QLabel and
QLineEdit objects.
\snippet dialogs/trivialwizard/trivialwizard.cpp 2
\section1 Conclusion Page
\image trivialwizard-example-conclusion.png
The conclusion page is created in the \c createConclusionPage()
function. This function's content is similar to \c createIntroPage(). A
QLabel is used to inform the user that the registration process has
completed successfully.
\snippet dialogs/trivialwizard/trivialwizard.cpp 6
\section1 \c main() Function
The \c main() function instantiates a QWizard object, \c wizard, and
adds all three QWizardPage objects to it. The \c wizard window title is
set to "Trivial Wizard" and its \c show() function is invoked to display
it.
\snippet dialogs/trivialwizard/trivialwizard.cpp 10
\sa QWizard, {Class Wizard Example}, {License Wizard Example}
*/

View File

@ -0,0 +1,261 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example tools/undoframework
\title Undo Framework Example
\ingroup examples-widgets-tools
\brief This example shows how to implement undo/redo functionality
with the Qt undo framework.
\image undoframeworkexample.png The Undo Diagram Example
In the Qt undo framework, all actions that the user performs are
implemented in classes that inherit QUndoCommand. An undo command
class knows how to both \l{QUndoCommand::}{redo()} - or just do
the first time - and \l{QUndoCommand::}{undo()} an action. For
each action the user performs, a command is placed on a
QUndoStack. Since the stack contains all commands executed
(stacked in chronological order) on the document, it can roll the
state of the document backwards and forwards by undoing and redoing
its commands. See the \l{Overview of Qt's Undo Framework}{overview
document} for a high-level introduction to the undo framework.
The undo example implements a simple diagram application. It is
possible to add and delete items, which are either box or
rectangular shaped, and move the items by dragging them with the
mouse. The undo stack is shown in a QUndoView, which is a list in
which the commands are shown as list items. Undo and redo are
available through the edit menu. The user can also select a command
from the undo view.
We use the \l{Graphics View Framework}{graphics view
framework} to implement the diagram. We only treat the related
code briefly as the framework has examples of its own (e.g., the
\l{Diagram Scene Example}).
The example consists of the following classes:
\list
\li \c MainWindow is the main window and arranges the
example's widgets. It creates the commands based
on user input and keeps them on the command stack.
\li \c AddCommand adds an item to the scene.
\li \c DeleteCommand deletes an item from the scene.
\li \c MoveCommand when an item is moved the MoveCommand keeps record
of the start and stop positions of the move, and it
moves the item according to these when \c redo() and \c undo()
is called.
\li \c DiagramScene inherits QGraphicsScene and
emits signals for the \c MoveComands when an item is moved.
\li \c DiagramItem inherits QGraphicsPolygonItem and represents
an item in the diagram.
\endlist
\section1 MainWindow Class Definition
\snippet tools/undoframework/mainwindow.h 0
The \c MainWindow class maintains the undo stack, i.e., it creates
\l{QUndoCommand}s and pushes and pops them from the stack when it
receives the \c triggered() signal from \c undoAction and \c
redoAction.
\section1 MainWindow Class Implementation
We will start with a look at the constructor:
\snippet tools/undoframework/mainwindow.cpp 0
In the constructor, we set up the DiagramScene and QGraphicsView.
We only want \c deleteAction to be enabled when we have selected an
item, so we connect to the \c selectionChanged() signal of the scene
to \c updateActions() slot.
Here is the \c createUndoView() function:
\snippet tools/undoframework/mainwindow.cpp 1
The QUndoView is a widget that display the text, which is set with
the \l{QUndoCommand::}{setText()} function, for each QUndoCommand
in the undo stack in a list. We put it into a docking widget.
Here is the \c createActions() function:
\snippet tools/undoframework/mainwindow.cpp 2
\dots
\snippet tools/undoframework/mainwindow.cpp 5
The \c createActions() function sets up all the examples actions
in the manner shown above. The
\l{QUndoStack::}{createUndoAction()} and
\l{QUndoStack::}{createRedoAction()} methods help us create actions that
are disabled and enabled based on the state of the stack. Also,
the text of the action will be updated automatically based on the
\l{QUndoCommand::}{text()} of the undo commands. For the other
actions we have implemented slots in the \c MainWindow class.
\dots
\snippet tools/undoframework/mainwindow.cpp 6
Once all actions are created we update their state by calling the
same function that is connected to the \c selectionChanged signal of
the scene.
The \c createMenus() and \c createToolBars() functions add the
actions to menus and toolbars:
\snippet tools/undoframework/mainwindow.cpp 7
\dots
\snippet tools/undoframework/mainwindow.cpp 8
\dots
\snippet tools/undoframework/mainwindow.cpp 9
Here is the \c itemMoved() slot:
\snippet tools/undoframework/mainwindow.cpp 11
We simply push a MoveCommand on the stack, which calls \c redo()
on it.
Here is the \c deleteItem() slot:
\snippet tools/undoframework/mainwindow.cpp 12
An item must be selected to be deleted. We need to check if it is
selected as the \c deleteAction may be enabled even if an item is
not selected. This can happen as we do not catch a signal or event
when an item is selected.
Here is the \c addBox() slot:
\snippet tools/undoframework/mainwindow.cpp 13
The \c addBox() function creates an AddCommand and pushes it on
the undo stack.
Here is the \c addTriangle() sot:
\snippet tools/undoframework/mainwindow.cpp 14
The \c addTriangle() function creates an AddCommand and pushes it
on the undo stack.
Here is the implementation of \c about():
\snippet tools/undoframework/mainwindow.cpp 15
The about slot is triggered by the \c aboutAction and displays an
about box for the example.
\section1 AddCommand Class Definition
\snippet tools/undoframework/commands.h 2
The \c AddCommand class adds DiagramItem graphics items to the
DiagramScene.
\section1 AddCommand Class Implementation
We start with the constructor:
\snippet tools/undoframework/commands.cpp 7
We first create the DiagramItem to add to the DiagramScene. The
\l{QUndoCommand::}{setText()} function let us set a QString that
describes the command. We use this to get custom messages in the
QUndoView and in the menu of the main window.
\snippet tools/undoframework/commands.cpp 8
\c undo() removes the item from the scene.
\snippet tools/undoframework/commands.cpp 9
We set the position of the item as we do not do this in the
constructor.
\section1 DeleteCommand Class Definition
\snippet tools/undoframework/commands.h 1
The DeleteCommand class implements the functionality to remove an
item from the scene.
\section1 DeleteCommand Class Implementation
\snippet tools/undoframework/commands.cpp 4
We know that there must be one selected item as it is not possible
to create a DeleteCommand unless the item to be deleted is
selected and that only one item can be selected at any time.
The item must be unselected if it is inserted back into the
scene.
\snippet tools/undoframework/commands.cpp 5
The item is simply reinserted into the scene.
\snippet tools/undoframework/commands.cpp 6
The item is removed from the scene.
\section1 MoveCommand Class Definition
\snippet tools/undoframework/commands.h 0
The \l{QUndoCommand::}{mergeWith()} is reimplemented to make
consecutive moves of an item one MoveCommand, i.e, the item will
be moved back to the start position of the first move.
\section1 MoveCommand Class Implementation
The constructor of MoveCommand looks like this:
\snippet tools/undoframework/commands.cpp 0
We save both the old and new positions for undo and redo
respectively.
\snippet tools/undoframework/commands.cpp 2
We simply set the items old position and update the scene.
\snippet tools/undoframework/commands.cpp 3
We set the item to its new position.
\snippet tools/undoframework/commands.cpp 1
Whenever a MoveCommand is created, this function is called to
check if it should be merged with the previous command. It is the
previous command object that is kept on the stack. The function
returns true if the command is merged; otherwise false.
We first check whether it is the same item that has been moved
twice, in which case we merge the commands. We update the position
of the item so that it will take the last position in the move
sequence when undone.
\section1 DiagramScene Class Definition
\snippet tools/undoframework/diagramscene.h 0
The DiagramScene implements the functionality to move a
DiagramItem with the mouse. It emits a signal when a move is
completed. This is caught by the \c MainWindow, which makes
MoveCommands. We do not examine the implementation of DiagramScene
as it only deals with graphics framework issues.
\section1 The \c main() Function
The \c main() function of the program looks like this:
\snippet tools/undoframework/main.cpp 0
We draw a grid in the background of the DiagramScene, so we use a
resource file. The rest of the function creates the \c MainWindow and
shows it as a top level window.
*/

View File

@ -0,0 +1,11 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/validators
\title Validators Example
\brief The Validators example shows the signal emission behavior of input
validators.
\borderedimage validators.png
*/

View File

@ -0,0 +1,193 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example widgets/windowflags
\title Window Flags Example
\ingroup examples-widgets
\brief The Window Flags example shows how to use the window flags
available in Qt.
A window flag is either a type or a hint. A type is used to
specify various window-system properties for the widget. A widget
can only have one type, and the default is Qt::Widget. However, a
widget can have zero or more hints. The hints are used to
customize the appearance of top-level windows.
A widget's flags are stored in a Qt::WindowFlags type which stores
an OR combination of the flags.
\borderedimage windowflags-example.png
\caption Screenshot of the Window Flags example
The example consists of two classes:
\list
\li \c ControllerWindow is the main application widget that allows
the user to choose among the available window flags, and displays
the effect on a separate preview window.
\li \c PreviewWindow is a custom widget displaying the name of
its currently set window flags in a read-only text editor.
\endlist
We will start by reviewing the \c ControllerWindow class, then we
will take a look at the \c PreviewWindow class.
\section1 ControllerWindow Class Definition
\snippet widgets/windowflags/controllerwindow.h 0
The \c ControllerWindow class inherits QWidget. The widget allows
the user to choose among the available window flags, and displays
the effect on a separate preview window.
We declare a private \c updatePreview() slot to refresh the
preview window whenever the user changes the window flags.
We also declare several private functions to simplify the
constructor: We call the \c createTypeGroupBox() function to
create a radio button for each available window type, using the
private \c createButton() function, and gather them within a group
box. In a similar way we use the \c createHintsGroupBox() function
to create a check box for each available hint, using the private
\c createCheckBox() function.
In addition to the various radio buttons and checkboxes, we need
an associated \c PreviewWindow to show the effect of the currently
chosen window flags.
\image windowflags_controllerwindow.png Screenshot of the Controller Window
\section1 ControllerWindow Class Implementation
\snippet widgets/windowflags/controllerwindow.cpp 0
In the constructor we first create the preview window. Then we
create the group boxes containing the available window flags using
the private \c createTypeGroupBox() and \c createHintsGroupBox()
functions. In addition we create a \uicontrol Quit button. We put the
button and a stretchable space in a separate layout to make the
button appear in the \c WindowFlag widget's right bottom corner.
Finally, we add the button's layout and the two goup boxes to a
QVBoxLayout, set the window title and refresh the preview window
using the \c updatePreview() slot.
\snippet widgets/windowflags/controllerwindow.cpp 1
\snippet widgets/windowflags/controllerwindow.cpp 2
The \c updatePreview() slot is called whenever the user changes
any of the window flags. First we create an empty Qt::WindowFlags
\c flags, then we determine which one of the types that is checked
and add it to \c flags.
\snippet widgets/windowflags/controllerwindow.cpp 3
We also determine which of the hints that are checked, and add
them to \c flags using an OR operator. We use \c flags to set the
window flags for the preview window.
\snippet widgets/windowflags/controllerwindow.cpp 4
We adjust the position of the preview window. The reason we do
that, is that playing around with the window's frame may on some
platforms cause the window's position to be changed behind our
back. If a window is located in the upper left corner of the
screen, parts of the window may not be visible. So we adjust the
widget's position to make sure that, if this happens, the window
is moved within the screen's boundaries. Finally, we call
QWidget::show() to make sure the preview window is visible.
\omit
\skipto pos
\printuntil /^\}/
\endomit
\snippet widgets/windowflags/controllerwindow.cpp 5
The private \c createTypeGroupBox() function is called from the
constructor.
First we create a group box, and then we create a radio button
(using the private \c createRadioButton() function) for each of
the available types among the window flags. We make Qt::Window the
initially applied type. We put the radio buttons into a
QGridLayout and install the layout on the group box.
We do not include the default Qt::Widget type. The reason is that
it behaves somewhat different than the other types. If the type is
not specified for a widget, and it has no parent, the widget is a
window. However, if it has a parent, it is a standard child
widget. The other types are all top-level windows, and since the
hints only affect top-level windows, we abandon the Qt::Widget
type.
\snippet widgets/windowflags/controllerwindow.cpp 6
The private \c createHintsGroupBox() function is also called from
the constructor.
Again, the first thing we do is to create a group box. Then we
create a checkbox, using the private \c createCheckBox() function,
for each of the available hints among the window flags. We put the
checkboxes into a QGridLayout and install the layout on the group
box.
\snippet widgets/windowflags/controllerwindow.cpp 7
The private \c createCheckBox() function is called from \c
createHintsGroupBox().
We simply create a QCheckBox with the provided text, connect it to
the private \c updatePreview() slot, and return a pointer to the
checkbox.
\snippet widgets/windowflags/controllerwindow.cpp 8
In the private \c createRadioButton() function it is a
QRadioButton we create with the provided text, and connect to the
private \c updatePreview() slot. The function is called from \c
createTypeGroupBox(), and returns a pointer to the button.
\section1 PreviewWindow Class Definition
\snippet widgets/windowflags/previewwindow.h 0
The \c PreviewWindow class inherits QWidget. It is a custom widget
that displays the names of its currently set window flags in a
read-only text editor. It is also provided with a QPushbutton that
closes the window.
We reimplement the constructor to create the \uicontrol Close button and
the text editor, and the QWidget::setWindowFlags() function to
display the names of the window flags.
\image windowflags_previewwindow.png Screenshot of the Preview Window
\section1 PreviewWindow Class Implementation
\snippet widgets/windowflags/previewwindow.cpp 0
In the constructor, we first create a QTextEdit and make sure that
it is read-only.
We also prohibit any line wrapping in the text editor using the
QTextEdit::setLineWrapMode() function. The result is that a
horizontal scrollbar appears when a window flag's name exceeds the
width of the editor. This is a reasonable solution since we
construct the displayed text with built-in line breaks. If no line
breaks were guaranteed, using another QTextEdit::LineWrapMode
would perhaps make more sense.
Then we create the \uicontrol Close button, and put both the widgets
into a QVBoxLayout before we set the window title.
\snippet widgets/windowflags/previewwindow.cpp 1
In our reimplementation of the \c setWindowFlags() function, we
first set the widgets flags using the QWidget::setWindowFlags()
function. Then we run through the available window flags, creating
a text that contains the names of the flags that matches the \c
flags parameter. Finally, we display the text in the widgets text
editor.
*/