Russian Qt Forum
Ноябрь 01, 2024, 02:46 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Как сделать делегата на основе стандартного виджета?  (Прочитано 17008 раз)
vashu1
Гость
« : Август 14, 2007, 15:33 »

Возникла проблема - отображать и редактировать в QTableView данные при помощи механизма чекбокса. На решение ушло несколько часов и полторы сотни строк кода.

Отображение QCheckBox при редактировании значения проблем не вызывает.
Но отображение данных QItemDelegate делает только при помощи paint который получает в качестве параметров QPainter, QStyleOptionViewItem и QModelIndex.

Как применить QPainter и/или QStyleOptionViewItem для отрисовки стандартного виджета, мягко говоря, непонятно.


Выставление Qt::CheckStateRole (enum Qt::ItemDataRole) в setData модели или добавление Qt::ItemIsUserCheckable (flags Qt::ItemFlags) во flags модели дает делегат не вида QCheckBox, а вида QSpinBox.

Пришлось пихать в paint делегата собственную отрисовку и еще делать псевдо-комбобокс, который использует эту отрисовку при редактировании. Код очевидный, у меня работает, так что приводить его здесь не стану.

Наверно я что-то проглядел. Неверю чтобы это было так сложно.
В том же WTL субклассирование делается максимум 30 строками.

добавлено спустя 1 минуту:

 Да, на всякий случай - Qt 4.3.0, бесплатная.
Записан
bigirbis
Гость
« Ответ #1 : Август 14, 2007, 17:09 »

Посмотри такой пример в Qt:
Spin Box Delegate Example
%QTDIR%\examples\itemviews\spinboxdelegate
Записан
EhTemka
Гость
« Ответ #2 : Август 14, 2007, 18:47 »

Была та же задача, и я так же(если я правильно понял) её решал. То биш через делегат.

Только я замещал виртуальные методы в делегате

Код:

QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const;

void setEditorData(QWidget *editor, const QModelIndex &index) const;

void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const;  

 void updateEditorGeometry(QWidget *editor,  const QStyleOptionViewItem &option,
                              const QModelIndex &index) const;


 что -то типа того

Код:


QWidget *GuiDelegateTree::createEditor(QWidget *parent,
    const QStyleOptionViewItem & option ,
    const QModelIndex &index ) const
{
    // некая проверка
    ....

    QCheckBox *editor = new QCheckBox(parent);    
    editor->installEventFilter(const_cast<GuiDelegateTree*>(this));

    return editor;
}

void GuiDelegateTree::setEditorData(QWidget *editor,
                                    const QModelIndex &index) const
{
    Qt::CheckState value = (Qt::CheckState)(index.model()->data(index, Qt::CheckStateRole).toInt());

    QCheckBox *checkBox = static_cast<QCheckBox*>(editor);
    checkBox->setCheckState(value);
}

void GuiDelegateTree::setModelData(QWidget *editor, QAbstractItemModel *model,
                                   const QModelIndex &index) const
{
    QCheckBox *checkBox = static_cast<QCheckBox*>(editor);        
    int value = (int)(checkBox->checkState());

    model->setData(index, value, Qt::CheckStateRole);    
}

void GuiDelegateTree::updateEditorGeometry(QWidget *editor,
    const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
    QRect r(option.rect.x() + option.rect.width()/2 - 7, option.rect.y() + option.rect.height()/2 - 7, 13, 13);
    editor->setGeometry(r);    
}


Таким способом можно практически любой виджет запихнуть для редактирования данных, даже сложный (состоящий из нескольких контролов)
Записан
vashu1
Гость
« Ответ #3 : Август 15, 2007, 09:32 »

Спасибо, но редактирование данных проблем и не вызывало.

Повторяю

Цитировать

Отображение QCheckBox при РЕДАКТИРОВАНИИ значения проблем не вызывает.
Но ОТОБРАЖЕНИЕ данных QItemDelegate делает при помощи paint который получает в качестве параметров QPainter, QStyleOptionViewItem и QModelIndex.




Методы

Цитировать


QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
                          const QModelIndex &index) const;

void setEditorData(QWidget *editor, const QModelIndex &index) const;

void setModelData(QWidget *editor, QAbstractItemModel *model,
                      const QModelIndex &index) const;  

 void updateEditorGeometry(QWidget *editor,  const QStyleOptionViewItem &option,
                              const QModelIndex &index) const;



позволяют использовать стандартные виджеты при ИЗМЕНЕНИИ данных.


Для ОТОБРАЖЕНИЯ данных QItemDelegate использует

void QItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const   [virtual]

Вопрос стоит так - как при помощи передаваемых в paint параметров отобразить данные при помощи стандартного виджета.

Или использовать какой-то другой способ, до которого я не докумекал.

(
Попытка произвести делегат и от QItemDelegat и от стандартного виджета выдает на компиляции Warning: Class CCheckBoxDelegate inherits from two QObject subclasses QItemDelegate and QCheckBox. This is not supported! и не дает результата.
)

Нарисовать данные paint'ом несложно, но дублировать работу стандартных виджетов - это что-то не то.
Записан
goer
Гость
« Ответ #4 : Август 15, 2007, 11:15 »

Цитировать
void QItemDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const  [virtual]
Renders the delegate using the given painter and style option for the item specified by index.


Говорят, что для рендера юзают паинтер(уже чем то инициализированый) и стайл опшн.
Далее.

Цитировать
The QStyleOptionViewItem class is used to describe the parameters used to draw an item in a view widget.
QStyleOptionViewItem contains all the information that QStyle functions need to draw the items for Qt's model/view classes.


Это должно наводить на мысль а том что рисовать стандартные виджеты в итем делегатах _нельзя_.
Записан
den'ka
Гость
« Ответ #5 : Август 15, 2007, 11:29 »

А если использовать защищенные ф-ции QItemDelegate: drawBackground, drawCheck, drawDecoration, drawDisplay, drawFocus? Помоему их должно хватить для симуляции QCheckBox'a Улыбающийся А еще посмотри ту же QItemDelegate::paint.
Записан
vashu1
Гость
« Ответ #6 : Август 15, 2007, 12:05 »

Цитировать

А если использовать защищенные ф-ции QItemDelegate: drawBackground, drawCheck, drawDecoration, drawDisplay, drawFocus?


За drawCheck спасибо - не заметил такой возможности.

И все-таки жаль что не сделали рендер, который бы просто принимал ссылку на виджет, или шаблонный класс. (((

добавлено спустя:

 В принципе в paint можно создать виджет - но после его удаления он сотрется.

Подошел бы способ который позволял только выводить изображение виджета - не создавая его как активный элемент.
Записан
VZ
Гость
« Ответ #7 : Август 15, 2007, 12:26 »

Цитата: "vashu1"
Возникла проблема - отображать и редактировать в QTableView данные при помощи механизма чекбокса. На решение ушло несколько часов и полторы сотни строк кода.


Как страшно жить...  :lol: мне помогало - в model.flags() включение Qt::ItemIsUserCheckable и исключение Qt::ItemIsEditable и обработка в model.data() роли Qt::CheckStateRole - что-то типа return QVariant(val ? Qt::Checked : Qt::Unchecked);
Записан
den'ka
Гость
« Ответ #8 : Август 15, 2007, 12:51 »

Попробуй еще QStylePainter. У меня получилось что-то такое так:

void CheckDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option,
        const QModelIndex & index ) const {
    if ( index.isValid() && index.column() ) {
        QItemDelegate::paint( painter, option, index );
        return;
    }
   QWidget * w = dynamic_cast<QWidget *>( painter->device() );
   if ( w ) {
      QStylePainter p( w );
      QStyleOptionButton opt;
      opt.initFrom( w );
      opt.rect = option.rect;
      opt.state |= QStyle::State_On;
      opt.text = "Hello";
      p.drawControl( QStyle::CE_CheckBox, opt );

   }
}

Вобщем походу так можно рисовать те виджеты которые поддерживает QStyle Улыбающийся
Записан
goer
Гость
« Ответ #9 : Август 15, 2007, 13:58 »

Цитировать
Подошел бы способ который позволял только выводить изображение виджета - не создавая его как активный элемент.


Может этого хватит:

Цитировать

QPixmap QPixmap::grabWidget ( QWidget * widget, const QRect & rectangle )  [static]

Creates a pixmap and paints the given widget, restricted by the given rectangle, in it. If the widget has any children, then they are also painted in the appropriate positions.
If no rectangle is specified (the default) the entire widget is painted.
If widget is 0, the specified rectangle doesn't overlap the widget's rectangle, or an error occurs, the function will return a null QPixmap. If the rectangle is a superset of the given widget, the areas outside the widget are covered with the widget's background.
This function actually asks widget to paint itself (and its children to paint themselves) by calling paintEvent() with painter redirection turned on. But QPixmap also provides the grabWindow() function which is a bit faster grabbing pixels directly off the screen. In addition, if there are overlaying windows, grabWindow(), unlike grabWidget(), will see them.
Warning: Do not call this function from QWidget::paintEvent().
See also grabWindow().
Записан
vashu1
Гость
« Ответ #10 : Август 15, 2007, 15:01 »

Цитировать

Как страшно жить...  мне помогало - в model.flags() включение Qt::ItemIsUserCheckable и исключение Qt::ItemIsEditable и обработка в model.data() роли Qt::CheckStateRole - что-то типа return QVariant(val ? Qt::Checked : Qt::Unchecked);


Исключать Qt::ItemIsEditable я не могу - так как юзверь данные должен вводить.


И у меня лично при применении

Цитировать


virtual bool setData(const QModelIndex& index,
          const QVariant& value, int role = Qt::EditRole )
{
...
Q_ASSERT((flags(index) & Qt::ItemIsUserCheckable) == Qt::ItemIsUserCheckable);
return QSqlTableModel::setData(index,
         QVariant(value.toBool() ? Qt::Checked : Qt::Unchecked), newRole)
...



появляется не чекбокс, а едитбокс со спинбоксом. (?)


Решение den'ka c QStyle решает проблему стандартного вывода, хотя, конечно печально, что приходится прибегать к таким извращениям.

добавлено спустя 41 минуту:

 
Цитировать

QPixmap QPixmap::grabWidget ( QWidget * widget, const QRect & rectangle ) [static]


Было-бы хорошим решением, но, по-моему, он grab'ит только показанный виджет - что есть bad.
Записан
VZ
Гость
« Ответ #11 : Август 16, 2007, 14:01 »

Цитата: "vashu1"
Цитировать

Как страшно жить...  мне помогало - в model.flags() включение Qt::ItemIsUserCheckable и исключение Qt::ItemIsEditable и обработка в model.data() роли Qt::CheckStateRole - что-то типа return QVariant(val ? Qt::Checked : Qt::Unchecked);


Исключать Qt::ItemIsEditable я не могу - так как юзверь данные должен вводить.


И у меня лично при применении

Цитировать


virtual bool setData(const QModelIndex& index,
          const QVariant& value, int role = Qt::EditRole )
{
...
Q_ASSERT((flags(index) & Qt::ItemIsUserCheckable) == Qt::ItemIsUserCheckable);
return QSqlTableModel::setData(index,
         QVariant(value.toBool() ? Qt::Checked : Qt::Unchecked), newRole)
...



появляется не чекбокс, а едитбокс со спинбоксом. (?)




Ох, прошу прощения за свой варварский С++, поскольку на ём уже лет 5 не пишу... Но, тем не менее - посмотри, у меня в Qt 4.3.1 всё работает как было запланировано.

Код:

#include <QtGui>
#include <QtSql>


class TestModel: public QAbstractTableModel
{
public:
    QVariant m_values[3][3];
    QVariant m_checks[3][3];

    TestModel(QObject * parent = 0): QAbstractTableModel(parent)
    {
        int row, column;
        for(row=0;row<3;row++)
            for(column=0;column<3;column++)
            {
                m_values[row][column] = QVariant( QString("%L1:%L2").arg(row).arg(column) );
                m_checks[row][column] = QVariant( (row+column%2) ? Qt::Checked : Qt::Unchecked );
            }    

    }
    int rowCount( const QModelIndex & parent = QModelIndex() ) const { return 3; }
    int columnCount( const QModelIndex & parent = QModelIndex() ) const { return 3; }

    virtual Qt::ItemFlags flags( const QModelIndex & index ) const
    {
        int column = index.column();
        switch( column )
        {        
        case 0: return Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable;
        case 1: return Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsUserCheckable;
        case 2: return Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsUserCheckable;
        default:return 0;
        }
    }


    virtual QVariant data( const QModelIndex & index, int role = Qt::DisplayRole) const
    {
        int row    = index.row();
        int column = index.column();
        switch( role )
        {
        case Qt::DisplayRole:
        case Qt::EditRole:
                return m_values[row][column];

        case Qt::CheckStateRole:
                if ( column>0 )
                    return m_checks[row][column];
                else
                    return QVariant();

        default:
                return QVariant();
        }
    }

    virtual bool setData( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole)
    {
        int row    = index.row();
        int column = index.column();
        switch( role )
        {
        case Qt::EditRole:
                m_values[row][column] = value;
                return true;

        case Qt::CheckStateRole:
                m_checks[row][column] = value;
                return true;

        default:
                return false;
        }
    }
};


QTableView *createView(const QString &title, QAbstractTableModel *model)
{
    QTableView *view = new QTableView;
    view->setModel(model);
    view->setWindowTitle(title);
    return view;
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    TestModel model;

    QTableView *view = createView(QObject::tr("My Table"), &model);

    view->show();
    return app.exec();
}
Записан
vashu1
Гость
« Ответ #12 : Август 17, 2007, 10:42 »

Цитировать

появляется не чекбокс, а едитбокс со спинбоксом. (?)


Туплю.
data не перегрузил.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.054 секунд. Запросов: 21.