Intruder
3 февраля 2020 г. 16:47

QTableView и список значений в ячейке

QTableView, QList, QVector

Всем доброго времени суток!

Подскажите, пожалуйста, как лучше сделать:
1. Имеется модель данных, посторенная с помощью QAbstractTableView. Сама таблица отражает объект в плоском виде.
2. В некоторых колонках нужно отобразить список значений, потому что в самом объекте присутствуют QList/QVector, которые в свою очередь тоже содержат некоторые свойства.

Вопрос у меня возникает следующий, как сделать так, чтобы можно было записывать в модель данных объект, который имеет в своем составе массив, как свойство. Если бы это была простая БД, то особых проблем бы не было, я просто нормализовал бы данные, связал связями и получил обычный Select через первичные и внешние ключи.
Но тут у меня объект программный и для него нужна модель данных. Модель я построил. Но вопрос остался с ячейками, где должны храниться массивы. Как тут быть?

2

Вам это нравится? Поделитесь в социальных сетях!

10
Evgenii Legotckoi
  • 3 февраля 2020 г. 17:20

Добрый день,
Покажите модель данных и класс объекта, который отвечает за репрезентацию данных. Обычно достаточно иметь поле вектора, а в модель превращать список/вектор в строку требуемых значений и отдавать его для Display роли.

    Intruder
    • 3 февраля 2020 г. 18:35

    Вот это модель данных:

    1. QVariant FeedThruTableModel::headerData(int section, Qt::Orientation orientation, int role) const
    2. {
    3. if(role != Qt::DisplayRole){
    4. return QVariant();
    5. }
    6.  
    7. if(orientation == Qt::Vertical){
    8. return section + 1;
    9. }
    10. switch(section){
    11. case HOLEIDENT:
    12. return tr("Hole Identification");
    13. case FUNCTIONALITEMNUMBER:
    14. return tr("Functional Item Number Identifier");
    15. case FUNCTIONALITEMTYPE:
    16. return tr("Functional Item Type");
    17. case IDFUNCTIONALITEMREF:
    18. return tr("Identifier");
    19. case INSTALLATIONIDENT:
    20. return tr("Installation Identifier");
    21. case APPLICREFID:
    22. return tr("Applicability Information");
    23. case CHANGETYPE:
    24. return tr("Change Type");
    25. case CHANGEMARK:
    26. return tr("Change Mark");
    27. case REASONFORUPDATEREFIDS:
    28. return tr("Reason For Update Ref Ids");
    29. case SECURITYCLASSIFICATION:
    30. return tr("Security Classification");
    31. case COMMERCIALCLASSIFICATION:
    32. return tr("Commercial Classification");
    33. case CAVEAT:
    34. return tr("Caveat");
    35. case AUTHORITYNAME:
    36. return tr("Authority Name");
    37. case AUTHORITYDOCUMENT:
    38. return tr("Authority Document");
    39. case CONTEXTIDENT:
    40. return tr("Context Identification");
    41. case MANUFACTURERCODEVALUE:
    42. return tr("CAGE Code");
    43. case ITEMORIGINATOR:
    44. return tr("Functional Item Originator");
    45. case NAMEVALUE:
    46. return tr("Functional Item Name");
    47. case CHANGETYPENAME:
    48. return tr("Change Type");
    49. case CHANGEMARKNAME:
    50. return tr("Change Mark");
    51. case REASONFORUPDATEREFIDSNAME:
    52. return tr("Reason For Update Ref Ids");
    53. case SHORTNAMEVALUE:
    54. return tr("Alternate Name");
    55. case DMREF: // Вот это поле объявлено как QVector<dmRef> *vectorDmRef
    56. return tr("References to data modules");
    57. case PMREF: // Вот это поле объявлено как QVector<pmRef> *vectorPmRef
    58. return tr("References to publications");
    59. case EXTERNALPUBREF: // Вот это поле как QVector<externalPubRef> *vectorExternalPubRef
    60. return tr("References to external documents");
    61. }
    62. return QVariant();
    63. }
    64.  

    Сам объект описан вот так:

    1. // Attributes of the tag <funcitonalItemRef>
    2. QMap<QString, QString> *functionalItemRefAttrMap = nullptr;
    3. // Tag <name>
    4. Name *nameObject = nullptr;
    5. // Tag <shortName>
    6. QString shortNameValue;
    7. // Tag <refs>
    8. Refs *refsObject = nullptr;

    Объект Refs:

    1. // The tag <dmRef>
    2. QVector<DmRef> *dmRefVector = nullptr;
    3. // The tag <pmRef>
    4. // Здесь пока ничего нет, но по аналогии с dmRef
    5. // The tag <externalPubRef>
    6. // Здесь пока ничего нет, но по аналогии с dmRef

    Или я что-то неправильно понял?

      Evgenii Legotckoi
      • 4 февраля 2020 г. 3:32

      Немного не догоняю, вот этот вектор dmRefVector должен как-то мапиться во все эти enum?

      Может тогда лучше QMap ? А потом уже возвращать и устанавливать данные в зависимости от этого enum?

        Intruder
        • 4 февраля 2020 г. 10:00

        Евгений, добрый день.

        Я планировал для элемента dmRef получать агрегатное значение и его отображать. Иными словами в модели будет храниться его подготовленное значение. Если представлять его в каком-то виде, то я предполагал взять QListView для этих целей. А зачем QMap ?
        Но, сейчас у меня возник еще один вопрос, а как эти данные готовить. С однозначными(атомарными) значениями все понятно, а тут? Я могу показать весь класс для этой модели, если нужно.

          Intruder
          • 4 февраля 2020 г. 12:37

          Евгений, у меня с обычными таблицами проблем вообще никаких не возникло, разобрался как это работает и в путь. Но тут получается, что объект, который я кладу в модель данных - он составной и в его составе могут быть другие обеъкты и/или массивы/списки/словари. А вот как в этом случае строить модель данных для меня не совсем понятно.
          Я тут поразмыслил и мне пришла в голову мысль, что нужно как-то модернизировать эти объекты-"матрешки". Например, вместо объекта, который входит в состав хранить не сам объект, а его идентификатор-ключ. В объекте, который будет включаться ввести дополнительное свойство, идентификатор. Вот только придется следить за уникальностью этих идентификаторов программно при создании.
          Вот только одного я не учел. В файл мне нельзя класть эти идентификаторы иначе я не пройду валидацию. Как вариант придется ломать формат и вводить комментарии или элементы, которые не влияют на валидацию.

            Evgenii Legotckoi
            • 4 февраля 2020 г. 14:08
            • (ред.)

            Класс объекта, который вы храните в модели должен иметь нужные методы для возврата и установки значений, а логику по возврату значений из "составных" кусков инкапсулируйте внутрь класса. И там уже внутри разбиайтесь что там есть, а чего нет. И в зависимости от типа данных и составных кусков сможете возвращать информацию в удобовармом для модели виде.

            Для управления уникальными указателями попробуйте использовать shared_ptr

              Intruder
              • 4 февраля 2020 г. 14:21

              Евгений, спасибо.
              Буду пробовать.

                Intruder
                • 5 февраля 2020 г. 17:32

                Евгений, добрый день.

                Я понял о чем Вы меня спрашивали, когда речь шла про вектор. Нет, этот вектор должен отображаться только в одну ячейку. Получается, что с точки зрения БД - мое значение не атомарно, а представляет собой набор значений, в свою очередь эти значения представляют собой QMap.

                  Intruder
                  • 5 февраля 2020 г. 23:53

                  Решил пойти сначала. Создал тестовый проект.
                  Тестовая форма
                  Здесь отображаются данные в верхней части, это данные основного объекта, а в нижней части отображаются данные объекта, который входит в состав основного объекта. Этим объектом является QVector<составной_объект>.
                  Классы, которые описывают основной и составной объект достаточно просты:
                  Составной объект class Contact и его описание

                  1. #ifndef CONTACT_H
                  2. #define CONTACT_H
                  3.  
                  4. #include <QMap>
                  5.  
                  6. class Contact
                  7. {
                  8. public:
                  9. Contact();
                  10. ~Contact(){}
                  11.  
                  12. QMap<QString, QString> *getMap() const;
                  13. void setMap(QMap<QString, QString> *value);
                  14. private:
                  15. QMap<QString, QString> *map = nullptr;
                  16. };
                  17.  
                  18. #endif // CONTACT_H
                  1. #include "contact.h"
                  2. Contact::Contact() {}
                  3.  
                  4. QMap<QString, QString> *Contact::getMap() const {return map;}
                  5.  
                  6. void Contact::setMap(QMap<QString, QString> *value) {map = value;}

                  Основной объект class ConnectionList и его описание

                  1. #ifndef CONNECTIONLIST_H
                  2. #define CONNECTIONLIST_H
                  3.  
                  4. #include "contact.h"
                  5.  
                  6. class ConnectionList
                  7. {
                  8. public:
                  9. ConnectionList();
                  10. ~ConnectionList();
                  11.  
                  12. QString getAttrValue() const;
                  13. void setAttrValue(const QString &value);
                  14.  
                  15. QVector<Contact> *getContactList() const;
                  16. void setContactList(QVector<Contact> *value);
                  17.  
                  18. private:
                  19. QString attrValue;
                  20. QVector<Contact> *contactList = nullptr;
                  21. };
                  22.  
                  23. #endif // CONNECTIONLIST_H
                  1. #include "connectionlist.h"
                  2.  
                  3. ConnectionList::ConnectionList(){}
                  4.  
                  5. QString ConnectionList::getAttrValue() const {return attrValue;}
                  6.  
                  7. void ConnectionList::setAttrValue(const QString &value) {attrValue = value;}
                  8.  
                  9. QVector<Contact> *ConnectionList::getContactList() const {return contactList;}
                  10.  
                  11. void ConnectionList::setContactList(QVector<Contact> *value) {contactList = value;}

                  Модель данных для отображения таблицы "Contact List" и описание:

                  1. #ifndef CONTACTMODEL_H
                  2. #define CONTACTMODEL_H
                  3.  
                  4. #include "contact.h"
                  5.  
                  6. #include <QAbstractTableModel>
                  7.  
                  8. class ContactModel : public QAbstractTableModel
                  9. {
                  10. Q_OBJECT
                  11.  
                  12. public:
                  13. ContactModel(QObject *parent = nullptr);
                  14.  
                  15. void appendContact(Contact *item);
                  16. void resetModel();
                  17. QVector<Contact> getContactList();
                  18.  
                  19. private:
                  20. enum Cols{
                  21. CONTACTIDENT = 0,
                  22. CONTACTFUNCTION,
                  23. CONTACTTYPE,
                  24. CONNECTEDFLAG,
                  25. CONTACTPARTNUMBER,
                  26. WIREINSTALLATION,
                  27. COLUMNCOUNT
                  28. };
                  29.  
                  30. typedef QHash<Cols, QVariant> ContactData;
                  31. typedef QList<ContactData> ContactGroup;
                  32. ContactGroup m_contactGroup;
                  33.  
                  34.  
                  35. // QAbstractItemModel interface
                  36. public:
                  37. int rowCount(const QModelIndex &parent) const;
                  38. int columnCount(const QModelIndex &parent) const;
                  39. QVariant data(const QModelIndex &index, int role) const;
                  40.  
                  41. // QAbstractItemModel interface
                  42. public:
                  43. QVariant headerData(int section, Qt::Orientation orientation, int role) const;
                  44.  
                  45. // QAbstractItemModel interface
                  46. public:
                  47. Qt::ItemFlags flags(const QModelIndex &index) const;
                  48. };
                  49.  
                  50. #endif // CONTACTMODEL_H
                  51.  
                  1. #include "contactmodel.h"
                  2.  
                  3. ContactModel::ContactModel(QObject *parent)
                  4. : QAbstractTableModel(parent)
                  5. {
                  6. }
                  7.  
                  8. QVariant ContactModel::headerData(int section, Qt::Orientation orientation, int role) const
                  9. {
                  10. if(role != Qt::DisplayRole){
                  11. return QVariant();
                  12. }
                  13.  
                  14. if(orientation == Qt::Vertical){
                  15. return section + 1;
                  16. }
                  17. switch(section){
                  18. case CONTACTIDENT:
                  19. return tr("CONTACTIDENT");
                  20. case CONTACTFUNCTION:
                  21. return tr("CONTACTFUNCTION");
                  22. case CONTACTTYPE:
                  23. return tr("CONTACTTYPE");
                  24. case CONNECTEDFLAG:
                  25. return tr("CONNECTEDFLAG");
                  26. case CONTACTPARTNUMBER:
                  27. return tr("CONTACTPARTNUMBER");
                  28. case WIREINSTALLATION:
                  29. return tr("WIREINSTALLATION");
                  30. }
                  31. return QVariant();
                  32. }
                  33.  
                  34. int ContactModel::rowCount(const QModelIndex &parent) const
                  35. {
                  36. Q_UNUSED(parent)
                  37. return m_contactGroup.count();
                  38.  
                  39. }
                  40.  
                  41. int ContactModel::columnCount(const QModelIndex &parent) const
                  42. {
                  43. Q_UNUSED(parent)
                  44. return COLUMNCOUNT;
                  45. }
                  46.  
                  47.  
                  48. void ContactModel::appendContact(Contact *item)
                  49. { ContactData contactItem;
                  50. if(item->getMap()->contains("1")){
                  51. contactItem[CONTACTIDENT] = item->getMap()->value(item->getMap()->key("1"));
                  52. }
                  53. if(item->getMap()->contains("2")){
                  54. contactItem[CONTACTFUNCTION] = item->getMap()->value(item->getMap()->key("2"));
                  55. }
                  56. if(item->getMap()->contains("3")){
                  57. contactItem[CONTACTTYPE] = item->getMap()->value(item->getMap()->key("3"));
                  58. }
                  59. if(item->getMap()->contains("4")){
                  60. contactItem[CONNECTEDFLAG] = item->getMap()->value(item->getMap()->key("4"));
                  61. }
                  62. if(item->getMap()->contains("5")){
                  63. contactItem[CONTACTPARTNUMBER] = item->getMap()->value(item->getMap()->key("5"));
                  64. }
                  65. if(item->getMap()->contains("6")){
                  66. contactItem[WIREINSTALLATION] = item->getMap()->value(item->getMap()->key("6"));
                  67. }
                  68. int row = m_contactGroup.count();
                  69. beginInsertRows(QModelIndex(), row, row);
                  70. m_contactGroup.append(contactItem);
                  71. endInsertRows();
                  72. }
                  73.  
                  74.  
                  75. Qt::ItemFlags ContactModel::flags(const QModelIndex &index) const
                  76. {
                  77. Qt::ItemFlags flags = QAbstractTableModel::flags(index);
                  78. if(index.isValid()){
                  79. flags |= Qt::ItemIsEditable;
                  80. }
                  81.  
                  82. return flags;
                  83. }
                  84.  
                  85.  
                  86. QVariant ContactModel::data(const QModelIndex &index, int role) const
                  87. {
                  88. if(!index.isValid() ||
                  89. m_contactGroup.count() <= index.row() ||
                  90. (role != Qt::DisplayRole && role != Qt::EditRole)){
                  91. return QVariant();
                  92. }
                  93. return m_contactGroup[index.row()][Cols(index.column())];
                  94. }
                  95.  

                  Модель данных для отображения верхней таблицы:

                  1. #ifndef CONNECTIONLISTMODEL_H
                  2. #define CONNECTIONLISTMODEL_H
                  3.  
                  4. #include "connectionlist.h"
                  5.  
                  6. #include <QAbstractTableModel>
                  7. #include <QObject>
                  8.  
                  9. class ConnectionListModel : public QAbstractTableModel
                  10. {
                  11. Q_OBJECT
                  12. public:
                  13. ConnectionListModel(QObject *parent = nullptr);
                  14.  
                  15. void appendConnection(ConnectionList *item);
                  16. void resetModel();
                  17. QVector<ConnectionList> getConnectionList();
                  18.  
                  19. private:
                  20. enum Columns{
                  21. CONNECTION = 0,
                  22. CONTACTLIST,
                  23. COLUMNCOUNT
                  24. };
                  25.  
                  26. typedef QHash<Columns, QVariant> ConnectionData;
                  27. typedef QMap<QString, QVariant> ContactData;
                  28. typedef QVariantList ContactList;
                  29. typedef QList<ConnectionData> ConnectionGroup;
                  30. ConnectionGroup m_connectionGroup;
                  31. ContactData m_contactMap;
                  32. ContactList m_contactList;
                  33.  
                  34. // QAbstractItemModel interface
                  35. public:
                  36. int rowCount(const QModelIndex &parent) const;
                  37. int columnCount(const QModelIndex &parent) const;
                  38. QVariant data(const QModelIndex &index, int role) const;
                  39. QVariant headerData(int section, Qt::Orientation orientation, int role) const;
                  40. Qt::ItemFlags flags(const QModelIndex &index) const;
                  41. };
                  42.  
                  43. #endif // CONNECTIONLISTMODEL_H
                  44.  
                  1. #include "connectionlistmodel.h"
                  2.  
                  3. ConnectionListModel::ConnectionListModel(QObject *parent)
                  4. : QAbstractTableModel(parent)
                  5. {
                  6.  
                  7. }
                  8.  
                  9. void ConnectionListModel::appendConnection(ConnectionList *item)
                  10. {
                  11. ConnectionData connectionItem;
                  12. if(!item->getAttrValue().isEmpty()){
                  13. connectionItem[CONNECTION] = item->getAttrValue();
                  14. }
                  15. if(item->getContactList() != nullptr){
                  16. m_contactMap.insert(item->getContactList()->at(0).getMap()->key("1"), item->getContactList()->at(0).getMap()->value("1"));
                  17. m_contactMap.insert(item->getContactList()->at(0).getMap()->key("2"), item->getContactList()->at(0).getMap()->value("2"));
                  18. m_contactList.append(m_contactMap);
                  19. m_contactList.append(m_contactMap);
                  20. connectionItem[CONTACTLIST] = QVariant::fromValue(m_contactList);
                  21. }
                  22.  
                  23. int row = m_connectionGroup.count();
                  24. beginInsertRows(QModelIndex(), row, row);
                  25. m_connectionGroup.append(connectionItem);
                  26. endInsertRows();
                  27.  
                  28. }
                  29.  
                  30.  
                  31. int ConnectionListModel::rowCount(const QModelIndex &parent) const
                  32. {
                  33. Q_UNUSED(parent)
                  34. return m_connectionGroup.count();
                  35. }
                  36.  
                  37. int ConnectionListModel::columnCount(const QModelIndex &parent) const
                  38. {
                  39. Q_UNUSED(parent)
                  40. return COLUMNCOUNT;
                  41. }
                  42.  
                  43. QVariant ConnectionListModel::data(const QModelIndex &index, int role) const
                  44. {
                  45. if(!index.isValid() ||
                  46. m_connectionGroup.count() <= index.row() ||
                  47. (role != Qt::DisplayRole && role != Qt::EditRole)){
                  48. return QVariant();
                  49. }
                  50. return m_connectionGroup[index.row()][Columns(index.column())];
                  51. }
                  52.  
                  53. QVariant ConnectionListModel::headerData(int section, Qt::Orientation orientation, int role) const
                  54. {
                  55. if(role != Qt::DisplayRole){
                  56. return QVariant();
                  57. }
                  58.  
                  59. if(orientation == Qt::Vertical){
                  60. return section + 1;
                  61. }
                  62. switch(section){
                  63. case CONNECTION:
                  64. return tr("CONNECTION");
                  65. case CONTACTLIST:
                  66. return tr("CONTACTLIST");
                  67. }
                  68. return QVariant();
                  69. }
                  70.  
                  71. Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const
                  72. {
                  73. Qt::ItemFlags flags = QAbstractTableModel::flags(index);
                  74. if(index.isValid()){
                  75. flags |= Qt::ItemIsEditable;
                  76. }
                  77.  
                  78. return flags;
                  79. }
                  80.  

                  Логика работы формы:

                  1. #ifndef MAINWINDOW_H
                  2. #define MAINWINDOW_H
                  3.  
                  4. #include "contactmodel.h"
                  5. #include "connectionlist.h"
                  6. #include "connectionlistmodel.h"
                  7.  
                  8. #include <QMainWindow>
                  9.  
                  10. namespace Ui {
                  11. class MainWindow;
                  12. }
                  13.  
                  14. class MainWindow : public QMainWindow
                  15. {
                  16. Q_OBJECT
                  17.  
                  18. public:
                  19. explicit MainWindow(QWidget *parent = nullptr);
                  20. ~MainWindow();
                  21.  
                  22. private:
                  23. Ui::MainWindow *ui;
                  24. ContactModel *_cModel = nullptr;
                  25. ConnectionListModel *_connListModel = nullptr;
                  26.  
                  27. };
                  28.  
                  29. #endif // MAINWINDOW_H
                  1. #include "mainwindow.h"
                  2. #include "ui_mainwindow.h"
                  3.  
                  4. MainWindow::MainWindow(QWidget *parent) :
                  5. QMainWindow(parent),
                  6. ui(new Ui::MainWindow)
                  7. {
                  8. ui->setupUi(this);
                  9. _cModel = new ContactModel();
                  10. ui->contactTable->setModel(_cModel);
                  11. ui->contactTable->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
                  12.  
                  13. _connListModel = new ConnectionListModel();
                  14. ui->connectionTable->setModel(_connListModel);
                  15. ui->connectionTable->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
                  16.  
                  17. Contact *cont1 = new Contact();
                  18. Contact *cont2 = new Contact();
                  19. ConnectionList *newConnectionList = new ConnectionList();
                  20.  
                  21. auto attrMap = new QMap<QString, QString>();
                  22. attrMap->insert("1", "1");
                  23. attrMap->insert("2", "2");
                  24. attrMap->insert("3", "3");
                  25. attrMap->insert("4", "4");
                  26. attrMap->insert("5", "5");
                  27. attrMap->insert("6", "6");
                  28. cont1->setMap(attrMap);
                  29. cont2->setMap(attrMap);
                  30. _cModel->appendContact(cont1);
                  31. _cModel->appendContact(cont2);
                  32.  
                  33. newConnectionList->setAttrValue("direct");
                  34. QVector<Contact> *contactList = new QVector<Contact>();
                  35. contactList->append(*cont1);
                  36. contactList->append(*cont2);
                  37. newConnectionList->setContactList(contactList);
                  38. _connListModel->appendConnection(newConnectionList);
                  39.  
                  40. }
                  41.  
                  42. MainWindow::~MainWindow()
                  43. {
                  44. delete ui;
                  45. }
                  46.  

                  Так вот при запуске проекта я получаю список в объекте m_connectionGroup в ячейке CONTACTLIST. Но данные этой ячейки не отображаются в таблице.
                  В принципе я добился того, что положил данные объекта в модель данных, но теперь я не понимаю:
                  1. Как отобразить эти данные в ячейку?
                  2. Если отобразить данные в ячейку не получится, то как связать верхнюю таблицу с нижней? С тем расчетом, что двигаясь по строкам верхней таблицы менялись значения в нижней.
                  3. Как в этом случае редактировать(добавлять/удалять) данные из модели?
                  4. Думал про делегаты, типа в ячейку добавить кнопку, которая бы вызывала дочернюю форму, а там отображались бы данные из списка. Но не знаю, правильное ли решение это будет? Да и с делегатами пока еще не работал.

                  Еще раз оговорюсь, что с простыми таблицами вроде бы все понятно. Я вроде бы разобрался, как это работает, а вот с таког рода объектами... Темный лес в общем.

                  Файл проекта приложил. Спасибо всем, кто поможет разобраться в такой задачке.
                  TestTableView.7z TestTableView.7z

                    Intruder
                    • 6 февраля 2020 г. 19:25

                    Итак, продолжаю вести что-то вроде рассказа о том, как и что у меня получается. В общем решил задачу с отоображением данных в основной и дочерней таблице. Если в крадце, то я сохранил в ячейку список QVariantList внутрь которого положил QMap . Эту колонку, которая содержит список элементов я не отображаю, но теперь я могу легко взять этот список элементов. Дальше на выбор сторки в повесил построение дочерней модели.
                    Как все сделаю, то напишу итоговый результат.
                    Просто я смотрю, много информации по работе с таблицами из БД, а чтобы это делалось программно такой информации практически нет.

                      Комментарии

                      Только авторизованные пользователи могут публиковать комментарии.
                      Пожалуйста, авторизуйтесь или зарегистрируйтесь