Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

singleton in python, with qobject



  • i need a singleton class, so here's what i got:

    __instances = {}
    
    
    class _Singleton(type):
        def __new__(meta, name, bases, members):
            for class_ in bases:
                if class_ is not Singleton and issubclass(class_, Singleton):
                    raise TypeError(("Invalid base type (as subclass of Singleton):", class_))
            return type.__new__(meta, name, bases, members)    
    
        def __call__(class_, *args, **kwargs):
            if class_ is Singleton:
                raise TypeError("The base Singleton type cannot be instantiated")
            if class_ not in __instances:
                __instances[class_] = super(_Singleton, class_).__call__(*args, **kwargs)
            return __instances[class_]
    
    Singleton = type('_', (), {}) # dummy type to allow base type creation
    Singleton = _Singleton('Singleton', (object, ), {})
    

    my controller object is a subclass of QObject, and i get an error:

    class Controller(QObject, '''metaclass='''Singleton):
    TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
    


  • The following is my implementation (and a usage example) of a singleton I use regularly:

    try: from PyQt5.QtCore import pyqtWrapperType
    except ImportError:
        from sip import wrappertype as pyqtWrapperType
    
    class Singleton(pyqtWrapperType, type):
        def __init__(cls, name, bases, dict):
            super().__init__(name, bases, dict)
            cls.instance=None
    
        def __call__(cls,*args,**kw):
            if cls.instance is None:
                cls.instance=super().__call__(*args, **kw)
            return cls.instance
    
    if __name__=="__main__":
        from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty
    
        class MyObject(QObject, metaclass=Singleton):
            def __init__(self, parent=None, **kwargs):
                super().__init__(parent, **kwargs)
    
                self._x=0
    
            def x(self): return self._x
    
            @pyqtSlot(int)
            def setX(self, x): self._x=x
    
            x=pyqtProperty(int, x, setX)
    
        print(MyObject().x)
        MyObject().x=1
        print(MyObject().x)
    

    I've actually shared this example/technique so many times now that I just quickly created a git repo (https://github.com/jazzycamel/PyQt5Singleton) and a PyPi package (https://pypi.python.org/pypi/PyQt5Singleton) so you can just install it via pip:

    $ pip install PyQt5Singleton
    

    and use it (as above):

    from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty
    from PyQt5Singleton import Singleton
    
    class MyObject(QObject, metaclass=Singleton):
        def __init__(self, parent=None, **kwargs):
            super().__init__(parent, **kwargs)
    
            self._x=0
    
        def x(self): return self._x
    
        @pyqtSlot(int)
        def setX(self, x): self._x=x
    
        x=pyqtProperty(int, x, setX)
    
    if __name__=="__main__":
        print(MyObject().x)
        MyObject().x=1
        print(MyObject().x)
    

    Hope this helps :)


Log in to reply