Исходный файл mre.py
import sys
from PySide6.QtCore import (Qt, QSize, QPoint, QRect,
QEvent, QObject,
Signal, SignalInstance, Slot,
)
from PySide6.QtGui import (QResizeEvent, QMoveEvent,
QFocusEvent, )
from PySide6.QtWidgets import (QWidget, QPushButton, QLabel,
QHBoxLayout, QVBoxLayout,
QMainWindow, QApplication,
QSizePolicy, QLayout, QLayoutItem
)
class CustomEventFilter(QObject):
"""Installs eventFilter obj on a widget.
Args:
widget: QWidget instance
"""
def __init__(self, widget: QWidget):
# if you don't pass widget to be a parent of this evFilter,
# then the event filter will most likely be destroyed.
super().__init__(widget)
# set protected variable
self._widget = widget
# Install this filterObj instance on the given widget
self.widget.installEventFilter(self)
# Encapsulation stuff
@property
def widget(self):
return self._widget
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
class MeatballEventFilter(CustomEventFilter):
def __init__(self, widget, sig: Signal = None):
super().__init__(widget)
self._sig = sig
@property
def widget(self) -> QWidget:
return self._widget
@property
def sig(self) -> SignalInstance:
return self._sig
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case QEvent.FocusOut:
# print("Lost focus!\nReason: {}".format(event.reason()))
# if event.reason() == Qt.FocusReason.ActiveWindowFocusReason:
# return True
# self.sig.emit(self.widget)
return True
case QEvent.FocusIn:
# print("Gained focus!")
return True
case _:
return super().eventFilter(obj, event)
class WindowEventFilter(CustomEventFilter):
def __init__(self, widget):
super().__init__(widget)
@property
def widget(self):
return super().widget
def eventFilter(self, obj: QWidget, event:QEvent):
match (event.type()):
case QEvent.MouseButtonRelease:
#TODO
print("mouse window!")
sub: SubBox = obj.findChild(SubBox)
evFilter: SubBoxEventFilter = sub.findChild(SubBoxEventFilter)
return evFilter.eventFilter(sub, event)
# return sub.eventFilter(sub, event) # this doesn't work.
case _:
return super().eventFilter(obj, event)
class SubBoxEventFilter(CustomEventFilter):
"""blah blah blah
"""
def __init__(self, widget: QWidget):
super().__init__(widget)
@property
def widget(self):
return super().widget
def eventFilter(self, obj: QObject, event:QEvent):
# print(type(obj))
match (event.type()):
case QEvent.Resize: # TODO
# Won't matter for final product(I think?) but for test purposes will be implemented here
return True
case QEvent.Move:
# Check meatball
print("Sub moving meatball!")
if obj.findChild(TestSongMeatball):
meatball: TestSongMeatball = obj.findChild(TestSongMeatball)
meatball.findPos()
return True
# Return True to notify app that moving took place.
return True
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
class TestSong(QWidget):
# Create signals to relay info about meatball
meatballCreated = Signal(QWidget, name="meatballCreated")
meatballDestroyed = Signal(QWidget, name="meatballDestroyed")
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: blue")
# Init layout on widget
layout = QHBoxLayout(self)
# Crate widgets
label = QLabel("Label")
meatball = TestSongButton("Meatball button")
layout.addWidget(label)
layout.addWidget(meatball)
# connect clicked signal to relay into a meatballCreate signal, passing this widget
# as a param
meatball.clicked.connect(
lambda checked: self.meatballCreated.emit(self))
class TestSongButton(QPushButton):
def __init__(self, text):
super().__init__(text)
self.setStyleSheet("background-color: purple")
class TestSongMeatball(QWidget):
def __init__(self, songWidget: TestSong, parent=None):
super().__init__(parent)
# Set reference to song widget
self._songWidget = songWidget
self.setStyleSheet("background-color: red")
# Create sizePolicy obj, init with horizontal and vertical preferences
policy = QSizePolicy()
policy.setHorizontalPolicy(QSizePolicy.Policy.Preferred)
policy.setVerticalPolicy(QSizePolicy.Policy.Preferred)
# Set policy, min and max size of the widget.
self.setSizePolicy(policy)
self.setMinimumSize(QSize(70, 50))
self.setMaximumSize(QSize(160, 80))
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
# Assign layout to widget
layout = QVBoxLayout(self)
# Add demo label for clarity
text = QLabel("Meatball Menu")
layout.addWidget(text)
@property
def songWidget(self):
return self._songWidget
def findPos(self):
p = self.parent()
# First check that there is a song meatball created as a child of monitorObj (_widget)
if p.findChild(TestSongMeatball):
# find song item and store its position
item_pos = self.songWidget.pos()
# Type hint for Python intellisense purposes
# find button within song item and store its position
button: TestSongButton = self.songWidget.findChild(TestSongButton)
button_pos = button.pos()
# since item sits inside SubBox, the meatball will have the same geometry starting point (I think?)
# So to find the button we need to add them
x,y = item_pos.x(), item_pos.y()
nx,ny, = button_pos.x(), button_pos.y()
# Add co-ordinates to get button's top left corner
meatball_pos = QPoint(x+nx,y+ny)
# Move to new position, then allow QT to find correct size, using SizePolicy.
self.move(meatball_pos)
self.adjustSize()
return
def sizeHint(self):
return QSize(80,60)
class SubBox(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: green")
# Create and apply layout
layout = QVBoxLayout(self)
@Slot()
def addWid(self, item: TestSong):
if self.findChild(TestSongMeatball):
print("Meatball already present.")
return
# Create meatball widget
meatball = TestSongMeatball(item, self)
evFilter = MeatballEventFilter(meatball, item.meatballDestroyed)
meatball.findPos()
meatball.show() # show meatball
meatball.setFocus()
return
@Slot()
def remWid(self, meatball: TestSongMeatball):
meatball.deleteLater()
meatball = None
return
class TestWindow(QMainWindow):
def __init__(self):
super().__init__()
window_widget = QWidget()
window_layout = QVBoxLayout(window_widget)
winFilter = WindowEventFilter(window_widget)
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
#TODO: put this into a function
# Give total layout a label
window_layout.addWidget(QLabel("Window"))
# Create sub-box
sub = SubBox()
# Install event filter to SubBox
subFilter = SubBoxEventFilter(sub)
test_widget = TestSong()
sub.layout().addWidget(test_widget)
window_layout.addWidget(sub)
test_widget.meatballCreated.connect(
lambda item: sub.addWid(item))
test_widget.meatballDestroyed.connect(
lambda meatball : sub.remWid(meatball))
# set window as central
self.setCentralWidget(window_widget)
def main():
# Create app
app = QApplication(sys.argv)
window = TestWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
Мне интересно, как лучше всего разделить этот код. До сих пор я пытался переместить классы CustomEventFilter, MeatballEventFilter, SubBoxEventFilter, WindowEventFilter в отдельный файл под названием EvFilter.py. Затем импортируйте SubBox, TestSongMeatball в EvFilter.py, чтобы QWidget.findChild() мог найти рассматриваемый виджет.
В результате у меня осталось два файла: EvFilter.py и mre.py, первый из которых содержит все подклассы CustomEventFilter и импортирует SubBox, TestSongMeatball к нему; а последний содержит TestSong, TestSongButton, TestSongMeatball, SubBox, TestWindow и main(), а затем импортирует все подклассы CustomEventFilter.
Я ясно вижу, как это приведет к циклическому импорту, и попытался исправить проблему с помощью исправлений, которые я нашел в Интернете.
Когда пытаюсь импортировать весь файл а затем получить доступ к отдельным классам, например:
import mre
sub = obj.findChild(mre.SubBox)
Функция findChild() вернет None.
Очевидно, что мне нужно реструктурировать программу, чтобы избежать этого недостатка, но не делайте этого. Я не знаю, как лучше всего это сделать. Или мне нужно найти дочерний элемент QWidget без использования findChild().
EvFilter.py
from PySide6.QtCore import (QEvent, QObject,
Signal, SignalInstance,
)
from PySide6.QtWidgets import (QWidget, )
from mre import (TestSongMeatball, SubBox, )
class CustomEventFilter(QObject):
"""Installs eventFilter obj on a widget.
Args:
widget: QWidget instance
"""
def __init__(self, widget: QWidget):
# if you don't pass widget to be a parent of this evFilter,
# then the event filter will most likely be destroyed.
super().__init__(widget)
# set protected variable
self._widget = widget
# Install this filterObj instance on the given widget
self.widget.installEventFilter(self)
# Encapsulation stuff
@property
def widget(self):
return self._widget
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
class MeatballEventFilter(CustomEventFilter):
def __init__(self, widget, sig: Signal = None):
super().__init__(widget)
self._sig = sig
@property
def widget(self) -> QWidget:
return self._widget
@property
def sig(self) -> SignalInstance:
return self._sig
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case QEvent.FocusOut:
# print("Lost focus!\nReason: {}".format(event.reason()))
# if event.reason() == Qt.FocusReason.ActiveWindowFocusReason:
# return True
# self.sig.emit(self.widget)
return True
case QEvent.FocusIn:
# print("Gained focus!")
return True
case _:
return super().eventFilter(obj, event)
class WindowEventFilter(CustomEventFilter):
def __init__(self, widget):
super().__init__(widget)
@property
def widget(self):
return super().widget
def eventFilter(self, obj: QWidget, event:QEvent):
match (event.type()):
case QEvent.MouseButtonRelease:
#TODO
print("mouse window!")
sub: SubBox = obj.findChild(SubBox)
evFilter: SubBoxEventFilter = sub.findChild(SubBoxEventFilter)
return evFilter.eventFilter(sub, event)
# return sub.eventFilter(sub, event) # this doesn't work.
case _:
return super().eventFilter(obj, event)
class SubBoxEventFilter(CustomEventFilter):
"""blah blah blah
"""
def __init__(self, widget: QWidget):
super().__init__(widget)
@property
def widget(self):
return super().widget
# Actual eventFilter function. Obj will be the widget this filterObj is installed on,
# Event is a subclass of QEvent and can be accessed through its enum:
# QEvent.Resize is similar to QResizeEvent
def eventFilter(self, obj: QObject, event:QEvent):
# print(type(obj))
match (event.type()):
case QEvent.Resize: # TODO
# Won't matter for final product(I think?) but for test purposes will be implemented here
return True
case QEvent.Move:
# Check meatball
print("Sub moving meatball!")
if obj.findChild(TestSongMeatball):
meatball: TestSongMeatball = obj.findChild(TestSongMeatball)
meatball.findPos()
return True
# Return True to notify app that moving took place.
return True
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
mre.py, без фильтров событий
import sys
from PySide6.QtCore import (Qt, QSize, QPoint, QRect,
QEvent, QObject,
Signal, SignalInstance, Slot,
)
from PySide6.QtGui import (QResizeEvent, QMoveEvent,
QFocusEvent, )
from PySide6.QtWidgets import (QWidget, QPushButton, QLabel,
QHBoxLayout, QVBoxLayout,
QMainWindow, QApplication,
QSizePolicy, QLayout, QLayoutItem
)
import EvFilter
class TestSong(QWidget):
# Create signals to relay info about meatball
meatballCreated = Signal(QWidget, name="meatballCreated")
meatballDestroyed = Signal(QWidget, name="meatballDestroyed")
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: blue")
# Init layout on widget
layout = QHBoxLayout(self)
# Crate widgets
label = QLabel("Label")
meatball = TestSongButton("Meatball button")
layout.addWidget(label)
layout.addWidget(meatball)
# connect clicked signal to relay into a meatballCreate signal, passing this widget
# as a param
meatball.clicked.connect(
lambda checked: self.meatballCreated.emit(self))
class TestSongButton(QPushButton):
def __init__(self, text):
super().__init__(text)
self.setStyleSheet("background-color: purple")
class TestSongMeatball(QWidget):
def __init__(self, songWidget: TestSong, parent=None):
super().__init__(parent)
# Set reference to song widget
self._songWidget = songWidget
self.setStyleSheet("background-color: red")
# Create sizePolicy obj, init with horizontal and vertical preferences
policy = QSizePolicy()
policy.setHorizontalPolicy(QSizePolicy.Policy.Preferred)
policy.setVerticalPolicy(QSizePolicy.Policy.Preferred)
# Set policy, min and max size of the widget.
self.setSizePolicy(policy)
self.setMinimumSize(QSize(70, 50))
self.setMaximumSize(QSize(160, 80))
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
# Assign layout to widget
layout = QVBoxLayout(self)
# Add demo label for clarity
text = QLabel("Meatball Menu")
layout.addWidget(text)
@property
def songWidget(self):
return self._songWidget
def findPos(self):
p = self.parent()
# First check that there is a song meatball created as a child of monitorObj (_widget)
if p.findChild(TestSongMeatball):
# find song item and store its position
item_pos = self.songWidget.pos()
# Type hint for Python intellisense purposes
# find button within song item and store its position
button: TestSongButton = self.songWidget.findChild(TestSongButton)
button_pos = button.pos()
# since item sits inside SubBox, the meatball will have the same geometry starting point (I think?)
# So to find the button we need to add them
x,y = item_pos.x(), item_pos.y()
nx,ny, = button_pos.x(), button_pos.y()
# Add co-ordinates to get button's top left corner
meatball_pos = QPoint(x+nx,y+ny)
# Move to new position, then allow QT to find correct size, using SizePolicy.
self.move(meatball_pos)
self.adjustSize()
return
def sizeHint(self):
return QSize(80,60)
class SubBox(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: green")
# Create and apply layout
layout = QVBoxLayout(self)
@Slot()
def addWid(self, item: TestSong):
if self.findChild(TestSongMeatball):
print("Meatball already present.")
return
# Create meatball widget
meatball = TestSongMeatball(item, self)
evFilter = EvFilter.MeatballEventFilter(meatball, item.meatballDestroyed)
meatball.findPos()
meatball.show() # show meatball
meatball.setFocus()
return
@Slot()
def remWid(self, meatball: TestSongMeatball):
meatball.deleteLater()
meatball = None
return
class TestWindow(QMainWindow):
def __init__(self):
super().__init__()
window_widget = QWidget()
window_layout = QVBoxLayout(window_widget)
winFilter = EvFilter.WindowEventFilter(window_widget)
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
#TODO: put this into a function
# Give total layout a label
window_layout.addWidget(QLabel("Window"))
# Create sub-box
sub = SubBox()
# Install event filter to SubBox
subFilter = EvFilter.SubBoxEventFilter(sub)
test_widget = TestSong()
sub.layout().addWidget(test_widget)
window_layout.addWidget(sub)
test_widget.meatballCreated.connect(
lambda item: sub.addWid(item))
test_widget.meatballDestroyed.connect(
lambda meatball : sub.remWid(meatball))
# set window as central
self.setCentralWidget(window_widget)
def main():
# Create app
app = QApplication(sys.argv)
window = TestWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
Подробнее здесь: https://stackoverflow.com/questions/792 ... th-pyside6
Как избежать циклического импорта с помощью PySide6 ⇐ Python
Программы на Python
-
Anonymous
1732515334
Anonymous
Исходный файл mre.py
import sys
from PySide6.QtCore import (Qt, QSize, QPoint, QRect,
QEvent, QObject,
Signal, SignalInstance, Slot,
)
from PySide6.QtGui import (QResizeEvent, QMoveEvent,
QFocusEvent, )
from PySide6.QtWidgets import (QWidget, QPushButton, QLabel,
QHBoxLayout, QVBoxLayout,
QMainWindow, QApplication,
QSizePolicy, QLayout, QLayoutItem
)
class CustomEventFilter(QObject):
"""Installs eventFilter obj on a widget.
Args:
widget: QWidget instance
"""
def __init__(self, widget: QWidget):
# if you don't pass widget to be a parent of this evFilter,
# then the event filter will most likely be destroyed.
super().__init__(widget)
# set protected variable
self._widget = widget
# Install this filterObj instance on the given widget
self.widget.installEventFilter(self)
# Encapsulation stuff
@property
def widget(self):
return self._widget
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
class MeatballEventFilter(CustomEventFilter):
def __init__(self, widget, sig: Signal = None):
super().__init__(widget)
self._sig = sig
@property
def widget(self) -> QWidget:
return self._widget
@property
def sig(self) -> SignalInstance:
return self._sig
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case QEvent.FocusOut:
# print("Lost focus!\nReason: {}".format(event.reason()))
# if event.reason() == Qt.FocusReason.ActiveWindowFocusReason:
# return True
# self.sig.emit(self.widget)
return True
case QEvent.FocusIn:
# print("Gained focus!")
return True
case _:
return super().eventFilter(obj, event)
class WindowEventFilter(CustomEventFilter):
def __init__(self, widget):
super().__init__(widget)
@property
def widget(self):
return super().widget
def eventFilter(self, obj: QWidget, event:QEvent):
match (event.type()):
case QEvent.MouseButtonRelease:
#TODO
print("mouse window!")
sub: SubBox = obj.findChild(SubBox)
evFilter: SubBoxEventFilter = sub.findChild(SubBoxEventFilter)
return evFilter.eventFilter(sub, event)
# return sub.eventFilter(sub, event) # this doesn't work.
case _:
return super().eventFilter(obj, event)
class SubBoxEventFilter(CustomEventFilter):
"""blah blah blah
"""
def __init__(self, widget: QWidget):
super().__init__(widget)
@property
def widget(self):
return super().widget
def eventFilter(self, obj: QObject, event:QEvent):
# print(type(obj))
match (event.type()):
case QEvent.Resize: # TODO
# Won't matter for final product(I think?) but for test purposes will be implemented here
return True
case QEvent.Move:
# Check meatball
print("Sub moving meatball!")
if obj.findChild(TestSongMeatball):
meatball: TestSongMeatball = obj.findChild(TestSongMeatball)
meatball.findPos()
return True
# Return True to notify app that moving took place.
return True
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
class TestSong(QWidget):
# Create signals to relay info about meatball
meatballCreated = Signal(QWidget, name="meatballCreated")
meatballDestroyed = Signal(QWidget, name="meatballDestroyed")
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: blue")
# Init layout on widget
layout = QHBoxLayout(self)
# Crate widgets
label = QLabel("Label")
meatball = TestSongButton("Meatball button")
layout.addWidget(label)
layout.addWidget(meatball)
# connect clicked signal to relay into a meatballCreate signal, passing this widget
# as a param
meatball.clicked.connect(
lambda checked: self.meatballCreated.emit(self))
class TestSongButton(QPushButton):
def __init__(self, text):
super().__init__(text)
self.setStyleSheet("background-color: purple")
class TestSongMeatball(QWidget):
def __init__(self, songWidget: TestSong, parent=None):
super().__init__(parent)
# Set reference to song widget
self._songWidget = songWidget
self.setStyleSheet("background-color: red")
# Create sizePolicy obj, init with horizontal and vertical preferences
policy = QSizePolicy()
policy.setHorizontalPolicy(QSizePolicy.Policy.Preferred)
policy.setVerticalPolicy(QSizePolicy.Policy.Preferred)
# Set policy, min and max size of the widget.
self.setSizePolicy(policy)
self.setMinimumSize(QSize(70, 50))
self.setMaximumSize(QSize(160, 80))
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
# Assign layout to widget
layout = QVBoxLayout(self)
# Add demo label for clarity
text = QLabel("Meatball Menu")
layout.addWidget(text)
@property
def songWidget(self):
return self._songWidget
def findPos(self):
p = self.parent()
# First check that there is a song meatball created as a child of monitorObj (_widget)
if p.findChild(TestSongMeatball):
# find song item and store its position
item_pos = self.songWidget.pos()
# Type hint for Python intellisense purposes
# find button within song item and store its position
button: TestSongButton = self.songWidget.findChild(TestSongButton)
button_pos = button.pos()
# since item sits inside SubBox, the meatball will have the same geometry starting point (I think?)
# So to find the button we need to add them
x,y = item_pos.x(), item_pos.y()
nx,ny, = button_pos.x(), button_pos.y()
# Add co-ordinates to get button's top left corner
meatball_pos = QPoint(x+nx,y+ny)
# Move to new position, then allow QT to find correct size, using SizePolicy.
self.move(meatball_pos)
self.adjustSize()
return
def sizeHint(self):
return QSize(80,60)
class SubBox(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: green")
# Create and apply layout
layout = QVBoxLayout(self)
@Slot()
def addWid(self, item: TestSong):
if self.findChild(TestSongMeatball):
print("Meatball already present.")
return
# Create meatball widget
meatball = TestSongMeatball(item, self)
evFilter = MeatballEventFilter(meatball, item.meatballDestroyed)
meatball.findPos()
meatball.show() # show meatball
meatball.setFocus()
return
@Slot()
def remWid(self, meatball: TestSongMeatball):
meatball.deleteLater()
meatball = None
return
class TestWindow(QMainWindow):
def __init__(self):
super().__init__()
window_widget = QWidget()
window_layout = QVBoxLayout(window_widget)
winFilter = WindowEventFilter(window_widget)
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
#TODO: put this into a function
# Give total layout a label
window_layout.addWidget(QLabel("Window"))
# Create sub-box
sub = SubBox()
# Install event filter to SubBox
subFilter = SubBoxEventFilter(sub)
test_widget = TestSong()
sub.layout().addWidget(test_widget)
window_layout.addWidget(sub)
test_widget.meatballCreated.connect(
lambda item: sub.addWid(item))
test_widget.meatballDestroyed.connect(
lambda meatball : sub.remWid(meatball))
# set window as central
self.setCentralWidget(window_widget)
def main():
# Create app
app = QApplication(sys.argv)
window = TestWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
Мне интересно, как лучше всего разделить этот код. До сих пор я пытался переместить классы CustomEventFilter, MeatballEventFilter, SubBoxEventFilter, WindowEventFilter в отдельный файл под названием EvFilter.py. Затем импортируйте SubBox, TestSongMeatball в EvFilter.py, чтобы QWidget.findChild() мог найти рассматриваемый виджет.
В результате у меня осталось два файла: EvFilter.py и mre.py, первый из которых содержит все подклассы CustomEventFilter и импортирует SubBox, TestSongMeatball к нему; а последний содержит TestSong, TestSongButton, TestSongMeatball, SubBox, TestWindow и main(), а затем импортирует все подклассы CustomEventFilter.
Я ясно вижу, как это приведет к циклическому импорту, и попытался исправить проблему с помощью исправлений, которые я нашел в Интернете.
Когда пытаюсь импортировать весь файл а затем получить доступ к отдельным классам, например:
import mre
sub = obj.findChild(mre.SubBox)
Функция findChild() вернет None.
Очевидно, что мне нужно реструктурировать программу, чтобы избежать этого недостатка, но не делайте этого. Я не знаю, как лучше всего это сделать. Или мне нужно найти дочерний элемент QWidget без использования findChild().
EvFilter.py
from PySide6.QtCore import (QEvent, QObject,
Signal, SignalInstance,
)
from PySide6.QtWidgets import (QWidget, )
from mre import (TestSongMeatball, SubBox, )
class CustomEventFilter(QObject):
"""Installs eventFilter obj on a widget.
Args:
widget: QWidget instance
"""
def __init__(self, widget: QWidget):
# if you don't pass widget to be a parent of this evFilter,
# then the event filter will most likely be destroyed.
super().__init__(widget)
# set protected variable
self._widget = widget
# Install this filterObj instance on the given widget
self.widget.installEventFilter(self)
# Encapsulation stuff
@property
def widget(self):
return self._widget
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
class MeatballEventFilter(CustomEventFilter):
def __init__(self, widget, sig: Signal = None):
super().__init__(widget)
self._sig = sig
@property
def widget(self) -> QWidget:
return self._widget
@property
def sig(self) -> SignalInstance:
return self._sig
def eventFilter(self, obj: QWidget, event: QEvent):
match (event.type()):
case QEvent.FocusOut:
# print("Lost focus!\nReason: {}".format(event.reason()))
# if event.reason() == Qt.FocusReason.ActiveWindowFocusReason:
# return True
# self.sig.emit(self.widget)
return True
case QEvent.FocusIn:
# print("Gained focus!")
return True
case _:
return super().eventFilter(obj, event)
class WindowEventFilter(CustomEventFilter):
def __init__(self, widget):
super().__init__(widget)
@property
def widget(self):
return super().widget
def eventFilter(self, obj: QWidget, event:QEvent):
match (event.type()):
case QEvent.MouseButtonRelease:
#TODO
print("mouse window!")
sub: SubBox = obj.findChild(SubBox)
evFilter: SubBoxEventFilter = sub.findChild(SubBoxEventFilter)
return evFilter.eventFilter(sub, event)
# return sub.eventFilter(sub, event) # this doesn't work.
case _:
return super().eventFilter(obj, event)
class SubBoxEventFilter(CustomEventFilter):
"""blah blah blah
"""
def __init__(self, widget: QWidget):
super().__init__(widget)
@property
def widget(self):
return super().widget
# Actual eventFilter function. Obj will be the widget this filterObj is installed on,
# Event is a subclass of QEvent and can be accessed through its enum:
# QEvent.Resize is similar to QResizeEvent
def eventFilter(self, obj: QObject, event:QEvent):
# print(type(obj))
match (event.type()):
case QEvent.Resize: # TODO
# Won't matter for final product(I think?) but for test purposes will be implemented here
return True
case QEvent.Move:
# Check meatball
print("Sub moving meatball!")
if obj.findChild(TestSongMeatball):
meatball: TestSongMeatball = obj.findChild(TestSongMeatball)
meatball.findPos()
return True
# Return True to notify app that moving took place.
return True
case _:
# For all events other than move and resize, handle according to QObject built-in eventFilter().
return super().eventFilter(obj, event)
mre.py, без фильтров событий
import sys
from PySide6.QtCore import (Qt, QSize, QPoint, QRect,
QEvent, QObject,
Signal, SignalInstance, Slot,
)
from PySide6.QtGui import (QResizeEvent, QMoveEvent,
QFocusEvent, )
from PySide6.QtWidgets import (QWidget, QPushButton, QLabel,
QHBoxLayout, QVBoxLayout,
QMainWindow, QApplication,
QSizePolicy, QLayout, QLayoutItem
)
import EvFilter
class TestSong(QWidget):
# Create signals to relay info about meatball
meatballCreated = Signal(QWidget, name="meatballCreated")
meatballDestroyed = Signal(QWidget, name="meatballDestroyed")
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: blue")
# Init layout on widget
layout = QHBoxLayout(self)
# Crate widgets
label = QLabel("Label")
meatball = TestSongButton("Meatball button")
layout.addWidget(label)
layout.addWidget(meatball)
# connect clicked signal to relay into a meatballCreate signal, passing this widget
# as a param
meatball.clicked.connect(
lambda checked: self.meatballCreated.emit(self))
class TestSongButton(QPushButton):
def __init__(self, text):
super().__init__(text)
self.setStyleSheet("background-color: purple")
class TestSongMeatball(QWidget):
def __init__(self, songWidget: TestSong, parent=None):
super().__init__(parent)
# Set reference to song widget
self._songWidget = songWidget
self.setStyleSheet("background-color: red")
# Create sizePolicy obj, init with horizontal and vertical preferences
policy = QSizePolicy()
policy.setHorizontalPolicy(QSizePolicy.Policy.Preferred)
policy.setVerticalPolicy(QSizePolicy.Policy.Preferred)
# Set policy, min and max size of the widget.
self.setSizePolicy(policy)
self.setMinimumSize(QSize(70, 50))
self.setMaximumSize(QSize(160, 80))
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
# Assign layout to widget
layout = QVBoxLayout(self)
# Add demo label for clarity
text = QLabel("Meatball Menu")
layout.addWidget(text)
@property
def songWidget(self):
return self._songWidget
def findPos(self):
p = self.parent()
# First check that there is a song meatball created as a child of monitorObj (_widget)
if p.findChild(TestSongMeatball):
# find song item and store its position
item_pos = self.songWidget.pos()
# Type hint for Python intellisense purposes
# find button within song item and store its position
button: TestSongButton = self.songWidget.findChild(TestSongButton)
button_pos = button.pos()
# since item sits inside SubBox, the meatball will have the same geometry starting point (I think?)
# So to find the button we need to add them
x,y = item_pos.x(), item_pos.y()
nx,ny, = button_pos.x(), button_pos.y()
# Add co-ordinates to get button's top left corner
meatball_pos = QPoint(x+nx,y+ny)
# Move to new position, then allow QT to find correct size, using SizePolicy.
self.move(meatball_pos)
self.adjustSize()
return
def sizeHint(self):
return QSize(80,60)
class SubBox(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet("background-color: green")
# Create and apply layout
layout = QVBoxLayout(self)
@Slot()
def addWid(self, item: TestSong):
if self.findChild(TestSongMeatball):
print("Meatball already present.")
return
# Create meatball widget
meatball = TestSongMeatball(item, self)
evFilter = EvFilter.MeatballEventFilter(meatball, item.meatballDestroyed)
meatball.findPos()
meatball.show() # show meatball
meatball.setFocus()
return
@Slot()
def remWid(self, meatball: TestSongMeatball):
meatball.deleteLater()
meatball = None
return
class TestWindow(QMainWindow):
def __init__(self):
super().__init__()
window_widget = QWidget()
window_layout = QVBoxLayout(window_widget)
winFilter = EvFilter.WindowEventFilter(window_widget)
fPolicy = Qt.FocusPolicy(Qt.StrongFocus)
self.setFocusPolicy(fPolicy)
#TODO: put this into a function
# Give total layout a label
window_layout.addWidget(QLabel("Window"))
# Create sub-box
sub = SubBox()
# Install event filter to SubBox
subFilter = EvFilter.SubBoxEventFilter(sub)
test_widget = TestSong()
sub.layout().addWidget(test_widget)
window_layout.addWidget(sub)
test_widget.meatballCreated.connect(
lambda item: sub.addWid(item))
test_widget.meatballDestroyed.connect(
lambda meatball : sub.remWid(meatball))
# set window as central
self.setCentralWidget(window_widget)
def main():
# Create app
app = QApplication(sys.argv)
window = TestWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
Подробнее здесь: [url]https://stackoverflow.com/questions/79221107/how-to-avoid-circular-imports-with-pyside6[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия