mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2024-11-24 04:20:46 +08:00
413 lines
16 KiB
Plaintext
413 lines
16 KiB
Plaintext
// 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
|
|
*/
|