Виталий Антипов
Виталий АнтиповFeb. 21, 2018, 9:54 a.m.

Проблема с ComboBox

Здравствуйте! Столкнулся со следующей проблемой. Есть кастомная таблица из ListView, в делегате которого содержится ComboBox. ComboBox заполняется изменяемой моделью данных, которая является результатом sql запроса. Так же ComboBox имеет свойство editable: true и значение editText отправляется в sql запрос для фильтрации данных. Другими словами этот ComboBox является фильтром. Проблема в том, что если количество строк ListView (а соответственно и количество ComboBox'ов) > 1, то изменение модели в одном ComboBox'е приводит к изменению модели в другом и значение editText в ранее использованном ComboBox'е меняется. Вопрос следующий: как убрать привязку editText к модели или как клонировать существующую модель, чтобы отвязаться от ее будущих данных?

ListView {
                    id: list
                    ...
                    model: inputkolopor.currentText
                    delegate: component
                }
                        Component {
                            id: component
                            Item {
                                id: item0
                ...
                    Rectangle {
                        id: rec03_02
                        ...
                        ComboBox {
                            id: combo_obRU
                            property string id: ""
                            ...
                            editable: true
                            textRole: 'podsh_obRU'
                            delegate: ItemDelegate {
                                width: combo_obRU.width
                                text: combo_obRU.textRole ? (Array.isArray(combo_obRU.model) ? modelData[combo_obRU.textRole] : model[combo_obRU.textRole]) : modelData
                                highlighted: combo_obRU.highlightedIndex === index
                            }
                            onCurrentTextChanged: {
                                if(currentIndex==-1){
                                    combo_obRU.id = ""
                                } else {
                                    combo_obRU.id = model_podsh.getId(currentIndex)
                                }
                            }
                        }
                        Button {
                            id: but_obRU
                            ...
                            onClicked: {
                                stackView.obRU = combo_obRU.editText
                                qmlFilterBearing()
                                combo_obRU.model = model_podsh
                                combo_obRU.popup.visible = true
                            }
                        }
                    }                   
                            }
                        }
We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

15
Evgenii Legotckoi
  • Feb. 21, 2018, 10:01 a.m.

Добрый день.

Как я понимаю все ComboBox`ы используют одну и ту же модель данных. Фактически, вам нужно собственную модель данных для каждого ComboBox`а.
Я правильно понимаю, что Вы использовали модель для фильтрации ту, что зарегистрирована через регистрацию Контекста? То есть через setContextProperty?

Если так, то я бы предложил Вам несколько иное решение, но мне его нужно дома смотреть, сейчас у меня нет кода под рукой.
Можно создать свою модель данных для каждого ComboBox в делегате. Тогда все КомбоБоксы будут независимы друг от друга.
    Виталий Антипов
    • Feb. 21, 2018, 10:17 a.m.

    Спасибо за ответ! Да, модель передаю через

    engine.rootContext()->setContextProperty("model_podsh", model_podsh);
    Иметь собственную модель для каждого ComboBox'а - первое что приходит на ум, но не очень понятно как передать модели. Попробовал решить в лоб:
    Button {
                                id: but_obRU
                                property int index_: index
                                ...
                                onClicked: {
                                    stackView.obRU = combo_obRU.editText
                                    qmlFilterBearing()
                                    if(index_==0){
                                    combo_obRU.model = model_podsh0
                                    }
                                    if(index_==1){
                                    combo_obRU.model = model_podsh1
                                    }
                                    if(index_==2){
                                    combo_obRU.model = model_podsh2
                                    }
                                    if(index_==3){
                                    combo_obRU.model = model_podsh3
                                    }
                                    if(index_==4){
                                    combo_obRU.model = model_podsh4
                                    }
                                    if(index_==5){
                                    combo_obRU.model = model_podsh5
                                    }
                                    if(index_==6){
                                    combo_obRU.model = model_podsh6
                                    }
                                    if(index_==7){
                                    combo_obRU.model = model_podsh7
                                    }
    } } 
    Результат тот-же, модель одна на всех.

      Нет. Это явно неправильное решение. К тому же, у вас может быть бесконечное число строк, а следовательно такое решение просто не подходит, не говоря уж о том, что оно неприемлемо для продакшена)))
      Когда буду дома, посмотрю код и скину возможный вариант создания отдельных моделей.

        Виталий Антипов
        • Feb. 21, 2018, 11:35 a.m.

        Буду ждать с нетерпением )

          В общем идея такая. Нужно написать класс, который будет отвечать за некоторую таблицу, как раз Ваша модель должна получиться.

          Я вам покажу пример, который может в данном случае использоваться.

          ResultsModel.h
          #pragma once
          
          #include <QSqlQueryModel>
          
          class ResultsModel : public QSqlQueryModel
          {
              using BaseClass = QSqlQueryModel;
          
              Q_OBJECT
          public:
              explicit ResultsModel(QObject* parent = nullptr);
          
              enum Roles
              {
                  IdRole = Qt::UserRole + 1,
                  NameRole,
                  TotalScoreRole,
                  TimeRole,
                  DateRole,
              };
          
              QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
          
          protected:
              QHash<int, QByteArray> roleNames() const;
          
          signals:
          
          public slots:
              void updateModel();
              int getId(int row);
          };
          ResultsModel.cpp
          #include "ResultsModel.h"
          
          ResultsModel::ResultsModel(QObject *parent) :
              BaseClass(parent)
          {
              updateModel();
          }
          
          QVariant ResultsModel::data(const QModelIndex &index, int role) const
          {
              int columnId = role - Qt::UserRole - 1;
              QModelIndex modelIndex = this->index(index.row(), columnId);
              return QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
          }
          
          QHash<int, QByteArray> ResultsModel::roleNames() const
          {
              QHash<int, QByteArray> roles;
              roles[IdRole] = "gamer_id";
              roles[NameRole] = "name";
              roles[TotalScoreRole] = "total_score";
              roles[TimeRole] = "time";
              roles[DateRole] = "date";
              return roles;
          }
          
          void ResultsModel::updateModel()
          {
              setQuery(QString("SELECT table_name FROM %1 ORDER BY total_score DESC;"));
          }
          
          int ResultsModel::getId(int row)
          {
              return data(this->index(row, 0), IdRole).toInt();
          }
          

          Дальше нужно зарегистрировать эту модель как тип данных QML
          qmlRegisterType<ResultsModel>("SQLHelper", 1, 0, "ResultsModel");

          Далее импортируем этот модуль SQLHelper в QML файле и используем эту модель из него в делегате
          import Arkanoid 1.0
          
          ...
          ComboBox {
              id: combo_obRU
          
              model: ResultsModel { id: comboBoxModel } // Вот это использование модели
          
          }
          ...
          Практически в каждом делегате будет своя модель данных. И они должны будут быть независимы друг от друга и будут иметь собственную выборку.
          Попробуйте такой вариант, полагаю, что это должно помочь Вам.

            qmlRegisterType<ListModelPodsh>("SQLHelper", 1, 0, "ListModelPodsh");
            import SQLHelper 1.0
            QQmlApplicationEngine failed to load component
            qrc:/main.qml:341 Type CreatBO unavailable
            qrc:/CreatBO.qml:7 module "SQLHelper" is not installed
              Виталий Антипов
              • Feb. 21, 2018, 4:34 p.m.

              Ой, извиняюсь... надо же перед engine.load(QUrl(QLatin1String("qrc:/main.qml")));

                Виталий Антипов
                • Feb. 21, 2018, 4:44 p.m.

                Не работает

                void ListModelPodsh::updateModel()
                {
                    QObject* stack = this->parent()->findChild<QObject*>("stackView");
                    QString obRU=(stack->property("obRU")).toString();
                    QString obEN=(stack->property("obEN")).toString();
                    QSqlDatabase db = QSqlDatabase::database();
                    db.transaction();
                    // Обновление производится SQL-запросом к базе данных
                    this->setQuery("SELECT BasePodsh.id, BasePodsh.Oboznachenie, BasePodsh.OboznachenieEN, BasePodsh.dvnutr, BasePodsh.Dnaruzh, BasePodsh.B, "
                                   "BasePodsh.dtk, BasePodsh.ztk, BasePodsh.Ugol, BasePodsh.Massa, BasePodsh.Static, BasePodsh.Dinamic, BasePodsh.Name "
                                   "FROM BasePodsh WHERE BasePodsh.Oboznachenie LIKE '%"+obRU+"%' and BasePodsh.OboznachenieEN LIKE '%"+obEN+"%' "
                                   "ORDER BY BasePodsh.Oboznachenie");
                    while(this->canFetchMore()){
                        this->fetchMore();
                    }
                    qDebug()<<"this->canFetchMore()"<<this->canFetchMore();
                    db.commit();
                    if(!db.commit()){
                    db.rollback();
                    }
                }
                Не могу из StackView фильтры для запроса получить, ведь StackView еще не создан когда создана модель.

                  Ну про StackView вообще ничего сказано изначально не было...


                  Как вариант можете с помощью этой модели или изначальной формировать какой-нибудь массив с данными, который будет использоваться в качестве модели данных для ComboBox`a. И устанавливать каждую новую модель при запросе в комбобокс.
                    Виталий Антипов
                    • Feb. 21, 2018, 5:19 p.m.

                    Спасибо за ответы, есть над чем подумать

                      Виталий Антипов
                      • Feb. 23, 2018, 8:14 a.m.

                      По вашей предыдущей подсказке создал новый класс для получения модели данных, зарегистрировал его как тип данных QML и посредством Q_PROPERTY добавил свойство filter для фильтрации запроса.

                      modelpodsh.h
                      #ifndef MODELPODSH_H
                      #define MODELPODSH_H
                      
                      #include <QObject>
                      #include <QSqlQueryModel>
                      #include <QSqlQuery>
                      #include <QDebug>
                      
                      class ModelPodsh : public QSqlQueryModel
                      {
                          using BaseClass = QSqlQueryModel;
                      
                          Q_OBJECT
                          Q_PROPERTY(QString filter READ filter WRITE setFilter)
                      public:
                          enum Roles {
                              IdRole = Qt::UserRole + 1,
                              podsh_obRURole,
                              podsh_obENRole,
                              podsh_dvnRole,
                              podsh_dnarRole,
                              podsh_bRole,
                              podsh_dtkRole,
                              podsh_ztkRole,
                              podsh_aRole,
                              podsh_massaRole,
                              podsh_staticRole,
                              podsh_dinamicRole,
                              podsh_nameRole
                          };
                      
                          explicit ModelPodsh(QObject *parent = 0);
                          
                          QString filter(){return filter_;}
                          void setFilter(QString filter_in){filter_=filter_in;}
                      
                          QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
                      
                      protected:
                          QHash<int, QByteArray> roleNames() const;
                      
                      signals:
                      
                      public slots:
                          void updateModel();
                          int getId(int row);
                      public:
                          QString filter_;
                      };
                      #endif // MODELPODSH_H
                      modelpodsh.cpp
                      #include "modelpodsh.h"
                      
                      ModelPodsh::ModelPodsh(QObject *parent) :
                          BaseClass(parent)
                      {
                          this->updateModel();
                      }
                      
                      QVariant ModelPodsh::data(const QModelIndex & index, int role) const {
                          int columnId = role - Qt::UserRole - 1;
                          QModelIndex modelIndex = this->index(index.row(), columnId);
                          return QSqlQueryModel::data(modelIndex, Qt::DisplayRole);
                      }
                      
                      QHash<int, QByteArray> ModelPodsh::roleNames() const {
                          QHash<int, QByteArray> roles;
                          roles[IdRole] = "id";
                          roles[podsh_obRURole] = "podsh_obRU";
                          roles[podsh_obENRole] = "podsh_obEN";
                          roles[podsh_dvnRole] = "podsh_dvn";
                          roles[podsh_dnarRole] = "podsh_dnar";
                          roles[podsh_bRole] = "podsh_b";
                          roles[podsh_dtkRole] = "podsh_dtk";
                          roles[podsh_ztkRole] = "podsh_ztk";
                          roles[podsh_aRole] = "podsh_a";
                          roles[podsh_massaRole] = "podsh_massa";
                          roles[podsh_staticRole] = "podsh_static";
                          roles[podsh_dinamicRole] = "podsh_dinamic";
                          roles[podsh_nameRole] = "podsh_name";
                          return roles;
                      }
                      
                      void ModelPodsh::updateModel()
                      {
                          QSqlDatabase db = QSqlDatabase::database();
                          db.transaction();
                          // Обновление производится SQL-запросом к базе данных
                          this->setQuery("SELECT BasePodsh.id, BasePodsh.Oboznachenie, BasePodsh.OboznachenieEN, BasePodsh.dvnutr, BasePodsh.Dnaruzh, BasePodsh.B, "
                                         "BasePodsh.dtk, BasePodsh.ztk, BasePodsh.Ugol, BasePodsh.Massa, BasePodsh.Static, BasePodsh.Dinamic, BasePodsh.Name "
                                         "FROM BasePodsh WHERE BasePodsh.Oboznachenie LIKE '%"+filter_+"%' "
                                         "ORDER BY BasePodsh.Oboznachenie");
                          while(this->canFetchMore()){
                              this->fetchMore();
                          }
                          db.commit();
                          if(!db.commit()){
                          db.rollback();
                          }
                      }
                      
                      int ModelPodsh::getId(int row)
                      {
                          return this->data(this->index(row, 0), IdRole).toInt();
                      }
                      
                      .qml
                      ...
                      ComboBox {
                      ...
                      }
                      ModelPodsh {
                                 id: newmodel
                                 }
                      Button{
                      id: combo_obRU
                      ...
                      onClicked: {
                      newmodel.filter = combo_obRU.editText
                      combo_obRU.model = newmodel
                      newmodel.updateModel()
                      combo_obRU.popup.visible = true
                      }
                      }
                      Проблема решена. Но мне не нравится низкая скорость. Набрав в комбобоксе текст для фильтра и нажав кнопку, список комбобокса с данными открывается через 3-4 секунды. И еще... модель комбобоксу приходится присваивать только по нажатию кнопки фильтра. Тут проблема в том, что "Редактируемый комбинированный блок автоматически завершает свой текст на основе того, что доступно в модели". С предопределенной моделью текст в комбобоксе набирается крайне медленно (иногда при этом программа падает), поэтому в начале модель лучше не иметь вовсе.

                        Дайте подумать...

                        Вам фильтр в комбобоксе фактически нужен для того, чтобы быстро найти в этом самом комбобоксе нужное значение из всего списка?

                        Я вот сейчас думаю на более свежую голову, а может быть мы с вами не в ту сторону пошли. Если Вам нужно просто найти значение из выборки, то может быть вам и не нужно каждый раз делать вызов запроса SQL. Ну была у вас одна модель... Сделали один фильтр при создании страницы с комбобоксами, и получили все значения сразу в каждом комбобоксе.

                        А вот этот самый editable должен просто включить возможность автозаполнения, то есть в существующей модели данных он будет искать нужное значение. То есть теоретически выполнить SQL-запрос нужно один раз. А потом, модель данных должна возвращать что-то, что отвечало бы за LIKE фильтр через метод data(), вот через него и должен искать ComboxBox нужную строку без изменения модели...

                        Вот сейчас я вас понял правильнее?
                          Виталий Антипов
                          • Feb. 23, 2018, 8:50 a.m.

                          Да. Но нужно найти не одно значение... Как пример: мне нужно найти подшипник 6306 2RS. Я просто вбиваю 306, комбобокс раскрывает свой список всех с 306 (6306, 6306 2RS, 6306 RS, 6306 2RSN и т.п.). Я выбираю нужный мне и дело в шляпе. По идее надо через proxymodel как-то работать, но для меня пока это сложно.

                            Виталий Антипов
                            • Feb. 23, 2018, 8:58 a.m.

                            Вот тут с gif наверное будет более понятно

                              Да, в принципе идея понятна.

                              Можно воспользоваться одной исходной моделью и делать по ней поиск. Найденные элементы добавлять в модель для отображения.
                              При этом исходная модель будет одна на все комбобоксы, а модель для отображения будет каждая для каждого комбобокса.
                              ListModel {
                                  id: sourceModel
                                  ListElement { text: "Banana" }
                                  ListElement { text: "Apple" }
                                  ListElement { text: "Coconut" }
                                  ListElement { text: "Aple" }
                                  ListElement { text: "Appppp" }
                              }
                              
                              ComboBox {
                                  id: comboBox
                                  editable: true
                              
                              
                                  model: ListModel {
                                      id: displayModel
                                  }
                              
                                  onAccepted: {
                                      displayModel.clear()
                                      for (var i = 0; i < sourceModel.count; ++i)
                                      {
                                          if (sourceModel.get(i).text.includes(editText))
                                              displayModel.append({text: sourceModel.get(i).text})
                                      }
                                  }
                              
                                  anchors.horizontalCenter: parent.horizontalCenter
                              }

                                Comments

                                Only authorized users can post comments.
                                Please, Log in or Sign up
                                d
                                • dsfs
                                • April 26, 2024, 4:56 a.m.

                                C ++ - Test 004. Pointers, Arrays and Loops

                                • Result:80points,
                                • Rating points4
                                d
                                • dsfs
                                • April 26, 2024, 4:45 a.m.

                                C++ - Test 002. Constants

                                • Result:50points,
                                • Rating points-4
                                d
                                • dsfs
                                • April 26, 2024, 4:35 a.m.

                                C++ - Test 001. The first program and data types

                                • Result:73points,
                                • Rating points1
                                Last comments
                                k
                                kmssrFeb. 8, 2024, 6:43 p.m.
                                Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                                Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                                EVA
                                EVADec. 25, 2023, 10:30 a.m.
                                Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                                J
                                JonnyJoDec. 25, 2023, 8:38 a.m.
                                Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                                G
                                GvozdikDec. 18, 2023, 9:01 p.m.
                                Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                                Now discuss on the forum
                                G
                                GarApril 22, 2024, 5:46 a.m.
                                Clipboard Как скопировать окно целиком в clipb?
                                DA
                                Dr Gangil AcademicsApril 20, 2024, 7:45 a.m.
                                Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
                                a
                                a_vlasovApril 14, 2024, 6:41 a.m.
                                Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
                                Павел Дорофеев
                                Павел ДорофеевApril 14, 2024, 2:35 a.m.
                                QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
                                f
                                fastrexApril 4, 2024, 4:47 a.m.
                                Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

                                Follow us in social networks