Russian Qt Forum

Qt => Model-View (MV) => Тема начата: chu от Апрель 23, 2012, 16:39



Название: [Решено] sql запрос в делегате
Отправлено: chu от Апрель 23, 2012, 16:39
Есть две таблицы:
1) список элементов с описанием их свойств  items (id,  property1, property2, ...);
2) список операций с элементами  actions (id, item_id, info1, info2, ... ).
В actions.item_id записывается один из items.id.
Хотел написать делегат для второй таблицы, который бы изменял цвет ячейки в зависимости от одного из свойств соответствующего элемента. Для этого переопределил в делегате функцию paint и попытался из неё вызвать sql-запрос, но упустил что функция paint имеет квалификатор const.
Подскажите какой-нибудь другой подход :)


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 23, 2012, 17:12
Делать селект надо хотя бы при создании элемента, а ни как при его отрисовке.


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 23, 2012, 23:23
В голову пришло только такое решение:
Код:
    model = new QSqlQueryModel();
    model->setQuery("SELECT a.id, a.item_id, i.item_type AS item_type FROM actions a "
                    "INNER JOIN items i ON i.id = a.item_id ");

    MyDelegate *delegate = new MyDelegate(ui->actionsTV);
    ui->actionsTV->setModel(model);
    ui->actionsTV->setItemDelegateForColumn(2,delegate);
    ui->actionsTV->resizeColumnsToContents();
Сменил QSqlTableModel на QSqlQueryModel, во вью таким образом появился отдельный столбец для отображения цвета
В делегате:
Код:
void AnswerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    int id = index.data(Qt::DisplayRole).toInt();
    if (option.state & QStyle::State_Selected)
             painter->fillRect(option.rect, option.palette.highlight());
    if (option.state & QStyle::State_Selected)
             painter->setBrush(option.palette.highlightedText());
         else
             painter->setBrush(QBrush(colors.at(id)));
    painter->fillRect(option.rect, painter->brush());
}
Но мне этот метод не нравится: отдельная колонка для цвета, уход от QSqlTableModel. В данном случае для меня это не критично, но  на будущее хотелось бы знать, можно ли реализовать по другому...


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 24, 2012, 07:24
А кто мешает установить Qt::BackgroundRole и не городить огород?


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 24, 2012, 11:57
А кто мешает установить Qt::BackgroundRole и не городить огород?
Мешает недостаток знаний. Мне модель свою писать и там переопределять роль в методе QVariant MyModel::data(const QModelIndex &index, int role) const?  Но он тоже константный.


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 24, 2012, 12:08
Не смотря на то что метод data константный, запрос из него высылать можно, и это отлично :)
GreatSnake, спасибо!


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 24, 2012, 12:23
Мешает недостаток знаний. Мне модель свою писать и там переопределять роль в методе QVariant MyModel::data(const QModelIndex &index, int role) const?  Но он тоже константный.
Зачем нужна своя модель для изменения цвета ячейки ???
На этапе создания элементов рассчитывай цвет и выставляй его через QAbstractItemModel::setData() с Qt::BackgroundRole.

Не смотря на то что метод data константный, запрос из него высылать можно, и это отлично :)
Делать селект из data() тоже неразумно, т.к. этот метод вызывается при отрисовке и не только, т.е. очень часто.


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 24, 2012, 13:31
Если я правильно понял, нужно реализовать что-то такое:
Код:
    model = new QSqlTableModel();
    model->setTable("actions");
    model->setSort(model->fieldIndex("id"),Qt::AscendingOrder);
    model->select();
    int row(0);
    QSqlQuery q;
    q.exec(QString("SELECT i.item_type FROM items i "
                   "INNER JOIN actions a ON i.id = a.item_id ORDER BY a.id"));
    while(q.next()) {
        int item_type = q.value(0).toInt();
        qDebug() << model->setData(model->index(row++,1),QBrush(colors.at(item_type)), Qt::BackgroundRole);
        qDebug() << model->lastError() << row << colors.at(item_type);
    }
Но setData возвращает false, model->lastError() ошибки не выдает. Что не так?


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 24, 2012, 13:45
Но setData возвращает false, model->lastError() ошибки не выдает. Что не так?
Обычно "setData возвращает false", если её подсовывают левый index.
Сколько колонок в model? Случайно не 1?


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 24, 2012, 13:53
Но setData возвращает false, model->lastError() ошибки не выдает. Что не так?
Обычно "setData возвращает false", если её подсовывают левый index.
Сколько колонок в model? Случайно не 1?
нет. колонок 6


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 24, 2012, 14:02
см. что возвращает model->index(row++,1).
И непонятно почему у тебя row, который применяется к таблице "actions" рассчитывается от размерности "items".
Чего-то с логикой не то.

PS. хотя судя по "INNER JOIN actions" количество строк вроде как должно быть одинаковым.
Распечатай размерность модели после model->select():
Код
C++ (Qt)
qDebug() << model->rowCount() << model->columnCount()


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 24, 2012, 14:22
Код:
26 6 
QModelIndex(0,4,0x0,QSqlTableModel(0x11798d0) )
QModelIndex(1,4,0x0,QSqlTableModel(0x11798d0) )
QModelIndex(2,4,0x0,QSqlTableModel(0x11798d0) )
...


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 25, 2012, 00:59
С индексами все в порядке, потому что когда я применяю setData с Qt::EditRole все корректно срабатывает
Код:
    qDebug() << model->setData(model->index(0,1), 123, Qt::EditRole);

 а вот с Qt::BackgroundRole setData возвращает false
Код:
    qDebug() << model->setData(model->index(0,1),QBrush(Qt::green, Qt::SolidPattern), Qt::BackgroundRole);
Пробовал еще вызывать setData c FontRole и DisplayRole - так же возвращает false.
С чем это может быть связано?


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 25, 2012, 10:26
QModelIndex(2,4,0x0,QSqlTableModel(0x11798d0) )
Цитировать
model->index(row++,1)
Что-то странное... Как при запросе индекса для колонки 1 модель тебе отдаёт индекс с колонкой 4 ???


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 25, 2012, 11:17
QModelIndex(2,4,0x0,QSqlTableModel(0x11798d0) )
Цитировать
model->index(row++,1)
Что-то странное... Как при запросе индекса для колонки 1 модель тебе отдаёт индекс с колонкой 4 ???

запрос для 4ой колонки делал


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 25, 2012, 11:24
темку на форуме нашел: http://www.prog.org.ru/index.php?topic=16338.msg114651#msg114651 (http://www.prog.org.ru/index.php?topic=16338.msg114651#msg114651)
Цитировать
Так в том и проблема что QSqlTableModel игнорирует все кроме Qt::EditRole и Qt::DisplayRole
Выходит, что нельзя для QSqlTableModel setData  с Qt::backgroundRole применять...  :(


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 25, 2012, 12:45
Выходит, что нельзя для QSqlTableModel setData  с Qt::backgroundRole применять...  :(
Можно! Вот тебе workaround:

Код
C++ (Qt)
#include <QMap>
#include <QPair>
 
class SqlQueryModel : public QSqlQueryModel
{
public:
 
typedef QMap< QPair< int, int >, QMap< char, QVariant > > RolesMap;
 
SqlQueryModel( QObject* parent = 0, QSqlDatabase db = QSqlDatabase() )
: QSqlQueryModel( parent, db ) {}
 
QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const
{
if( !index.isValid() )
return QVariant();
if( role == Qt::DisplayRole || role == Qt::EditRole )
return QSqlQueryModel::data( index, role );
RolesMap::const_iterator it = roles_data_.find( qMakePair( index.row(), index.column() ) );
return it != roles_data_.end() ? it.value()[ char( role )] : QVariant();
 
}
bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole )
{
if( !index.isValid() )
return false;
if( role == Qt::DisplayRole || role == Qt::EditRole )
return QSqlQueryModel::setData( index, value, role );
roles_data_[ qMakePair( index.row(), index.column() ) ][ char( role ) ] = value;
return true;
}
bool removeColumns( int column, int count, const QModelIndex& parent = QModelIndex() )
{
Q_UNUSED(parent);
int rows = rowCount();
if( !QSqlQueryModel::removeColumns( column, count ) )
return false;
for( int r = 0; r < rows; r++ )
for( int c = column; c < column + count; c++ )
{
RolesMap::iterator it = roles_data_.find( qMakePair( r, c ) );
if( it != roles_data_.end() )
roles_data_.erase( it );
}
return true;
}
bool removeRows( int row, int count, const QModelIndex& parent = QModelIndex() )
{
Q_UNUSED(parent);
int cols = columnCount();
if( !QSqlQueryModel::removeRows( row, count ) )
return false;
for( int r = row; r < row + count; r++ )
for( int c = 0; c < cols; c++ )
{
RolesMap::iterator it = roles_data_.find( qMakePair( r, c ) );
if( it != roles_data_.end() )
roles_data_.erase( it );
}
return true;
}
void clear()
{
QSqlQueryModel::clear();
roles_data_.clear();
}
 
private:
RolesMap roles_data_;
};
 
Используй SqlQueryModel вместо QSqlQueryModel и всё заработает)

PS. Писал на скорую руку, так что может кое-что упустил и сделал не совсем оптимально)


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 25, 2012, 17:24
Цитировать
Можно! Вот тебе workaround:
Я имел в виду, что нельзя setData с Qt::backgroundRole применять без переопределения модели.

За workaround спасибо! Работает как надо :)


Название: Re: sql запрос в делегате
Отправлено: GreatSnake от Апрель 25, 2012, 17:29
На самом деле тебе достаточно было перегрузить QSqlQueryModel::data(), в которой для Qt::BackgroundRole рассчитывать и возвращать цвет.


Название: Re: sql запрос в делегате
Отправлено: chu от Апрель 26, 2012, 10:49
На самом деле тебе достаточно было перегрузить QSqlQueryModel::data(), в которой для Qt::BackgroundRole рассчитывать и возвращать цвет.
Так я и сделал:
Код:
QVariant MyModel::data(const QModelIndex &index, int role) const
{
    QVariant value = QSqlTableModel::data(index, role);
    if (role == Qt::BackgroundRole) {
        if(index.column()==3) {
            int id = index.sibling(index.row(),0).data().toInt();
            QColor color = QColor(colorMap.value(id));
            value = qVariantFromValue(color);
            return value;
        }
        else {
            return value;
        }
    }
}
colorMap рассчитывается за пределами модели, при селекте модели.


Название: Re: [Решено] sql запрос в делегате
Отправлено: GreatSnake от Апрель 26, 2012, 10:54
Судя по коду, MyModel::data() возвращает значение только для (role == Qt::BackgroundRole).


Название: Re: [Решено] sql запрос в делегате
Отправлено: chu от Апрель 26, 2012, 12:02
Судя по коду, MyModel::data() возвращает значение только для (role == Qt::BackgroundRole).
Проверял вызов data() с другими ролями, все в порядке!