#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, поэтому он не может быть меньше размера значка, поэтому используйте его с осторожностью;