I'm having an issue with overriding the text displayed for a QTreeView using a QStyledItemDelegate. When some condition is met following code is executed:
void MyDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
.
.
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt, index);
QString text = opt.text;
text = text + QString("TEST");
opt.text = text;
QStyledItemDelegate::paint(painter, opt, index);
}
I confirmed in the debbugger that TEST is added to opt.text.
However, when I run my program and look at the TreeVuew it is still displaying the original text without the TEST string appended.
It seems that when I call QStyledItemDelegate::paint(painter, opt, index), it's ignoring the change I've made to the opt parameter.
The default implementation of the QStyledItemDelegate::paint() method uses it's own QStyleOptionViewItem instance initialized with data from the model.
From the Qt 5.4.0 source code:
void QStyledItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = QStyledItemDelegatePrivate::widget(option);
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
}
Solution:
Do not call the default implementation and implement your delegate's paint() method like this:
void MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyleOptionViewItem itemOption(option);
initStyleOption(&itemOption, index);
itemOption.text = "Test Text"; // override text
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter, nullptr);
}
The alternative solution, if you want to change the displayed text in a view, is to override displayText() method.
Example for Qt5:
mydelegate.h
virtual QString displayText(const QVariant &value,
const QLocale &locale) const override;
mydelegate.cpp
QString MyDelegate::displayText(const QVariant &value,
const QLocale &locale) const
{
Q_UNUSED(locale)
QString result = value.toString() + "TEST";
return result;
}
doc link: https://doc.qt.io/qt-5/qstyleditemdelegate.html#displayText
Depending what type of delegates it is, I would also try to override the setEditorData() method or even the createEditor() (where you can add values different from your model). It is less time consuming than doing such operation in paint.
Otherwise, you can use something like that to draw your text where you want:
painter->drawText(option.rect, Qt::AlignJustify, text + "_test");
You probably have a reason for doing so, but it seems like something is wrong in your design if you want to add extra text on the fly?
Possible QStyledItemDelegate::paint picks a text directly from index.data( Qt::DisplayRole ).toString(). That's why text is not changed. You may debug through Qt sources to be sure.
I propose you to use QIdentityProxyModel to do such things. Delegates are not designed for such solutions. You just need to override 1 method. So your code should look like this:
class MyProxyModel : public QIdentityProxyModel
{
// ...
};
QVariant MyProxyModel::data(const QModelIndex &index, int role) const override
{
if ( /*Conditions when you don't want to change source text*/ )
return QIdentityProxyModel::data( index, role );
// Extra check for editors or other roles to return original data
if ( role == Qt::EditRole || role != Qt::DisplayRole )
return QIdentityProxyModel::data( index, role );
const auto sourceIndex = mapToSource( index );
const auto originalText = sourceModel()->data( sourceIndex, Qt::DisplayRole ).toString();
const auto newText = QString( "%1 [TEST]" ).arg( originalText );
return newText;
}
// Usage
auto yourModel = YourOriginalModel( this );
auto proxy = MyProxyModel( this );
proxy->setSourceModel( yourModel );
view->setModel( proxy );
Related
I have a tableView that has a column that is using a comboBox. I need to fill the comboBox using the delegate class with the data from the model class. I was using signals and slots for this task but I know there is a method using data.
This is how i create and fill the comboBox. I need to get the file row directly from the model class without already storing it in the delegate.
QWidget *CDelegate :: createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const
{
if(index.column() == COL_ComboBox)
{
QComboBox *editor = new QComboBox(parent);
for(int i=0; i<file.at(index.row()).size(); i++)
editor -> addItem(file.at(index.row()).at(i))
return editor;
}
...
}
As I understand, you want to fill QComboBox with data from model of your QTableView. As you see, const QModelIndex & index parameter in createEditor function provides you access to this model. Look for method model of QModelIndex class. Thats' why, your createEditor function may be like this:
QWidget *CDelegate :: createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const
{
if(index.column() == COL_ComboBox)
{
QComboBox *editor = new QComboBox(parent);
const QAbstractItemModel *model = index.model();
while(/*condition*/)
{
// take data from model
// QVariant dt = model->data(...);
// fill editor with data from dt
// editor->addItem(...)
}
return editor;
}
...
}
I defined pure virtual method QStyledItemDelegate::paint as:
void FooViewDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
bool selected = option.state & QStyle::State_Selected;
// ...
// drawing code
}
But I cant't figure how to know is the drawing item current or no (The same item as from QListView::currentIndex()).
Qt MVC is not designed for such usecases, because, theoretically, delegate should not know, what view you are using (it may be QListView or QTableView).
So, a "good way" is to keep this information inside your delegate (because model may be used by sevaral views). Fox example (pseudo-code):
class FooViewDelegate : ...
{
private:
QModelIndex _currentIndex;
void connectToView( QAbstractItemView *view )
{
connect( view, &QAbstractItemView::currentChanged, this, &FooViewDelegate ::onCurrentChanged );
}
void onCurrentChanged( const QModelIndex& current, const QModelIndex& prev )
{
_currentIndex = current;
}
public:
void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
bool selected = index == _currentIndex;
// ...
// drawing code
}
}
The parent of the delegate is the view, you can directly obtain the current index from the view.
void FooViewDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const
{
bool selected = index == parent()->currentIndex();
}
You're along the right track:
auto current = option.state & QStyle::State_HasFocus;
The item with focus is the current item.
I read an older post however that is not work for me.
I would like to set the background color of every row whose 6th argument is true.
I tried to overwrite the Paint method in my subclass of QSqlRelationalDelegate but apparently it does not do anything.
MoviesDelegate::MoviesDelegate(QObject *parent)
: QSqlRelationalDelegate(parent)
{ }
void MoviesDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if( index.sibling( index.row(), 6 ).data().toBool() )
{
QStyleOptionViewItemV4 optionViewItem = option;
optionViewItem.backgroundBrush = QBrush( Qt::yellow );
drawDisplay( painter, optionViewItem,
optionViewItem.rect,index.data().toString() );
drawFocus( painter, optionViewItem, optionViewItem.rect);
}
else
QSqlRelationalDelegate::paint(painter, option, index);
}
How can I fix it?
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
// Grab cell value and cast it to boolean
bool boolValue = index.model()->data(index).toBool();
// Paint cell background depending on the bool value
if(boolValue)
painter->fillRect(option.rect, QColor(179, 229, 255));
else
painter->fillRect(option.rect, Qt::red);
// Paint text
QStyledItemDelegate::paint(painter, option, index);
}
I have reimplemented paint() function for QTreeWidget, I want to show data of second column bold, but it doesn't work.
How can i fix it?
void extendedQItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
const QString txt = index.data().toString();
painter->save();
QFont painterFont;
if (index.column() == 1) {
painterFont.setBold(true);
painterFont.setStretch(20);
}
painter->setFont(painterFont);
drawDisplay(painter, option, rect, txt);
painter->restore();
}
I attached a screen shot of the problem, second half should be bold
You forgot to add your extendedQItemDelegate to the QTreeView/QTreeWidget object via the setItemDelegate member function.
As an example:
QTreeWidget* tree_view = ...;
extendedQItemDelegate* extended_item_delegate = ...;
tree_view->setItemDelegate(extended_item_delegate);
You need to make a copy of the const QStyleOptionViewItem &option, apply your font changes to that copy, then paint using your copy instead of the original option passed to the function.
void extendedQItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
const QString txt = index.data().toString();
painter->save();
QStyleOptionViewItem optCopy = option; // make a copy to modify
if (index.column() == 1) {
optCopy.font.setBold(true); // set attributes on the copy
optCopy.font.setStretch(20);
}
drawDisplay(painter, optCopy, rect, txt); // use copy to paint with
painter->restore();
}
(Just realized this is an old question but it had popped up to the top of qt tags.)
I'm subclassing QAbstractItemDelegate. This is my code. Suggestions are welcome:
QWidget *ParmDelegate::createWidget(Parm *p, const QModelIndex &index) const {
QWidget *w;
if (index.column() == 0) {
w = new QLabel(p->getName().c_str());
} else {
if (p->isSection())
return NULL;
w = p->createControl();
}
return w;
}
QWidget *ParmDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {
cout << "createEditor called" << endl;
Parm *p = reinterpret_cast<Parm*>(index.internalPointer());
QWidget *retval = createWidget(p, index);
retval->setFocusPolicy(Qt::StrongFocus);
retval->setParent(parent);
return retval;
}
void ParmDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const {
QRect rect(option.rect);
editor->setGeometry(QRect(QPoint(0,0), rect.size()));
}
void ParmDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
Parm *p = reinterpret_cast<Parm*>(index.internalPointer());
scoped_ptr<QWidget> w(createWidget(p, index));
if (!w)
return;
QRect rect(option.rect);
w->setGeometry(QRect(QPoint(0,0), rect.size()));
w->render(painter, rect.topLeft());
}
QSize ParmDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
Parm *p = reinterpret_cast<Parm*>(index.internalPointer());
scoped_ptr<QWidget> w(createWidget(p, index));
if (!w)
return QSize(0,0);
return w->sizeHint();
}
bool ParmDelegate::editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index ) {
cout << "editorEvent called" << endl;
return false;
}
When this is run, I only see that editorEvent gets called twice for every edit event -- no createEditor!
From Qt's AbstractItemDelegate documentation:
To provide custom editing, there are two approaches that can be used. The first approach is to create an editor widget and display it directly on top of the item. To do this you must reimplement createEditor() to provide an editor widget, setEditorData() to populate the editor with the data from the model, and setModelData() so that the delegate can update the model with data from the editor.
The second approach is to handle user events directly by reimplementing editorEvent().
This appears to say that you are missing something to trigger the first approach. My guess is that your model's data() function isn't returning the proper value for the Qt::EditRole option.
I had implemented a TableView which i had inhertied from QItemDelegate. Then i had similar problem. I tracked it down to not calling 'return QItemDelegate::editorEvent(event, model, option, index);' in the editorEvent(...) method.
You can try this. Maybe it helps.