Evgenii Legotckoi
Evgenii LegotckoiNov. 18, 2015, 10:14 a.m.

Qt/C++ - Lesson 039. How to paint stroke in QSqlTableModel by value in the column?

In the lesson on working with QSqlTableModel was detailed mapping table data from a SQL database. But with the question from one of the readers, do a small addition to this example.

The question was how to paint an entire row depending on the value in one of the columns. In the example of operation performed with the addition of QSqlTableModel data base rows with the date, time, and a pseudo-random number notification of the pseudo-random number. In this regard, the lines were selected as test lines, where the pseudo-random number is equal to 41.

To do this, you must inherit from QSqlTableModel class and override the method QSqlTableModel::data() . Then use as a data model, a new class of heir.


tablemodel.h

#ifndef TABLEMODEL_H
#define TABLEMODEL_H

#include <QObject>
#include <QSqlTableModel>

class TableModel : public QSqlTableModel
{
    Q_OBJECT
public:
    explicit TableModel(QObject *parent = 0);
    QVariant data(const QModelIndex &idx, int role) const;

signals:

public slots:
};

#endif // TABLEMODEL_H

tablemodel.cpp

I note that in the original example, column 0 is not shown in the table, so we choose to obtain data from the column 3, and not from the speakers 2. Data acquisition is done by calling the base class.

#include "tablemodel.h"
#include <QColor>

TableModel::TableModel(QObject *parent) : QSqlTableModel(parent)
{

}

QVariant TableModel::data(const QModelIndex &idx, int role) const
{
    if(role == Qt::BackgroundColorRole){
        if(QSqlTableModel::data(this->index(idx.row(), 3)).toInt() == 41){
            return QColor(Qt::red);
        }
    } else if(role == Qt::DisplayRole){
        return QSqlTableModel::data(idx);
    }
    return QVariant();
}

Result

The result is an application that displays a table in which are painted in red color line, where there is a pseudo-random number equal to 41. Moreover, such an approach is valid for all classes inherited respectively from QAbstractItemModel . That is, for QSqlQueryModel , QSqlRelationalTableModel etc.

Archive with an example of a lesson on the OS Ubuntu - double check which way creates a database file in the DataBase class.

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!

Юрий
  • Oct. 8, 2017, 1:29 p.m.

Как передать переменную в класс наследник?

Evgenii Legotckoi
  • Oct. 8, 2017, 2:37 p.m.

А чутка подробнее ситуацию можете описать? Я не понял формулировки вопроса.

Юрий
  • Oct. 8, 2017, 3:20 p.m.

Я хочу из БД дергать значения цветов из профиля пользователя. Как мне передать user_id?

Evgenii Legotckoi
  • Oct. 8, 2017, 3:38 p.m.

То есть хотите подкрасить строку в таблице по цвету из профиля пользователя?

Юрий
  • Oct. 8, 2017, 4:35 p.m.

Да именно так. Но как передать id пользователя не знаю.

Evgenii Legotckoi
  • Oct. 8, 2017, 4:44 p.m.

Теоретически можно воспользоваться QSqlQuery, чтобы выдернуть данные из профиля прямо в методе data. А сам id можно дёрнуть через метод data, как взято значение в колонке через QSqlTableModel::data(this->index(idx.row(), 3)).toInt()

Юрий
  • Oct. 8, 2017, 5:06 p.m.

Так id пользователя нет в данной таблице.

qry->prepare("select * from users where  id=" + id_user);

colorstatus1 = qry->value("colorstatus1").toString();

Variant value = QSqlQueryModel::data(idx, role);
if(role == Qt::BackgroundColorRole)
{
    if (idx.sibling(idx.row(),6).data( Qt::DisplayRole ).toInt() == 1)
{
    return qVariantFromValue(QColor(colorstatus1));
}
Evgenii Legotckoi
  • Oct. 8, 2017, 5:16 p.m.

Ну я откуда знал, что у Вас нет этого id в таблице. И это разговор идёт про таблицу с профилями пользователей? Как тогда Вы делаете соответствие между пользователями и их данными, то есть цветами и т.д?

И вот этот код, что Вы привели, он вообще сработал?
Юрий
  • Oct. 8, 2017, 5:24 p.m.

Суть такая, есть таблица, в ней меняются статусы и соответственно цвет строк. В профиле пользователя настраивается эти цвета. Код не работает. В том то и проблема. Пользователь проходит аутентификацию и я получаю его id., а вот как мне передать это id. Сигналы и слоты в данном случае не подходят, я просто не знаю(((

Evgenii Legotckoi
  • Oct. 8, 2017, 5:38 p.m.

Так, хорошо. статусы меняются. То есть записываете состояние статуса в определённую таблицу... Но там же должен быть внешний ключ на пользователя? Если его нет, то сделайте внешний ключ на пользователя и тогда сможете без проблем дёргать статус и по нему прилагать нужный цвет. Обычно в таких случаях всегда в таблице профилей имеется внешний ключ на пользователя. Реляционные базы данных так и работают.

Юрий
  • Oct. 8, 2017, 5:46 p.m.

В профиле пользователя настраивается только интерфейс. В одной таблице работают несколько пользователей и все хотят что бы у них статусы били разными цветами(как раз настраивается в профиле).  Я записывал данные в файл настроек

 QSettings *settings = new QSettings( "settings.conf", QSettings::IniFormat );
    settings->beginGroup( "ColorStatus" );
    settings->setValue("colorstatus1", ui->lineEdit_colorstatus1->text());
и потом просто читал данные из файла. Просто хочу реализовать через БД.
Evgenii Legotckoi
  • Oct. 8, 2017, 5:53 p.m.

Ну понятно тогда. Сделайте тогда некую таблицу профилей вместо файла настроек, в которой будет храниться набор цветов для каждого варианта статуса. В таблице нужно создать несколько колонок, например четыре колонки для четырёх вариантов статуса. И обязательно внешний ключ на id пользователя. Когда пользователь аутентифицируется, то тогда можно будет дёрнуть через QSqlQuery, например, цвет статуса из таблицы профилей.

Юрий
  • Oct. 8, 2017, 6:11 p.m.

Когда пользователь аутентифицируется, то тогда можно будет дёрнуть через QSqlQuery, например, цвет статуса из таблицы профилей.   

Можно как-то объявить переменную QString доступную из разных классов(форм или файлов .cpp)?

TableModel::TableModel(QObject *parent) : QSqlTableModel(parent)
{
    QSqlQuery *qry = new QSqlQuery();
    qry->prepare("select * from users where  id=" +id_user);
    if(qry->exec())
    { qry->next();
        colorstatus1 = qry->value("colorstatus1").toString();
    }
}
 
QVariant TableModel::data(const QModelIndex &idx, int role) const
{
    if(role == Qt::BackgroundColorRole){
      if (idx.sibling(idx.row(),6).data( Qt::DisplayRole ).toInt() == 1)
        {
            return qVariantFromValue(QColor(colorstatus1));
        }
    } else if(role == Qt::DisplayRole){
        return QSqlTableModel::data(idx);
    }
    return QVariant();
}
Evgenii Legotckoi
  • Oct. 8, 2017, 6:23 p.m.

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

Что вообще должна показывать эта самая TableModel? Список всех пользователей с их статусами?
Юрий
  • Oct. 8, 2017, 6:41 p.m.

Статусы ремонта. "Ждем запчасти", "Без ремонта" и т.д. Я объявляю  public: static QString id_user;

А потом как получить эту переменную?
Юрий
  • Oct. 8, 2017, 9:10 p.m.

Спасибо большое. Все разобрался))) Все оказалось  как всегда просто.

k
  • March 23, 2018, 10:06 a.m.

Почему-то при встраивании кода в урок 008, при редактировании строки, значения в виджетах пропадают и при сохранении изменений ничего не меняется. Где вызывается функция date?

k
  • March 23, 2018, 10:41 a.m.

Нашел ошибку. В функии data() в конце вместо QVariant(), вернул QSqlTableModel::data(idx,role). И все заработало!

МА
  • May 27, 2020, 12:37 p.m.

Вопрос- с переопределенным методом data и нашим классом- наследником- как удалять строки из модели?

            // Get all selections
            QModelIndexList indexes = ui->tableView->selectionModel()->selection().indexes();
            for (int i = 0; i < indexes.count(); ++i)
            {
                QModelIndex index = indexes.at(i);
                // To get the row/column numbers use index.row() / index.column()
                //qDebug() << "index.row() = " << index.row();
                model->removeRow(index.row());
                model->submitAll();
            }

метод model->removeRow(index.row());
не работает.

Evgenii Legotckoi
  • May 28, 2020, 4:29 a.m.

Для QSqlTableModel концептуально верным подходом являтся удаление запросом к базе данных.

МА
  • May 28, 2020, 4:35 a.m.
  • (edited)

del

МА
  • May 28, 2020, 4:35 a.m.

Это как понять? Т.е. не через модель удалять, а через запрос к базе? А в чем тогда правильность? Если правильней с моделью работать, а не с базой в обход модели? Или я не так понял.

Evgenii Legotckoi
  • May 28, 2020, 4:55 a.m.

А. Да вы правы. Я немного попутал модель эту QSqlTableModel с QSqlQueryModel. У них есть разница, что QSqlQueryModel является read only, а поэтому функционал по удалению нужно дописывать.

Я думаю, всё дело в том, что или стратегия удаления не совсем верно настроена, либо там происходит какая-то ошибка.
Выведите в qDebug резальтат lastError() из модели. Там может быть по факту что угодно.

            QModelIndexList indexes = ui->tableView->selectionModel()->selection().indexes();
            for (int i = 0; i < indexes.count(); ++i)
            {
                QModelIndex index = indexes.at(i);
                // To get the row/column numbers use index.row() / index.column()
                //qDebug() << "index.row() = " << index.row();
                model->removeRow(index.row());
                model->submitAll();
                qDebug() << model->lastError();
            }
МА
  • May 28, 2020, 5:06 a.m.

QSqlError("", "", "")
Причем, если я использую Ваш подход- наследованный класс

TableModel  *model = new TableModel(this);

то не удаляет, как только делаю родной класс:

QSqlTableModel  *model = new QSqlTableModel (this); // работает

То удаляет и ошибка:
QSqlError("", "", "")
такая же.

Evgenii Legotckoi
  • May 28, 2020, 5:19 a.m.

После работы гляну проект. Пока нет мыслей.

МА
  • May 28, 2020, 5:29 a.m.

ок, буду ждать. Это странно, т.к. мы не переопределяли метод удаления.

Evgenii Legotckoi
  • May 28, 2020, 5:35 a.m.

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

МА
  • May 28, 2020, 8:09 a.m.
 model->setData(model->index(1, 0), 7);

Вот еще странность- не может сделать запись в уже имеющейся ячейке.
А в оригинальном пишет, но с глюком.

Evgenii Legotckoi
  • May 28, 2020, 4:06 p.m.

Да, метод data всё-таки влиял, я переписал его так и заработало удаление

QVariant TableModel::data(const QModelIndex &idx, int role) const
{
    if (role == Qt::BackgroundColorRole)
    {
        if (QSqlTableModel::data(this->index(idx.row(), 3)).toInt() == 1186278907)
        {
            return QColor(Qt::red);
        }
    }
    else if (role == Qt::DisplayRole)
    {
        return QSqlTableModel::data(idx, role);
    }
    return QSqlTableModel::data(idx, role);
}

Далее ваш код для удаления переписал иначе

QModelIndexList rowIndexes = ui->tableView->selectionModel()->selectedRows();
model->removeRows(rowIndexes.first().row(), rowIndexes.size());
model->submitAll();
model->select();

Это будет более эффективно

МА
  • May 28, 2020, 4:08 p.m.

Спасибо, завтра првоерю. А

 model->setData(model->index(1, 0), 7);

Тоже заработало?

Evgenii Legotckoi
  • May 28, 2020, 4:14 p.m.

Ну в моём примере, который в статье сработало так

model->setData(model->index(1, 1), 7);

Поскольку model->index(1, 0) - это индекс колонки id, которая скрыта, поэтому без дополнительных настроек таблицы вы не увидите результата

МА
  • May 29, 2020, 7:27 a.m.
            QModelIndexList rowIndexes = ui->tableView->selectionModel()->selectedRows();
            model->removeRows(rowIndexes.first().row(), rowIndexes.size());
            model->submitAll();
            model->select();

не работает. Вылетает с ошибкой.

Evgenii Legotckoi
  • May 29, 2020, 7:30 a.m.
  • (edited)

У меня работает. Исправлял в проекте, который приложен к статье.
А что происходит в вашем коде, с учётом места вызова этого кода, я знать не могу ;)
Дебажьте и добавляйте условия, которые обезопасят вызов кода

a
  • Dec. 11, 2022, 4:37 a.m.

Здравствуйте.
В Вашем примере в строке QSqlTableModel::data(this->index(idx.row(), 3))
3 - это индекс столбца. А есть ли возможность в данном случае как-то использовать имя столбца?

Evgenii Legotckoi
  • Dec. 12, 2022, 3:40 a.m.

Добрый день. Нет, данный метод оперирует целочисленными значениями, которые являются индексами столбца и строки, для создания объекта QModelIndex, по которому уже метод data возвращает значение в ячейке.
Если ваш вопрос касается читаемости кода, то в таком случае правильным решением будет создать enum для столбцов, который будет отображать фактическое имя столбца.

    enum EColumnNames
    {
        FIRST_COLUMN = 1,
        SECOND_COLUMN = 2,
        THIRD_COLUMN = 3
    };

    QSqlTableModel::data(this->index(idx.row(), EColumnNames::THIRD_COLUMN));

a
  • Dec. 12, 2022, 9:06 a.m.

Спасибо за ответ. Нет, дело не в читаемости кода, в разных таблицах у меня есть столбцы с одинаковым именем, но с разными индексами. Хотел сделать решение по имени столбца для всех таблиц сразу.

Evgenii Legotckoi
  • Dec. 12, 2022, 9:12 a.m.
  • (edited)

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

Comments

Only authorized users can post comments.
Please, Log in or Sign up
АФ

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

  • Result:30points,
  • Rating points-10
ПЩ

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

  • Result:80points,
  • Rating points4
d

C++ - Test 005. Structures and Classes

  • Result:50points,
  • Rating points-4
Last comments
k
kmssrFeb. 9, 2024, 5:43 a.m.
Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVADec. 25, 2023, 9:30 p.m.
Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJoDec. 25, 2023, 7:38 p.m.
Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
GvozdikDec. 19, 2023, 8:01 a.m.
Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Now discuss on the forum
AC
Alexandru CodreanuJan. 19, 2024, 10:57 p.m.
QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…
BlinCT
BlinCTDec. 27, 2023, 7:57 p.m.
Растягивать Image на парент по высоте Ну и само собою дял включения scrollbar надо чтобы был Flickable. Так что выходит как то так Flickable{ id: root anchors.fill: parent clip: true property url linkFile p…
Дмитрий
ДмитрийJan. 10, 2024, 3:18 p.m.
Qt Creator загружает всю оперативную память Проблема решена. Удалось разобраться с помощью утилиты strace. Запустил ее: strace ./qtcreator Начал выводиться весь лог работы креатора. В один момент он начал считывать фай…
Evgenii Legotckoi
Evgenii LegotckoiDec. 12, 2023, 5:48 p.m.
Побуквенное сравнение двух строк Добрый день. Там случайно не высылается этот сигнал textChanged ещё и при форматировани текста? Если решиать в лоб, то можно просто отключать сигнал/слотовое соединение внутри слота и …

Follow us in social networks