© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB
21 февраля 2018 г. 14: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
                            }
                        }
                    }                   
                            }
                        }
  • #
  • 21 февраля 2018 г. 15:01

Добрый день.

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

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

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

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
                                }
} } 
Результат тот-же, модель одна на всех.

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

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

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

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

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

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

Не работает

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. И устанавливать каждую новую модель при запросе в комбобокс.

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

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

Вот сейчас я вас понял правильнее?

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

Вот тут с 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
}

Ответы

Только авторизованные пользователи могут отвечать на форуме.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
15 июня 2018 г. 12:42
Nicky

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

  • Результат 100 баллов
  • Очки рейтинга 10
15 июня 2018 г. 12:36
Nicky

C++ - Тест 003. Условия и циклы

  • Результат 57 баллов
  • Очки рейтинга -2
15 июня 2018 г. 12:29
Nicky

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

  • Результат 46 баллов
  • Очки рейтинга -6
Последние комментарии
18 июня 2018 г. 7:12
EVILEG

PyQt5 - Урок 007. Работаем с QML QtQuick (Сигналы и слоты)

Я вот сейчас банальность скажу, но у меня всё работало. Так что даже и не знаю, надо на код смотреть, что ещё у вас добавлено или отсутствует из библиотек. P/S/ Извините, вы сейчас вс...
18 июня 2018 г. 7:10
EVILEG

Qt/C++ - Урок 042. PopUp уведомление в стиле Gnome с помощью Qt

Недоработки, вряд ли этот зверь вообще является официально поддерживаемым
18 июня 2018 г. 7:01
EVILEG

QML - Урок 016. База данных SQLite и работа с ней в QML Qt

что-то мне сдаётся, что здесь просто пересобрать проект нужно с удалением build каталога
18 июня 2018 г. 7:00
EVILEG

Qt - WinAPI. Как показать запущенное приложение поверх своего приложения

Если зарыться в API системы, то, думаю, что можно, тут тоже использовался WinAPI.
16 июня 2018 г. 15:19
pro100belik

Qt - WinAPI. Как показать запущенное приложение поверх своего приложения

А можно по ID процесса  выводить на передний план окно? myProcess->processId();
Сейчас обсуждают на форуме
19 июня 2018 г. 7:56
EVILEG

как редактировать порядок обхода этементов по нажатию TAB в Qt5 qml

Что-то наподобие такого TextField { Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus()}
19 июня 2018 г. 6:31
kabanov

Как сохранить фокус в TextField после перезагрузки модели

Rectangle { ListView { id: listView delegate: Item { id: cDelegate Item { Row { ComboBox { ...
18 июня 2018 г. 10:51
alex_lip

Qml and JavaScript

В том то и дело что просто в JS так нельзя Если использовать state - onReleased - не нужен вот так все работает Text { ...
18 июня 2018 г. 7:16
EVILEG

почему не выполняется код после вызова слота?

в рамках какого кода, из вашего вопроса не понятно, к чему вы задали этот вопрос и к чему это относится. Если мне ещё ясно, к какой статье этот вопрос был задан, поскольку я слежу за всем ре...

Рекомендуемые страницы