我想在许多 QGraphicsItem 继承类上有类似的自定义功能。
在这样的实践中:
class QGraphicsRectItem(QGraphicsRectItem):
def mouseMoveEvent(self, event):
print( "mouseMoveEvent :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
super().mouseMoveEvent( event )
class QGraphicsEllipseItem(QGraphicsEllipseItem):
def mouseMoveEvent(self, event):
print( "mouseMoveEvent :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
super().mouseMoveEvent( event )
class QGraphicsPixmapItem(QGraphicsPixmapItem):
def mouseMoveEvent(self, event):
print( "mouseMoveEvent :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
super().mouseMoveEvent( event )
class QGraphicsSimpleTextItem(QGraphicsSimpleTextItem):
def mouseMoveEvent(self, event):
print( "mouseMoveEvent :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
super().mouseMoveEvent( event )
我的想法是使用类型的元类(QGraphicsItem)但是我在编写这一行代码时遇到问题:
super().mouseMoveEvent( event )
在元类里面
所以,这段代码不起作用:
class QGraphicsItemMeta(type(QGraphicsItem)):
def __new__(cls, name, bases, dct):
print("name= %s" %(name))
print("bases= %s" %(bases))
print("dct=" %(dct) )
newDct = {}
for name, method in dct.items():
if type(method) is FunctionType:
print("method name= %s" % (name))
newDct[name] = method
else:
print("attribut name= %s" % (name))
newDct[name] = method
def mouseMoveEventMyCustomize(self, event):
print( "mouseMoveEventNew :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
super().mouseMoveEvent( event )
newDct['mouseMoveEvent'] = mouseMoveEventMyCustomize
return super().__new__(cls, name, bases, newDct)
class QGraphicsRectItem(QGraphicsRectItem, metaclass = QGraphicsItemMeta):
pass
class QGraphicsEllipseItem(sEllipseItem, metaclass = QGraphicsItemMeta):
pass
class QGraphicsPixmapItem(QGraphicsPixmapItem, metaclass = QGraphicsItemMeta):
pass
class QGraphicsSimpleTextItem(QGraphicsSimpleTextItem, metaclass = QGraphicsItemMeta):
pass
在元类中,而不是这个:
super().mouseMoveEvent( event )
我试过这个:
bases().mouseMoveEvent( event )
但没有成功。
我该如何解决?
更新:应用Mixin解决方案的反馈专家在评论中给出
import sys
from PyQt5.QtWidgets import *
class QGraphicsItemMixin( QGraphicsItem ):
def mouseMoveEvent(self, event):
print( "QGraphicsItemMixin :: mouseMoveEvent :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
super().mouseMoveEvent( event )
class QGraphicsRectItem(QGraphicsRectItem, QGraphicsItemMixin):
pass
class QGraphicsEllipseItem(QGraphicsEllipseItem, QGraphicsItemMixin):
pass
class MainWindow(QWidget):
def __init__(self ):
super(QWidget, self).__init__()
self.scene = QGraphicsScene()
self.scene.setSceneRect( 0.0, 0.0, 640.0, 480.0 )
view= QGraphicsView()
view.setScene(self.scene)
layout = QVBoxLayout()
layout.addWidget( view )
self.setLayout( layout )
self.myRect= QGraphicsRectItem( 0, 0, 100, 100)
self.scene.addItem( self.myRect )
self.myEllipse= QGraphicsEllipseItem( 100, 200, 100, 100)
self.scene.addItem( self.myEllipse )
self.myRect.setFlag( QGraphicsItem.ItemIsMovable, True )
self.myEllipse.setFlag( QGraphicsItem.ItemIsMovable,True )
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
这种情况对于元类来说有点矫枉过正,尽管这种方法可能有效。
无论如何,关于
super()
这并不是一件小事,因为 super
的无参数形式只有在调用写在类主体中硬编码的方法中时才有效。
最简单的做法是在
__new__
方法本身的非局部变量中引用当前类——你正在修补的类——这个变量将是实际类,它是 super()
的返回值
,在 metaclass.__new__
方法中调用时。
所以,如果您的代码与 PyQt 中的类没有元类冲突,则可以这样工作:
class QGraphicsItemMeta(type(QGraphicsItem)):
def __new__(mcls, name, bases, dct): # __new__ actually gets the metaclass itself, not "cls"
...
cls = None # just a placeholder - this is the variable that will be used inside the new method
newDct = {}
for name, method in dct.items():
if type(method) is FunctionType:
print("method name= %s" % (name))
else:
print("attribut name= %s" % (name))
newDct[name] = method
def mouseMoveEventMyCustomize(self, event):
# nonlocal cls # <- the statement is really not needed, as cls will be read only
# inside the method.
print( "mouseMoveEventNew :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
# here we used the paramtrized version of `super` refering
# to "cls"
super(cls, self).mouseMoveEvent( event )
newDct['mouseMoveEvent'] = mouseMoveEventMyCustomize
cls = super().__new__(cls, name, bases, newDct)
# the nonlocal variable now contains a reference to the newly created class
return cls
然而,正如我所说,在这种情况下你最好完全没有元类——因为任何 Qt 也确实使用元类,你将依赖它而不与你的相冲突。
使用 init_subclass 的方法是:
from functools import wraps
def track_mouse(func):
@wraps(func)
def wrapper(self, event):
pos = event.pos()
print(f"mouseMoveEventNew :: event pos ={pos.x:.1f}, {pos.y():%.1f}")
return func(self, event)
wrapper._customized = True
return wrapper
class MouseMonitorMixin:
def __init_subclass__(cls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)
original = getattr(cls, "mouseMoveEvent", None)
if not original or getattr(original, "_customized", False):
return
setattr(cls, "mouseMoveEvent", track_mouse(original))
只需继承你的类,以及它们继承的原始类:
class QGraphicsRectItem(QGraphicsRectItem, MouseMonitorMixin):
pass
...
更新:反馈应用juanpa.arrivillaga在原始问题的上下文中给出的元类解决方案。
import sys
from types import FunctionType
from PyQt5.QtWidgets import *
class QGraphicsItemMeta(type(QGraphicsItem)):
def __new__(cls, name, bases, dct):
print("name= %s" %(name))
print("bases= %s" %(bases))
print("dct=" %(dct) )
newDct = {}
for name, method in dct.items():
if type(method) is FunctionType:
print("method name= %s" % (name))
newDct[name] = method
else:
print("attribut name= %s" % (name))
newDct[name] = method
klass= super().__new__(cls, name, bases, newDct)
def mouseMoveEventMyCustomize(self, event):
print( "mouseMoveEventNew :: event pos =(%.1f, %.1f) " %( event.pos().x(), event.pos().y() ) )
super(klass, self).mouseMoveEvent(event)
klass.mouseMoveEvent = mouseMoveEventMyCustomize
return klass
class QGraphicsRectItem(QGraphicsRectItem, metaclass = QGraphicsItemMeta):
pass
class MainWindow(QWidget):
def __init__(self ):
super(QWidget, self).__init__()
self.scene = QGraphicsScene()
self.scene.setSceneRect( 0.0, 0.0, 640.0, 480.0 )
view= QGraphicsView()
view.setScene(self.scene)
layout = QVBoxLayout()
layout.addWidget( view )
self.setLayout( layout )
self.myRect= QGraphicsRectItem( 0, 0, 100, 100)
self.scene.addItem( self.myRect )
self.myRect.setFlag( QGraphicsItem.ItemIsMovable, True )
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()