合用 HasTraits 和 PyQt 信号 class
Use HasTraits and PyQt signals in one class
我有一个巨大的 traits 应用程序,它 运行 进入了 enthought traits 的限制。主要是使用 @on_traits_changed 装饰器时的性能问题。如果可以的话,用 PyQt4(或 PyQt5)信号规避这些问题会非常简单:
from traits.api import *
from PyQt4 import QtCore
class Foo(HasTraits, QtCore.QObject):
pass
错误堆栈:
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
但据我所知这是不可能的。有什么解决方法吗?
编辑:添加导入
EDIT2:添加了错误堆栈
我建议的解决方案是忘记 Qt。任何你可以用自定义 Qt 信号和插槽做的事情,你可以用纯 Python 做。这是一个 class、PSignal 的示例,它与 Qt 中的 Signal 具有完全相同的 public 接口。 PSignal 的实例可以添加到任何 Python 对象,而无需子 classing QObject,甚至根本不使用 Qt 库。与 Qt 信号不同,它们可以在任何 class 的 __init__
方法内实例化为实例变量,而不是在 class 级别(如 Qt 所要求的)。此外,emit
是一个标准的 Python 方法,可以接受任意数量的参数和关键字,从而使整个机制更 'Pythonic'。这里唯一缺少的是Signals和Slots的线程切换行为,这里好像没有要求。
通过覆盖子classes 中的方法emit
和_emit
,您可以使这个简单的小工具适应各种情况,这是使用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()
我有一个巨大的 traits 应用程序,它 运行 进入了 enthought traits 的限制。主要是使用 @on_traits_changed 装饰器时的性能问题。如果可以的话,用 PyQt4(或 PyQt5)信号规避这些问题会非常简单:
from traits.api import *
from PyQt4 import QtCore
class Foo(HasTraits, QtCore.QObject):
pass
错误堆栈:
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
但据我所知这是不可能的。有什么解决方法吗?
编辑:添加导入
EDIT2:添加了错误堆栈
我建议的解决方案是忘记 Qt。任何你可以用自定义 Qt 信号和插槽做的事情,你可以用纯 Python 做。这是一个 class、PSignal 的示例,它与 Qt 中的 Signal 具有完全相同的 public 接口。 PSignal 的实例可以添加到任何 Python 对象,而无需子 classing QObject,甚至根本不使用 Qt 库。与 Qt 信号不同,它们可以在任何 class 的 __init__
方法内实例化为实例变量,而不是在 class 级别(如 Qt 所要求的)。此外,emit
是一个标准的 Python 方法,可以接受任意数量的参数和关键字,从而使整个机制更 'Pythonic'。这里唯一缺少的是Signals和Slots的线程切换行为,这里好像没有要求。
通过覆盖子classes 中的方法emit
和_emit
,您可以使这个简单的小工具适应各种情况,这是使用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()