Qt_Demo_01_Address Book Example(通讯簿示例)

1.说明

此示例提供了一个通讯簿,允许按字母顺序将联系人分组为9个组:ABC、DEF、GHI、,大众。。。,XYZ。这是通过在同一个模型上使用多个视图来实现的,每个视图都使用qsortfilterproxy类的实例进行过滤。

1.Overview(概述)

The address book contains 5 classes: MainWindow, AddressWidget, TableModel, NewAddressTab and AddDialog. The MainWindow class uses AddressWidget as its central widget and provides File and Tools menus.
**
通讯簿包含5个类:MainWindow、AddressWidget、TableModel、NewAddressTab和AddDialog。MainWindow类使用AddressWidget作为其中心小部件,并提供文件和工具菜单。
Qt_Demo_01_Address Book Example(通讯簿示例)_第1张图片

The 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 NewAddressTab. The NewAddressTab class is a subclass of QWidget that is only used whenever the address book is empty, prompting the user to add some contacts. AddressWidget also interacts with an instance of TableModel to add, edit and remove entries to the address book.
**
AddressWidget类是一个QTabWidget子类,用于操作示例中显示的10个选项卡:9个字母组选项卡和NewAddressTab的一个实例。NewAddressTab类是QWidget的一个子类,仅在通讯簿为空时使用,提示用户添加一些联系人。AddressWidget还与TableModel实例交互,以向通讯簿添加、编辑和删除条目。

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.
**
TableModel是QAbstractTableModel的一个子类,它提供了访问数据的标准模型/视图API。它包含添加的联系人列表。然而,这些数据并非在单个选项卡中全部可见。相反,根据字母组,QTableView用于提供相同数据的9种不同视图。

QSortFilterProxyModel is the class responsible for filtering the contacts for each group of contacts. Each proxy model uses a QRegExp to filter out contacts that do not belong in the corresponding alphabetical group. The AddDialog class is used to obtain information from the user for the address book. This QDialog subclass is instantiated by NewAddressTab to add contacts, and by AddressWidget to add and edit contacts.
**
QSortFilterProxy模型是负责过滤每组联系人的联系人的类。每个代理模型使用QRegExp过滤掉不属于相应字母组的联系人。AddDialog类用于从用户处获取地址簿的信息。这个QDialog子类由NewAddressTab实例化以添加联系人,由AddressWidget实例化以添加和编辑联系人。

We begin by looking at the TableModel implementation.
**
我们首先来看TableModel的实现。

2.TableModel Class Definition(TableModel类定义)

The 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: rowCount(), columnCount(), data(), headerData(). For TableModel to be editable, it has to provide implementations insertRows(), removeRows(), setData() and flags() functions.
**
TableModel类提供了标准API,通过子类化QAbstractTableModel来访问其联系人列表中的数据。为此必须实现的基本函数有:rowCount()、columnCount()、data()、headerData()。为了使TableModel可编辑,它必须提供insertRows()、removeRows()、setData()和flags()函数的实现。

struct Contact
  {
      QString name;
      QString address;

      bool operator==(const Contact &other) const
      {
          return name == other.name && address == other.address;
      }
  };

  inline QDataStream &operator<<(QDataStream &stream, const Contact &contact)
  {
      return stream << contact.name << contact.address;
  }

  inline QDataStream &operator>>(QDataStream &stream, Contact &contact)
  {
      return stream >> contact.name >> contact.address;
  }

  class TableModel : public QAbstractTableModel
  {
      Q_OBJECT

  public:
      TableModel(QObject *parent = 0);
      TableModel(QList<Contact> contacts, QObject *parent = 0);

      int rowCount(const QModelIndex &parent) const override;
      int columnCount(const QModelIndex &parent) const override;
      QVariant data(const QModelIndex &index, int role) const override;
      QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
      Qt::ItemFlags flags(const QModelIndex &index) const override;
      bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
      bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
      bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
      QList<Contact> getContacts() const;

  private:
      QList<Contact> contacts;
  };

Two constructors are used, a default constructor which uses TableModel’s own QList and one that takes QList as an argument, for convenience.
**
使用了两个构造函数,一个默认构造函数使用TableModel自己的QList,另一个为方便起见,将QList作为参数。

3.TableModel Class Implementation(TableModel类实现)

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.
**
我们实现了头文件中定义的两个构造函数。第二个构造函数使用参数值初始化模型中的联系人列表。

  TableModel::TableModel(QObject *parent)
      : QAbstractTableModel(parent)
  {
  }

  TableModel::TableModel(QList<Contact> contacts, QObject *parent)
      : QAbstractTableModel(parent)
      , contacts(contacts)
  {
  }

The rowCount() and columnCount() functions return the dimensions of the model. Whereas, rowCount()'s value will vary depending on the number of contacts added to the address book, columnCount()'s value is always 2 because we only need space for the Name and Address columns.
**
rowCount()和columnCount()函数返回模型的维度。然而,rowCount()的值将根据添加到通讯簿的联系人数量而变化,columnCount()的值始终为2,因为我们只需要为名称和地址列留出空间。

Note: The Q_UNUSED() macro prevents the compiler from generating warnings regarding unused parameters.
**
注意:Q_UNUSED()宏防止编译器生成有关未使用参数的警告。

  int TableModel::rowCount(const QModelIndex &parent) const
  {
      Q_UNUSED(parent);
      return contacts.size();
  }

  int TableModel::columnCount(const QModelIndex &parent) const
  {
      Q_UNUSED(parent);
      return 2;
  }

The data() function returns either a Name or 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 AddressWidget.
**
函数的作用是:根据提供的模型索引的内容,返回名称或地址。模型索引中存储的行号用于引用联系人列表中的项目。选择由QItemSelectionModel处理,将用AddressWidget解释。

  QVariant TableModel::data(const QModelIndex &index, int role) const
  {
      if (!index.isValid())
          return QVariant();

      if (index.row() >= contacts.size() || index.row() < 0)
          return QVariant();

      if (role == Qt::DisplayRole) {
          const auto &contact = contacts.at(index.row());

          if (index.column() == 0)
              return contact.name;
          else if (index.column() == 1)
              return contact.address;
      }
      return QVariant();
  }

The headerData() function displays the table’s header, Name and 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 AddressWidget implementation).
**
函数的作用是:显示表格的标题、名称和地址。如果您需要地址簿的编号条目,可以使用我们在本例中隐藏的垂直头(请参阅AddressWidget实现)

  QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
  {
      if (role != Qt::DisplayRole)
          return QVariant();

      if (orientation == Qt::Horizontal) {
          switch (section) {
              case 0:
                  return tr("Name");

              case 1:
                  return tr("Address");

              default:
                  return QVariant();
          }
      }
      return QVariant();
  }

The insertRows() function is called before new data is added, otherwise the data will not be displayed. The beginInsertRows() and endInsertRows() functions are called to ensure all connected views are aware of the changes.
**
在添加新数据之前调用insertRows()函数,否则将不显示数据。调用beginInsertRows()和endInsertRows()函数以确保所有连接的视图都知道这些更改。

  bool TableModel::insertRows(int position, int rows, const QModelIndex &index)
  {
      Q_UNUSED(index);
      beginInsertRows(QModelIndex(), position, position + rows - 1);

      for (int row = 0; row < rows; ++row)
          contacts.insert(position, { QString(), QString() });

      endInsertRows();
      return true;
  }

The removeRows() function is called to remove data. Again, beginRemoveRows() and endRemoveRows() are called to ensure all connected views are aware of the changes.
**
调用removeRows()函数以删除数据。再次调用beginRemoveRows()和endRemoveRows(),以确保所有连接的视图都知道这些更改。


  bool TableModel::removeRows(int position, int rows, const QModelIndex &index)
  {
      Q_UNUSED(index);
      beginRemoveRows(QModelIndex(), position, position + rows - 1);

      for (int row = 0; row < rows; ++row)
          contacts.removeAt(position);

      endRemoveRows();
      return true;
  }

The 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, setData() must be called twice, as each row has 2 columns. It is important to emit the dataChanged() signal as it tells all connected views to update their displays.
**
setData()函数是将数据逐项而不是逐行插入表中的函数。这意味着要填充通讯簿中的一行,必须调用setData(),因为每行有2列。发出dataChanged()信号很重要,因为它会告诉所有连接的视图更新其显示。

  bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
  {
      if (index.isValid() && role == Qt::EditRole) {
          int row = index.row();

          auto contact = contacts.value(row);

          if (index.column() == 0)
              contact.name = value.toString();
          else if (index.column() == 1)
              contact.address = value.toString();
          else
              return false;

          contacts.replace(row, contact);
          emit dataChanged(index, index, {role});

          return true;
      }

      return false;
  }

The flags() function returns the item flags for the given index.
**
函数的作用是:返回给定索引的项标志。

  Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
  {
      if (!index.isValid())
          return Qt::ItemIsEnabled;

      return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
  }

We set the Qt::ItemIsEditable flag because we want to allow the 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.
**
我们设置了Qt::ItemIsEditable标志,因为我们希望允许编辑TableModel。虽然在本例中,我们没有使用QTableView对象的编辑功能,但我们在这里启用了这些功能,以便我们可以在其他程序中重用该模型

The last function in TableModel, getContacts() returns the QList 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 AddressWidget.
**
TableModel中的最后一个函数getContacts()返回QList对象,该对象包含通讯簿中的所有联系人。我们稍后使用此函数获取联系人列表,以检查现有条目,将联系人写入文件并读回。AddressWidget提供了进一步的解释

  QList<Contact> TableModel::getContacts() const
  {
      return contacts;
  }
4.AddressWidget Class Definition(AddressWidget类定义)

The 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.
**
从技术上讲,AddressWidget类是本示例中涉及的主要类,因为它提供了添加、编辑和删除联系人、将联系人保存到文件以及从文件中加载联系人的功能

class AddressWidget : public QTabWidget
  {
      Q_OBJECT

  public:
      AddressWidget(QWidget *parent = 0);
      void readFromFile(const QString &fileName);
      void writeToFile(const QString &fileName);

  public slots:
      void showAddEntryDialog();
      void addEntry(QString name, QString address);
      void editEntry();
      void removeEntry();

  signals:
      void selectionChanged (const QItemSelection &selected);

  private:
      void setupTabs();

      TableModel *table;
      NewAddressTab *newAddressTab;
      QSortFilterProxyModel *proxyModel;
  };


AddressWidget extends QTabWidget in order to hold 10 tabs (NewAddressTab and the 9 alphabet group tabs) and also manipulates table, the TableModel object, proxyModel, the QSortFilterProxyModel object that we use to filter the entries, and tableView, the QTableView object.
**
AddressWidget扩展了QTabWidget,以容纳10个选项卡(NewAddressTab和9个字母组选项卡),还可以操作table、TableModel对象、proxyModel、用于过滤条目的QSortFilterProxyModel对象和tableView、QTableView对象

5.AddressWidget Class Implementation

The AddressWidget constructor accepts a parent widget and instantiates NewAddressTab, TableModel and QSortFilterProxyModel. The 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 setupTabs().
**
AddressWidget构造函数接受父小部件,并实例化NewAddressTab、TableModel和QSortFilterProxy模型。添加了NewAddressTab对象,用于指示通讯簿为空,其余9个选项卡使用setupTabs()设置


  AddressWidget::AddressWidget(QWidget *parent)
      : QTabWidget(parent)
  {
      table = new TableModel(this);
      newAddressTab = new NewAddressTab(this);
      connect(newAddressTab, &NewAddressTab::sendDetails,
          this, &AddressWidget::addEntry);

      addTab(newAddressTab, "Address Book");

      setupTabs();
  }

The setupTabs() function is used to set up the 9 alphabet group tabs, table views and proxy models in AddressWidget. Each proxy model in turn is set to filter contact names according to the relevant alphabet group using a case-insensitive QRegExp object. The table views are also sorted in ascending order using the corresponding proxy model’s sort() function.
**
函数用于在AddressWidget中设置9个字母组选项卡、表视图和代理模型。每个代理模型依次设置为使用不区分大小写的QRegExp对象根据相关字母组过滤联系人名称。表视图也使用相应代理模型的sort()函数按升序排序。

Each table view’s selectionMode is set to QAbstractItemView::SingleSelection and 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.
**
每个表视图的selectionMode设置为QAbstractItemView::SingleSelection,selectionBehavior设置为QAbstractItemView::SelectRows,允许用户同时选择一行中的所有项目。每个QTableView对象都会自动获得一个QItemSelectionModel,用于跟踪所选索引。

void AddressWidget::setupTabs()
  {
      QStringList groups;
      groups << "ABC" << "DEF" << "GHI" << "JKL" << "MNO" << "PQR" << "STU" << "VW" << "XYZ";

      for (int i = 0; i < groups.size(); ++i) {
          QString str = groups.at(i);
          QString regExp = QString("^[%1].*").arg(str);

          proxyModel = new QSortFilterProxyModel(this);
          proxyModel->setSourceModel(table);
          proxyModel->setFilterRegExp(QRegExp(regExp, Qt::CaseInsensitive));
          proxyModel->setFilterKeyColumn(0);

          QTableView *tableView = new QTableView;
          tableView->setModel(proxyModel);

          tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
          tableView->horizontalHeader()->setStretchLastSection(true);
          tableView->verticalHeader()->hide();
          tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
          tableView->setSelectionMode(QAbstractItemView::SingleSelection);

          tableView->setSortingEnabled(true);

          connect(tableView->selectionModel(),
              &QItemSelectionModel::selectionChanged,
              this, &AddressWidget::selectionChanged);

          connect(this, &QTabWidget::currentChanged, this, [this](int tabIndex) {
              auto *tableView = qobject_cast<QTableView *>(widget(tabIndex));
              if (tableView)
                  emit selectionChanged(tableView->selectionModel()->selection());
          });

          addTab(tableView, str);
      }
  }

The QItemSelectionModel class provides a selectionChanged signal that is connected to AddressWidget’s selectionChanged() signal. We also connect QTabWidget::currentChanged() signal to the lambda expression which emits AddressWidget’s selectionChanged() as well. These connections are necessary to enable the Edit Entry… and Remove Entry actions in MainWindow’s Tools menu. It is further explained in MainWindow’s implementation.
**
QItemSelectionModel类提供了一个连接到AddressWidget的selectionChanged()信号的selectionChanged信号。我们还将QTabWidget::currentChanged()信号连接到lambda表达式,该表达式还发出AddressWidget的selectionChanged()。这些连接是启用编辑条目所必需的。。。并删除主窗口工具菜单中的输入操作。在MainWindow的实现中进一步解释了这一点。

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.
**
地址簿中的每个表视图都作为一个选项卡添加到QTabWidget,并带有相关标签,这些标签是从组的QStringList中获得的。
Qt_Demo_01_Address Book Example(通讯簿示例)_第2张图片
We provide 2 addEntry() functions: 1 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 newAddressTab to insert data without having to popup a dialog.
**
我们提供了2个addEntry()函数:1个用于接受用户输入,另一个用于执行向地址簿中添加新条目的实际任务。我们将添加条目的责任分为两部分,以允许newAddressTab在不弹出对话框的情况下插入数据。

The first addEntry() function is a slot connected to the MainWindow’s Add Entry… action. This function creates an AddDialog object and then calls the second addEntry() function to actually add the contact to table.
**
第一个addEntry()函数是一个连接到主窗口的Add条目的插槽。。。行动该函数创建一个AddDialog对象,然后调用第二个addEntry()函数将联系人实际添加到表中。

  void AddressWidget::showAddEntryDialog()
  {
      AddDialog aDialog;

      if (aDialog.exec()) {
          QString name = aDialog.nameText->text();
          QString address = aDialog.addressText->toPlainText();

          addEntry(name, address);
      }
  }

Basic validation is done in the second addEntry() function to prevent duplicate entries in the address book. As mentioned with TableModel, this is part of the reason why we require the getter method getContacts().
**
基本验证在第二个addEntry()函数中完成,以防止地址簿中出现重复条目。如TableModel所述,这是我们需要getter方法getContacts()的部分原因。

  void AddressWidget::addEntry(QString name, QString address)
  {
      if (!table->getContacts().contains({ name, address })) {
          table->insertRows(0, 1, QModelIndex());

          QModelIndex index = table->index(0, 0, QModelIndex());
          table->setData(index, name, Qt::EditRole);
          index = table->index(0, 1, QModelIndex());
          table->setData(index, address, Qt::EditRole);
          removeTab(indexOf(newAddressTab));
      } else {
          QMessageBox::information(this, tr("Duplicate Name"),
              tr("The name \"%1\" already exists.").arg(name));
      }
  }

If the model does not already contain an entry with the same name, we call setData() to insert the name and address into the first and second columns. Otherwise, we display a QMessageBox to inform the user.
**
如果模型中尚未包含同名条目,则调用setData()将名称和地址插入第一列和第二列。否则,我们会显示一个QMessageBox来通知用户。

Note: The newAddressTab is removed once a contact is added as the address book is no longer empty.
**
注意:由于通讯簿不再为空,一旦添加联系人,就会删除newAddressTab。

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 selectionModel from the tableView to obtain the selected indexes.
**
首先,我们使用QTabWidget::currentwidt()获得活动选项卡的QTableView对象。然后,我们从tableView中提取selectionModel以获得所选索引。

  void AddressWidget::editEntry()
  {
      QTableView *temp = static_cast<QTableView*>(currentWidget());
      QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
      QItemSelectionModel *selectionModel = temp->selectionModel();

      QModelIndexList indexes = selectionModel->selectedRows();
      QString name;
      QString address;
      int row = -1;

      foreach (QModelIndex index, indexes) {
          row = proxy->mapToSource(index).row();
          QModelIndex nameIndex = table->index(row, 0, QModelIndex());
          QVariant varName = table->data(nameIndex, Qt::DisplayRole);
          name = varName.toString();

          QModelIndex addressIndex = table->index(row, 1, QModelIndex());
          QVariant varAddr = table->data(addressIndex, Qt::DisplayRole);
          address = varAddr.toString();
      }

Next we extract data from the row the user intends to edit. This data is displayed in an instance of AddDialog with a different window title. The table is only updated if changes have been made to data in aDialog.
**
接下来,我们从用户打算编辑的行中提取数据。该数据以不同的窗口标题显示在AddDialog的实例中。只有在ADILOG中的数据发生更改时,该表才会更新。


      AddDialog aDialog;
      aDialog.setWindowTitle(tr("Edit a Contact"));

      aDialog.nameText->setReadOnly(true);
      aDialog.nameText->setText(name);
      aDialog.addressText->setText(address);

      if (aDialog.exec()) {
          QString newAddress = aDialog.addressText->toPlainText();
          if (newAddress != address) {
              QModelIndex index = table->index(row, 1, QModelIndex());
              table->setData(index, newAddress, Qt::EditRole);
          }
      }
  }

Qt_Demo_01_Address Book Example(通讯簿示例)_第3张图片

Entries are removed using the removeEntry() function. The selected row is removed by accessing it through the QItemSelectionModel object, selectionModel. The newAddressTab is re-added to the AddressWidget only if the user removes all the contacts in the address book.
**
使用removeEntry()函数删除条目。通过QItemSelectionModel对象selectionModel访问所选行,即可将其删除。只有当用户删除通讯簿中的所有联系人时,才会将newAddressTab重新添加到AddressWidget

  void AddressWidget::removeEntry()
  {
      QTableView *temp = static_cast<QTableView*>(currentWidget());
      QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
      QItemSelectionModel *selectionModel = temp->selectionModel();

      QModelIndexList indexes = selectionModel->selectedRows();

      foreach (QModelIndex index, indexes) {
          int row = proxy->mapToSource(index).row();
          table->removeRows(row, 1, QModelIndex());
      }

      if (table->rowCount(QModelIndex()) == 0) {
          insertTab(0, newAddressTab, "Address Book");
      }
  }

The writeToFile() function is used to save a file containing all the contacts in the address book. The file is saved in a custom .dat format. The contents of the list of contacts are written to file using QDataStream. If the file cannot be opened, a QMessageBox is displayed with the related error message.
**
writeToFile()函数用于保存包含通讯簿中所有联系人的文件。文件保存在自定义文件中。dat格式。联系人列表的内容使用QDataStream写入文件。如果无法打开文件,则会显示QMessageBox,并显示相关错误消息。

  void AddressWidget::writeToFile(const QString &fileName)
  {
      QFile file(fileName);

      if (!file.open(QIODevice::WriteOnly)) {
          QMessageBox::information(this, tr("Unable to open file"), file.errorString());
          return;
      }

      QDataStream out(&file);
      out << table->getContacts();
  }

The readFromFile() function loads a file containing all the contacts in the address book, previously saved using writeToFile(). QDataStream is used to read the contents of a .dat file into a list of contacts and each of these is added using addEntry().
**
函数的作用是:加载一个包含通讯录中所有联系人的文件,该文件以前使用writeToFile()保存。QDataStream用于读取的内容。将dat文件添加到联系人列表中,并使用addEntry()添加每个联系人。


  void AddressWidget::readFromFile(const QString &fileName)
  {
      QFile file(fileName);

      if (!file.open(QIODevice::ReadOnly)) {
          QMessageBox::information(this, tr("Unable to open file"),
              file.errorString());
          return;
      }

      QList<Contact> contacts;
      QDataStream in(&file);
      in >> contacts;

      if (contacts.isEmpty()) {
          QMessageBox::information(this, tr("No contacts in file"),
                                   tr("The file you are attempting to open contains no contacts."));
      } else {
          for (const auto &contact: qAsConst(contacts))
              addEntry(contact.name, contact.address);
      }
  }
6.NewAddressTab Class Definition(NewAddressTab类定义)

The 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 AddressWidget’s implementation.
**
NewAddressTab类提供了一个信息性选项卡,告诉用户地址簿是空的。它根据地址簿的内容出现和消失,如AddressWidget的实现中所述。
Qt_Demo_01_Address Book Example(通讯簿示例)_第4张图片
The NewAddressTab class extends QWidget and contains a QLabel and QPushButton.
**
NewAddressTab类扩展了QWidget,并包含QLabel和QPushButton。

  class NewAddressTab : public QWidget
  {
      Q_OBJECT

  public:
      NewAddressTab(QWidget *parent = 0);

  public slots:
      void addEntry();

  signals:
      void sendDetails(QString name, QString address);

  private:
      QLabel *descriptionLabel;
      QPushButton *addButton;
      QVBoxLayout *mainLayout;

  };
7.NewAddressTab Class Implementation(NewAddressTab类实现)

The constructor instantiates the addButton, descriptionLabel and connects the addButton’s signal to the addEntry() slot.
**
构造函数实例化addButton、descriptionLabel,并将addButton的信号连接到addEntry()插槽。

  NewAddressTab::NewAddressTab(QWidget *parent)
  {
      Q_UNUSED(parent);

      descriptionLabel = new QLabel(tr("There are currently no contacts in your address book. "
                                        "\nClick Add to add new contacts."));

      addButton = new QPushButton(tr("Add"));

      connect(addButton, &QAbstractButton::clicked, this, &NewAddressTab::addEntry);

      mainLayout = new QVBoxLayout;
      mainLayout->addWidget(descriptionLabel);
      mainLayout->addWidget(addButton, 0, Qt::AlignCenter);

      setLayout(mainLayout);
  }

The addEntry() function is similar to AddressWidget’s addEntry() in the sense that both functions instantiate an AddDialog object. Data from the dialog is extracted and sent to AddressWidget’s addEntry() slot by emitting the sendDetails() signal.
**
addEntry()函数类似于AddressWidget的addEntry(),因为这两个函数都实例化了AddDialog对象。通过发出sendDetails()信号,从对话框中提取数据并发送到AddressWidget的addEntry()插槽。

  void NewAddressTab::addEntry()
  {
      AddDialog aDialog;

      if (aDialog.exec()) {
          QString name = aDialog.nameText->text();
          QString address = aDialog.addressText->toPlainText();

          emit sendDetails(name, address);
      }
  }

Qt_Demo_01_Address Book Example(通讯簿示例)_第5张图片

8.AddDialog Class Definition(AddDialog类定义)

The AddDialog class extends QDialog and provides the user with a QLineEdit and a QTextEdit to input data into the address book.
**
AddDialog类扩展了QDialog,并为用户提供了一个QLineEdit和一个QTextEdit来将数据输入到通讯簿中

class AddDialog : public QDialog
  {
      Q_OBJECT

  public:
      AddDialog(QWidget *parent = 0);
      QLineEdit *nameText;
      QTextEdit *addressText;

  private:
      QLabel *nameLabel;
      QLabel *addressLabel;
      QPushButton *okButton;
      QPushButton *cancelButton;
  };

Qt_Demo_01_Address Book Example(通讯簿示例)_第6张图片

9.AddDialog Class Implementation(AddDialog类实现)

The AddDialog’s constructor sets up the user interface, creating the necessary widgets and placing them into layouts.
**
AddDialog的构造函数设置用户界面,创建必要的小部件并将其放入布局中。

AddDialog::AddDialog(QWidget *parent)
      : QDialog(parent)
  {
      nameLabel = new QLabel("Name");
      addressLabel = new QLabel("Address");
      okButton = new QPushButton("OK");
      cancelButton = new QPushButton("Cancel");

      nameText = new QLineEdit;
      addressText = new QTextEdit;

      QGridLayout *gLayout = new QGridLayout;
      gLayout->setColumnStretch(1, 2);
      gLayout->addWidget(nameLabel, 0, 0);
      gLayout->addWidget(nameText, 0, 1);

      gLayout->addWidget(addressLabel, 1, 0, Qt::AlignLeft|Qt::AlignTop);
      gLayout->addWidget(addressText, 1, 1, Qt::AlignLeft);

      QHBoxLayout *buttonLayout = new QHBoxLayout;
      buttonLayout->addWidget(okButton);
      buttonLayout->addWidget(cancelButton);

      gLayout->addLayout(buttonLayout, 2, 1, Qt::AlignRight);

      QVBoxLayout *mainLayout = new QVBoxLayout;
      mainLayout->addLayout(gLayout);
      setLayout(mainLayout);

      connect(okButton, &QAbstractButton::clicked, this, &QDialog::accept);
      connect(cancelButton, &QAbstractButton::clicked, this, &QDialog::reject);

      setWindowTitle(tr("Add a Contact"));
  }

To give the dialog the desired behavior, we connect the OK and Cancel buttons to the dialog’s accept() and 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.
**
为了给对话框提供所需的行为,我们将OK和Cancel按钮连接到对话框的accept()和reject()插槽。由于对话框仅作为名称和地址信息的容器,因此我们不需要为其实现任何其他功能。

10.MainWindow Class Definition(主窗口类定义)

The MainWindow class extends QMainWindow and implements the menus and actions necessary to manipulate the address book.
**
MainWindow类扩展了QMainWindow,并实现了操作通讯簿所需的菜单和操作。
Qt_Demo_01_Address Book Example(通讯簿示例)_第7张图片

  class MainWindow : public QMainWindow
  {
      Q_OBJECT

  public:
      MainWindow();

  private slots:
      void updateActions(const QItemSelection &selection);
      void openFile();
      void saveFile();

  private:
      void createMenus();

      AddressWidget *addressWidget;
      QMenu *fileMenu;
      QMenu *toolMenu;
      QAction *openAct;
      QAction *saveAct;
      QAction *exitAct;
      QAction *addAct;
      QAction *editAct;
      QAction *removeAct;
  };

The MainWindow class uses an AddressWidget as its central widget and provides the File menu with Open, Close and Exit actions, as well as the Tools menu with Add Entry…, Edit Entry… and Remove Entry actions.
**
MainWindow类使用AddressWidget作为其中心小部件,并提供具有打开、关闭和退出操作的文件菜单,以及具有添加条目的工具菜单。。。,编辑条目。。。并删除输入操作。

11.MainWindow Class Implementation(主窗口类实现)

The constructor for MainWindow instantiates AddressWidget, sets it as its central widget and calls the createMenus() function.
**
主窗口的构造函数实例化AddressWidget,将其设置为其中心小部件,并调用createMenus()函数。

  MainWindow::MainWindow()
  {
      addressWidget = new AddressWidget;
      setCentralWidget(addressWidget);
      createMenus();
      setWindowTitle(tr("Address Book"));
  }

The createMenus() function sets up the File and Tools menus, connecting the actions to their respective slots. Both the Edit Entry… and 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.
**
函数的作用是:设置文件和工具菜单,将动作连接到各自的窗口。编辑条目。。。默认情况下,删除条目操作被禁用,因为无法在空通讯簿上执行此类操作。它们仅在添加一个或多个联系人时启用。

  void MainWindow::createMenus()
  {
      fileMenu = menuBar()->addMenu(tr("&File"));

      openAct = new QAction(tr("&Open..."), this);
      fileMenu->addAction(openAct);
      connect(openAct, &QAction::triggered, this, &MainWindow::openFile);
      ...

      editAct = new QAction(tr("&Edit Entry..."), this);
      editAct->setEnabled(false);
      toolMenu->addAction(editAct);
      connect(editAct, &QAction::triggered, addressWidget, &AddressWidget::editEntry);

      toolMenu->addSeparator();

      removeAct = new QAction(tr("&Remove Entry"), this);
      removeAct->setEnabled(false);
      toolMenu->addAction(removeAct);
      connect(removeAct, &QAction::triggered, addressWidget, &AddressWidget::removeEntry);

      connect(addressWidget, &AddressWidget::selectionChanged,
          this, &MainWindow::updateActions);
  }

Apart from connecting all the actions’ signals to their respective slots, we also connect AddressWidget’s selectionChanged() signal to its updateActions() slot.
**
除了将所有动作的信号连接到各自的插槽外,我们还将AddressWidget的selectionChanged()信号连接到其updateActions()插槽。

The openFile() function allows the user to choose a file with the open file dialog. The chosen file has to be a custom .dat file that contains address book contacts. This function is a slot connected to openAct in the File menu.
**
openFile()函数允许用户通过“打开文件”对话框选择文件。所选文件必须是自定义文件。包含通讯簿联系人的dat文件。此函数是一个连接到文件菜单中openAct的插槽。


  void MainWindow::openFile()
  {
      QString fileName = QFileDialog::getOpenFileName(this);
      if (!fileName.isEmpty())
          addressWidget->readFromFile(fileName);
  }

The saveFile() function allows the user to save a file with the save file dialog. This function is a slot connected to saveAct in the File menu.
**
saveFile()函数允许用户使用“保存文件”对话框保存文件。此函数是连接到文件菜单中的saveAct的插槽。

  void MainWindow::saveFile()
  {
      QString fileName = QFileDialog::getSaveFileName(this);
      if (!fileName.isEmpty())
          addressWidget->writeToFile(fileName);
  }

The updateActions() function enables and disables Edit Entry… and 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 AddressWidget’s selectionChanged() signal.
**
updateActions()函数启用和禁用编辑条目。。。并根据通讯簿的内容删除条目。如果通讯簿为空,则禁用这些操作;否则,它们将被启用。此函数是一个连接到AddressWidget的selectionChanged()信号的插槽。

  void MainWindow::updateActions(const QItemSelection &selection)
  {
      QModelIndexList indexes = selection.indexes();

      if (!indexes.isEmpty()) {
          removeAct->setEnabled(true);
          editAct->setEnabled(true);
      } else {
          removeAct->setEnabled(false);
          editAct->setEnabled(false);
      }
  }
12.main() Function(main()函数)

The main function for the address book instantiates QApplication and opens a MainWindow before running the event loop.
**
通讯簿的主函数实例化QApplication,并在运行事件循环之前打开一个主窗口。

  int main(int argc, char *argv[])
  {
      QApplication app(argc, argv);
      MainWindow mw;
      mw.show();
      return app.exec();
  }
13.Files:
  • itemviews/addressbook/adddialog.cpp
  • itemviews/addressbook/addressbook.pro
  • itemviews/addressbook/addresswidget.cpp
  • itemviews/addressbook/addresswidget.h
  • itemviews/addressbook/main.cpp
  • itemviews/addressbook/mainwindow.cpp
  • itemviews/addressbook/mainwindow.h
  • itemviews/addressbook/newaddresstab.cpp
  • itemviews/addressbook/newaddresstab.h
  • itemviews/addressbook/tablemodel.cpp
  • itemviews/addressbook/tablemodel.h
14.Code
  1. itemviews/addressbook/adddialog.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "adddialog.h"

#include 

//! [0]
AddDialog::AddDialog(QWidget *parent)
    : QDialog(parent)
{
    nameLabel = new QLabel("Name");
    addressLabel = new QLabel("Address");
    okButton = new QPushButton("OK");
    cancelButton = new QPushButton("Cancel");

    nameText = new QLineEdit;
    addressText = new QTextEdit;

    QGridLayout *gLayout = new QGridLayout;
    gLayout->setColumnStretch(1, 2);
    gLayout->addWidget(nameLabel, 0, 0);
    gLayout->addWidget(nameText, 0, 1);

    gLayout->addWidget(addressLabel, 1, 0, Qt::AlignLeft|Qt::AlignTop);
    gLayout->addWidget(addressText, 1, 1, Qt::AlignLeft);

    QHBoxLayout *buttonLayout = new QHBoxLayout;
    buttonLayout->addWidget(okButton);
    buttonLayout->addWidget(cancelButton);

    gLayout->addLayout(buttonLayout, 2, 1, Qt::AlignRight);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addLayout(gLayout);
    setLayout(mainLayout);

    connect(okButton, &QAbstractButton::clicked, this, &QDialog::accept);
    connect(cancelButton, &QAbstractButton::clicked, this, &QDialog::reject);

    setWindowTitle(tr("Add a Contact"));
}
//! [0]

  1. itemviews/addressbook/adddialog.h
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef ADDDIALOG_H
#define ADDDIALOG_H

#include 

QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
class QTextEdit;
class QLineEdit;
QT_END_NAMESPACE

//! [0]
class AddDialog : public QDialog
{
    Q_OBJECT

public:
    AddDialog(QWidget *parent = 0);
    QLineEdit *nameText;
    QTextEdit *addressText;

private:
    QLabel *nameLabel;
    QLabel *addressLabel;
    QPushButton *okButton;
    QPushButton *cancelButton;
};
//! [0]

#endif // ADDDIALOG_H

  1. itemviews/addressbook/addressbook.pro
QT += widgets
requires(qtConfig(listview))

SOURCES   = adddialog.cpp \
            addresswidget.cpp \
            main.cpp \
            mainwindow.cpp \
            newaddresstab.cpp \
            tablemodel.cpp
HEADERS   = adddialog.h \
            addresswidget.h \
            mainwindow.h \
            newaddresstab.h \
            tablemodel.h

# install
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/itemviews/addressbook
INSTALLS += target

  1. itemviews/addressbook/addresswidget.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "adddialog.h"
#include "addresswidget.h"

#include 

//! [0]
AddressWidget::AddressWidget(QWidget *parent)
    : QTabWidget(parent)
{
    table = new TableModel(this);
    newAddressTab = new NewAddressTab(this);
    connect(newAddressTab, &NewAddressTab::sendDetails,
        this, &AddressWidget::addEntry);

    addTab(newAddressTab, "Address Book");

    setupTabs();
}
//! [0]

//! [2]
void AddressWidget::showAddEntryDialog()
{
    AddDialog aDialog;

    if (aDialog.exec()) {
        QString name = aDialog.nameText->text();
        QString address = aDialog.addressText->toPlainText();

        addEntry(name, address);
    }
}
//! [2]

//! [3]
void AddressWidget::addEntry(QString name, QString address)
{
    if (!table->getContacts().contains({ name, address })) {
        table->insertRows(0, 1, QModelIndex());

        QModelIndex index = table->index(0, 0, QModelIndex());
        table->setData(index, name, Qt::EditRole);
        index = table->index(0, 1, QModelIndex());
        table->setData(index, address, Qt::EditRole);
        removeTab(indexOf(newAddressTab));
    } else {
        QMessageBox::information(this, tr("Duplicate Name"),
            tr("The name \"%1\" already exists.").arg(name));
    }
}
//! [3]

//! [4a]
void AddressWidget::editEntry()
{
    QTableView *temp = static_cast<QTableView*>(currentWidget());
    QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
    QItemSelectionModel *selectionModel = temp->selectionModel();

    QModelIndexList indexes = selectionModel->selectedRows();
    QString name;
    QString address;
    int row = -1;

    foreach (QModelIndex index, indexes) {
        row = proxy->mapToSource(index).row();
        QModelIndex nameIndex = table->index(row, 0, QModelIndex());
        QVariant varName = table->data(nameIndex, Qt::DisplayRole);
        name = varName.toString();

        QModelIndex addressIndex = table->index(row, 1, QModelIndex());
        QVariant varAddr = table->data(addressIndex, Qt::DisplayRole);
        address = varAddr.toString();
    }
//! [4a]

//! [4b]
    AddDialog aDialog;
    aDialog.setWindowTitle(tr("Edit a Contact"));

    aDialog.nameText->setReadOnly(true);
    aDialog.nameText->setText(name);
    aDialog.addressText->setText(address);

    if (aDialog.exec()) {
        QString newAddress = aDialog.addressText->toPlainText();
        if (newAddress != address) {
            QModelIndex index = table->index(row, 1, QModelIndex());
            table->setData(index, newAddress, Qt::EditRole);
        }
    }
}
//! [4b]

//! [5]
void AddressWidget::removeEntry()
{
    QTableView *temp = static_cast<QTableView*>(currentWidget());
    QSortFilterProxyModel *proxy = static_cast<QSortFilterProxyModel*>(temp->model());
    QItemSelectionModel *selectionModel = temp->selectionModel();

    QModelIndexList indexes = selectionModel->selectedRows();

    foreach (QModelIndex index, indexes) {
        int row = proxy->mapToSource(index).row();
        table->removeRows(row, 1, QModelIndex());
    }

    if (table->rowCount(QModelIndex()) == 0) {
        insertTab(0, newAddressTab, "Address Book");
    }
}
//! [5]

//! [1]
void AddressWidget::setupTabs()
{
    QStringList groups;
    groups << "ABC" << "DEF" << "GHI" << "JKL" << "MNO" << "PQR" << "STU" << "VW" << "XYZ";

    for (int i = 0; i < groups.size(); ++i) {
        QString str = groups.at(i);
        QString regExp = QString("^[%1].*").arg(str);

        proxyModel = new QSortFilterProxyModel(this);
        proxyModel->setSourceModel(table);
        proxyModel->setFilterRegExp(QRegExp(regExp, Qt::CaseInsensitive));
        proxyModel->setFilterKeyColumn(0);

        QTableView *tableView = new QTableView;
        tableView->setModel(proxyModel);

        tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
        tableView->horizontalHeader()->setStretchLastSection(true);
        tableView->verticalHeader()->hide();
        tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
        tableView->setSelectionMode(QAbstractItemView::SingleSelection);

        tableView->setSortingEnabled(true);

        connect(tableView->selectionModel(),
            &QItemSelectionModel::selectionChanged,
            this, &AddressWidget::selectionChanged);

        connect(this, &QTabWidget::currentChanged, this, [this](int tabIndex) {
            auto *tableView = qobject_cast<QTableView *>(widget(tabIndex));
            if (tableView)
                emit selectionChanged(tableView->selectionModel()->selection());
        });

        addTab(tableView, str);
    }
}
//! [1]

//! [7]
void AddressWidget::readFromFile(const QString &fileName)
{
    QFile file(fileName);

    if (!file.open(QIODevice::ReadOnly)) {
        QMessageBox::information(this, tr("Unable to open file"),
            file.errorString());
        return;
    }

    QList<Contact> contacts;
    QDataStream in(&file);
    in >> contacts;

    if (contacts.isEmpty()) {
        QMessageBox::information(this, tr("No contacts in file"),
                                 tr("The file you are attempting to open contains no contacts."));
    } else {
        for (const auto &contact: qAsConst(contacts))
            addEntry(contact.name, contact.address);
    }
}
//! [7]

//! [6]
void AddressWidget::writeToFile(const QString &fileName)
{
    QFile file(fileName);

    if (!file.open(QIODevice::WriteOnly)) {
        QMessageBox::information(this, tr("Unable to open file"), file.errorString());
        return;
    }

    QDataStream out(&file);
    out << table->getContacts();
}
//! [6]

  1. itemviews/addressbook/addresswidget.h
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef ADDRESSWIDGET_H
#define ADDRESSWIDGET_H

#include "newaddresstab.h"
#include "tablemodel.h"

#include 
#include 

QT_BEGIN_NAMESPACE
class QSortFilterProxyModel;
class QItemSelectionModel;
QT_END_NAMESPACE

//! [0]
class AddressWidget : public QTabWidget
{
    Q_OBJECT

public:
    AddressWidget(QWidget *parent = 0);
    void readFromFile(const QString &fileName);
    void writeToFile(const QString &fileName);

public slots:
    void showAddEntryDialog();
    void addEntry(QString name, QString address);
    void editEntry();
    void removeEntry();

signals:
    void selectionChanged (const QItemSelection &selected);

private:
    void setupTabs();

    TableModel *table;
    NewAddressTab *newAddressTab;
    QSortFilterProxyModel *proxyModel;
};
//! [0]

#endif // ADDRESSWIDGET_H

  1. itemviews/addressbook/main.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "mainwindow.h"

#include 

//! [0]
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow mw;
    mw.show();
    return app.exec();
}
//! [0]

  1. itemviews/addressbook/mainwindow.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "mainwindow.h"

#include 
#include 
#include 

//! [0]
MainWindow::MainWindow()
{
    addressWidget = new AddressWidget;
    setCentralWidget(addressWidget);
    createMenus();
    setWindowTitle(tr("Address Book"));
}
//! [0]

//! [1a]
void MainWindow::createMenus()
{
    fileMenu = menuBar()->addMenu(tr("&File"));

    openAct = new QAction(tr("&Open..."), this);
    fileMenu->addAction(openAct);
    connect(openAct, &QAction::triggered, this, &MainWindow::openFile);
//! [1a]

    saveAct = new QAction(tr("&Save As..."), this);
    fileMenu->addAction(saveAct);
    connect(saveAct, &QAction::triggered, this, &MainWindow::saveFile);

    fileMenu->addSeparator();

    exitAct = new QAction(tr("E&xit"), this);
    fileMenu->addAction(exitAct);
    connect(exitAct, &QAction::triggered, this, &QWidget::close);

    toolMenu = menuBar()->addMenu(tr("&Tools"));

    addAct = new QAction(tr("&Add Entry..."), this);
    toolMenu->addAction(addAct);
    connect(addAct, &QAction::triggered, addressWidget, &AddressWidget::showAddEntryDialog);

//! [1b]
    editAct = new QAction(tr("&Edit Entry..."), this);
    editAct->setEnabled(false);
    toolMenu->addAction(editAct);
    connect(editAct, &QAction::triggered, addressWidget, &AddressWidget::editEntry);

    toolMenu->addSeparator();

    removeAct = new QAction(tr("&Remove Entry"), this);
    removeAct->setEnabled(false);
    toolMenu->addAction(removeAct);
    connect(removeAct, &QAction::triggered, addressWidget, &AddressWidget::removeEntry);

    connect(addressWidget, &AddressWidget::selectionChanged,
        this, &MainWindow::updateActions);
}
//! [1b]

//! [2]
void MainWindow::openFile()
{
    QString fileName = QFileDialog::getOpenFileName(this);
    if (!fileName.isEmpty())
        addressWidget->readFromFile(fileName);
}
//! [2]

//! [3]
void MainWindow::saveFile()
{
    QString fileName = QFileDialog::getSaveFileName(this);
    if (!fileName.isEmpty())
        addressWidget->writeToFile(fileName);
}
//! [3]

//! [4]
void MainWindow::updateActions(const QItemSelection &selection)
{
    QModelIndexList indexes = selection.indexes();

    if (!indexes.isEmpty()) {
        removeAct->setEnabled(true);
        editAct->setEnabled(true);
    } else {
        removeAct->setEnabled(false);
        editAct->setEnabled(false);
    }
}
//! [4]

  1. itemviews/addressbook/mainwindow.h
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "addresswidget.h"

#include 

//! [0]
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

private slots:
    void updateActions(const QItemSelection &selection);
    void openFile();
    void saveFile();

private:
    void createMenus();

    AddressWidget *addressWidget;
    QMenu *fileMenu;
    QMenu *toolMenu;
    QAction *openAct;
    QAction *saveAct;
    QAction *exitAct;
    QAction *addAct;
    QAction *editAct;
    QAction *removeAct;
};
//! [0]

#endif // MAINWINDOW_H

  1. itemviews/addressbook/newaddresstab.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "adddialog.h"
#include "newaddresstab.h"

#include 

//! [0]
NewAddressTab::NewAddressTab(QWidget *parent)
{
    Q_UNUSED(parent);

    descriptionLabel = new QLabel(tr("There are currently no contacts in your address book. "
                                      "\nClick Add to add new contacts."));

    addButton = new QPushButton(tr("Add"));

    connect(addButton, &QAbstractButton::clicked, this, &NewAddressTab::addEntry);

    mainLayout = new QVBoxLayout;
    mainLayout->addWidget(descriptionLabel);
    mainLayout->addWidget(addButton, 0, Qt::AlignCenter);

    setLayout(mainLayout);
}
//! [0]

//! [1]
void NewAddressTab::addEntry()
{
    AddDialog aDialog;

    if (aDialog.exec()) {
        QString name = aDialog.nameText->text();
        QString address = aDialog.addressText->toPlainText();

        emit sendDetails(name, address);
    }
}
//! [1]

  1. itemviews/addressbook/newaddresstab.h
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef NEWADDRESSTAB_H
#define NEWADDRESSTAB_H

#include 

QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
class QVBoxLayout;
QT_END_NAMESPACE

//! [0]
class NewAddressTab : public QWidget
{
    Q_OBJECT

public:
    NewAddressTab(QWidget *parent = 0);

public slots:
    void addEntry();

signals:
    void sendDetails(QString name, QString address);

private:
    QLabel *descriptionLabel;
    QPushButton *addButton;
    QVBoxLayout *mainLayout;

};
//! [0]

#endif

  1. itemviews/addressbook/tablemodel.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "tablemodel.h"

//! [0]
TableModel::TableModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}

TableModel::TableModel(QList<Contact> contacts, QObject *parent)
    : QAbstractTableModel(parent)
    , contacts(contacts)
{
}
//! [0]

//! [1]
int TableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return contacts.size();
}

int TableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return 2;
}
//! [1]

//! [2]
QVariant TableModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= contacts.size() || index.row() < 0)
        return QVariant();

    if (role == Qt::DisplayRole) {
        const auto &contact = contacts.at(index.row());

        if (index.column() == 0)
            return contact.name;
        else if (index.column() == 1)
            return contact.address;
    }
    return QVariant();
}
//! [2]

//! [3]
QVariant TableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal) {
        switch (section) {
            case 0:
                return tr("Name");

            case 1:
                return tr("Address");

            default:
                return QVariant();
        }
    }
    return QVariant();
}
//! [3]

//! [4]
bool TableModel::insertRows(int position, int rows, const QModelIndex &index)
{
    Q_UNUSED(index);
    beginInsertRows(QModelIndex(), position, position + rows - 1);

    for (int row = 0; row < rows; ++row)
        contacts.insert(position, { QString(), QString() });

    endInsertRows();
    return true;
}
//! [4]

//! [5]
bool TableModel::removeRows(int position, int rows, const QModelIndex &index)
{
    Q_UNUSED(index);
    beginRemoveRows(QModelIndex(), position, position + rows - 1);

    for (int row = 0; row < rows; ++row)
        contacts.removeAt(position);

    endRemoveRows();
    return true;
}
//! [5]

//! [6]
bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole) {
        int row = index.row();

        auto contact = contacts.value(row);

        if (index.column() == 0)
            contact.name = value.toString();
        else if (index.column() == 1)
            contact.address = value.toString();
        else
            return false;

        contacts.replace(row, contact);
        emit dataChanged(index, index, {role});

        return true;
    }

    return false;
}
//! [6]

//! [7]
Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::ItemIsEnabled;

    return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}
//! [7]

//! [8]
QList<Contact> TableModel::getContacts() const
{
    return contacts;
}
//! [8]

  1. itemviews/addressbook/tablemodel.h
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of The Qt Company Ltd nor the names of its
**     contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef TABLEMODEL_H
#define TABLEMODEL_H

#include 
#include 

//! [0]

struct Contact
{
    QString name;
    QString address;

    bool operator==(const Contact &other) const
    {
        return name == other.name && address == other.address;
    }
};

inline QDataStream &operator<<(QDataStream &stream, const Contact &contact)
{
    return stream << contact.name << contact.address;
}

inline QDataStream &operator>>(QDataStream &stream, Contact &contact)
{
    return stream >> contact.name >> contact.address;
}

class TableModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    TableModel(QObject *parent = 0);
    TableModel(QList<Contact> contacts, QObject *parent = 0);

    int rowCount(const QModelIndex &parent) const override;
    int columnCount(const QModelIndex &parent) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
    bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
    bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
    QList<Contact> getContacts() const;

private:
    QList<Contact> contacts;
};
//! [0]

#endif // TABLEMODEL_H

你可能感兴趣的:(Qt,qt,c++)