Виталий Антипов
Виталий Антипов21 февраля 2018 г. 9:54

Проблема с 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
                            }
                        }
                    }                   
                            }
                        }
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

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

15
Evgenii Legotckoi
  • 21 февраля 2018 г. 10:01

Добрый день.

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

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

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

    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
                                    }
    } } 
    Результат тот-же, модель одна на всех.
      Evgenii Legotckoi
      • 21 февраля 2018 г. 10:25

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

        Виталий Антипов
        • 21 февраля 2018 г. 11:35

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

          Evgenii Legotckoi
          • 21 февраля 2018 г. 15:52

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

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

          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 } // Вот это использование модели
          
          }
          ...
          Практически в каждом делегате будет своя модель данных. И они должны будут быть независимы друг от друга и будут иметь собственную выборку.
          Попробуйте такой вариант, полагаю, что это должно помочь Вам.

            Виталий Антипов
            • 21 февраля 2018 г. 16:32
            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
              Виталий Антипов
              • 21 февраля 2018 г. 16:34

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

                Виталий Антипов
                • 21 февраля 2018 г. 16:44

                Не работает

                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 еще не создан когда создана модель.
                  Evgenii Legotckoi
                  • 21 февраля 2018 г. 17:06

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


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

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

                      Виталий Антипов
                      • 23 февраля 2018 г. 8:14

                      По вашей предыдущей подсказке создал новый класс для получения модели данных, зарегистрировал его как тип данных 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 секунды. И еще... модель комбобоксу приходится присваивать только по нажатию кнопки фильтра. Тут проблема в том, что "Редактируемый комбинированный блок автоматически завершает свой текст на основе того, что доступно в модели". С предопределенной моделью текст в комбобоксе набирается крайне медленно (иногда при этом программа падает), поэтому в начале модель лучше не иметь вовсе.
                        Evgenii Legotckoi
                        • 23 февраля 2018 г. 8:42

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

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

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

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

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

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

                            Виталий Антипов
                            • 23 февраля 2018 г. 8:58

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

                              Evgenii Legotckoi
                              • 25 февраля 2018 г. 5:02

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

                              Можно воспользоваться одной исходной моделью и делать по ней поиск. Найденные элементы добавлять в модель для отображения.
                              При этом исходная модель будет одна на все комбобоксы, а модель для отображения будет каждая для каждого комбобокса.
                              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
                              }

                                Комментарии

                                Только авторизованные пользователи могут публиковать комментарии.
                                Пожалуйста, авторизуйтесь или зарегистрируйтесь
                                d
                                • dsfs
                                • 26 апреля 2024 г. 4:56

                                C++ - Тест 004. Указатели, Массивы и Циклы

                                • Результат:80баллов,
                                • Очки рейтинга4
                                d
                                • dsfs
                                • 26 апреля 2024 г. 4:45

                                C++ - Тест 002. Константы

                                • Результат:50баллов,
                                • Очки рейтинга-4
                                d
                                • dsfs
                                • 26 апреля 2024 г. 4:35

                                C++ - Тест 001. Первая программа и типы данных

                                • Результат:73баллов,
                                • Очки рейтинга1
                                Последние комментарии
                                k
                                kmssr8 февраля 2024 г. 18:43
                                Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                                АК
                                Анатолий Кононенко5 февраля 2024 г. 1:50
                                Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                                EVA
                                EVA25 декабря 2023 г. 10:30
                                Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                                J
                                JonnyJo25 декабря 2023 г. 8:38
                                Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                                G
                                Gvozdik18 декабря 2023 г. 21:01
                                Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                                Сейчас обсуждают на форуме
                                Evgenii Legotckoi
                                Evgenii Legotckoi2 мая 2024 г. 14:07
                                Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
                                IscanderChe
                                IscanderChe30 апреля 2024 г. 4:22
                                Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
                                G
                                Gar22 апреля 2024 г. 5:46
                                Clipboard Как скопировать окно целиком в clipb?
                                Павел Дорофеев
                                Павел Дорофеев14 апреля 2024 г. 2:35
                                QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
                                f
                                fastrex4 апреля 2024 г. 4:47
                                Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

                                Следите за нами в социальных сетях