24 апреля 2019 г. 6:07

Помогите разобраться с CheckBox delegate в QAbstractTableModel

Delegate, qcheckbox, QAbstractTableModel

Доброго времени суток.
Где то я упустил что-то при изучении делегатов и их использовании.

Отображение работает. Но после того как выбираешь другую строку таблицы, предидущее выделение сбрсывается.
Не получается выбрать несколько объектов.


Файлы реализации делегата CheckBoxDelegate использовал такие же как в этой статье https://evileg.com/ru/post/478/

Реализация модели modelterminals.h

#ifndef MODELTERMINALS_H
#define MODELTERMINALS_H
#include "terminalclass.h"

#include <QObject>
#include <QAbstractTableModel>

class ModelTerminals : public QAbstractTableModel
{
    Q_OBJECT
    QList<TerminalClass> m_listTerm;
public:
    ModelTerminals(QList<TerminalClass> lsTerm);

    // QAbstractItemModel interface
public:
    int rowCount(const QModelIndex &parent) const;
    int columnCount(const QModelIndex &parent) const;
    QVariant data(const QModelIndex &index, int role) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role);
    Qt::ItemFlags flags(const QModelIndex &index) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;



};

#endif // MODELTERMINALS_H

modelterminals.cpp

#include "modelterminals.h"

ModelTerminals::ModelTerminals(QList<TerminalClass> lsTerm ) :
    m_listTerm(lsTerm)
{

}


int ModelTerminals::rowCount(const QModelIndex &parent) const
{
    return m_listTerm.size();
    Q_UNUSED(parent)
}

int ModelTerminals::columnCount(const QModelIndex &parent) const
{
    return m_listTerm.at(0).colParam();
    Q_UNUSED(parent)
}

QVariant ModelTerminals::data(const QModelIndex &index, int role) const
{
    if(!index.isValid()) { return QVariant();}
    TerminalClass t = m_listTerm[index.row()];
    switch (role) {
    case Qt::DisplayRole:
        switch (index.column()) {
        case 0: return t.isCheced();
        case 1: return t.terminalID();
        case 2: return t.terminalName();
        case 3: return t.regionID();
        case 4: return t.serverName();
        }
        break;
    case Qt::CheckStateRole:
        if(index.column() == 0) {
            return (!t.isCheced()) ? Qt::Checked : Qt::Unchecked;
        }
        break;
    default:
        break;
    }
    return QVariant();
}

bool ModelTerminals::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid()) {return false;}

    TerminalClass t = m_listTerm[index.row()];

    if(role == Qt::CheckStateRole){
        if(value.toInt() == Qt::Checked){
            t.setIsCheced(true);
        } else {
            t.setIsCheced(false);
        }
        emit dataChanged(index,index);
        return true;
    }
    return false;
}

Qt::ItemFlags ModelTerminals::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flags = QAbstractTableModel::flags(index);
    if(index.isValid() && index.column() == 0){
        flags |= Qt::ItemIsEditable;
    }
    return flags;
}

QVariant ModelTerminals::headerData(int section, Qt::Orientation orientation, int role) const
{
    if( role != Qt::DisplayRole) { return QVariant();}

    if( orientation == Qt::Vertical ) { return section; }

    switch (section) {
    case 0: return "";
    case 1: return "АЗС";
    case 2: return "Адрес";
    case 3: return "Регион";
    case 4: return "Сервер";
    default:
        break;
    }
    return QVariant();
}

Получаю данные для модели и создаю модель

#include "fuelnamedialog.h"
#include "ui_fuelnamedialog.h"

FuelNameDialog::FuelNameDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::FuelNameDialog)
{
    ui->setupUi(this);

    createListTerminals();
    createModelterminals();
}

FuelNameDialog::~FuelNameDialog()
{
    delete ui;
}

void FuelNameDialog::createModelterminals()
{
    qInfo(logInfo()) << "Заружено терминалов:" << listTerminals.size();
    CheckBoxDelegate *delegate = new CheckBoxDelegate();
    modelTerminals = new ModelTerminals(listTerminals);

    ui->tableViewTerminals->setModel(modelTerminals);
    ui->tableViewTerminals->setItemDelegateForColumn(0,delegate);

    ui->tableViewTerminals->verticalHeader()->hide();
    ui->tableViewTerminals->resizeColumnsToContents();

}

void FuelNameDialog::createListTerminals()
{
    QSqlQuery q;
    TerminalClass t;
    if(!q.exec("SELECT t.TERMINAL_ID, TRIM(t.name) AS NAME, t.OWNER_ID AS REGION, c.SERVER_NAME  FROM TERMINALS t "
               "LEFT JOIN CONNECTIONS c ON c.TERMINAL_ID = t.TERMINAL_ID "
               "WHERE t.TERMINALTYPE = 3 AND c.CONNECT_ID = 2 "
               "ORDER BY t.TERMINAL_ID")) {
        qInfo(logInfo()) << Q_FUNC_INFO  << "Не возможно получить список терминалов" << q.lastError().text();
        return;
    }
    while(q.next()){
        t.setIsCheced(false);
        t.setTerminalID(q.value(0).toInt());
        t.setTerminalName(q.value(1).toString());
        t.setRegionID(q.value(2).toInt());
        t.setServerName(q.value(3).toString());
        listTerminals.append(t);
    }

}
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
10

Добрый день

А вот этот код

    case Qt::CheckStateRole:
    if(index.column() == 0) {
        return (!t.isCheced()) ? Qt::Checked : Qt::Unchecked;
    }

Не должен быть написан так?

    case Qt::CheckStateRole:
    if(index.column() == 0) {
        return (t.isCheced()) ? Qt::Checked : Qt::Unchecked;
    }

Согласен. но ситуация не поменялась. Такое чуство что данные не записываются в модель.

Думаю, что в методе setData нужно в роли Qt::EditRole делать установку значения. Скорее всего у вас просто не заходит в ту ветку кода выполнение программы. Обычно для установки значения используется именно роль Qt::EditRole

  • 25 апреля 2019 г. 6:51
  • Ответ был помечен как решение.

Изменил функцию теперь работает.

bool ModelTerminals::setData(const QModelIndex &index, const QVariant &value, int role)
{
    Q_UNUSED(role)
    if(!index.isValid()) {return false;}

    m_listTerm[index.row()].setIsCheced(value.toBool());
    emit QAbstractTableModel::dataChanged(index,index);
    return true;

}

Продолжаем вопросы... (Сори если что :-) Как теперь отловить сигнал checked от делегата?

Пока сделал так:

connect(ui->tableViewTerminals->model(),&QAbstractTableModel::dataChanged,this,&SelectTerminalPage::terminalChecked);

Где:

void SelectTerminalPage::terminalChecked()
{
    const int rowCount = ui->tableViewTerminals->model()->rowCount(QModelIndex());

    for(int i =0; i < rowCount; ++i){
        if(ui->tableViewTerminals->model()->index(i,0).data(Qt::CheckStateRole).toInt() == Qt::Checked)
            qInfo(logInfo()) << "Выбрана" << i << "строка";
    }

}

но это отрабатывает изменение модели А вот сам клик на checkbox бы отловить...

Ну по идее есть такой сигнал commitData у делегатов. В данном сигнале высылается указатель на виджет перед записью данных в модель. Можете попробовать на его основе что-то сообразить.

Еще обнаружил такую ситуацию. Оказывается данные в модель записываются, лиш после того как фокус получает любой item TableView.
Т.е. Состояние CheckBox записывается в модель не при изменении его статуса а лишь тогда когда с него теряется фокус.

Ну в общем-то да, есть такая ситуация. Думаю, что это правильное поведение. Дело в том, что если логика под капотом очень сложная и влияет на большое количество компонентов, то апдейт при редактировании может очень сильно уменьшить скорость работы софта, поскольку такие апдейты будут выполняться тогда, когда по факту в 99% случае они вовсе не нужны.

Реализовать такую ситуацию:
Я выбрал несколько строк и не прешел на другую ячейку а нажал кнопку "Выполнить" например.
И тогда получается что последняя мною выбранная запись не попадет для дальнейшей обработки. Или есть таки способ принудительно опросить делегаты и записать их состояние в модель?

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Как стать автором?

Внесите вклад в развитие сообщества EVILEG.

Узнайте, как стать автором сайта.

Изучить
Donate

Добрый день, Дорогие Пользователи !!!

Я Евгений Легоцкой, разработчик EVILEG. И это мой хобби-проект, который помогает учиться программированию другим программистам и разработчикам

Если сайт помог вам, и вы хотите также поддержать развитие сайта, то вы можете сделать пожертвование следующими способами

PayPalYandex.Money
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг Timeweb
АС
26 мая 2020 г. 12:29
Артём Сун-Дун-Чан

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

  • Результат:50баллов,
  • Очки рейтинга-4
МН
25 мая 2020 г. 12:33
Митя Нагибин

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

  • Результат:50баллов,
  • Очки рейтинга-4
f
25 мая 2020 г. 6:05
falcon

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

  • Результат:66баллов,
  • Очки рейтинга-1
Последние комментарии
28 мая 2020 г. 16:14
Евгений Легоцкой

Qt/C++ - Урок 039. Как закрасить строку в QSqlTableModel по значению в столбце

Ну в моём примере, который в статье сработало так model->setData(model->index(1, 1), 7); Поскольку model->index(1, 0) - это индекс колонки id, которая скрыта, поэтому…
МА
28 мая 2020 г. 16:08
Михаил А

Qt/C++ - Урок 039. Как закрасить строку в QSqlTableModel по значению в столбце

Спасибо, завтра првоерю. А model->setData(model->index(1, 0), 7); Тоже заработало?
28 мая 2020 г. 16:06
Евгений Легоцкой

Qt/C++ - Урок 039. Как закрасить строку в QSqlTableModel по значению в столбце

Да, метод data всё-таки влиял, я переписал его так и заработало удаление QVariant TableModel::data(const QModelIndex &idx, int role) const{ if (role == Qt::BackgroundColorRole) {…
28 мая 2020 г. 15:49
Евгений Легоцкой

Django - Урок 011. Добавление комментариев на сайт с Django

Он более функциональный и его функционал объективно лучше поддерживается Django. Из первого, что приходит на ум: Это наличие полей типа Array Поддержка полей для JSON …
28 мая 2020 г. 15:42
progammist

Django - Урок 011. Добавление комментариев на сайт с Django

а в чем явное преимущество postgresql над mysql?)
Сейчас обсуждают на форуме
RG
28 мая 2020 г. 19:21
Rovshan Gurbanov

Сборка под старые версии Android

У меня SDK почти все версии есть, NDK есть версии 10, 17, 21. Но собирается приложение только с NDK v21 под Android версии 7.0 и выше Версия Qt у меня 5.14.2
28 мая 2020 г. 8:58
Евгений Легоцкой

Освобождение памяти QMainWindow::setCentralWidget

Да, соглашусь. Просто удаление происходит позже, а не сразу.
28 мая 2020 г. 6:43
Михаиллл

При подключении к git как указать пароль?

Нужно сделать ssh-keygen и потом полученый из файла код скопировать в ssh ключ в бикбакете
F
28 мая 2020 г. 2:42
Fidan

QML

Да, проблема ушла, спасибо.
1 мая 2020 г. 9:19
Михаиллл

Как в Qt в qmenu добавить scrollarea

Вот это наследованный класс меню. Но посути это обычное меню. #pragma once#include <QtWidgets>class TransMenu : public QMenu { Q_OBJECTpublic: TransMenu(QWidget* parent = …
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB