Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use HasTraits and PyQt signals in one class

I have a huge traits application, which is running into the limitations of enthought traits. Mainly performance issues when using the @on_traits_changed decorator. It would be pretty straightforward to circumvent those issues with PyQt4 (or PyQt5) signals, if i could do:

from traits.api import *
from PyQt4 import QtCore

class Foo(HasTraits, QtCore.QObject):
    pass

Error stack:

TypeError                                 Traceback (most recent call last)
<ipython-input-3-ecdfa57492f7> in <module>()
      2 from PyQt4 import QtCore
      3
----> 4 class Foo(HasTraits, QtCore.QObject):
      5     pass

C:\Python27\lib\site-packages\traits\has_traits.pyc in __new__(cls, class_name,
bases, class_dict)
    427
    428         # Finish building the class using the updated class dictionary:
--> 429         klass = type.__new__( cls, class_name, bases, class_dict )
    430
    431         # Fix up all self referential traits to refer to this class:

TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict)
subclass of the metaclasses of all its bases

But from all i know that is not possible. Is there some workaround?

EDIT: added imports

EDIT2: added error stack

like image 395
HeinzKurt Avatar asked Aug 07 '15 08:08

HeinzKurt


1 Answers

My suggested solution is to forget about Qt. Anything you can do with custom Qt Signals and Slots you can do with pure Python. Here is an example of a class, PSignal, that has exactly the same public interface as Signal in Qt. Instances of PSignal can be added to any Python object without the necessity of subclassing QObject, or even without using the Qt library at all. Unlike Qt Signals they can be instantiated as instance variables inside the __init__ method of any class, rather than at class level (as Qt requires). Also, emit is a standard Python method and takes any number of arguments and keywords, thus making the whole mechanism more 'Pythonic'. The only thing lacking here is the thread-switching behavior of Signals and Slots, which does not seem to be a requirement here.

By overriding methods emit and _emit in subclasses you can adapt this simple gadget to a variety of situations, something you can't do easily with Qt Signals.

DEBUG = False

class PSignal:
    def __init__(self):
        self.__handlers = [] 

    def connect(self,f):
        """f is a python function."""
        if not callable(f):
            raise ValueError("Object {!r} is not callable".format(f))
        self.__handlers.append(f)
        if DEBUG:
            print("Connecting",f,self.__handlers)

    def disconnect(self,f):
        for f1 in self.__handlers:
            if f == f1:
                self.__handlers.remove(f)
                return

    def emit(self,*x,**y):
        self._emit(*x,**y)

    def _emit(self,*x,**y):
        for f in self.__handlers:
            try:
                if DEBUG:
                    print("emit",f,len(x),x,y)
                f(*x,**y)
            except Exception:
                print("Error in signal",f)
                traceback.print_exc()
like image 142
Paul Cornelius Avatar answered Oct 05 '22 22:10

Paul Cornelius