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,222 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example draganddrop/dropsite
\title Drop Site Example
\brief The example shows how to distinguish the various MIME formats available
in a drag and drop operation.
\image dropsite-example.png Screenshot of the Drop Site example
The Drop Site example accepts drops from other applications, and displays
the MIME formats provided by the drag object.
There are two classes, \c DropArea and \c DropSiteWindow, and a \c main()
function in this example. A \c DropArea object is instantiated in
\c DropSiteWindow; a \c DropSiteWindow object is then invoked in the
\c main() function.
\section1 DropArea Class Definition
The \c DropArea class is a subclass of QLabel with a public slot,
\c clear(), and a \c changed() signal.
\snippet draganddrop/dropsite/droparea.h DropArea header part1
In addition, \c DropArea contains reimplementations of four \l{QWidget}
event handlers:
\list 1
\li \l{QWidget::dragEnterEvent()}{dragEnterEvent()}
\li \l{QWidget::dragMoveEvent()}{dragMoveEvent()}
\li \l{QWidget::dragLeaveEvent()}{dragLeaveEvent()}
\li \l{QWidget::dropEvent()}{dropEvent()}
\endlist
These event handlers are further explained in the implementation of the
\c DropArea class.
\snippet draganddrop/dropsite/droparea.h DropArea header part2
\section1 DropArea Class Implementation
In the \c DropArea constructor, we set the \l{QWidget::setMinimumSize()}
{minimum size} to 200x200 pixels, the \l{QFrame::setFrameStyle()}
{frame style} to both QFrame::Sunken and QFrame::StyledPanel, and we align
its contents to the center.
\snippet draganddrop/dropsite/droparea.cpp DropArea constructor
Also, we enable drop events in \c DropArea by setting the
\l{QWidget::acceptDrops()}{acceptDrops} property to \c true. Then,
we enable the \l{QWidget::autoFillBackground()}{autoFillBackground}
property and invoke the \c clear() function.
The \l{QWidget::dragEnterEvent()}{dragEnterEvent()} event handler is
called when a drag is in progress and the mouse enters the \c DropArea
object. For the \c DropSite example, when the mouse enters \c DropArea,
we set its text to "<drop content>" and highlight its background.
\snippet draganddrop/dropsite/droparea.cpp dragEnterEvent() function
Then, we invoke \l{QDropEvent::acceptProposedAction()}
{acceptProposedAction()} on \a event, setting the drop action to the one
proposed. Lastly, we emit the \c changed() signal, with the data that was
dropped and its MIME type information as a parameter.
For \l{QWidget::dragMoveEvent()}{dragMoveEvent()}, we just accept the
proposed QDragMoveEvent object, \a event, with
\l{QDropEvent::acceptProposedAction()}{acceptProposedAction()}.
\snippet draganddrop/dropsite/droparea.cpp dragMoveEvent() function
The \c DropArea class's implementation of \l{QWidget::dropEvent()}
{dropEvent()} extracts the \a{event}'s mime data and displays it
accordingly.
\snippet draganddrop/dropsite/droparea.cpp dropEvent() function part1
The \c mimeData object can contain one of the following objects: an image,
HTML text, Markdown text, plain text, or a list of URLs.
\snippet draganddrop/dropsite/droparea.cpp dropEvent() function part2
\list
\li If \c mimeData contains an image, we display it in \c DropArea with
\l{QLabel::setPixmap()}{setPixmap()}.
\li If \c mimeData contains HTML, we display it with
\l{QLabel::setText()}{setText()} and set \c{DropArea}'s text format
as Qt::RichText.
\li If \c mimeData contains Markdown, we display it with
\l{QLabel::setText()}{setText()} and set \c{DropArea}'s text format
as Qt::MarkdownText.
\li If \c mimeData contains plain text, we display it with
\l{QLabel::setText()}{setText()} and set \c{DropArea}'s text format
as Qt::PlainText. In the event that \c mimeData contains URLs, we
iterate through the list of URLs to display them on individual
lines.
\li If \c mimeData contains other types of objects, we set
\c{DropArea}'s text, with \l{QLabel::setText()}{setText()} to
"Cannot display data" to inform the user.
\endlist
We then set \c{DropArea}'s \l{QWidget::backgroundRole()}{backgroundRole} to
QPalette::Dark and we accept \c{event}'s proposed action.
\snippet draganddrop/dropsite/droparea.cpp dropEvent() function part3
The \l{QWidget::dragLeaveEvent()}{dragLeaveEvent()} event handler is
called when a drag is in progress and the mouse leaves the widget.
\snippet draganddrop/dropsite/droparea.cpp dragLeaveEvent() function
For \c{DropArea}'s implementation, we clear invoke \c clear() and then
accept the proposed event.
The \c clear() function sets the text in \c DropArea to "<drop content>"
and sets the \l{QWidget::backgroundRole()}{backgroundRole} to
QPalette::Dark. Lastly, it emits the \c changed() signal.
\snippet draganddrop/dropsite/droparea.cpp clear() function
\section1 DropSiteWindow Class Definition
The \c DropSiteWindow class contains a constructor and a public slot,
\c updateFormatsTable().
\snippet draganddrop/dropsite/dropsitewindow.h DropSiteWindow header
The class also contains a private instance of \c DropArea, \c dropArea,
QLabel, \c abstractLabel, QTableWidget, \c formatsTable, QDialogButtonBox,
\c buttonBox, and two QPushButton objects, \c clearButton and
\c quitButton.
\section1 DropSiteWindow Class Implementation
In the constructor of \c DropSiteWindow, we instantiate \c abstractLabel
and set its \l{QLabel::setWordWrap()}{wordWrap} property to \c true. We
also call the \l{QLabel::adjustSize()}{adjustSize()} function to adjust
\c{abstractLabel}'s size according to its contents.
\snippet draganddrop/dropsite/dropsitewindow.cpp constructor part1
Then we instantiate \c dropArea and connect its \c changed() signal to
\c{DropSiteWindow}'s \c updateFormatsTable() slot.
\snippet draganddrop/dropsite/dropsitewindow.cpp constructor part2
We now set up the QTableWidget object, \c formatsTable. Its
horizontal header is set using a QStringList object, \c labels. The number
of columms are set to two and the table is not editable. Also, the
\c{formatTable}'s horizontal header is formatted to ensure that its second
column stretches to occupy additional space available.
\snippet draganddrop/dropsite/dropsitewindow.cpp constructor part3
Three QPushButton objects, \c clearButton, \c copyButton, and \c quitButton,
are instantiated and added to \c buttonBox - a QDialogButtonBox object. We
use QDialogButtonBox here to ensure that the push buttons are presented in a
layout that conforms to the platform's style.
\snippet draganddrop/dropsite/dropsitewindow.cpp constructor part4
The \l{QPushButton::clicked()}{clicked()} signals for \c copyButton,
\c clearButton, and \c quitButton are connected to \c copy(),
\c clear() and \l{QWidget::close()}{close()}, respectively.
For the layout, we use a QVBoxLayout, \c mainLayout, to arrange our widgets
vertically. We also set the window title to "Drop Site" and the minimum
size to 350x500 pixels.
\snippet draganddrop/dropsite/dropsitewindow.cpp constructor part5
We move on to the \c updateFormatsTable() function. This function updates
the \c formatsTable, displaying the MIME formats of the object dropped onto
the \c DropArea object. First, we set \l{QTableWidget}'s
\l{QTableWidget::setRowCount()}{rowCount} property to 0. Then, we validate
to ensure that the QMimeData object passed in is a valid object.
\snippet draganddrop/dropsite/dropsitewindow.cpp updateFormatsTable() part1
Once we are sure that \c mimeData is valid, we iterate through its
supported formats.
\note The \l{QMimeData::formats()}{formats()} function returns a
QStringList object, containing all the formats supported by the
\c mimeData.
\snippet draganddrop/dropsite/dropsitewindow.cpp updateFormatsTable() part2
Within each iteration, we create a QTableWidgetItem, \c formatItem and we
set its \l{QTableWidgetItem::setFlags()}{flags} to Qt::ItemIsEnabled, and
its \l{QTableWidgetItem::setTextAlignment()}{text alignment} to Qt::AlignTop
and Qt::AlignLeft.
A QString object, \c text, is customized to display data according to the
contents of \c format. We invoke \l{QString}'s \l{QString::simplified()}
{simplified()} function on \c text, to obtain a string that has no
additional space before, after or in between words.
\snippet draganddrop/dropsite/dropsitewindow.cpp updateFormatsTable() part3
If \c format contains a list of URLs, we iterate through them, using spaces
to separate them. On the other hand, if \c format contains an image, we
display the data by converting the text to hexadecimal.
\snippet draganddrop/dropsite/dropsitewindow.cpp updateFormatsTable() part4
Once \c text has been customized to contain the appropriate data, we insert
both \c format and \c text into \c formatsTable with
\l{QTableWidget::setItem()}{setItem()}. Lastly, we invoke
\l{QTableView::resizeColumnToContents()}{resizeColumnToContents()} on
\c{formatsTable}'s first column.
\section1 The main() Function
Within the \c main() function, we instantiate \c DropSiteWindow and invoke
its \l{QWidget::show()}{show()} function.
\snippet draganddrop/dropsite/main.cpp main() function
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 563 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

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.
*/

Some files were not shown because too many files have changed in this diff Show More