Виталий Антипов
Виталий Антипов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
                                AD

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

                                • Result:50points,
                                • Rating points-4
                                m

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

                                • Result:80points,
                                • Rating points4
                                m

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

                                • Result:20points,
                                • Rating points-10
                                Last comments
                                Evgenii Legotckoi
                                Evgenii LegotckoiNov. 1, 2024, 12:37 a.m.
                                Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                                A
                                ALO1ZEOct. 19, 2024, 6:19 p.m.
                                Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                                ИМ
                                Игорь МаксимовOct. 5, 2024, 5:51 p.m.
                                Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                                d
                                dblas5July 5, 2024, 9:02 p.m.
                                QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                                k
                                kmssrFeb. 9, 2024, 5:43 a.m.
                                Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                                Now discuss on the forum
                                m
                                moogoNov. 22, 2024, 6:17 p.m.
                                Mosquito Spray System Effective Mosquito Systems for Backyard | Eco-Friendly Misting Control Device & Repellent Spray - Moogo ; Upgrade your backyard with our mosquito-repellent device! Our misters conce…
                                Evgenii Legotckoi
                                Evgenii LegotckoiJune 25, 2024, 1:11 a.m.
                                добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                                t
                                tonypeachey1Nov. 15, 2024, 5:04 p.m.
                                google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
                                NSProject
                                NSProjectJune 4, 2022, 1:49 p.m.
                                Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

                                Follow us in social networks