【问题标题】:How do I use generic typing with PyQt subclass without metaclass conflicts?如何在没有元类冲突的情况下将泛型类型与 PyQt 子类一起使用?
【发布时间】:2019-07-05 05:19:13
【问题描述】:

我曾尝试使用 sip 包装器类型的 abc.ABCMeta,它在使用 abc.ABC 进行子类化时效果很好。

class QABCMeta(wrappertype, ABCMeta):
    pass

class WidgetBase(QWidget, metaclass=QABCMeta):
    ...

class InterfaceWidget(WidgetBase, ABC):
    ...

class MainWidget(InterfaceWidget):
    ...

但它不适用于typing.Generic

class QGenericMeta(wrappertype, GenericMeta):
    pass

class WidgetBase(QWidget, Generic[T], metaclass=QGenericMeta):
    ...

class GenericWidget(WidgetBase[float]):
    ...

它提出了:

line 980, in __new__
    self if not origin else origin._gorg)
TypeError: can't apply this __setattr__ to sip.wrappertype object

我希望它像往常一样使用通用子类:

class TableBase(QTableWidget, Generic[T]):
    @abstractmethod
    def raw_item(self, row: int) -> T:
        ...
    def data(self) -> Iterator[T]:
        yield from (self.raw_item(row) for row in range(self.rowCount()))

class MainTable(TableBase[float]):
    def raw_item(self, row: int) -> float:
        return float(self.item(row, 1).text())  # implementation

table = MainTable()
for data in table.data():
    data: float

但是当没有继承 Generic[T] 时,data 仍然是 Any

可以用 PEP 560 解决类型检查吗?

【问题讨论】:

    标签: python pyqt5 metaclass typing pyside2


    【解决方案1】:

    嗯,我找到了答案。

    由于typing.Generic的元类是abc.ABC,它也应该基于abc.ABCMeta。但这仅适用于 Python 3.7 或更高版本。

    然后,只需使用type(QObject) 而不是sip.wrappertype

    # -*- coding: utf-8 -*-
    
    from abc import abstractmethod, ABC, ABCMeta
    from typing import TypeVar, Generic, Iterator
    from PyQt5.QtCore import QObject
    from PyQt5.QtWidgets import QTableWidget
    
    QObjectType = type(QObject)
    T = TypeVar('T')
    
    
    class QABCMeta(QObjectType, ABCMeta):
        pass
    
    
    class BaseWidget(QTableWidget, Generic[T], metaclass=QABCMeta):
    
        @abstractmethod
        def raw_item(self, row: int) -> T:
            ...
    
        def data(self) -> Iterator[T]:
            yield from (self.raw_item(row) for row in range(self.rowCount()))
    
    
    class TestWidget(BaseWidget[float], ABC):  # optional inherit ABC.
    
        def raw_item(self, row: int) -> float:
            return float(self.item(row, 1).text())
    
    
    if __name__ == '__main__':
        w = TestWidget()
        for f in w.data():
            pass
    

    此代码适用于 PyCharm IDE,变量f 的注解为float

    PyQt5改成PySide2也可以!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-06
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 1970-01-01
      • 2019-08-20
      • 2018-02-02
      • 2015-09-12
      相关资源
      最近更新 更多