cannot select QAbstractItemView item, when it's disabled - qt

When I set flags of QAbstractItemModel selectable but not enabled, I can't select items by mouse click. However internally select() function selects objects.
Is this qt bug, or I do something wrong?

From what I understood, you want to "Disable" the item, but at the same time, be able to select it. it's fairly easy to fake that on the model.
if ( role == Qt::BackgroundRole ){
return QVariant(QApplication::palette()->color(QPalette::Inactive, QPalette::Window );
}
This will paint your item as grayed out, and you will still be able to select it.

You're doing something wrong. If you disable a widget it is greyed out and it doesn't receive user mouse clicks and keyboard input.

I just had similar problem (I need to copy disabled items). Here is solution that sets correct style for disabled items (without ignoring any style sheets).
Create custom item delegate for your model.
/// Returns false only if item needs to be rendered as disabled.
bool isIndexEnabled(const QModelIndex &index)
{
// Implement this function.
}
class ItemDelegate : public QStyledItemDelegate {
public:
explicit ItemDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent) {}
protected:
void initStyleOption(
QStyleOptionItemView *option, const QModelIndex &index) const override
{
QStyledItemDelegate::initStyleOption(option, index);
if (!isIndexEnabled(index))
option->state &= ~QStyle::State_Enabled;
}
};
Set the new item delegate to your model.
auto itemDelegate = new ItemDelegate(model)
model->setItemDelegate(itemDelegate);

Related

QCombobox not using custom delegate to render current item

I am using a custom delegate to render rich text in a QComboBox. It is used to allow users to select a color to plot a variable on a graph. It works for items in the drop down menu, but not the selected item. Any help would be appreciated.
Here is the code for the delegate I am using:
class CustomDelegate : public QStyledItemDelegate
{
public:
CustomDelegate();
protected:
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
};
void CustomDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
QStyleOptionViewItemV4 optionV4 = option;
initStyleOption(&optionV4, index);
QStyle *style = optionV4.widget? optionV4.widget->style() : QApplication::style();
QTextDocument doc;
doc.setHtml(optionV4.text);
/// Painting item without text
optionV4.text = QString();
style->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter);
QAbstractTextDocumentLayout::PaintContext ctx;
// Highlighting text if item is selected
if (optionV4.state & QStyle::State_Selected)
ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText));
QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &optionV4);
painter->save();
painter->translate(textRect.topLeft());
painter->setClipRect(textRect.translated(-textRect.topLeft()));
doc.documentLayout()->draw(painter, ctx);
painter->restore();
}
QSize CustomDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const{
QStyleOptionViewItemV4 optionV4 = option;
initStyleOption(&optionV4, index);
QTextDocument doc;
doc.setHtml(optionV4.text);
doc.setTextWidth(optionV4.rect.width());
return QSize(doc.idealWidth(), doc.size().height());
}
I set the options and delegate for the combo box using code similar, I removed some of the options to reduce the size of the code:
ui->SelectColor->clear();
ui->SelectColor->addItem("Select Color");
ui->SelectColor->addItem("<font color='blue'>Blue</font>");
ui->SelectColor->addItem("<font color='darkBlue'>Dark Blue</font>");
ui->SelectColor->addItem("<font color='red'>Red</font>");
ui->SelectColor->addItem("Dark Yellow");
ui->SelectColor->addItem("<font color='magenta'>Dark Magenta</font>");
ui->SelectColor->addItem("White");
ui->SelectColor->setItemDelegate(new CustomDelegate);
Some of the options are just the names because they don't look good when rendered.
Your custom styled item delegate is applied to items that are part of QComboBox (i.e. ones in QComboBox popup), not for the current item which is represented. The easiest way to achieve what you want would be to introduce your custom class inheriting QComboBox, and then override void QWidget::paintEvent(QPaintEvent *event) method by applying changes you would like to introduce, e.g. set some colored text. Another way (in case you can not introduce another class inheriting QComboBox for some reason) would be to introduce an event filter that does something just after the QPaintEvent. However, usage of event filters might be tricky, and I would advise you to just introduce some another class, and then override paint event.
Now, in case you want to show current item in the same way as an option in the combo box list popup, you could do the following (code is not complete, and you should apply it to your needs yourself):
virtual void paintEvent(QPaintEvent* e) override
{
// QComboBox::paintEvent(e); - this will leave just a rectangle in which you can perform your custom drawings.
// Will make your option colored at least.
QPainter p(this);
QTextDocument doc;
doc.setHtml(this->currentText());
doc.drawContents(&p, rect());
}
Finally, it is worth to mention that QComboBox painting is pretty complex, and you might still need to rewrite pretty everything that Qt already has done for you, so it would be possible to apply other styles you might want not to lose. In order to do that, you should take a look into the source code of this class. You can do this in the Code Browser by Woboq for C & C++ (QComboBox). Hopefully, this clarifies the problem for you more, and now you know what to do in order to achieve your goal.

Catch item creation moment in QTreeView to set custom widget

I have custom model inherited from QAbstractItemModel and custom view inherited from QAbstractItemView. Model is a wrap on data organized as a tree. When model is changed it emits neccessary signals to notify view about changes. View has default item delegate.
And now I want to craete a custom widget for every item in a view and set it with QAbstractItemView::setIndexWidget(). How can I catch and handle every item creation in the view to make that?
you are better off using an itemdelegate.
class MyItemDelegate: public QAbstractItemDelegate
{
Q_OBJECT
QWidget *widget;
public:
MyItemDelegate(QObject *p):QAbstractItemDelegate(p)
{
//create widget
}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
//initialize painting widget
widget->render(painter);
}
}

Qt Child Frame passing KeyEvents to Parent Frame

I have implemented an OSGQt based model viewer. I am currently attempting to reposition submodels by changing their properties in a child QFrame to the main MainWindow based viewer frame. The child frame is non-modal and the key events are being passed to the OSG Viewer based handler in the MainWindow instead of the child window.
What would be the appropriate attributes for the child window to keep the keyboard focus there and allow typing new values in the QLineEdit based modifier for the QTreeWidgetItem column.
I have implemented a working solution by using a custom ItemDelegate as follows.
class DoublePositionEditDelegate: public QStyledItemDelegate {
public:
DoublePositionEditDelegate(QObject* parent=0): QStyledItemDelegate(parent) {}
virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QDoubleSpinBox* dspinbox = new QDoubleSpinBox(parent);
dspinbox->setMaximum(1000000.0);
dspinbox->setMinimum(-1000000.0);
dspinbox->grabKeyboard();
return dspinbox;
}
};
A more elegant/better solution is still welcome.

Qt: Signal while a QTableView item data is being edited instead of after edit is done?

I have a QTableView which has some QString based items in its model. I implemented setData in my table model, so editing is working (I can change the data in the cell, setData is called to update the model, and the table is properly updated).
Currently setData is only called when the user is done with editing, e.g. after they hit Enter or click out of the text entry box to finalize the text entry. I want to update other other parts of the table while the user is typing/editing into the text edit control instead of after they're done and the edited contents are finalized.
A simple example of what I want to have is for the next table cell to display a count of how many characters have been entered into the cell being edited, but to do this as the user is typing/editing the cell contents, not just after the edit is finalized and setData is called.
Any pointers to what I should be looking for? Thanks!
You can subclass QStyledItemDelegate and commit the data whenever something changes, and then set that delegate for the view with QAbstractItemView::setItemDelegate.
class MyDelegate : public QStyledItemDelegate {
QSignalMapper *mapper;
public:
MyDelegate(QObject*parent = 0)
: QStyledItemDelegate(parent)
, mapper(new QSignalMapper(this))
{
connect(mapper, SIGNAL(mapped(QWidget*)), SIGNAL(commitData(QWidget*)));
}
QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option,
const QModelIndex & index ) const
{
QWidget *editor = QStyledItemDelegate::createEditor(parent, option, index);
if(qobject_cast<QLineEdit*>(editor)) {
connect(editor, SIGNAL(textChanged(QString)), mapper, SLOT(map()));
mapper->setMapping(editor, editor);
}
return editor;
}
};
The answer offered by #alexisdm did not work for me when I needed a persistent
editor enabled by QAbstractTableModel::setPersistentEditor(QModelIndex()).
The following solves this problem:
class Delegate : public QStyledItemDelegate
{
Q_OBJECT
public:
// ... omitted for brevity
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override
{
auto *editor = static_cast<QLineEdit*>(
QStyledItemDelegate::createEditor(parent, option, index));
if (editor) {
connect(editor,
&QLineEdit::textChanged,
[=] (const QString &)
{
const_cast<Delegate*>(this)->commitData(editor);
});
}
return editor;
}
// ... omitted for brevity
};
We simply cast the constness from this and make it commit data for the editor.
Note that in the lambda we capture the editor variable by value [=] because otherwise, capturing with reference would make the value of editor undefined when the function runs out of scope.

Qt: TextEdit created by QStyledItemDelegate can't receive Escape key press event?

I have a QTableView to display a bunch of data, which is stored in a QAbstractTableModel. I also want to edit the data sometime, so I use a QStyledItemDelegate to create an editor (of QTextEdit type) when I double click on the cell. However, I want to handle the key Press event myself, but I never catch Key_Escape pressing in the Text Edit editor (Other keys such as Enter and Ascii can be captured). I checked the code, and found that Escape is directly connected to QTableView's closeEditor() function, which is a virtual method and is automatically invoked. As far as I know, key press event is the bottom layer of event handling, but it is not true with such case.
I do need to capture the Escape key press event so that I can handle it myself, can anyone tell me how to do this? Thanks!
I got the answer, and I think would help others:
Override QStyledItemDelegate::eventFilter() method:
MyItemDelegate::eventFilter(QObject* editor, QEvent* event)
{
if(event->type()==QEvent::KeyPress)
return false;
return QStyledItemDelegate::eventFilter(editor, event);
}
According to Qt's documentation, QStyledItemDelegate::eventFilter() returns true if the given editor is a valid QWidget and the given event is handled; otherwise returns false. Tab, Backtab, Enter, Return and Ecs are handled by default. So, if you want to handle the key press event yourself, you must let eventFilter return false when KeyPress event occurs. So that the editor's keyPressEvent() method will be invoked instead.
QStyledItemDelegate::eventFilter is not a possibility beacuse is virtual protected
In order to get the events you should subclass your own QEditLine and override ::keyPressEvent there. Take notice of the code. I pass actual row and colum of my QTableWidget cell in order to know what we are editing in the overrided QLineEditor.
//.h
class MyStyledItemDelegate : public QStyledItemDelegate
{
public:
MyStyledItemDelegate(QObject *parent = 0);
QWidget* createEditor(QWidget* parent,const QStyleOptionViewItem &option,const QModelIndex &index) const;
};
//.cpp
#include "mylineedit.h"
MyStyledItemDelegate::MyStyledItemDelegate(QObject *parent)
:QStyledItemDelegate(parent)
{
}
QWidget* MyStyledItemDelegate::createEditor(QWidget* parent,const QStyleOptionViewItem &option,const QModelIndex &index) const
{
MyLineEdit* editor = new MyLineEdit(parent,index.row(),index.column());
return editor;
}
/////////////////////////////////////////////////////////////
//My own QLineEdit
/////////////////////////////////////////////////////////////
//.h
class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
int _nRow;
int _nCol;
public:
MyLineEdit(QWidget *parent = 0,int nRow=-1, int nCol=-1);
virtual void keyPressEvent(QKeyEvent* event);
signals:
void mySignal(const QVector<QVariant> &);
public slots:
};
//.cpp
MyLineEdit::MyLineEdit(QWidget *parent, int nRow,int nCol):
QLineEdit(parent)
{
_nRow=nRow;
_nCol=nCol;
}
/////////////////////////////////////////////////////////////////////////
void MyLineEdit::keyPressEvent(QKeyEvent* event)
{
qDebug() << "MyLineEdit::OnKeyPressEvent:"<<event->text()<< " row="<<_nRow<<" col=" <<_nCol;
///SET YOUR CODE HERE
QLineEdit::keyPressEvent(event);
}

Resources