© 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
                            }
                        }
                    }                   
                            }
                        }

Добрый день.

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

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

Для Django рекомендую VDS-хостинг TIMEWEB

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

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

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

Для Django рекомендую VDS-хостинг TIMEWEB

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

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

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

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 } // Вот это использование модели

}
...
Практически в каждом делегате будет своя модель данных. И они должны будут быть независимы друг от друга и будут иметь собственную выборку.
Попробуйте такой вариант, полагаю, что это должно помочь Вам.

Для Django рекомендую VDS-хостинг TIMEWEB

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

Для Django рекомендую VDS-хостинг TIMEWEB

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

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

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

Для Django рекомендую VDS-хостинг TIMEWEB

Да. Но нужно найти не одно значение... Как пример: мне нужно найти подшипник 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
}

Для Django рекомендую VDS-хостинг TIMEWEB

Ответы

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

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

  • Результат 100баллов,
  • Очки рейтинга10
24 сентября 2018 г. 17:37
edorofeeva

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

  • Результат 66баллов,
  • Очки рейтинга-1
23 сентября 2018 г. 14:38
No Names

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

  • Результат 60баллов,
  • Очки рейтинга-1
Последние комментарии
24 сентября 2018 г. 15:09
Евгений Легоцкой

Qt Linux - Урок 001. Автозапуск Qt приложения под Linux

А вот здесь у меня есть пример использования supervisor. https://evileg.com/ru/post/3/ Вся статья вам там не интересна, интересен только шаг с настройкой supervisor. Он получается ...
24 сентября 2018 г. 15:00
avovana

Qt Linux - Урок 001. Автозапуск Qt приложения под Linux

Не могли бы дать ссылку на пример? Какое-то рабочее использование. Т.е. у меня есть Qt Gui App, которое я бы хотел запускать при старте системы и в случае, если оно грохнется. Если о чем Вы го...
24 сентября 2018 г. 14:55
Евгений Легоцкой

Qt Linux - Урок 001. Автозапуск Qt приложения под Linux

Если честно, то я не уверен, что это вообще можно реализовать через *.desktop файл. Я сделал предположение на основе того, что вы сказали про *.desktop и рестарт. Все варианты, котор...
24 сентября 2018 г. 14:47
avovana

Qt Linux - Урок 001. Автозапуск Qt приложения под Linux

Просто сейчас правлю сам файл example.desktop. Пытаюсь понять какую пару key=value мне нужно дописать.
24 сентября 2018 г. 14:42
Евгений Легоцкой

Qt Linux - Урок 001. Автозапуск Qt приложения под Linux

Ну я имел ввиду, что дописать в коде вот сюда то, о чём вы говорили про рестарт QString autorunContent("[Desktop Entry]\n" "Type=Application\n" ...
Сейчас обсуждают на форуме
24 сентября 2018 г. 16:47
Евгений_Канусовский@1981

Чтение файлов в python

Добрый вечер Евгений и форумчане! Столкнулся с проблемой чтения файлов в python: файлы с обычным текстом в формате las и txt читаются, например: ~Version information VERS.          ...
24 сентября 2018 г. 13:29
Евгений Легоцкой

Трансляция видео с помощью VLC по RTP

Добрый день! Я не сталкивался, но предположу, что нужно настроить Input Codec в VLC. В настройках есть секция Input Codec, возможно, что там установлено низкое разрешение. ...
21 сентября 2018 г. 8:25
Евгений Легоцкой

Прокси-модель, содержащая на 1 столбец больше, чем модель-источник.

Попробуйте ещё PySide 2 - это официально поддерживаемый пакет привязок Python к Qt, возможно, что там не будет таких проблем.
20 сентября 2018 г. 20:06
Евгений Легоцкой

Qt Installer Framework

Добрый день. Зачем собирать Qt Installer Framework-то из исходников? Я ещё понимаю Qt собирают из исходников статически (хотя тоже считаю по большей части бесполезной тратой времени),...
Присоединяйтесь к нам в социальных сетях