ПРИМЕЧАНИЕ: ниже редактируется более полный пример
Я хочу реализовать следующее в 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)
Это упрощенный пример, я также хочу обернуть таким образом другие виджеты, чтобы изменение внутренних виджетов было практически невозможно.
Насколько я могу судить, проблема в том, что отключенный дочерний виджет отклоняет событие мыши (поскольку он отключен) и отказывается снова принимать его (или любое другое событие) от родительского виджета.
Любая помощь вообще будет принята с благодарностью.
РЕДАКТИРОВАТЬ: ниже приведен более четкий пример того, что я имею в виду:
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton
class ComplexInnerWidget(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.btn1 = QPushButton('button 1')
self.btn1.clicked.connect(self._btn1_click)
layout.addWidget(self.btn1)
self.btn2 = QPushButton('button 2')
self.btn2.clicked.connect(self._btn2_click)
layout.addWidget(self.btn2)
def _btn1_click(self, *a):
print('button 1')
def _btn2_click(self, *a):
print('button 2')
class MyWidget(QWidget):
def __init__(self, inner_widget: QWidget, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QVBoxLayout(self)
self.inner = inner_widget
self.inner.setEnabled(False)
layout.addWidget(self.inner)
if __name__ == '__main__':
from PyQt5.QtWidgets import QApplication
app = QApplication([])
inner = ComplexInnerWidget()
w = MyWidget(inner)
w.show()
res = app.exec_()
exit(res)
я хочу, чтобы пользователь мог нажимать отключенный внутренний виджет, тем самым включая его полностью (т.е. как btn1, так и btn2 становятся включенными) и одновременно нажимая соответствующую кнопку. Мне нужно, чтобы это было сделано без изменения ComplexInnerWidget
вообще (поскольку пользователь должен иметь возможность ввести любой виджет в качестве параметра для MyWidget
)
РЕДАКТИРОВАТЬ 2: решение eyllanesc работает для приведенного примера, но я настроил его для MyWidget
, чтобы он мог поддерживать несколько виджетов и быть вложенным в другие виджеты:
from PyQt5 import QtCore, QtWidgets
class ComplexInnerWidget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QtWidgets.QVBoxLayout(self)
self.btn1 = QtWidgets.QPushButton('button 1')
self.btn1.clicked.connect(self._btn1_click)
layout.addWidget(self.btn1)
self.btn2 = QtWidgets.QPushButton('button 2')
self.btn2.clicked.connect(self._btn2_click)
layout.addWidget(self.btn2)
self.le = QtWidgets.QLineEdit('abcdef')
layout.addWidget(self.le)
def _btn1_click(self, *a):
print('button 1')
def _btn2_click(self, *a):
print('button 2')
class MyWidget(QtWidgets.QWidget):
class EnableMouseHelper(QtCore.QObject):
def __init__(self, *args, warden):
super().__init__(*args)
self.warden = warden
def eventFilter(self, obj, event):
if obj.isWidgetType() and event.type() == QtCore.QEvent.MouseButtonPress:
if self.warden in obj.window().findChildren(QtWidgets.QWidget) \
and self.warden.underMouse() and not self.warden.isEnabled():
self.warden.setEnabled(True)
obj.setFocus()
return super().eventFilter(obj, event)
def __init__(self, inner_widget: QtWidgets.QWidget, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QtWidgets.QVBoxLayout(self)
self.inner = inner_widget
self.inner.setEnabled(False)
layout.addWidget(self.inner)
self.helper = self.EnableMouseHelper(warden=self.inner)
QtWidgets.QApplication.instance().installEventFilter(self.helper)
class OuterWidget(QtWidgets.QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(MyWidget(ComplexInnerWidget()))
layout.addWidget(MyWidget(ComplexInnerWidget()))
le = QtWidgets.QLineEdit('hi there')
le.setEnabled(False)
layout.addWidget(le)
le = QtWidgets.QLineEdit('hi there')
layout.addWidget(le)
if __name__ == '__main__':
from PyQt5.QtWidgets import QApplication
app = QApplication([])
w = OuterWidget()
w.show()
res = app.exec_()
exit(res)