Qt ItemDelegate with a tool button: can't click - qt

I use a custom widget for an item delegate.
This widget is composed of a combobox and a tool button, see below for the source.
Now when I use this widget in an item delegate, pressing on the tool button has no effect if the combobox has not the focus.
For a demo, see this video: http://youtu.be/o5AgjC4cCqY
Any idea how to handle this?
Thanks a lot!
Source of the widget:
QgsFieldExpressionWidget::QgsFieldExpressionWidget( QWidget *parent )
: QWidget( parent )
{
QHBoxLayout* layout = new QHBoxLayout( this );
layout->setContentsMargins( 0, 0, 0, 0 );
mCombo = new QComboBox( this );
mCombo->setEditable( true );
mCombo->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Minimum );
mButton = new QToolButton( this );
mButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
mButton->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionEditorOpen.svg" ) );
layout->addWidget( mCombo );
layout->addWidget( mButton );
}
Source of the delegate:
QgsComposerColumnSourceDelegate::QgsComposerColumnSourceDelegate( QgsVectorLayer* vlayer, QObject* parent ) : QItemDelegate( parent ),
mVectorLayer( vlayer )
{
}
QWidget* QgsComposerColumnSourceDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
Q_UNUSED( option );
Q_UNUSED( index );
QgsFieldExpressionWidget *fieldExpression = new QgsFieldExpressionWidget( parent );
fieldExpression->setLayer( mVectorLayer );
connect( fieldExpression, SIGNAL( fieldChanged( QString ) ), this, SLOT( commitAndCloseEditor() ) );
return fieldExpression;
}
void QgsComposerColumnSourceDelegate::setEditorData( QWidget* editor, const QModelIndex& index ) const
{
QString field = index.model()->data( index, Qt::EditRole ).toString();
//set the value for the field combobox
QgsFieldExpressionWidget *fieldExpression = static_cast<QgsFieldExpressionWidget*>( editor );
fieldExpression->setField( field );
}
void QgsComposerColumnSourceDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const
{
QgsFieldExpressionWidget *fieldExpression = static_cast<QgsFieldExpressionWidget*>( editor );
QString field = fieldExpression->currentField();
model->setData( index, field, Qt::EditRole );
}
void QgsComposerColumnSourceDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
Q_UNUSED( index );
editor->setGeometry( option.rect );
}

Add the following line to your QgsFieldExpressionWidget constructor anywhere after the QComboBox is created:
setFocusProxy(mCombo);

Related

How to add 2 custom buttons for QLineEdit?

I tried to add two buttons for my line edit. The "clear" appears only when the text in line edit not empty, and the "bulb" is always shown.
MyLineEdit.cpp
MyLineEdit::MyLineEdit( QWidget *p_parent ) : QLineEdit( p_parent )
{
m_buttonAction = addAction( QIcon( ":/bulb" ), QLineEdit::TrailingPosition );
QToolButton *button = dynamic_cast<QToolButton *>( m_buttonAction->associatedWidgets().at( 1 ) );
button->setCursor( QCursor( Qt::PointingHandCursor ) );
m_buttonAction->setVisible( true );
m_clearAction = addAction( QIcon( ":/clear" ), QLineEdit::TrailingPosition );
QToolButton *clear = dynamic_cast<QToolButton *>( m_clearAction->associatedWidgets().at( 1 ) );
clear->setCursor( QCursor( Qt::PointingHandCursor ) );
m_clearAction->setVisible( false );
connect( this, &MyLineEdit::textChanged, this, &MyLineEdit::toggleClearButton );
connect( m_clearAction, &QAction::triggered, this, &QLineEdit::clear );
connect(m_buttonAction, &QAction::triggered, this, &MyLineEdit::doSomething);
}
void MyLineEdit::toggleClearButton()
{
text().isEmpty() ? m_clearAction->setVisible( false ) : m_clearAction->setVisible( true );
}
void MyLineEdit::doSomething()
{
}
MyLineEdit.h
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
MyLineEdit( QWidget *p_parent = nullptr );
private slots:
void toggleClearButton();
void doSomething();
private:
QAction *m_clearAction = nullptr;
QAction *m_buttonAction = nullptr;
};
When I run the program, the "bulb" does not appear at the beginning
Only when I type something in the line edit, the "bulb" is then shown as I want
Why is the "bulb" not shown at the beginning, where do I have error here?

How to setBold for matching characters with QSortFilterProxyModel?

This is an example on Qt Website
In this case the matching characters are not bold, and other characters are bold. I want to do the other way around: the matching characters should be bold and others are not bold. How can I do it?
MyLineEdit.cpp
MyLineEdit::MyLineEdit( QWidget *p_parent ) : QWidget( p_parent )
{
............
m_completer = new Completer();
m_lineEdit = new QLineEdit;
m_lineEdit->setObjectName( "lineEdit" );
connect( m_lineEdit, &LineEdit::textChanged, this, &MyLineEdit::setTextForFilter );
}
void MyLineEdit::setTextForFilter( const QString &p_text )
{
m_completer->setTextForFilter( p_text );
}
Completer.cpp
Completer::Completer( QWidget *p_parent ) : QWidget( p_parent )
{
.........
QGridLayout *tableViewLayout = new QGridLayout( this );
m_caption = new Label;
tableViewLayout->addWidget( m_caption );
m_table = new QTreeView();
m_table->setItemDelegate( new CompleterDelegate() );
m_sourceModel = new CompleterModel( this );
m_proxyModel = new CompleterProxyModel( this );
m_proxyModel->setSourceModel( m_sourceModel );
m_table->setModel( m_proxyModel );
tableViewLayout->addWidget( m_table );
}
void Completer::setTextForFilter( const QString &p_text )
{
QRegExp regExp( p_text, Qt::CaseInsensitive, QRegExp::RegExp );
m_proxyModel->setFilterRegExp( regExp );
}
CompleterModel.cpp
QVariant CompleterModel::data( const QModelIndex &p_index, int p_role ) const
{
int row = p_index.row();
int col = p_index.column();
switch ( p_role )
{
case Qt::DisplayRole:
if ( col < m_completerData.rowData().at( row ).columnCount() )
{
return m_completerData.rowData().at( row ).columnData( col ).first;
}
break;
case Qt::TextAlignmentRole:
return Qt::AlignLeft;
case Qt::UserRole:
return QVariant( static_cast<int>( m_completerData.rowData().at( row ).type() ) );
}
return QVariant();
}
CompleterProxyModel.cpp
bool CompleterProxyModel::filterAcceptsRow( int p_sourceRow, const QModelIndex &p_sourceParent ) const
{
QModelIndex index0 = sourceModel()->index( p_sourceRow, 0, p_sourceParent );
QModelIndex index1 = sourceModel()->index( p_sourceRow, 1, p_sourceParent );
return ( sourceModel()->data( index0 ).toString().contains( filterRegExp() ) || sourceModel()->data( index1 ).toString().contains( filterRegExp() ) );
}
You are going to need a custom delegate and do your own rendering. Using rich text is unfortunately not supported out of the box.

QItemDelegate: painting an unwanted QCheckBox

I have an editable model, which inherits QAbstractTableModel. Also have a custom delegate to go with it. This is my first editable model, and I think I'm missing something. I'm pretty much following the examples found at Nokia. My model tells the delegate that the data is editable via flags(). When I do this, it draws a QSpinBox in the cell.
Underlying model is a simple std::map. The key is days, the value is a rate.
Generally, what is painted in any editable cell, is a QCheckBox, but is ghosted out, then the data. If I double-click on the value, I am shown the editor, which happens to be a custom widget based on QDoubleSpinbox.
Qt::ItemFlags my_model_t::flags( const QModelIndex& index ) const
{
if ( !index.isValid() ) {
return Qt::NoItemFlags;
}
if ( index.column() == col_rates ) {
return QAbstractItemModel::flags( index ) | Qt::ItemIsEditable;
}
return QAbstractItemModel::flags( index );
}
QVariant my_model_t::data( const QModelIndex& index, int role ) const
{
if ( !index.isValid() ) {
return QVariant();
}
if ( role == Qt::DisplayRole || Qt::EditRole ) {
if ( static_cast<int>( rates.size() ) <= index.row() ) {
return QVariant();
}
int day = vec[index.row()];
if ( index.column() == col_days ) {
return day;
} else if ( index.column() == col_rates ) {
std::map<int, double>::const_iterator it = rates.find( day );
if ( it != rates.end() ) {
return (*it).second;
}
}
}
return QVariant();
}
QWidget* my_delegate_t::createEditor( QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& index ) const
{
gui_spinbox* editor = new gui_spinbox( parent );
if ( index.column() == col_rate ) {
const my_model_t* model = static_cast<my_model_t*>( index.model() );
}
return editor;
}
void my_delegate_t::setEditorData( QWidget* editor, const QModelIndex& index ) const
{
double value = index.model()->data( index, Qt::EditRole ).toDouble();
gui_spinbox* spin_box = static_cast<gui_spinbox*>( editor );
if ( spin_box ) {
spin_box->setValue( value );
}
}
void my_delegate_t::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const
{
gui_spinbox* spin_box = static_cast<gui_spinbox*>( editor );
if ( spin_box ) {
double value = spin_box->value();
model->setData( index, value, Qt::EditRole );
}
}
void my_delegate_t::updateEditorGemoetry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& /*index*/ ) const
{
editor->setGeometry( option.rect );
}
I'm not pretty sure about your problem. Do you want to paint a QCheckBox?
In your createEditor method, you created an editor use QSpinbox but in the Title you said it was unwanted.
If you want to be shown an editor based on an QCheckBox when you double clicked an item you just create a checkBox in the createEditor method and use checkBox in setEditorData and setModelData.

HowTo draw pixmap in QHeaderView section using delegate?

Problem: I need to draw small pixmap to QHeaderView section like presented on this figure (pixmap located on the right corner of the section, marked with red square):
As I understand, there is two ways to do that:
reimplement QHeaderView's paintSection() method.
create an delegate from QStyledItemDelegate class and reimplement paint() method.
If I tried (1) variant with this code below, filter pixmap are not shown at all:
void DecorativeHeaderView::paintSection( QPainter* painter, const QRect& rect, int logicalIndex ) const
{
if( !rect.isValid() )
{
return;
}
// get the state of the section
QStyleOptionHeader option;
initStyleOption( &option );
// setup the style options structure
option.rect = rect;
option.section = logicalIndex;
option.iconAlignment = Qt::AlignVCenter | Qt::AlignHCenter;
QVariant variant = model()->headerData( logicalIndex, orientation(), Qt::DecorationRole );
option.icon = qvariant_cast< QIcon >( variant );
if( option.icon.isNull() )
{
option.icon = qvariant_cast< QPixmap >( variant );
}
// draw the section
if( !option.icon.isNull() )
{
style()->drawControl( QStyle::CE_Header, &option, painter, this );
}
else
{
QHeaderView::paintSection( painter, rect, logicalIndex );
// HERE is where I'm trying to draw my filter picture!!!
if( logicalIndex == filteredLogicalIndex_ )
{
QPixmap pixmap( ":/spreadsheet/images/spreadsheet/filter_icon_table.png" );
int x = rect.right() - pixmap.width();
int y = rect.top() + ( rect.height() - pixmap.height() ) / 2;
painter->drawPixmap( QPoint( x, y ), pixmap );
}
}
}
The (2) variant is this:
class HeaderDelegate : public QStyledItemDelegate
{
Q_OBJECT
Q_DISABLE_COPY( HeaderDelegate )
public:
HeaderDelegate( QObject* parent = 0 ) : QStyledItemDelegate( parent ) {}
virtual ~HeaderDelegate() {}
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
}; // HeaderDelegate
void HeaderDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
// THIS method never starts!!!
if( index.column() == 2 )
{
QPixmap pixmap( ":/spreadsheet/images/spreadsheet/filter_icon_table.png" );
int x = option.rect.right() - pixmap.width();
int y = option.rect.top() + ( option.rect.height() - pixmap.height() ) / 2;
painter->save();
painter->drawPixmap( QPoint( x, y ), pixmap );
painter->restore();
}
QStyledItemDelegate::paint( painter, option, index );
}
DecorativeHeaderView::DecorativeHeaderView( Qt::Orientation orientation, QWidget* parent /* = 0 */ )
: QHeaderView( orientation, parent )
, filteredLogicalIndex_( -1 )
{
setItemDelegate( new HeaderDelegate( this ) );
}
Delegate created, but function did not start the paint() method!
Any help?
Thanks!
It Seems like it isn't possible at this time.
From Qt documentation:
Note: Each header renders the data for each section itself, and does not rely on a delegate. As a result, calling a header's setItemDelegate() function will have no effect.
How to have dynamic pixmap in a QHeaderView item
But you may override QHeaderView::paintSection method.
subclassing-QHeaderView
class HeaderView : public QHeaderView {
Q_OBJECT
public:
HeaderView(Qt::Orientation orientation, QWidget * parent = 0)
: QHeaderView(orientation, parent), p("333222.jpeg") {
}
int sectionSizeHint ( int /*logicalIndex*/ ) const { return p.width(); }
protected:
void paintSection(QPainter * painter, const QRect & rect, int /*logicalIndex*/) const {
painter->drawPixmap(rect, p);
}
private:
QPixmap p;
};

QStyledItemDelegate - How does updateEditorGeometry works?

I'm using Qt 4.7.
I have a model that I display in a QTableView in two columns, and my goal is to provide inline editing of this model in my QTableView.
+-----------------+----------------+
| Axis position | Axis range |
+-----------------+----------------+
| Left | Fixed [0,1] |
| Left | Source: SRC1 |
| Right | Source: SRC2 |
| Left | Fixed [5,10] |
+-----------------+----------------+
The first column is editable by using a simple QComboxBox to switch between Right and Left, and it works quite well. The problem is with my second column, which is editable using a custom widget.
This widget is kind of simple, it describes a range. So there is a QComboBox to select the kind of range ("Fixed": the values are given by the user, "Source": the value are adjusted dynamically from the min/max of a source).
Here is the source code of my custom widget:
class RangeEditor : public QWidget
{
Q_OBJECT
public:
RangeEditor( ... );
~RangeEditor();
public:
CurveView::ConfigAxes::Range range () const;
QVariant minimum() const;
QVariant maximum() const;
DataModel* model () const;
void range ( CurveView::ConfigAxes::Range range );
void minimum( QVariant minimum );
void maximum( QVariant maximum );
void model ( DataModel* model );
public slots:
void rangeTypeChanged( int type );
private: // --- External editors
QComboBox* editRange_;
QSpinBox* editMinimum_;
QSpinBox* editMaximum_;
QComboBox* editModel_;
};
RangeEditor::RangeEditor( ... ) : QWidget(parent)
{
editRange_ = new QComboBox(this);
editMinimum_ = new QSpinBox (this);
editMaximum_ = new QSpinBox (this);
editModel_ = new QComboBox(this);
QHBoxLayout* layout = new QHBoxLayout();
setLayout(layout);
layout->addWidget( editRange_ );
layout->addWidget( editMinimum_ );
layout->addWidget( editMaximum_ );
layout->addWidget( editModel_ );
editRange_->addItem( "Fixed" );
editRange_->addItem( "Source" );
editModel_->setCurrentIndex(0);
editModel_->hide();
QObject::connect( editRange_, SIGNAL(currentIndexChanged(int)),
this, SLOT (rangeTypeChanged(int)) );
}
void RangeEditor::rangeTypeChanged( int type )
{
if ( type==CurveView::ConfigAxes::FIXED )
{
editMinimum_->show();
editMaximum_->show();
editModel_->hide();
}
else if ( type==CurveView::ConfigAxes::SOURCE )
{
editMinimum_->hide();
editMaximum_->hide();
editModel_->show();
}
}
Okay, so now, I created a QStyledItemDelegate to provide the view a custom editor for my columns. Here is how I did it:
class ConfigAxesDelegate : public QStyledItemDelegate
{
public:
ConfigAxesDelegate( ... );
~ConfigAxesDelegate();
public:
virtual QWidget* createEditor ( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual void setEditorData ( QWidget* editor, const QModelIndex& index ) const;
virtual void setModelData ( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const;
virtual void updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
};
QWidget* ConfigAxesDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
if ( index.column()==0 ) // Position
{
PositionEditor* editor = new PositionEditor(parent);
return editor;
}
else if ( index.column()==1 ) // Range
{
RangeEditor* editor = new RangeEditor(parent);
return editor;
}
else
{
return QStyledItemDelegate::createEditor(parent,option,index);
}
}
void ConfigAxesDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
// WHAT TO DO HERE?
editor->setGeometry( option.rect );
}
Basically, what I get is a single pixel height editor.
Here is a screenshot of the result:
I tried to changed updateEditorGeometry to the following:
void ConfigAxesDelegate::updateEditorGeometry( QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
QRect r = option.rect;
r.setSize( editor->sizeHint() );
editor->setGeometry( r );
}
Which seems to fix the size problem, but not the position:
I feel kind of lost since I don't know if the problem comes from my custom widget (not providing enough information for Qt to compute its position properly), or the view (maybe some margins that would crush the editor size), or the updateEditorGeometry() method.
Any help greatly appreciated, thanks for reading!
I would say setting editor's geometry by calling:
editor->setGeometry(rect);
should work correctly; What happens in your case is that your editor is built using QHBoxLayout which has default margins and spacing set. Default height of your tableview rows is less then editor's height and this makes your editor to resize; one pixel row on your screen shot would be: top margin + what's left from controls + bottom margin.
By enabling the vertical header for your tableview you would be able to resize row height to make your editor controls completely visible.
What you could possibly do:
1.Remove\decrease spacing and margins for the layout:
QHBoxLayout* layout = new QHBoxLayout();
layout->setSpacing(1);
layout->setMargin(1);
setLayout(layout);
in this case, updating editor's geometry this way:
QRect rect = option.rect;
QSize sizeHint = editor->sizeHint();
if (rect.width()<sizeHint.width()) rect.setWidth(sizeHint.width());
if (rect.height()<sizeHint.height()) rect.setHeight(sizeHint.height());
editor->setGeometry(rect);
or just
editor->setGeometry(rect);
should work fine for you
2.You can also consider using popup editors for your rows\cells values
3.Resize widget's row heights to fit cell editors.
hope this helps, regards

Resources