QTreeView selection clears text color - c++

I have subclassed QTreeView and made model subclassed from QAbstractTableModeland everything works fine. If something is being changed in QTreeView from code (not by user), then that row's text color becomes red. I have implemented this trough checking Qt::TextColorRole from data() function and returning Qt::red.
But if that particular row is being selected, then text color changes automatically to black (and background color to light green, which is normal). After deselecting that row everything is OK again. In debug mode I've seen that data() function returns true value for selected row (Qt::red).
Now how can I solve this problem, what may cause to this incorrect behaviour?
Thank you in advance!

I've found a way of doing this trough a delegate. Here is the code
class TextColorDelegate: public QItemDelegate
{
public:
explicit TextColorDelegate(QObject* parent = 0) : QItemDelegate(parent)
{ }
void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyleOptionViewItem ViewOption(option);
QColor itemForegroundColor = index.data(Qt::ForegroundRole).value<QColor>();
if (itemForegroundColor.isValid())
{
if (itemForegroundColor != option.palette.color(QPalette::WindowText))
ViewOption.palette.setColor(QPalette::HighlightedText, itemForegroundColor);
}
QItemDelegate::paint(painter, ViewOption, index);
}
};
And for using delegate you should write something like this
pTable->setItemDelegate(new TextColorDelegate(this));
where pTable's type is QTableView*;

Does the following code keep your text color red?
QPalette p = view->palette();
p.setColor(QPalette::HighlightedText, QColor(Qt::red));
view->setPalette(p);

Related

QTreeView items spacing

How can I set some space between QTreeView items?
qss margin/paddin - cant use, it's just make item higher (looks bad when selected).
Delegate - Cant too, it's breaks branch element. I tried inherit QTreeView and make something in child, but nothing (and it's my best variant now).
class Vias: public {
public:
Vias(QWidget *parent) : QTreeView(parent)
{}
protected:
QRect visualRect(const QModelIndex &index) const override
{
QRect rect = QTreeView::visualRect(index);
return QRect({QPoint{rect.x(),rect.y()+6},QSize{rect.width(),rect.height()-12}});
}
};
I'm also using delegate, where adding 12 to height. Result. Some glitches here

Why do I not see the drop indicator in a QTableView?

I use drag and drop in my QTableView (works). However, I do not see any drop indicator. I should see a line where the drop is supposed to be inserted, shouldn't I? At least here they say so.
My init is pretty much standard.
// see model for implementing logic of drag
this->viewport()->setAcceptDrops(allowDrop);
this->setDragEnabled(allowDrag);
this->setDropIndicatorShown(true);
this->m_model->allowDrop(allowDrop);
I have no idea why I do not see the indicator. A style sheet is used with the views, could that be the reason. However, I have disabled the stylesheet and still do not see it.
The view uses entire rows for selection, not sure if this causes an issue. So any hint is appreciated.
-- Edit --
As of the comment below, tried all selection modes: single, multi or extended, no visual effect. Also tried cell instead of row selection, again no improvement.
-- Edit 2 --
Currently evaluating another style proxy example, similar to the one below, originally referenced here
-- Related --
QTreeView draw drop indicator
How to highlight the entire row on mouse hover in QTableWidget: Qt5
https://forum.qt.io/topic/12794/mousehover-entire-row-selection-in-qtableview/7
https://stackoverflow.com/a/23111484/356726
I faced the same problem, I tried two options which both worked for me. IIRC, the help came from an answer on SO.
if you are subclassing QTreeView, you can override its paintEvent() method. It is calling by default the drawTree() method and the paintDropIndicator() one (the latter being part of QAbstractItemView private class).
You can call drawTree() from your paintEvent(), and it should override the default drag and drop indicator as well :
class MyTreeView : public QTreeView
{
public:
explicit MyTreeView(QWidget* parent = 0) : QTreeView(parent) {}
void paintEvent(QPaintEvent * event)
{
QPainter painter(viewport());
drawTree(&painter, event->region());
}
};
the other method is to subclass QProxyStyle and overriding the drawPrimitive() method. When you get the element QStyle::PE_IndicatorItemViewItemDrop as a parameter, you can paint it your own way.
The code will look like this:
class MyOwnStyle : public QProxyStyle
{
public:
MyOwnStyle(QStyle* style = 0) : QProxyStyle(style) {}
void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
{
if (element == QStyle::PE_IndicatorItemViewItemDrop)
{
//custom paint here, you can do nothing as well
QColor c(Qt::white);
QPen pen(c);
pen.setWidth(1);
painter->setPen(pen);
if (!option->rect.isNull())
painter->drawLine(option->rect.topLeft(), option->rect.topRight());
}
else
{
// the default style is applied
QProxyStyle::drawPrimitive(element, option, painter, widget);
}
}
};

Delegate erasing text in QTreeView using QStandardItemModel

I'm having some difficulty adding a delegate to my QTreeView.
I have added some QStandardItems through a model which works fine, but when I add the delegate, the text is erased and only the icons are visible.
This is the code i'm using for my delegate:
void SeqNavDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() == 0 && option.state & QStyle::State_Enabled)
{
const QIcon icon(QLatin1String(":/SeqNavMenu/images/green.png"));
QRect iconRect(option.rect.right() - option.rect.height(),
option.rect.top(),
option.rect.height(),
option.rect.height());
icon.paint(painter, iconRect, Qt::AlignRight);
}
}
What i would like to do is combine the two, which is to say, have the text and checkboxes, and to the right have the icons that i have put in the delegate.
Maybe someone can point me in the right direction here ?
Cheers.
When you assign a delegate to a view, the view stops rendering items by itself (actually it does it with another delegate which is replaced by yours). So it delegates the rendering to you. And you programed the delegate to draw icons only. Thats why you see only icons.
If you need to draw a check-box and a text as well you need to draw it by yourself or call the ancestors method paint somewhere in your implementation. So if you inherited SeqNavDelegate from QStyledItemDelegate call :
QStyledItemDelegate::paint(painter, option, index);

How to set a delegate for a single cell in Qt item view?

Rather perplexed by this omission--but in Qt's QAbstractItemView class, it's possible to set a QAbstractItemDelegate (i.e., QItemDelegate or QStyledItemDelegate) to the entire view, a single row, or a single column, using the setItemDelegate* methods. In addition the item delegate for an individual cell can be queried, with QAbstractItemView::itemDelegate(const QModelIndex&), along with the delegate for rows, columns. and the entire view. But there appears to be no way to set an item delegate to an individual cell. Am I missing something? Any reason this should be?
No you can't set item delegate only for one cell or one column but you can easly set item delegate for whole widget and choose in which cell, column or row you want to use your custom painting or something.
For e.g.
void WidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
if (index.column() == 1)
{
// ohh it's my column
// better do something creative
}
else // it's just a common column. Live it in default way
QItemDelegate::paint(painter, option, index);
}
You can find some more information here
I'd recommend reimplementing createEditor function instead:
QWidget * WidgetDelegate::createEditor(
QWidget *parent,
const QStyleOptionViewItem &,
const QModelIndex &index) const
{
QWidget *widget = 0;
if (index.isValid() && index.column() < factories.size())
{
widget = factories[index.column()]->createEditor(index.data(Qt::EditRole).userType(), parent);
if (widget)
widget->setFocusPolicy(Qt::WheelFocus);
}
return widget;
}
Sinc Qt 4.2, QAbstractItemView provides setItemDelegateForColumn() and setItemDelegateForRow().

In a QStyledItemDelegate for a QListWidget, how do I paint the selected item to match the standard list?

I have a QStyledItemDelegate for my QList widget:
class MappingDisplayWidgetDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
MappingDisplayWidgetDelegate(QObject *parent=NULL);
virtual void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
virtual QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
};
and it's paint method looks like:
void MappingDisplayWidgetDelegate::paint ( QPainter * painter,
const QStyleOptionViewItem & option,
const QModelIndex & index ) const
{
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, option.palette.highlight());
else if (option.state & QStyle::State_MouseOver)
painter->fillRect(option.rect, option.palette.midlight());
QFontMetrics fm(option.font);
QString filename = index.data(Qt::DisplayRole).toString();
QRect outline = option.rect;
int outmid = outline.center().y();
QRect fnBound = fm.boundingRect(filename);
int fnBoundMid = fnBound.center().y();
fnBound.moveLeft(outline.left());
fnBound.translate(0, outmid - fnBoundMid);
painter->drawText(fnBound, Qt::AlignVCenter | Qt::AlignLeft, filename);
}
Now this works, BUT the handling of the State_Selected and State_MouseOver doesn't produce the same results as the default lists. Here's a screen shot (I'm running on a Win7 system) of the code above on the left, and a standard QListWidget on the right. You can see that the QListWidget has nice gradients, while my items have only simple colors.
I want to paint my items to properly match the standard widgets, but I can't quite figure out how. I'm not seeing anything in the options that would give me the info I need.
Edited to add:
Note that this is a somewhat toy example. The real code has a bunch of other stuff besides the one string, and has an appropriate (and working) sizeHint function. This is just a toy to demonstrate the problem.
In the source code for drawing that element, the following call is made:
// draw the background
proxy()->drawPrimitive(PE_PanelItemViewItem, opt, p, widget);
I suspect you may want to do something similar to the following:
style().drawPrimitive(QStyle::PE_PanelItemViewItem, option, painter, NULL);
You may have to play around with the option parameter to get the right rectangle target. Unfortunately, the computer I am using right now isn't set up to test any of this, so YMMV.
I had the same question--I wanted to have a totally custom paint function, but take advantage of the built-in pretty selection painting. I've tried the below code, and so far I'm finding that it works great with no negative consequences:
void MassIndexItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex& index) const {
QStyledItemDelegate::paint(painter, option, QModelIndex());
//Above, note that rather than passing >index< to the inherited function, I pass a default, invalid index.
//Since my model's data() function returns an empty QVariant for invalid indexes,
//I get the default selection painting, but no other data is painted in any way.
//Below, I can do whatever painting I want and it will show up on top of the pretty selection paint
painter->drawEllipse(option.rect);
}
I'm detailing this as a usable solution, though it's not a great one.
If I don't put anything in the DisplayRole of the item, then I can call the underlying QStyledItemDelegate::paint routine and it will simply draw the proper background, thusly:
void MappingDisplayWidgetDelegate::paint ( QPainter * painter,
const QStyleOptionViewItem & option,
const QModelIndex & index ) const
{
QStyledItemDelegate::paint(painter, option, index);
QFontMetrics fm(option.font);
QString filename = index.data(Qt::UserRole).toString();
QRect outline = option.rect;
int outmid = outline.center().y();
QRect fnBound = fm.boundingRect(filename);
int fnBoundMid = fnBound.center().y();
fnBound.moveLeft(outline.left());
fnBound.translate(0, outmid - fnBoundMid);
painter->drawText(fnBound, Qt::AlignVCenter | Qt::AlignLeft,
filename);
}
I don't like this, and I'm going to be trying Dave's suggestion momentarily, but if one gets stuck, this does seem to work. On the down side it means you can't use the DisplayRole, so you can't use findItems in the QListWidget.