How to indent the title of a QDockWidget using my own style - qt

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, &copy, 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.

Related

Change height and icon position of QToolBox tabs, using proxys

Ok, so I want to customize the height and the position of icons of the tabs from a QToolBox widget. I am not able to do it using stylesheet (I tried different options, but none worked, see question: Customizing QToolBox: tab height), so I decided to give it a try to using a proxy. I know there are the controlElements:
CE_ToolBar
CE_ToolBoxTabShape
CE_ToolBoxTabLabel
And so I can use them on the drawControl function to get what I want.
void drawControl(ControlElement oCtrElement,
const QStyleOption *poStyleOptionption,
QPainter *poPainter,
const QWidget *poWidget) const
But I am not sure how to implement it... Any help or example?
Thanks,
UPDATE:
I have created the proxy, assigned it to the qtoolbox and added conditions to see if I could do something:
...
ToolBoxProxy* tabStyle = new ToolBoxProxy();
ui->toolBox->setStyle(tabStyle);
...
toolboxproxy.h
#include <QProxyStyle>
#include <QPainter>
class ToolBoxProxy : public QProxyStyle
{
public:
explicit ToolBoxProxy();
void drawControl(ControlElement oCtrElement, const QStyleOption * poStylrOptionption, QPainter * poPainter, const QWidget * poWidget = 0) const;
};
toolboxproxy.cpp
#include "toolboxproxy.h"
#include <QDebug>
ToolBoxProxy::ToolBoxProxy(){
}
void ToolBoxProxy::drawControl(ControlElement oCtrElement, const QStyleOption *poStyleOptionption, QPainter *poPainter, const QWidget *poWidget) const
{
if (oCtrElement == CE_ToolBar) {
qDebug() << "CE_ToolBar";
} else if (oCtrElement == CE_ToolBoxTabShape) {
qDebug() << "CE_TOOLBOXTABSHAPE";
} else if (oCtrElement == CE_ToolBoxTabLabel) {
qDebug() << "CE_ToolBoxTabLabel";
}
QProxyStyle::drawControl(oCtrElement, poStyleOptionption, poPainter, poWidget);
}
I don't know why, but the software never prints out any of the 3 qDebug()... but if I set a breakpoint, it only reaches the function when the control element is any of those:
CE_ItemViewItem
CE_ShapedFrame
CE_ToolButtonLabel

Qt: QTreeView: center icon on header

For some columns from a QTreeView widget, I use an icon. The icon is set on
QVariant headerData (int section, Qt::Orientation orientation, int role) const{
if(role == Qt::DecorationRole)
{
QIcon icon;
if (section == 0) {
icon.addFile(":/icon1");
} else if (section == 1){
icon.addFile(":/icon2");
}
}
if(role == Qt::TextAlignmentRole)
{
return (Qt::AlignLeft + Qt::AlignVCenter);
}
The header looks like this:
I want to align the icons with the text. TextAlignmentRole only works for text, but not for the icons. How can I do that?
I also tried by setting the default alignment:
m_treeview->header()->setDefaultAlignment(Qt::AlignCenter); but no luck.
In order to center icon with text you will have to implement your own proxy style to create this specific style behavior.
#include <QProxyStyle>
#include <QPainter>
class HeaderProxyStyle : public QProxyStyle
{
public:
void drawControl(ControlElement oCtrElement, const QStyleOption * poStylrOptionption, QPainter * poPainter, const QWidget * poWidget = 0) const;
};
Center icon with text implementation
void HeaderProxyStyle::drawControl(ControlElement oCtrElement, const QStyleOption *poStylrOptionption, QPainter *poPainter, const QWidget *poWidget) const
{
// Header label?
if (oCtrElement == CE_HeaderLabel) {
// YES - Allocate style option header
QStyleOptionHeader *poStyleOptionHeader =
(QStyleOptionHeader *) poStylrOptionption;
// Get header icon
QIcon oIcon = qvariant_cast<QIcon>(poStyleOptionHeader->icon);
// Icon is valid?
if(oIcon.isNull()){
// No - Draw text header
QProxyStyle::drawControl(oCtrElement, poStylrOptionption, poPainter, poWidget);
return;
}
// Set icon size 16x16
QSize oIconSize = QSize(16,16);
// Get header section rect
QRect oRect = poStyleOptionHeader->rect;
// Create header icon pixmap
QPixmap oIconPixmap = oIcon.pixmap(oIconSize.width(),oIconSize.height());
// Calculate header text width
int iTextWidth = poStyleOptionHeader->fontMetrics.width(poStyleOptionHeader->text);
QRect oCenterRec = QRect(oRect.left(),
oRect.top() + (oRect.height - iTextSize)/2,
oIconPixmap.width(),oIconPixmap.height());
QRect oTextRect = QRect(oCenterRec.left()+ oIconSize.width(),
oCenterRec.top(), oCenterRec.width() + iTextWidth, oCenterRec.height());
// Draw icon
poPainter->drawPixmap(oCenterRec, oIconPixmap);
// Draw text
poPainter->drawText(oTextRect, poStyleOptionHeader->text);
return;
}
QProxyStyle::drawControl(oCtrElement, poStylrOptionption, poPainter, poWidget);
}
Then apply this header style in your tree view
// Set header style
m_treeview->header()->setStyle(&m_oHeaderStyle);

Qt: QHeaderView place sort-indicator on the right of the header text

If I set: QHeaderView::down-arrow { subcontrol-position: center left}, the down-arrow is on the left of the column, and if I set center right, it is placed on the right of the column, but I want to place the arrow next to the title (on the right side).
You need to set subcontrol-origin: margin | border | padding | content;
Look into below documentation link to understand box model ( which explains the margin rectangle, the border rectangle, the padding rectangle, and the content rectangle).
http://doc.qt.io/qt-5/stylesheet-customizing.html#the-box-model
So try adding subcontrol-origin:padding to your code, which may add next to your content.
Try something like below:
QHeaderView::down-arrow { subcontrol-origin:padding; subcontrol-position: center right;}
The way I sorted was by creating a QProxyStyle for the header, and override drawControl.
I also assigned an empty icon to hide the default one.
treeviewwidget.cpp at initialize():
treeviewHeaderProxy* m_oHeaderStyle = new treeviewHeaderProxy();
treeview->header()->setStyle(m_oHeaderStyle);
treeview->header()->setDefaultAlignment(Qt::AlignCenter);
treeview->header()->setStyleSheet("QHeaderView::down-arrow { image: url(:/shared/empty); }"
"QHeaderView::up-arrow { image: url(:/shared/empty); } ");
treeviewHeaderProxy.h:
class treeviewHeaderProxy : public QProxyStyle
{
public:
explicit treeviewHeaderProxy();
void drawControl(ControlElement oCtrElement, const QStyleOption * poStylrOptionption, QPainter * poPainter, const QWidget * poWidget = 0) const;
};
treeviewHeaderProxy.cpp:
void treeviewHeaderProxy::drawControl(ControlElement oCtrElement, const QStyleOption *poStyleOptionption, QPainter *poPainter, const QWidget *poWidget) const
{
// Header label?
if (oCtrElement == CE_HeaderLabel) {
QStyleOptionHeader *poStyleOptionHeader = (QStyleOptionHeader *) poStyleOptionption;
QStyleOptionHeader::SortIndicator sortOption = poStyleOptionHeader->sortIndicator;
QRect oRect = poStyleOptionHeader->rect;
// Text
int iTextWidth = poStyleOptionHeader->fontMetrics.width(poStyleOptionHeader->text);
int iTextHeight = poStyleOptionHeader->fontMetrics.height();
QRect oTextRect = QRect(oRect.left() + oRect.width(), catRect.top() + (oRect.height() - iTextHeight)/2,
iTextWidth*1.2, iTextHeight);
poPainter->setPen(SUPER_DARK_GREY);
poPainter->drawText(oTextRect, poStyleOptionHeader->text); // Draw text
// Sort Indicator
QPixmap oSortPixmap;
switch(sortOption){
case QStyleOptionHeader::SortDown:
oSortPixmap = QIcon(":/shared/drop_up_grey").pixmap(10,10);
break;
case QStyleOptionHeader::SortUp:
oSortPixmap = QIcon(":/shared/drop_down_grey").pixmap(10,10);
break;
}
if(!oSortPixmap.isNull()){
QRect oSortRect = QRect(oTextRect.left() + oTextRect.width(), oRect.top() + (oRect.height() - oSortPixmap.height())/2,
oSortPixmap.width(), oSortPixmap.height());
poPainter->drawPixmap(oSortRect, oSortPixmap); // Draw sortIcon
}
return;
}
QProxyStyle::drawControl(oCtrElement, poStyleOptionption, poPainter, poWidget);
}

QTreeWidgetItem color

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

Painting QPixmap in the center of QTableView cell

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.

Resources