Увеличение размера значка addAction PyQt5

#python #pyqt #pyqt5

#python #pyqt #pyqt5

Вопрос:

У меня есть QLineEdit, в который я хотел добавить кнопку очистки в конце. Я включил кнопку очистки в QLineEdit, она работала нормально. Мне нужно добавить пользовательскую кнопку очистки в конце QLineEdit, поэтому я использовал addAction() QLineEdit и добавил свой пользовательский значок. Проблема в том, что я не могу найти решение для увеличения размера, я попытался увеличить размер изображения, но это не работает.

 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)
 

Ответ №1:

QIcon не имеет определенного размера, поскольку он определяется только виджетом, который его использует. Хотя большинство виджетов, использующих значки, имеют iconSize свойство, значки действий в QLineEdit отображаются по-другому.

До Qt 5.11 (исключено) размер был жестко задан до 16 пикселей, если редактирование строки было меньше 34 пикселей или 32, если оно было выше.

Начиная с Qt 5.11 размер извлекается с использованием стиля (через pixelMetric() ), и это может быть переопределено с помощью прокси-стиля:

 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)
        # ...
 

Однако для предыдущих версий Qt все немного сложнее. Единственное решение, которое я придумал, — это установить фильтры событий для всех QToolButton, которые являются дочерними элементами строки edit (каждое действие использует внутреннюю QToolButton, включая действие clear), вручную установить их геометрию (необходимую для правильных действий по щелчку) и нарисовать ее в фильтре событий.

Ниже приведена реализация proxystyle на случай, если текущая версия правильно поддерживает ее, как объяснялось ранее:

 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_())
 

Рассмотрим следующее:

  • используя обходной путь до версии 5.11, позиционирование не является идеальным для пикселей, я попытался имитировать то, что делает QLineEdit, чтобы максимально упростить код;
  • изображение не совсем то же самое, самое главное, что на значке отсутствует оттенок «подсветки» при нажатии, и если в стиле используются эффекты увеличения / уменьшения для кнопки очистки, эти эффекты будут недоступны;
  • метод QProxyStyle также влияет sizeHint на QLineEdit, поэтому он не может быть меньше размера значка, поэтому используйте его с осторожностью;