I am using the following stylesheet on a QTreeWidget to change the items style:
QTreeWidget::item
{
padding-left:10px;
padding-top: 1px;
padding-bottom: 1px;
border-left: 10px;
}
After that, I am trying to use the following code to change the color of some specific cells:
// item is a QTreeWidgetItem
item->setBackgroundColor(1, QColor(255, 129, 123));
But the color is not changing. I then discovered that, if I remove the stylesheet from the QTreeWidget, then the color change works.
Any idea how to make the background color change to work keeping the stylesheet?
Use a custom delegate to paint your items instead of stylesheets.
Reimplement the paint() method to control the way how are the items drawn:
class CMyDelegate : public QStyledItemDelegate
{
public:
CMyDelegate(QObject* parent) : QStyledItemDelegate(parent) {}
void CMyDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
}
void CMyDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyleOptionViewItemV4 itemOption(option)
initStyleOption(&itemOption, index);
itemOption.rect.adjust(-10, 0, 0, 0); // Make the item rectangle 10 pixels smaller from the left side.
// Draw your item content.
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter, nullptr);
// And now you can draw a bottom border.
painter->setPen(Qt::black);
painter->drawLine(itemOption.rect.bottomLeft(), itemOption.rect.bottomRight());
}
And this is how to use your delegate:
CMyDelegate* delegate = new CMyDelegate(tree);
tree->setItemDelegate(delegate);
More documentation here: http://doc.qt.io/qt-5/model-view-programming.html#delegate-classes
Related
I have a custom QStyledItemDelegate that paints a QPixmap in a particular column. When that cell is hovered over with the mouse, I would like to paint it differently.
Below is my paint event, which does paint the cell correctly when not State_MouseOver. However, it changes the color when I hover anywhere on the row. How can I make it change only when the mouse is hovering over the cell with the pixmap in it?
void myDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
switch(index.column()) {
case DAY_COLUMN:
{
QSize btnSize = QSize(option.rect.height() * .9, option.rect.height() * .9);
QRect r = option.rect;
int x = r.right() - btnSize.width() - 10;
int y = r.top();
QRect btnRect = QRect(x, y, btnSize.width(), btnSize.height());
QPixmap pixmap(":/icons/edit.png");
// If hovered over, change color.
if(option.state & QStyle::State_MouseOver) {
auto mask = pixmap.createMaskFromColor(QColor("Black"), Qt::MaskOutColor);
pixmap.fill(QColor("Red"));
pixmap.setMask(mask);
}
painter->drawPixmap(btnRect, pixmap, pixmap.rect());
return;
}
/*.... draw other column(s) as appropriate ...*/
}
}
I'm using this delegate on all rows withing a QTreeView.
Qt 5.12
It can be because the selection behavior of the QTreeView is QAbstractItemView::SelectRows by default.
You can change it using:
m_tree_view.setSelectionBehavior(QAbstractItemView::SelectItems);
See more:
QAbstractItemView::SelectionBehavior
QTreeView source code
I have a QTableView that works very well, the first column holds some thumbnails, in each cell of this column the thumbnails are vertically centered, but not horizontally centered.
Do I really need to use a delegate?
If yes, How to center them horizontally using QStyledItemDelegate?
Construct your own delegate and inherit QStyledItemDelegate. Override the paint method.
Then do something like this:
void
MyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
QPixmap pixmap;
pixmap.load("Your pixmap file path");
pixmap = pixmap.scaled(option.rect.width(), option.rect.height(), Qt::KeepAspectRatio);
// Position our pixmap
const int x = option.rect.center().x() - pixmap.rect().width() / 2;
const int y = option.rect.center().y() - pixmap.rect().height() / 2;
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
painter->drawPixmap(QRect(x, y, pixmap.rect().width(), pixmap.rect().height()), pixmap);
}
Drawing by yourself is not necessary, but a custom delegate - is. The styled item delegate uses the style's control element drawing code to draw a CE_ItemViewItem - see the source code for Qt 5.5.0. The drawing code takes the style option's decorationAlignment member into account. Unfortunately, there's no data role that would pass that alignment to the styles's implementation. Instead, you have to override the alignment in your delegate:
class DecorationAligningDelegate : public QStyledItemDelegate {
Q_OBJECT
Qt::Alignment const m_alignment;
public:
explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject * parent = 0) :
QStyledItemDelegate(parent), m_alignment(alignment) {}
Qt::Alignment alignment() const { return m_alignment; }
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
auto opt = option;
opt.decorationAlignment = m_alignment;
QStyledItemDelegate::paint(painter, opt, index);
}
};
Then, to center the thumbnails:
view.setItemDelegateForColumn(0,
new DecorationAligningDelegate(Qt::AlignHCenter, &view));
//or
view->setItemDelegateForColumn(0,
new DecorationAligningDelegate(Qt::AlignHCenter, view));
If you really wished to paint it all yourself, even though it's unnecessary, the rectangle of the item to be painted is given in the style option (option.rect). To draw the pixmap centered in the item's rectangle, you could do as follows:
QStyleOption option;
QPixmap pix;
QPainter painter;
...
painter.save();
auto loc = option.rect.center() - pix.rect().center()
painter.drawPixmap(loc, pix);
painter.restore();
I will just leave my version that literary is a combination of the two answers.
class DecorationAligningDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit DecorationAligningDelegate(Qt::Alignment alignment, QObject *parent = nullptr)
: QStyledItemDelegate(parent), m_alignment(alignment) {}
Qt::Alignment alignment() const { return m_alignment; }
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QIcon icon = QIcon(qvariant_cast<QIcon>(index.data(Qt::DecorationRole)));
if (option.state & QStyle::State_Selected)
{
painter->fillRect(option.rect, option.palette.highlight());
}
icon.paint(painter, option.rect, m_alignment);
}
private:
Q_DISABLE_COPY(VDecorationAligningDelegate)
Qt::Alignment const m_alignment;
};
I assume you define your item like this:
auto *item = new QTableWidgetItem();
item->setIcon(QIcon("Your pixmap file path"));
Don't forget about setting a delegate.
I want to indent the title of a QDockWidget w/o adding spaces.
#include <QStyle>
#include <QProxyStyle>
#include <iostream>
class my_style : public QProxyStyle
{
Q_OBJECT
public:
my_style (QStyle* style = 0) :
QProxyStyle (style)
{
}
virtual ~my_style ()
{
}
virtual QRect subElementRect (SubElement element, const QStyleOption * option, const QWidget * widget = 0) const
{
QRect rect = QProxyStyle::subElementRect (element, option, widget);
if (element == QStyle::SE_DockWidgetTitleBarText)
{
rect.adjust (50, 0, 0, 0);
}
//std::cerr << "debug:" << element << std::endl;
return rect;
}
};
I have no clue why but when I apply my style it's never running into that if. If I debug the method I only get an output for two different elements which are the buttons in the title bar.
subElementRect is not called to get the title area for all styles. At least, XP, Vista and MacOSX styles are using directly QStyleOption::rect which is passed as parameter to the drawControl function for CE_DockWidgetTitle.
To handle both cases, you should also reimplement drawControl:
void drawControl(ControlElement element, const QStyleOption *option,
QPainter *painter, const QWidget *widget) const
{
const QStyleOptionDockWidget *dockWidget;
if(element == CE_DockWidgetTitle &&
(dockWidget = qstyleoption_cast<const QStyleOptionDockWidget *>(option)))
{
QStyleOptionDockWidget copy = *dockWidget;
copy.rect.adjust(50,0,0,0);
// or you can add spaces in the title to avoid the border moving left too
// copy.title = QString(50 / copy.fontMetrics.width(" "), QChar(' ')) + copy.title;
QProxyStyle::drawControl(element, ©, painter, widget);
return;
}
QProxyStyle::drawControl(element, option, painter, widget);
}
Alternatively you could use a style sheet, with a padding or a margin:
dockWidget->setStyleSheet("::title { position: relative; padding-left: 50px;"
" text-align: left center }");
The "position" rule does nothing, but is necessary, because strangely the style is only applied if some other rule categories are also present.
The text needs to be vertically realigned too because the alignment seem to be lost when using a style sheet.
How can I set the cell borders in a QTableWidget to 0px? Preferably I can choose which sides of a cell's border to set to 0, but I can live with setting them all to 0 as well.
EDIT: Either setting the border to 0px or setting the color to white would be good as well.
You can disable all the borders with QTableWidget::setShowGrid, and re-enable some of them with a style sheet (for instance: "QTableView::item { border-left: 1px solid black; }")
The latter are cell interior borders, so they might not be as well aligned as the grid.
If you want to change the borders individually for each cell, you need to write a delegate (like in that answer).
check if QTableWidget's setShowGrid would work for you, smth like this:
tableWidget->setShowGrid(false);
hope this helps, regards
The easiest way to do it for me without affecting widget's focus policy and using qss is to create the following custom delegate and install it for table:
*.h:
class FocusControlDelegate : public QStyledItemDelegate {
public:
FocusControlDelegate(QObject *parent = 0);
virtual void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const;
void setFocusBorderEnabled(bool enabled);
protected:
bool f_focus_border_enabled;
};
*.cpp:
FocusControlDelegate::FocusControlDelegate(QObject *parent) : QStyledItemDelegate(parent) {
f_focus_border_enabled = false;
}
void FocusControlDelegate::setFocusBorderEnabled(bool enabled) {
f_focus_border_enabled = enabled;
}
void FocusControlDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const {
QStyledItemDelegate::initStyleOption(option, index);
if(!f_focus_border_enabled && option->state & QStyle::State_HasFocus)
option->state = option->state & ~QStyle::State_HasFocus;
}
I want to be able to paint on top of my application's window so that I can annotate all the widgets with some extra diagnostic information, similar to the CSS developer tools in Firefox (eg add widget classes, styles, highlight borders etc).
I can walk the widget tree and extract the relevant information, but the question is how can I overlay all the application windows with this information?
One way would be to override my QMainWindow's paint event, but this has to be done for all top level windows. Is there an alternative method where you can paint on the QDesktopWidget for instance? Or any hooks into each QWidget's paint method? Anything that involves subclassing QWidget itself won't work with the standard widgets.
This follows on from my previous question:
Are there any useful tools for diagnosing Qt layout and spacing problems?
cheers
Mandrill
EDIT:
Thanks to Dmitry I've now got a really simple method that is easily extensible:
class DiagnosticStyle : public QWindowsVistaStyle
{
Q_OBJECT
public:
typedef QWindowsVistaStyle BaseStyle;
void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const;
};
void DiagnosticStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
{
BaseStyle::drawControl(element, option, painter, widget);
if (widget && painter) {
// draw a border around the widget
painter->setPen(QColor("red"));
painter->drawRect(widget->rect());
// show the classname of the widget
QBrush translucentBrush(QColor(255,246,240, 100));
painter->fillRect(widget->rect(), translucentBrush);
painter->setPen(QColor("darkblue"));
painter->drawText(widget->rect(), Qt::AlignLeft | Qt::AlignVCenter, widget->metaObject()->className());
}
}
qApp->setStyle(new DiagnosticStyle());
You can create own style class based on QMotifStyle or other ... and paint on any widget/control related to him information.
void MyStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option,QPainter *painter, const QWidget *widget) const
{
QStyle::State flags = option->state;
QRect rect = option->rect;
QPalette pal = option->palette;
QBrush brush;
switch (element)
{
case PE_FrameTabWidget:
{
painter->save();
// for example: draw anything on TabWidget
painter->drawPixmap(rect,centerPm,centerPm.rect());
painter->restore();
}
break;
default:
QMotifStyle::drawPrimitive(element, option, painter, widget);
break;
}
}
Somewhere in Qt5 the styles (GTK, Windows, etc) were made internal. Now you need to use QCommonStyle.
If anyone's wondering how to do this with Qt5+. Here's a self-contained version of #the_mandrill's code above.
class DiagnosticStyle : public QCommonStyle
{
Q_OBJECT
public:
typedef QStyle BaseStyle;
void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const
{
QCommonStyle::drawControl(element, option, painter, widget);
if (widget && painter) {
// draw a border around the widget
painter->setPen(QColor("red"));
painter->drawRect(widget->rect());
// show the classname of the widget
QBrush translucentBrush(QColor(255,246,240, 100));
painter->fillRect(widget->rect(), translucentBrush);
painter->setPen(QColor("darkblue"));
painter->drawText(widget->rect(), Qt::AlignLeft | Qt::AlignVCenter, widget->metaObject()->className());
}
};
};
Then, in your main window constructor call
qApp->setStyle(new DiagnosticStyle());