I can not correctly align icons and button texts.
I have generate the app gui with Designer, by default they appear like this:
I add some code,
win.pb_ejecutar.setStyleSheet("QPushButton { text-align: left; }")
And I've got this
but what I need would be this, icon left align and text center align
I've done it by adding spaces to the name, but I find it not very elegant
Anyone help me ?? Thanks
The alignment between the icon and the text are the same, so there is no solution with Qt Style Sheet, so the other alternative is to use a QProxyStyle:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class ProxyStyle(QtWidgets.QProxyStyle):
def drawControl(self, element, option, painter, widget=None):
if element == QtWidgets.QStyle.CE_PushButtonLabel:
icon = QtGui.QIcon(option.icon)
option.icon = QtGui.QIcon()
super(ProxyStyle, self).drawControl(element, option, painter, widget)
if element == QtWidgets.QStyle.CE_PushButtonLabel:
if not icon.isNull():
iconSpacing = 4
mode = (
QtGui.QIcon.Normal
if option.state & QtWidgets.QStyle.State_Enabled
else QtGui.QIcon.Disabled
)
if (
mode == QtGui.QIcon.Normal
and option.state & QtWidgets.QStyle.State_HasFocus
):
mode = QtGui.QIcon.Active
state = QtGui.QIcon.Off
if option.state & QtWidgets.QStyle.State_On:
state = QtGui.QIcon.On
window = widget.window().windowHandle() if widget is not None else None
pixmap = icon.pixmap(window, option.iconSize, mode, state)
pixmapWidth = pixmap.width() / pixmap.devicePixelRatio()
pixmapHeight = pixmap.height() / pixmap.devicePixelRatio()
iconRect = QtCore.QRect(
QtCore.QPoint(), QtCore.QSize(pixmapWidth, pixmapHeight)
)
iconRect.moveCenter(option.rect.center())
iconRect.moveLeft(option.rect.left() + iconSpacing)
iconRect = self.visualRect(option.direction, option.rect, iconRect)
iconRect.translate(
self.proxy().pixelMetric(
QtWidgets.QStyle.PM_ButtonShiftHorizontal, option, widget
),
self.proxy().pixelMetric(
QtWidgets.QStyle.PM_ButtonShiftVertical, option, widget
),
)
painter.drawPixmap(iconRect, pixmap)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
app.setStyle('fusion')
proxy_style = ProxyStyle(app.style())
app.setStyle(proxy_style)
w = QtWidgets.QWidget()
lay = QtWidgets.QVBoxLayout(w)
icons = [
app.style().standardIcon(standardIcon)
for standardIcon in (
QtWidgets.QStyle.SP_MediaPlay,
QtWidgets.QStyle.SP_MediaPause,
QtWidgets.QStyle.SP_MediaSeekBackward,
QtWidgets.QStyle.SP_MediaSeekForward,
)
]
for text, icon in zip("Play Pause Backward Forward".split(), (icons)):
button = QtWidgets.QPushButton(text)
button.setIcon(icon)
lay.addWidget(button)
w.show()
sys.exit(app.exec_())
Related
from PyQt5 import QtCore, QtGui, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QGridLayout(self)
self.getImageButton = QtWidgets.QPushButton('Select')
layout.addWidget(self.getImageButton)
self.getImageButton.clicked.connect(self.resimac)
self.resim1 = QtWidgets.QLabel()
layout.addWidget(self.resim1)
self.resim1.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignVCenter)
# I'm assuming the following...
self.resim1.setScaledContents(True)
self.resim1.setFixedSize(701,451)
# install an event filter to "capture" mouse events (amongst others)
self.resim1.installEventFilter(self)
def resimac(self):
filename, filter = QtWidgets.QFileDialog.getOpenFileName(None, 'Resim Yükle', '.', 'Image Files (*.png *.jpg *.jpeg *.bmp *.tif)')
if not filename:
return
self.resim1.setPixmap(QtGui.QPixmap(filename))
def eventFilter(self, source, event):
# if the source is our QLabel, it has a valid pixmap, and the event is
# a left click, proceed in trying to get the event position
if (source == self.resim1 and source.pixmap() and not source.pixmap().isNull() and
event.type() == QtCore.QEvent.MouseButtonPress and
event.button() == QtCore.Qt.LeftButton):
self.getClickedPosition(event.pos())
return super().eventFilter(source, event)
def getClickedPosition(self, pos):
# consider the widget contents margins
contentsRect = QtCore.QRectF(self.resim1.contentsRect())
if pos not in contentsRect:
# outside widget margins, ignore!
return
# adjust the position to the contents margins
pos -= contentsRect.topLeft()
pixmapRect = self.resim1.pixmap().rect()
if self.resim1.hasScaledContents():
x = pos.x() * pixmapRect.width() / contentsRect.width()
y = pos.y() * pixmapRect.height() / contentsRect.height()
pos = QtCore.QPoint(x, y)
else:
align = self.resim1.alignment()
# for historical reasons, QRect (which is based on integer values),
# returns right() as (left+width-1) and bottom as (top+height-1),
# and so their opposite functions set/moveRight and set/moveBottom
# take that into consideration; using a QRectF can prevent that; see:
# https://doc.qt.io/qt-5/qrect.html#right
# https://doc.qt.io/qt-5/qrect.html#bottom
pixmapRect = QtCore.QRectF(pixmapRect)
# the pixmap is not left aligned, align it correctly
if align & QtCore.Qt.AlignRight:
pixmapRect.moveRight(contentsRect.x() + contentsRect.width())
elif align & QtCore.Qt.AlignHCenter:
pixmapRect.moveLeft(contentsRect.center().x() - pixmapRect.width() / 2)
# the pixmap is not top aligned (note that the default for QLabel is
# Qt.AlignVCenter, the vertical center)
if align & QtCore.Qt.AlignBottom:
pixmapRect.moveBottom(contentsRect.y() + contentsRect.height())
elif align & QtCore.Qt.AlignVCenter:
pixmapRect.moveTop(contentsRect.center().y() - pixmapRect.height() / 2)
if not pos in pixmapRect:
# outside image margins, ignore!
return
# translate coordinates to the image position and convert it back to
# a QPoint, which is integer based
pos = (pos - pixmapRect.topLeft()).toPoint()
print('X={}, Y={}'.format(pos.x(), pos.y()))
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
This is somebody else's example code.I managed to get pixel location using the above code but how to put a dot on the clicked position on the image on every click?
Also, when I click on another part of the image, I want the dot to appear on the new clicked position and be removed from the last clicked position.
The logic is to store the original QPixmap, copy it, draw the point with QPainter and set it in the QLabel:
class Window(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.pixmap = QtGui.QPixmap()
# ...
def resimac(self):
filename, filter = QtWidgets.QFileDialog.getOpenFileName(
None, "Resim Yükle", ".", "Image Files (*.png *.jpg *.jpeg *.bmp *.tif)"
)
if not filename:
return
self.pixmap = QtGui.QPixmap(filename)
self.resim1.setPixmap(self.pixmap.copy())
# ...
def getClickedPosition(self, pos):
# ...
pos = (pos - pixmapRect.topLeft()).toPoint()
pixmap = self.pixmap.copy()
painter = QtGui.QPainter(pixmap)
painter.drawPoint(pos)
painter.end()
self.resim1.setPixmap(pixmap)
print("X={}, Y={}".format(pos.x(), pos.y()))
pixmap = self.pixmap.copy()
painter = QtGui.QPainter(pixmap)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setBrush(QtGui.QColor("black"))
length = 10
rect = QtCore.QRect(0, 0, length, length)
rect.moveCenter(pos)
painter.drawEllipse(rect)
painter.end()
self.resim1.setPixmap(pixmap)
So, i want to display both the text and the ICON as a menubar item.
I have used the below statement as:
self.helpMenu = menubar1.addMenu(QtGui.QIcon("home.png"),"&TEXT")
But this displays only the icon and not the text.
So need help to fix it
Premise
It seems that, despite Qt provides an addMenu() function to create a menu that has both an icon and text, it is not fully supported.
There is a related and very old bug report on the matter, which has been flagged as closed and "Out of scope". I cannot test it right now, but I'm going to suppose that it's due to the native menubar support, which is mostly intended for macOS and Linux distros that support that feature.
That said, a workaround is possible, and that's done through a QProxyStyle.
It's a bit complex, but it works seamlessly given that:
it's enabled only when the native menubar feature is not used (whether it's available or just disabled);
it uses the 'fusion' style or the default style on Windows;
The trick is to ensure that the proxy returns a correct size for sizeFromContents() that includes both the text and the icon if both exist, and to use the default implementations as much as possible in drawControl() and drawItemText() (which is called from more standard styles).
class MenuProxy(QtWidgets.QProxyStyle):
menuHack = False
alertShown = False
def useMenuHack(self, element, opt, widget):
if (element in (self.CT_MenuBarItem, self.CE_MenuBarItem) and
isinstance(widget, QtWidgets.QMenuBar) and
opt.icon and not opt.icon.isNull() and opt.text):
if not self.alertShown:
if widget.isNativeMenuBar():
# this will probably not be shown...
print('WARNING: menubar items with icons and text not supported for native menu bars')
styleName = self.baseStyle().objectName()
if not 'windows' in styleName and styleName != 'fusion':
print('WARNING: menubar items with icons and text not supported for "{}" style'.format(
styleName))
self.alertShown = True
return True
return False
def sizeFromContents(self, content, opt, size, widget=None):
if self.useMenuHack(content, opt, widget):
# return a valid size that includes both the icon and the text
alignment = (QtCore.Qt.AlignCenter | QtCore.Qt.TextShowMnemonic |
QtCore.Qt.TextDontClip | QtCore.Qt.TextSingleLine)
if not self.proxy().styleHint(self.SH_UnderlineShortcut, opt, widget):
alignment |= QtCore.Qt.TextHideMnemonic
width = (opt.fontMetrics.size(alignment, opt.text).width() +
self.pixelMetric(self.PM_SmallIconSize) +
self.pixelMetric(self.PM_LayoutLeftMargin) * 2)
textOpt = QtWidgets.QStyleOptionMenuItem(opt)
textOpt.icon = QtGui.QIcon()
height = super().sizeFromContents(content, textOpt, size, widget).height()
return QtCore.QSize(width, height)
return super().sizeFromContents(content, opt, size, widget)
def drawControl(self, ctl, opt, qp, widget=None):
if self.useMenuHack(ctl, opt, widget):
# create a new option with no icon to draw a menubar item; setting
# the menuHack allows us to ensure that the icon size is taken into
# account from the drawItemText function
textOpt = QtWidgets.QStyleOptionMenuItem(opt)
textOpt.icon = QtGui.QIcon()
self.menuHack = True
self.drawControl(ctl, textOpt, qp, widget)
self.menuHack = False
# compute the rectangle for the icon and call the default
# implementation to draw it
iconExtent = self.pixelMetric(self.PM_SmallIconSize)
margin = self.pixelMetric(self.PM_LayoutLeftMargin) / 2
top = opt.rect.y() + (opt.rect.height() - iconExtent) / 2
iconRect = QtCore.QRect(opt.rect.x() + margin, top, iconExtent, iconExtent)
pm = opt.icon.pixmap(widget.window().windowHandle(),
QtCore.QSize(iconExtent, iconExtent),
QtGui.QIcon.Normal if opt.state & self.State_Enabled else QtGui.QIcon.Disabled)
self.drawItemPixmap(qp, iconRect, QtCore.Qt.AlignCenter, pm)
return
super().drawControl(ctl, opt, qp, widget)
def drawItemText(self, qp, rect, alignment, palette, enabled, text, role=QtGui.QPalette.NoRole):
if self.menuHack:
margin = (self.pixelMetric(self.PM_SmallIconSize) +
self.pixelMetric(self.PM_LayoutLeftMargin))
rect = rect.adjusted(margin, 0, 0, 0)
super().drawItemText(qp, rect, alignment, palette, enabled, text, role)
class Test(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
menu = self.menuBar().addMenu(QtGui.QIcon.fromTheme('document-new'), 'File')
menu.addAction(QtGui.QIcon.fromTheme('application-exit'), 'Quit')
self.menuBar().addMenu(QtGui.QIcon.fromTheme('edit-cut'), 'Edit')
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle(MenuProxy(QtWidgets.QStyleFactory.create('fusion')))
# or, for windows systems:
# app.setStyle(MenuProxy())
test = Test()
test.show()
sys.exit(app.exec_())
I have the same story with Windows 7 and PyQt 5.12.2 and tried to solve it like this:
import sys
from PyQt5.Qt import *
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.centralWidget = QLabel("Hello, World")
self.centralWidget.setAlignment(Qt.AlignCenter)
self.setCentralWidget(self.centralWidget)
menuBar = QMenuBar(self)
self.setMenuBar(menuBar)
self.helpContentAction = QAction(QIcon("img/readMe.png"), "&Help Content", self)
self.aboutAction = QAction("&About", self)
# helpMenu = menuBar.addMenu(QIcon("img/qtlogo.png"), "&Help")
helpMenu = menuBar.addMenu(" &Help") # +++
# ^^^^^^^^^^^^
helpMenu.addAction(self.helpContentAction)
helpMenu.addAction(self.aboutAction)
qss = """
QMenuBar {
background-color: qlineargradient(
x1:0, y1:0, x2:0, y2:1,
stop:0 lightgray, stop:1 darkgray
);
}
QMenuBar::item {
background-color: darkgray;
padding: 1px 5px 1px -25px; /* +++ */
background: transparent;
image: url(img/qtlogo.png); /* +++ * /
}
QMenuBar::item:selected {
background-color: lightgray;
}
QMenuBar::item:pressed {
background: lightgray;
}
"""
if __name__ == "__main__":
app = QApplication(sys.argv)
# app.setStyle('Fusion')
app.setStyleSheet(qss) # +++
app.setFont(QFont("Times", 10, QFont.Bold))
win = Window()
win.setWindowTitle("Python Menus")
win.resize(600, 350)
win.show()
sys.exit(app.exec_())
I have QLineEdit in which I wanted to add a clear button at the end of it. I enabled clear button in QLineEdit, it was working fine. I need to add a custom clear button at the end of the QLineEdit, so I used addAction() of QLineEdit and added my custom icon. The problem is that I can't find a solution to increase the size, I tried increasing the image size and it's not working.
class TextBox(QFrame):
def __init__(self, parent):
super(TextBox, self).__init__(parent=parent)
self.setObjectName("textBox")
self.isActive = False
self.lineEdit = QLineEdit()
self.lineEdit.addAction(QIcon("assets/icons/clear#3x.png"), QLineEdit.TrailingPosition)
A QIcon does not have a specific size, as it's only "decided" by the widget that uses it. While most widgets that use icons have a iconSize property, the icons of actions in a QLineEdit are shown in a different way.
Up until Qt 5.11 (excluded), the size was hardcoded to 16 pixels if the line edit was smaller than 34 pixels or 32 if it was taller.
Starting from Qt 5.11 the size is retrieved using the style (through pixelMetric()), and this can be overridden using a proxy style:
class Proxy(QtWidgets.QProxyStyle):
def pixelMetric(self, metric, opt=None, widget=None):
if (metric == self.PM_SmallIconSize and
isinstance(widget, QtWidgets.QLineEdit)):
size = widget.property('iconSize')
if size is not None:
return size
return widget.fontMetrics().height()
return super().pixelMetric(metric, opt, widget)
class LineEdit(QtWidgets.QLineEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setProperty('iconSize', 64)
# ...
For previous versions of Qt, though, things are a bit tricky. The only solution I came up with is to install event filters on all QToolButton that are children of the line edit (every action uses an internal QToolButton, including the clear action), manually set their geometry (required for correct click actions) and paint it in the event filter.
The following includes the proxystyle implementation in case the current version correctly supports it as explained before:
from PyQt5 import QtWidgets, QtCore, QtGui
if int(QtCore.QT_VERSION_STR.split('.')[1]) > 11:
IconSizeFix = False
else:
IconSizeFix = True
class Proxy(QtWidgets.QProxyStyle):
def pixelMetric(self, metric, opt=None, widget=None):
if (metric == self.PM_SmallIconSize and
isinstance(widget, QtWidgets.QLineEdit)):
size = widget.property('iconSize')
if size is not None:
return size
return widget.fontMetrics().height()
return super().pixelMetric(metric, opt, widget)
class LineEdit(QtWidgets.QLineEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setProperty('iconSize', 64)
self.setClearButtonEnabled(True)
self.addAction(QtGui.QIcon("icon.png"), self.TrailingPosition)
font = self.font()
font.setPointSize(48)
self.setFont(font)
def checkButtons(self):
for button in self.findChildren(QtWidgets.QToolButton):
button.installEventFilter(self)
def actionEvent(self, event):
super().actionEvent(event)
if IconSizeFix:
self.checkButtons()
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.Paint:
if (source.defaultAction().objectName() == '_q_qlineeditclearaction' and
not self.text()):
return True
qp = QtGui.QPainter(source)
state = QtGui.QIcon.Disabled
if source.isEnabled():
state = QtGui.QIcon.Active if source.isDown() else QtGui.QIcon.Normal
iconSize = QtCore.QSize(*[self.property('iconSize')] * 2)
qp.drawPixmap(source.rect(), source.icon().pixmap(
self.windowHandle(), iconSize, state, QtGui.QIcon.Off))
return True
return super().eventFilter(source, event)
def resizeEvent(self, event):
if not IconSizeFix:
return
self.checkButtons()
buttons = self.findChildren(QtWidgets.QToolButton)
if not buttons:
return
left = []
right = []
center = self.rect().center().x()
for button in buttons:
geo = button.geometry()
if geo.center().x() < center:
left.append(button)
else:
right.append(button)
left.sort(key=lambda x: x.geometry().x())
right.sort(key=lambda x: x.geometry().x())
iconSize = self.property('iconSize')
margin = iconSize / 4
top = (self.height() - iconSize) / 2
leftMargin = rightMargin = 0
if left:
x = margin
leftEdge = left[-1].geometry().right()
for button in left:
geo = QtCore.QRect(x, top, iconSize, iconSize)
button.setGeometry(geo)
x += iconSize + margin
leftMargin = x - leftEdge - margin
if right:
rightEdge = self.width() - margin
x = rightEdge - len(right) * iconSize - (len(right) - 1) * margin
rightMargin = self.width() - rightEdge + margin
for button in right:
geo = QtCore.QRect(x, top, iconSize, iconSize)
button.setGeometry(geo)
x += iconSize + margin
self.setTextMargins(leftMargin, 0, rightMargin, 0)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
app.setStyle(Proxy())
w = LineEdit()
w.show()
sys.exit(app.exec_())
Consider the following:
using the pre-5.11 workaround the positioning is not pixel-perfect, I tried to mimic what QLineEdit does to keep the code as simple as possible;
the painting is not exactly the same, most importantly the icon is missing the "highlight" shade when clicked, and if the style uses fade in/out effects for the clear button those effects won't be available;
the QProxyStyle method also affects the sizeHint of the QLineEdit, so it can not be smaller than the icon size, so use it with care;
I have been working on PyQt focus method for different widgets whenever Enter key pressed instead of conventional Tab key.
Furthermore, I am able to set focus to the widget which I intended upon Enter key event. However, whenever the focus is on QComboBox or QRadioButton, these two widgets don't seems to highlight like QLineEdit or QPushButton widgets.
I know that I have to set focus policy to StrongFocus and I have tried that and several other methods but unable to fix this issue.
Moreover, this behaviour is working perfectly fine and combo box or radio button seems to highlight as well with the Tab key.
Please find below my tried code until now and also snapshot for the actual results.
from PyQt5 import QtCore
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Combo_and_Radio_Focus(QDialog):
def __init__(self):
super().__init__()
# setting title
self.setWindowTitle("Combo Box Focus Test")
# setting geometry
self.setGeometry(600, 200, 650, 400)
# Some widgets for testing
self.le1 = QLineEdit(self)
self.le1.setFixedSize(100, 25)
self.le2 = QLineEdit(self)
self.le2.setFixedSize(100, 25)
self.cbo1 = QComboBox(self)
self.cbo1.setFixedSize(100, 25)
self.cbo1.setStyleSheet('QComboBox {background-color: white;}')
self.cbo1.setFocusPolicy(Qt.StrongFocus)
self.cbo1.addItems(["", "Item1", "Item2", "Item3"])
self.cbo2 = QComboBox(self)
self.cbo2.setFixedSize(100, 25)
self.cbo2.setStyleSheet('QComboBox {background-color: white;}')
self.cbo2.setFocusPolicy(Qt.StrongFocus)
self.cbo2.addItems(["", "Item1", "Item2", "Item3"])
self.RB1 = QRadioButton("1")
self.RB1.setChecked(True)
self.RB1.setFixedSize(100, 20)
self.RB1.setFocusPolicy(Qt.StrongFocus)
self.RB2 = QRadioButton("2")
self.RB2.setFixedSize(100, 20)
self.RB2.setFocusPolicy(Qt.StrongFocus)
self.vbl = QVBoxLayout()
self.vbl.addWidget(self.le1)
self.vbl.addWidget(self.le2)
self.vbl.addWidget(self.cbo1)
self.vbl.addWidget(self.cbo2)
self.vbl.addWidget(self.RB1)
self.vbl.addWidget(self.RB2)
self.setLayout(self.vbl)
# showing all the widgets
self.show()
def keyPressEvent(self, qKeyEvent):
if int(qKeyEvent.modifiers()) == QtCore.Qt.AltModifier:
qKeyEvent.ignore()
return
if qKeyEvent.key() == QtCore.Qt.Key_Return or qKeyEvent.key() == QtCore.Qt.Key_Enter:
QWidget.focusNextChild(self)
else:
super().keyPressEvent(qKeyEvent)
App = QApplication(sys.argv)
App.setStyle(QStyleFactory.create('Fusion'))
# create the instance of our Window
combo_and_Radio_Focus = Combo_and_Radio_Focus()
# start the app
sys.exit(App.exec())
LineEdit1_Highlighted Below
ComboBox1_Dont_Highlight Below
When the Tab is pressed to make the focus change then the application sets the Qt.WA_KeyboardFocusChange attribute in the window and that is used by the QStyle to make the border painting but since this is not the case then that attribute has to be set directly:
def keyPressEvent(self, qKeyEvent):
if qKeyEvent.modifiers() & QtCore.Qt.AltModifier:
qKeyEvent.ignore()
elif qKeyEvent.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
self.focusNextChild()
self.window().setAttribute(Qt.WA_KeyboardFocusChange)
else:
super().keyPressEvent(qKeyEvent)
How do I make a pixmap's alignment centered to its column in a QTreeView? I have 2 columns with icons that are aligned to left, but I want one of them centered, so this needs to work on a single column and not force the whole table to one alignment.
I'm using a QTreeView with QAbstractItemModel as its model. On one column I flagged it as QtCore.Qt.DecorationRole and return a pixmap in the model's data() method so that it displays images along that column.
All works well, except that the images all align left, and for the life of me I can't get them centered horizontally.
In the data() method, I tried returning QtCore.Qt.AlignCenter if the role was QtCore.Qt.TextAlignmentRole, but that seems to only effect text (duh!).
Is there another way to achieve this? I'm not interested in taking the route of delegates if possible.
A possible solution is to overwrite the delegate's initStyleOption() method:
from PySide2 import QtCore, QtGui, QtWidgets
class IconCenterDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super(IconCenterDelegate, self).initStyleOption(option, index)
option.decorationAlignment = (
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter
)
option.decorationPosition = QtWidgets.QStyleOptionViewItem.Top
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = QtWidgets.QTreeView()
model = QtGui.QStandardItemModel(w)
w.setModel(model)
delegate = IconCenterDelegate(w)
w.setItemDelegateForColumn(1, delegate)
icons = [
"SP_TitleBarMinButton",
"SP_TitleBarMenuButton",
"SP_TitleBarMaxButton",
"SP_TitleBarCloseButton",
"SP_TitleBarNormalButton",
"SP_TitleBarShadeButton",
"SP_TitleBarUnshadeButton",
"SP_TitleBarContextHelpButton",
"SP_MessageBoxInformation",
"SP_MessageBoxWarning",
"SP_MessageBoxCritical",
"SP_MessageBoxQuestion",
"SP_DesktopIcon",
]
parent = model.invisibleRootItem()
for icon_name in icons:
icon = QtWidgets.QApplication.style().standardIcon(
getattr(QtWidgets.QStyle, icon_name)
)
its = []
for _ in range(3):
it = QtGui.QStandardItem()
it.setIcon(icon)
its.append(it)
parent.appendRow(its)
model.appendRow(it)
w.resize(640, 480)
w.expandAll()
w.show()
sys.exit(app.exec_())
If you want the icons of all the columns to be aligned centrally then you could overwrite the viewOptions() method of the view:
class TreeView(QtWidgets.QTreeView):
def viewOptions(self):
option = super().viewOptions()
option.decorationAlignment = (
QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter
)
option.decorationPosition = QtWidgets.QStyleOptionViewItem.Top
return option