Значок изменения PyQt LineEdit QAction на отмеченном/не отмеченном

Мне нужно добавить кнопку в QLineEdit, сделать ее доступной и изменить значок в соответствии с отмеченным/непроверенным состоянием. Я делаю так:

icon = QtGui.QIcon()
icon.addPixmap(QtGui.QPixmap(os.path.join("Images", "L.png")), QtGui.QIcon.Normal, QtGui.QIcon.On)
icon.addPixmap(QtGui.QPixmap(os.path.join("Images", "Home.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
searchActionBttn = QtWidgets.QAction("None", self.searchIn)
searchActionBttn.triggered.connect(lambda: print(searchActionBttn.isChecked()))
searchActionBttn.setCheckable(True)
searchActionBttn.setIcon(icon)
self.searchIn.addAction(searchActionBttn, QtWidgets.QLineEdit.LeadingPosition)

Но значок не изменился, когда я нажал на него.


person СергейП    schedule 15.07.2020    source источник


Ответы (1)


Проблема вызвана тем, что QToolButton, связанный с QAction, имеет собственный метод paintEvent, поэтому он не учитывает состояние флажка.

// https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/widgets/qlineedit_p.cpp#n354
void QLineEditIconButton::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QWindow *window = qt_widget_private(this)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest);
    QIcon::Mode state = QIcon::Disabled;
    if (isEnabled())
        state = isDown() ? QIcon::Active : QIcon::Normal;
    const QLineEditPrivate *lep = lineEditPrivate();
    const int iconWidth = lep ? lep->sideWidgetParameters().iconSize : 16;
    const QSize iconSize(iconWidth, iconWidth);
    const QPixmap iconPixmap = icon().pixmap(window, iconSize, state, QIcon::Off);
    QRect pixmapRect = QRect(QPoint(0, 0), iconSize);
    pixmapRect.moveCenter(rect().center());
    painter.setOpacity(m_opacity);
    painter.drawPixmap(pixmapRect, iconPixmap);
}

(выделено мной)

Как видите, состояние меняется только пока кнопка нажата, поэтому от варианта использования QAction лучше отказаться.

Другой вариант - установить QToolButton напрямую через макет.

import os
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets


class LineEdit(QtWidgets.QLineEdit):
    @property
    def _internal_layout(self):
        if not hasattr(self, "_internal_layout_"):
            self._internal_layout_ = QtWidgets.QHBoxLayout(self)
        self._internal_layout_.addStretch()
        self._internal_layout_.setContentsMargins(2, 2, 2, 2)
        return self._internal_layout_

    def add_button(self, button):
        self._internal_layout.insertWidget(self._internal_layout.count() - 2, button)
        QtCore.QTimer.singleShot(0, partial(self._fix_cursor_position, button))
        button.setFocusProxy(self)

    def _fix_cursor_position(self, button):
        self.setTextMargins(button.geometry().right(), 0, 0, 0)


app = QtWidgets.QApplication([])

icon = QtGui.QIcon()
icon.addPixmap(
    QtGui.QPixmap(os.path.join("Images", "L.png")), QtGui.QIcon.Normal, QtGui.QIcon.On
)
icon.addPixmap(
    QtGui.QPixmap(os.path.join("Images", "Home.png")),
    QtGui.QIcon.Normal,
    QtGui.QIcon.Off,
)

button = QtWidgets.QToolButton()
button.setStyleSheet("border: none")
button.setCheckable(True)
button.setIcon(icon)

searchIn = LineEdit()
searchIn.add_button(button)

searchIn.show()

app.exec_()
person eyllanesc    schedule 15.07.2020