如何在元类中调用基函数?

问题描述 投票:0回答:3

我想在许多 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 )

但没有成功。

我该如何解决?

python pyqt pyqt5 metaclass
3个回答
0
投票

更新:应用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_()

0
投票

这种情况对于元类来说有点矫枉过正,尽管这种方法可能有效。

无论如何,关于

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
...

0
投票

更新:反馈应用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_()
© www.soinside.com 2019 - 2024. All rights reserved.