Повторно отправить событие недавно включенному дочернему виджету

Я хочу реализовать следующее в Qt (в частности, PyQt, но я верю, что решение будет одинаковым как в Python, так и в C++):

Я хочу, чтобы у виджета был внутренний виджет, который по умолчанию отключен, и при щелчке виджет будет включен, и нажатие мыши будет распространяться на него. Например, в следующем окне / виджете:

Если я нажму между c а также d Я бы хотел QLineEdit чтобы включить его, сфокусируйтесь и курсор находится между c а также d, Я добрался до повторного включения QLineEdit но я не могу отправить это событие обратно.

Это мой код до сих пор:

from PyQt5.QtWidgets import QWidget, QLineEdit, QVBoxLayout, QPushButton, QApplication


class MyWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        layout = QVBoxLayout(self)
        self.edit = QLineEdit('abcdef')
        self.edit.setEnabled(False)
        layout.addWidget(self.edit)

        self.disable_btn = QPushButton('disable edit')
        self.disable_btn.clicked.connect(self._disable_edit)
        layout.addWidget(self.disable_btn)

    def _disable_edit(self, *a):
        self.edit.setEnabled(False)

    def mousePressEvent(self, a0):
        if not self.edit.isEnabled() and self.edit.underMouse():
            self.edit.setEnabled(True)
            QApplication.instance().sendEvent(self.edit, a0)  # <-- this doesn't seem to work
        super().mousePressEvent(a0)


if __name__ == '__main__':
    from PyQt5.QtWidgets import QApplication

    app = QApplication([])
    w = MyWidget()
    w.show()
    res = app.exec_()
    exit(res)

Это упрощенный пример, я также хочу обернуть другие виджеты таким образом, чтобы изменение внутренних виджетов практически невозможно.

Насколько я могу судить, проблема в том, что отключенный дочерний виджет отклоняет событие мыши (так как оно отключено) и отказывается снова брать его (или любое другое событие) из родительского виджета.

Любая помощь будет принята с благодарностью.

2 ответа

Вы не можете отправить объект события, так как Qt удалит его, когда его использует виджет, и вам нужно создать другое событие с теми же данными. Я создал класс, который позволяет вам регистрировать виджеты, чтобы дать вам это свойство без необходимости перезаписывать класс.

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

class EnableMouseHelper(QtCore.QObject):
    def __init__(self, parent=None):
        super(EnableMouseHelper, self).__init__(parent)
        self._widgets = []

    def addWidget(self, widget):
        if isinstance(widget, QtWidgets.QWidget):
            self._widgets.append(widget)
            widget.installEventFilter(self)
            return True
        return False

    def removeWidget(self, widget):
        if widget is self._widgets:
            widget.removeEventFilter(self)
            self._widgets.remove(widget)

    def eventFilter(self, obj, event):
        if obj in self._widgets and event.type() == QtCore.QEvent.MouseButtonPress:
            if not obj.isEnabled():
                new_event = QtGui.QMouseEvent(
                    event.type(),
                    event.localPos(),
                    event.windowPos(),
                    event.screenPos(),
                    event.button(),
                    event.buttons(),
                    event.modifiers(),
                    event.source()
                )
                obj.setEnabled(True)
                QtCore.QCoreApplication.postEvent(obj, new_event)
                obj.setFocus()
        return super(EnableMouseHelper, self).eventFilter(obj, event)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    helper = EnableMouseHelper()


    le = QtWidgets.QLineEdit(
        text='abcdef',
        enabled=False
    )
    btn = QtWidgets.QPushButton(
        text='disable edit',
        clicked=partial(le.setEnabled, False)
    )
    helper.addWidget(le)

    w = QtWidgets.QWidget()
    lay = QtWidgets.QVBoxLayout(w)
    lay.addWidget(le)
    lay.addWidget(btn)
    w.show()
    sys.exit(app.exec_())

Попытайся:

from PyQt5.QtWidgets import QWidget, QLineEdit, QVBoxLayout, QPushButton, QApplication

class LineEdit(QLineEdit):                                    # +++
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setText('abcdef')
        self.setStyleSheet('color: blue; font-size: 32px')

    def mousePressEvent(self, event):
        super(LineEdit, self).mousePressEvent(event)
        self.cursor = self.cursorPosition() 

    def mouseReleaseEvent(self, event):
        self.setFocus()
        self.setCursorPosition(self.cursor)  


class MyWidget(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        layout    = QVBoxLayout(self)

#        self.edit = QLineEdit('abcdef')
        self.edit = LineEdit()                                  # +++

        self.edit.setEnabled(False)
        layout.addWidget(self.edit)

        self.disable_btn = QPushButton('disable edit')
        self.disable_btn.clicked.connect(self._disable_edit)
        layout.addWidget(self.disable_btn)

    def _disable_edit(self, *a):
        self.edit.setEnabled(False)

    def mousePressEvent(self, a0):
        if not self.edit.isEnabled() and self.edit.underMouse():
            self.edit.setEnabled(True)
            QApplication.instance().sendEvent(self.edit, a0)  # <-- this does seem to work
        super().mousePressEvent(a0)


if __name__ == '__main__':
    from PyQt5.QtWidgets import QApplication
    app = QApplication([])
    w = MyWidget()
    w.show()
    res = app.exec_()
    exit(res)

Другие вопросы по тегам