I had tried the abc.ABCMeta
with sip wrapper type, and it works well when subclass with abc.ABC
.
class QABCMeta(wrappertype, ABCMeta):
pass
class WidgetBase(QWidget, metaclass=QABCMeta):
...
class InterfaceWidget(WidgetBase, ABC):
...
class MainWidget(InterfaceWidget):
...
But it is not works on typing.Generic
.
class QGenericMeta(wrappertype, GenericMeta):
pass
class WidgetBase(QWidget, Generic[T], metaclass=QGenericMeta):
...
class GenericWidget(WidgetBase[float]):
...
It raised:
line 980, in __new__
self if not origin else origin._gorg)
TypeError: can't apply this __setattr__ to sip.wrappertype object
I expected it to use generic subclass as usual:
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
But the data
is still Any
when without inherit Generic[T]
.
Can it solved with PEP 560 to do type checking?
Well, I found the answer.
Since the metaclass of typing.Generic
is abc.ABC
, it should based on abc.ABCMeta
too. But this is only works with Python 3.7 or above.
And then, just use type(QObject)
instead of 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
This code is works for PyCharm IDE, the annotation of variable f
is float
.
When change PyQt5
to PySide2
, it also works!