Make everything lint free.
parent
1af57a6b78
commit
077c1bc842
|
@ -10,7 +10,7 @@ output-format=text
|
|||
msg-template={path}:{line}:{obj} {msg_id}({symbol}) {msg}
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,too-few-public-methods,no-self-use,duplicate-code,invalid-name,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-statements,typecheck,redefined-builtin
|
||||
disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,too-few-public-methods,no-self-use,duplicate-code,invalid-name,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-statements,typecheck,redefined-builtin,too-many-public-methods,consider-using-enumerate
|
||||
|
||||
[FORMAT]
|
||||
max-line-length=120
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#
|
||||
# @end:license
|
||||
|
||||
import enum
|
||||
import logging
|
||||
import pprint
|
||||
import sys
|
||||
|
@ -248,6 +249,14 @@ def ScalarProperty(
|
|||
|
||||
return prop, sig
|
||||
|
||||
ENUM = TypeVar('ENUM', bound=enum.Enum)
|
||||
def EnumProperty(
|
||||
name: str, pytype: Type[ENUM], qtype: Union[type, str] = None, *,
|
||||
default: ENUM = None,
|
||||
validate: Callable[['BaseObject', ENUM], None] = None
|
||||
) -> Tuple['QtCore.Property[ENUM]', QtCore.Signal]:
|
||||
return ScalarProperty(name, pytype, qtype, default=default, validate=validate) # type: ignore
|
||||
|
||||
|
||||
_qobj_types = (QtCore.QRectF, QtGui.QColor)
|
||||
QOBJ = TypeVar('QOBJ', QtCore.QRectF, QtGui.QColor)
|
||||
|
|
|
@ -48,9 +48,9 @@ class ConnectIOBuffer(EngineChange):
|
|||
|
||||
class Port(base.BaseObject):
|
||||
name, nameChanged = base.ScalarProperty('uri', str)
|
||||
direction, directionChanged = base.ScalarProperty(
|
||||
direction, directionChanged = base.EnumProperty(
|
||||
'direction', engine.PortDirection, qtype=int, default=engine.PortDirection.INPUT)
|
||||
type, typeChanged = base.ScalarProperty('type', engine.PortType, qtype=int, default=engine.PortType.NOT_SET)
|
||||
type, typeChanged = base.EnumProperty('type', engine.PortType, qtype=int, default=engine.PortType.NOT_SET)
|
||||
hasControlValue, hasControlValueChanged = base.ScalarProperty('hasControlValue', bool)
|
||||
controlValue, controlValueChanged = base.ScalarProperty('controlValue', float)
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
# @end:license
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from PySide2 import QtCore
|
||||
|
||||
|
@ -31,38 +32,38 @@ logger = logging.getLogger(__name__)
|
|||
class Node(GraphNode):
|
||||
addSamples = QtCore.Signal(int, list)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.setNodeMessageHandler(self.__handleNodeMessage)
|
||||
|
||||
def __handleNodeMessage(self, msg_serialized: bytes):
|
||||
def __handleNodeMessage(self, msg_serialized: bytes) -> None:
|
||||
msg = node_message_fb.NodeMessage(msg_serialized)
|
||||
self.addSamples.emit(msg.sample_rate, msg.samples)
|
||||
|
||||
@QtCore.Slot(int)
|
||||
def setTimeScale(self, v):
|
||||
def setTimeScale(self, v: int) -> None:
|
||||
if v == self.node.timeScale:
|
||||
return
|
||||
with self.project.captureChanges("{}: Set time scale".format(self.node.title)):
|
||||
self.node.timeScale = v
|
||||
|
||||
@QtCore.Slot(int)
|
||||
def setYScale(self, v):
|
||||
def setYScale(self, v: int) -> None:
|
||||
if v == self.node.yScale:
|
||||
return
|
||||
with self.project.captureChanges("{}: Set Y scale".format(self.node.title)):
|
||||
self.node.yScale = v
|
||||
|
||||
@QtCore.Slot(float)
|
||||
def setYOffset(self, v):
|
||||
def setYOffset(self, v: float) -> None:
|
||||
if v == self.node.yOffset:
|
||||
return
|
||||
with self.project.captureChanges("{}: Set Y offset".format(self.node.title)):
|
||||
self.node.yOffset = v
|
||||
|
||||
@QtCore.Slot(int)
|
||||
def setHoldTime(self, v):
|
||||
def setHoldTime(self, v: int) -> None:
|
||||
if v == self.node.holdTime:
|
||||
return
|
||||
with self.project.captureChanges("{}: Set hold time".format(self.node.title)):
|
||||
|
|
|
@ -19,12 +19,14 @@
|
|||
# @end:license
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2 import QtCore
|
||||
|
||||
from noisicaa import engine
|
||||
from noisicaa.ui import ui_base
|
||||
from noisicaa.ui.DeviceDB import DeviceDB, Device
|
||||
from noisicaa.ui.GraphNode import GraphNode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -33,18 +35,18 @@ logger = logging.getLogger(__name__)
|
|||
class PortList(QtCore.QAbstractListModel):
|
||||
changed = QtCore.Signal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
def __init__(self, parent: QtCore.QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.__ports = []
|
||||
self.__ports = [] # type: List[str]
|
||||
|
||||
def clear(self):
|
||||
def clear(self) -> None:
|
||||
if self.__ports:
|
||||
self.beginRemoveRows(QtCore.QModelIndex(), 0, len(self.__ports) - 1)
|
||||
self.__ports.clear()
|
||||
self.endRemoveRows()
|
||||
|
||||
def setFromDevice(self, device):
|
||||
def setFromDevice(self, device: Device) -> None:
|
||||
self.clear()
|
||||
|
||||
ports = [
|
||||
|
@ -58,15 +60,15 @@ class PortList(QtCore.QAbstractListModel):
|
|||
|
||||
self.changed.emit()
|
||||
|
||||
def roleNames(self):
|
||||
def roleNames(self) -> Dict[int, bytes]:
|
||||
return {
|
||||
Qt.DisplayRole: b'portName',
|
||||
}
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
def rowCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int: # pylint: disable=unused-argument
|
||||
return len(self.__ports)
|
||||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
def data(self, index: QtCore.QModelIndex, role: int = Qt.DisplayRole) -> Any:
|
||||
if not index.isValid():
|
||||
return None
|
||||
if index.row() < 0 or index.row() >= len(self.__ports):
|
||||
|
@ -78,11 +80,15 @@ class PortList(QtCore.QAbstractListModel):
|
|||
|
||||
|
||||
class SinkDevices(QtCore.QSortFilterProxyModel):
|
||||
def __init__(self, deviceDB, parent=None):
|
||||
def __init__(self, deviceDB: DeviceDB, parent: QtCore.QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.setSourceModel(deviceDB)
|
||||
|
||||
def filterAcceptsRow(self, source_row, source_parent):
|
||||
def filterAcceptsRow(
|
||||
self,
|
||||
source_row: int,
|
||||
source_parent: QtCore.QModelIndex # pylint: disable=unused-argument
|
||||
) -> bool:
|
||||
device = self.sourceModel()[source_row]
|
||||
return any(
|
||||
port.type == engine.PortType.AUDIO and port.direction == engine.PortDirection.INPUT
|
||||
|
@ -92,7 +98,7 @@ class SinkDevices(QtCore.QSortFilterProxyModel):
|
|||
class Node(GraphNode):
|
||||
isConnected, isConnectedChanged = ui_base.Property('isConnected', bool)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.__devices = SinkDevices(self.app.deviceDB, self)
|
||||
|
||||
|
@ -104,15 +110,15 @@ class Node(GraphNode):
|
|||
self.node.deviceChanged.connect(lambda _: self.__updateIsConnected())
|
||||
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def devices(self):
|
||||
def devices(self) -> QtCore.QAbstractItemModel:
|
||||
return self.__devices
|
||||
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def devicePorts(self):
|
||||
def devicePorts(self) -> QtCore.QAbstractItemModel:
|
||||
return self.__devicePorts
|
||||
|
||||
@QtCore.Slot(str)
|
||||
def setDevice(self, v):
|
||||
def setDevice(self, v: str) -> None:
|
||||
if v == self.node.device:
|
||||
return
|
||||
|
||||
|
@ -128,14 +134,14 @@ class Node(GraphNode):
|
|||
self.node.devicePorts[i] = ports[i] if i < len(ports) else ''
|
||||
|
||||
@QtCore.Slot(int, str)
|
||||
def setDevicePort(self, idx, v):
|
||||
def setDevicePort(self, idx: int, v: str) -> None:
|
||||
if v == self.node.devicePorts[idx]:
|
||||
return
|
||||
|
||||
with self.project.captureChanges("{}: Change device port".format(self.node.title)):
|
||||
self.node.devicePorts[idx] = v
|
||||
|
||||
def __updateDevicePorts(self):
|
||||
def __updateDevicePorts(self) -> None:
|
||||
try:
|
||||
device = self.app.deviceDB.getDevice(self.node.device)
|
||||
except KeyError:
|
||||
|
@ -143,7 +149,7 @@ class Node(GraphNode):
|
|||
else:
|
||||
self.__devicePorts.setFromDevice(device)
|
||||
|
||||
def __updateIsConnected(self):
|
||||
def __updateIsConnected(self) -> None:
|
||||
for r in range(self.__devices.rowCount()):
|
||||
if self.__devices.data(self.__devices.index(r, 0)) == self.node.device:
|
||||
self.isConnected = True
|
||||
|
|
|
@ -22,13 +22,18 @@ import importlib
|
|||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import typing
|
||||
from typing import Dict, Type
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from noisicaa.ui.GraphNode import GraphNode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UIRegistry:
|
||||
def __init__(self):
|
||||
self.classes = {}
|
||||
def __init__(self) -> None:
|
||||
self.classes = {} # type: Dict[str, Type[GraphNode]]
|
||||
|
||||
base = os.path.dirname(__file__)
|
||||
for node in os.listdir(base):
|
||||
|
@ -43,22 +48,27 @@ class UIRegistry:
|
|||
|
||||
logger.info("Importing UI module for node '%s'...", node)
|
||||
|
||||
ui_mod_name = 'noisicaa.node_lib.{}.{}'.format(node, 'ui')
|
||||
ui_mod_name = 'noisicaa.node_lib.{}.ui'.format(node)
|
||||
ui_mod = importlib.import_module(ui_mod_name)
|
||||
|
||||
cls = getattr(ui_mod, 'CLASS')
|
||||
assert cls is not None, ui_mod_name
|
||||
uri = getattr(ui_mod, 'URI')
|
||||
assert uri is not None, ui_mod_name
|
||||
|
||||
seen = set()
|
||||
for c in ui_mod.CLASS.__mro__:
|
||||
for c in cls.__mro__:
|
||||
# https://bugreports.qt.io/browse/PYSIDE-983
|
||||
assert c.__name__ not in seen, (
|
||||
"Duplicate class name {} in MRO of class {}.{}".format(
|
||||
c.__name__, ui_mod.CLASS.__module__, ui_mod.CLASS.__qualname__))
|
||||
c.__name__, cls.__module__, cls.__qualname__))
|
||||
seen.add(c.__name__)
|
||||
|
||||
assert ui_mod.URI not in self.classes, (
|
||||
"Duplicate URI {} in {}".format(ui_mod.URI, ui_mod_name))
|
||||
self.classes[ui_mod.URI] = ui_mod.CLASS
|
||||
assert uri not in self.classes, (
|
||||
"Duplicate URI {} in {}".format(uri, ui_mod_name))
|
||||
self.classes[uri] = cls
|
||||
|
||||
def getClass(self, uri):
|
||||
def getClass(self, uri: str) -> 'Type[GraphNode]':
|
||||
try:
|
||||
return self.classes[uri]
|
||||
except KeyError:
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
# @end:license
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from noisicaa.ui import ui_base
|
||||
from noisicaa.ui.GraphNode import GraphNode
|
||||
|
@ -31,12 +32,12 @@ class Node(GraphNode):
|
|||
current, currentChanged = ui_base.Property('current', float, default=-80.0)
|
||||
peak, peakChanged = ui_base.Property('peak', float, default=-80.0)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.setNodeMessageHandler(self.__handleNodeMessage)
|
||||
|
||||
def __handleNodeMessage(self, msg_serialized: bytes):
|
||||
def __handleNodeMessage(self, msg_serialized: bytes) -> None:
|
||||
msg = node_message_fb.NodeMessage(msg_serialized)
|
||||
self.current = msg.current
|
||||
self.peak = msg.peak
|
||||
|
|
|
@ -21,11 +21,13 @@
|
|||
import contextlib
|
||||
import os
|
||||
import os.path
|
||||
from typing import Dict, Iterator, List
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2 import QtCore
|
||||
from PySide2 import QtGui
|
||||
from PySide2 import QtQml
|
||||
from PySide2 import QtQuick
|
||||
import flatbuffers
|
||||
|
||||
import noisicaa
|
||||
|
@ -44,24 +46,24 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class RecentProjects(QtCore.QStringListModel):
|
||||
def __init__(self, parent):
|
||||
def __init__(self, parent: QtCore.QObject) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.__paths = []
|
||||
self.__paths = [] # type: List[str]
|
||||
|
||||
def paths(self):
|
||||
def paths(self) -> List[str]:
|
||||
return list(self.__paths)
|
||||
|
||||
def setPaths(self, lst):
|
||||
def setPaths(self, lst: List[str]) -> None:
|
||||
self.__paths = list(lst)
|
||||
self.setStringList(self.__paths)
|
||||
|
||||
def roleNames(self):
|
||||
def roleNames(self) -> Dict[int, bytes]:
|
||||
return {
|
||||
Qt.DisplayRole: b'path',
|
||||
}
|
||||
|
||||
def addPath(self, path):
|
||||
def addPath(self, path: str) -> None:
|
||||
for idx, p in reversed(list(enumerate(self.__paths))):
|
||||
if p == path:
|
||||
del self.__paths[idx]
|
||||
|
@ -98,17 +100,17 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
self._project = None # type: Project
|
||||
self._qmlEngine = None # type: QtQml.QQmlApplicationEngine
|
||||
self._projectWindowComponent = None # type: QtQml.QQmlComponent
|
||||
self._window = None # type: Window
|
||||
self._window = None # type: QtQuick.QQuickItem
|
||||
|
||||
@ui_base.ConstProperty(str)
|
||||
def appVersion(self) -> str:
|
||||
return noisicaa.__version__
|
||||
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def recentProjects(self):
|
||||
def recentProjects(self) -> RecentProjects:
|
||||
return self._recentProjects
|
||||
|
||||
def addRecentProject(self, path):
|
||||
def addRecentProject(self, path: str) -> None:
|
||||
self._recentProjects.addPath(path)
|
||||
self.settings.setValue('recentProjects', self._recentProjects.paths())
|
||||
|
||||
|
@ -145,7 +147,7 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
raise NotImplementedError
|
||||
|
||||
@contextlib.contextmanager
|
||||
def settingsGroup(self, group) -> QtCore.QSettings:
|
||||
def settingsGroup(self, group: str) -> Iterator[QtCore.QSettings]:
|
||||
self.settings.beginGroup(group)
|
||||
try:
|
||||
yield self.settings
|
||||
|
@ -156,7 +158,7 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
def engine(self) -> engine_lib.Engine:
|
||||
return self._engine
|
||||
|
||||
def createQmlComponent(self, path):
|
||||
def createQmlComponent(self, path: str) -> QtQml.QQmlComponent:
|
||||
path = os.path.join(os.path.dirname(os.path.dirname(noisicaa.__file__)), path)
|
||||
url = QtCore.QUrl.fromLocalFile(path)
|
||||
component = QtQml.QQmlComponent(self.qmlEngine)
|
||||
|
@ -167,9 +169,9 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
raise RuntimeError("Failed to load component '{}'".format(path))
|
||||
return component
|
||||
|
||||
def updateAudioBackend(self):
|
||||
def updateAudioBackend(self) -> None:
|
||||
with self.settingsGroup('audio') as settings:
|
||||
backend = settings.value('backend', 'jack')
|
||||
backend = str(settings.value('backend', 'jack'))
|
||||
|
||||
builder = flatbuffers.Builder(1024)
|
||||
builder.Finish(engine_lib.CreateBackendSettings(
|
||||
|
@ -182,7 +184,7 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
return SettingsDialog(parent=self, context=self.__context)
|
||||
|
||||
@QtCore.Slot()
|
||||
def newProject(self):
|
||||
def newProject(self) -> None:
|
||||
logger.info("New project...")
|
||||
if self._project is not None:
|
||||
self._project.cleanup()
|
||||
|
@ -194,7 +196,7 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
self._window.setProperty('d', self._project)
|
||||
|
||||
@QtCore.Slot(str)
|
||||
def openProject(self, path):
|
||||
def openProject(self, path: str) -> None:
|
||||
if path.startswith('file://'):
|
||||
path = path[7:]
|
||||
logger.info("Open project '%s'...", path)
|
||||
|
@ -211,7 +213,7 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
self._window.setProperty('d', self._project)
|
||||
|
||||
@QtCore.Slot()
|
||||
def showEngineProgramWindow(self):
|
||||
def showEngineProgramWindow(self) -> None:
|
||||
if self.__engineProgramWindow is None:
|
||||
self.__engineProgramComponent = self.createQmlComponent(
|
||||
os.path.join(os.path.dirname(__file__), 'EngineProgramWindow.qml'))
|
||||
|
@ -222,5 +224,5 @@ class App(ui_base.PropertyContainer, QtCore.QObject):
|
|||
self.__engineProgramWindow.show()
|
||||
self.__engineProgramWindow.requestActivate()
|
||||
|
||||
def __programChanged(self):
|
||||
def __programChanged(self, unused_: object=None) -> None:
|
||||
self.engineProgram = self._engine.program_code()
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
# @end:license
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2 import QtCore
|
||||
|
@ -29,15 +30,15 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class CreateNodeDialog(ui_base.ProjectMixin, QtCore.QObject):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.__model = QtCore.QSortFilterProxyModel(self)
|
||||
self.__model.setSourceModel(self.app.nodeDB)
|
||||
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def nodeDB(self) -> QtCore.QAbstractItemModel:
|
||||
return self.__model
|
||||
nodeDB = QtCore.Property(QtCore.QObject, fget=nodeDB, constant=True)
|
||||
|
||||
@QtCore.Slot(str)
|
||||
def setFilter(self, filter: str) -> None:
|
||||
|
|
|
@ -20,29 +20,32 @@
|
|||
|
||||
import bisect
|
||||
import logging
|
||||
from typing import Any, Dict, Iterator, List
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2 import QtCore
|
||||
|
||||
from noisicaa import engine
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Port:
|
||||
def __init__(self, name, direction, type):
|
||||
def __init__(self, name: str, direction: engine.PortDirection, type: engine.PortType) -> None:
|
||||
self.name = name
|
||||
self.direction = direction
|
||||
self.type = type
|
||||
|
||||
|
||||
class Device:
|
||||
def __init__(self, name):
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
self.ports = []
|
||||
self.ports = [] # type: List[Port]
|
||||
|
||||
def addPort(self, name, direction, type):
|
||||
def addPort(self, name: str, direction: engine.PortDirection, type: engine.PortType) -> None:
|
||||
self.ports.append(Port(name, direction, type))
|
||||
|
||||
def __lt__(self, other):
|
||||
def __lt__(self, other: object) -> bool:
|
||||
if isinstance(other, str):
|
||||
return self.name < other
|
||||
if isinstance(other, Device):
|
||||
|
@ -51,29 +54,29 @@ class Device:
|
|||
|
||||
|
||||
class DeviceDB(QtCore.QAbstractListModel):
|
||||
def __init__(self, parent=None):
|
||||
def __init__(self, parent: QtCore.QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.__devices = []
|
||||
self.__devices = [] # type: List[Device]
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Iterator[Device]:
|
||||
yield from self.__devices
|
||||
|
||||
def __getitem__(self, idx):
|
||||
def __getitem__(self, idx: int) -> Device:
|
||||
return self.__devices[idx]
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
return len(self.__devices)
|
||||
|
||||
def roleNames(self):
|
||||
def roleNames(self) -> Dict[int, bytes]:
|
||||
return {
|
||||
Qt.DisplayRole: b'deviceName',
|
||||
}
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
def rowCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int: # pylint: disable=unused-argument
|
||||
return len(self.__devices)
|
||||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
def data(self, index: QtCore.QModelIndex, role: int = Qt.DisplayRole) -> Any:
|
||||
if not index.isValid():
|
||||
return None
|
||||
if index.row() < 0 or index.row() >= len(self.__devices):
|
||||
|
@ -84,13 +87,13 @@ class DeviceDB(QtCore.QAbstractListModel):
|
|||
return device.name
|
||||
return None
|
||||
|
||||
def getDevice(self, name):
|
||||
def getDevice(self, name: str) -> Device:
|
||||
idx = bisect.bisect_left(self.__devices, name)
|
||||
if idx < len(self.__devices) and self.__devices[idx].name == name:
|
||||
return self.__devices[idx]
|
||||
raise KeyError(name)
|
||||
|
||||
def addDevice(self, name):
|
||||
def addDevice(self, name: str) -> Device:
|
||||
idx = bisect.bisect_left(self.__devices, name)
|
||||
assert idx >= len(self.__devices) or self.__devices[idx].name != name, "Device {} already exists".format(name)
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
# @end:license
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from PySide2 import QtCore
|
||||
|
||||
|
@ -33,24 +34,24 @@ logger = logging.getLogger(__name__)
|
|||
class GraphConnection(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
||||
highlighted, highlightedChanged = ui_base.Property('highlighted', bool)
|
||||
|
||||
def __init__(self, *, connection, nodeMap, **kwargs):
|
||||
def __init__(self, *, connection: model.Connection, nodeMap: Dict[int, GraphNode], **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.__connection = connection
|
||||
self.__srcNode = nodeMap[self.__connection.srcNodeId]
|
||||
self.__destNode = nodeMap[self.__connection.destNodeId]
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return '<{} {}>'.format(type(self).__name__, self.__connection)
|
||||
|
||||
@ui_base.ConstProperty(model.Connection)
|
||||
def connection(self):
|
||||
def connection(self) -> model.Connection:
|
||||
return self.__connection
|
||||
|
||||
@ui_base.ConstProperty(GraphNode)
|
||||
def srcNode(self):
|
||||
def srcNode(self) -> GraphNode:
|
||||
return self.__srcNode
|
||||
|
||||
@ui_base.ConstProperty(GraphNode)
|
||||
def destNode(self):
|
||||
def destNode(self) -> GraphNode:
|
||||
return self.__destNode
|
||||
|
|
|
@ -21,11 +21,13 @@
|
|||
import logging
|
||||
import sys
|
||||
import textwrap
|
||||
from typing import Any, Callable, Dict, List
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2 import QtCore
|
||||
from PySide2 import QtGui
|
||||
from PySide2 import QtWidgets
|
||||
from PySide2 import QtQml
|
||||
from PySide2 import QtQuick
|
||||
|
||||
from noisicaa import model
|
||||
from noisicaa import engine
|
||||
|
@ -51,7 +53,7 @@ class GraphNodePort(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObj
|
|||
glowing, glowingChanged = ui_base.Property('glowing', bool)
|
||||
numConnections, numConnectionsChanged = ui_base.Property('numConnection', int)
|
||||
|
||||
def __init__(self, *, node, port, **kwargs):
|
||||
def __init__(self, *, node: 'GraphNode', port: model.Port, **kwargs: Any) -> None:
|
||||
super().__init__(parent=node, **kwargs)
|
||||
|
||||
self.__node = node
|
||||
|
@ -62,11 +64,11 @@ class GraphNodePort(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObj
|
|||
|
||||
# Can't declare this as GraphNode, because this would cause a dependency cycle.
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def node(self):
|
||||
def node(self) -> 'GraphNode':
|
||||
return self.__node
|
||||
|
||||
@ui_base.ConstProperty(model.Port)
|
||||
def port(self):
|
||||
def port(self) -> model.Port:
|
||||
return self.__port
|
||||
|
||||
@QtCore.Slot(float)
|
||||
|
@ -81,17 +83,15 @@ class GraphNodePort(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObj
|
|||
|
||||
|
||||
class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
||||
def __init__(self, *, node, **kwargs):
|
||||
def __init__(self, *, node: model.GraphNode, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.__node_message_listener = None
|
||||
|
||||
self.__node = node
|
||||
self.__description = self.app.nodeDB.nodeDescription(self.__node.uri)
|
||||
|
||||
self.__ports = {}
|
||||
self.__in_ports = []
|
||||
self.__out_ports = []
|
||||
self.__ports = {} # type: Dict[str, GraphNodePort]
|
||||
self.__in_ports = [] # type: List[GraphNodePort]
|
||||
self.__out_ports = [] # type: List[GraphNodePort]
|
||||
for port in self.__node.ports:
|
||||
wrapper = GraphNodePort(node=self, port=port, context=self.context)
|
||||
self.__ports[port.name] = wrapper
|
||||
|
@ -107,41 +107,43 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
self.__active = False
|
||||
self.__selected = False
|
||||
|
||||
self.__nodeMessageListener = None
|
||||
self.__nodeMessageListener = None # type: int
|
||||
|
||||
self.__body = None
|
||||
self.__bodyComponent = None
|
||||
self.__body = None # type: QtQuick.QQuickItem
|
||||
self.__bodyComponent = None # type: QtQml.QQmlComponent
|
||||
|
||||
def cleanup(self):
|
||||
def cleanup(self) -> None:
|
||||
if self.__nodeMessageListener is not None:
|
||||
self.engine.remove_node_message_listener(self.__node.id, self.__nodeMessageListener)
|
||||
self.__nodeMessageListener = None
|
||||
|
||||
def setNodeMessageHandler(self, handler):
|
||||
def setNodeMessageHandler(self, handler: Callable[[bytes], None]) -> None:
|
||||
assert self.__nodeMessageListener is None
|
||||
self.__nodeMessageListener = self.engine.add_node_message_listener(self.__node.id, handler)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return '<{} {}>'.format(type(self).__name__, self.__node)
|
||||
|
||||
@ui_base.ConstProperty(model.GraphNode)
|
||||
def node(self):
|
||||
def node(self) -> model.GraphNode:
|
||||
return self.__node
|
||||
|
||||
@ui_base.ConstProperty(engine.NodeDescription)
|
||||
def description(self):
|
||||
def description(self) -> engine.NodeDescription:
|
||||
return self.__description
|
||||
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def body(self):
|
||||
def body(self) -> QtQuick.QQuickItem:
|
||||
if self.__body is None and self.__description.ui is not None and self.__description.ui.body_qml is not None:
|
||||
logger.info("%s#%016x: Loading body component '%s'...", self.__node.uri, self.__node.id, self.__description.ui.body_qml)
|
||||
logger.info(
|
||||
"%s#%016x: Loading body component '%s'...",
|
||||
self.__node.uri, self.__node.id, self.__description.ui.body_qml)
|
||||
self.__bodyComponent = self.app.createQmlComponent(self.__description.ui.body_qml)
|
||||
self.__body = self.__bodyComponent.createWithInitialProperties({'d': self})
|
||||
return self.__body
|
||||
|
||||
@QtCore.Slot()
|
||||
def moveDone(self):
|
||||
def moveDone(self) -> None:
|
||||
with self.project.captureChanges("{}: Move node".format(self.__node.title)):
|
||||
self.__node.rect = QtCore.QRectF(
|
||||
int(self.__rect.x() / self.__zoom + 10) // 20 * 20,
|
||||
|
@ -150,7 +152,7 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
int(self.__rect.height() / self.__zoom + 10) // 20 * 20)
|
||||
|
||||
@QtCore.Slot()
|
||||
def resizeDone(self):
|
||||
def resizeDone(self) -> None:
|
||||
with self.project.captureChanges("{}: Resize node".format(self.__node.title)):
|
||||
self.__node.rect = QtCore.QRectF(
|
||||
self.__rect.x() / self.__zoom,
|
||||
|
@ -159,34 +161,13 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
self.__rect.height() / self.__zoom)
|
||||
|
||||
@QtCore.Slot()
|
||||
def showContextMenu(self):
|
||||
menu = QtWidgets.QMenu(self.__view)
|
||||
|
||||
rename_action = menu.addAction("Rename...")
|
||||
rename_action.triggered.connect(self.__rename)
|
||||
|
||||
color_menu = menu.addMenu("Set color")
|
||||
color_action = SelectColorAction(color_menu)
|
||||
color_action.colorSelected.connect(self.__setColor)
|
||||
color_menu.addAction(color_action)
|
||||
|
||||
menu.addAction("Cut")
|
||||
menu.addAction("Copy")
|
||||
menu.addAction("Duplicate")
|
||||
delete_action = menu.addAction("Delete")
|
||||
delete_action.triggered.connect(self.delete)
|
||||
|
||||
if not menu.isEmpty():
|
||||
menu.popup(QtGui.QCursor.pos())
|
||||
|
||||
@QtCore.Slot()
|
||||
def delete(self):
|
||||
def delete(self) -> None:
|
||||
with self.project.captureChanges("{}: Delete node".format(self.__node.title)):
|
||||
self.model.deleteNode(self.__node)
|
||||
|
||||
def getRect(self):
|
||||
def getRect(self) -> QtCore.QRectF:
|
||||
return self.__rect
|
||||
def setRect(self, r):
|
||||
def setRect(self, r: QtCore.QRectF) -> None:
|
||||
r = QtCore.QRectF(
|
||||
int(r.x() / self.__zoom + 10) // 20 * 20 * self.__zoom,
|
||||
int(r.y() / self.__zoom + 10) // 20 * 20 * self.__zoom,
|
||||
|
@ -223,7 +204,7 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
rectChanged = QtCore.Signal(QtCore.QRectF)
|
||||
rect = QtCore.Property(QtCore.QRectF, fget=getRect, fset=setRect, notify=rectChanged)
|
||||
|
||||
def __nodeRectChanged(self, rect):
|
||||
def __nodeRectChanged(self, rect: QtCore.QRectF) -> None:
|
||||
self.setRect(QtCore.QRectF(
|
||||
int(self.__zoom * rect.x()),
|
||||
int(self.__zoom * rect.y()),
|
||||
|
@ -231,22 +212,22 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
int(self.__zoom * rect.height())))
|
||||
|
||||
@QtCore.Slot(str, result=GraphNodePort)
|
||||
def port(self, portName):
|
||||
def port(self, portName: str) -> GraphNodePort:
|
||||
return self.__ports.get(portName)
|
||||
|
||||
def ports(self):
|
||||
@ui_base.ConstProperty(List[GraphNodePort], list)
|
||||
def ports(self) -> List[GraphNodePort]:
|
||||
return [self.__ports[port.name] for port in self.__node.ports]
|
||||
ports = QtCore.Property(list, fget=ports, constant=True)
|
||||
|
||||
def setZoom(self, v):
|
||||
def setZoom(self, v: float) -> None:
|
||||
if v == self.__zoom:
|
||||
return
|
||||
self.__zoom = v
|
||||
self.__nodeRectChanged(self.__node.rect)
|
||||
|
||||
def isActive(self):
|
||||
def isActive(self) -> bool:
|
||||
return self.__active
|
||||
def setActive(self, v):
|
||||
def setActive(self, v: bool) -> None:
|
||||
if v == self.__active:
|
||||
return
|
||||
self.__active = v
|
||||
|
@ -254,9 +235,9 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
activeChanged = QtCore.Signal(bool)
|
||||
active = QtCore.Property(bool, fget=isActive, fset=setActive, notify=activeChanged)
|
||||
|
||||
def isSelected(self):
|
||||
def isSelected(self) -> bool:
|
||||
return self.__selected
|
||||
def setSelected(self, v):
|
||||
def setSelected(self, v: bool) -> None:
|
||||
if v == self.__selected:
|
||||
return
|
||||
self.__selected = v
|
||||
|
@ -270,7 +251,8 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
with self.project.captureChanges("{}: Rename node".format(self.__node.title)):
|
||||
self.__node.title = title
|
||||
|
||||
def nodeColors(self):
|
||||
@ui_base.ConstProperty(List[QtGui.QColor], list)
|
||||
def nodeColors(self) -> List[QtGui.QColor]:
|
||||
return [
|
||||
QtGui.QColor(180, 180, 180),
|
||||
QtGui.QColor(205, 205, 205),
|
||||
|
@ -282,8 +264,8 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
QtGui.QColor(255, 205, 205),
|
||||
QtGui.QColor(255, 230, 230),
|
||||
|
||||
QtGui.QColor(255, 155, 0.1),
|
||||
QtGui.QColor(255, 180, 0.3),
|
||||
QtGui.QColor(255, 155, 26),
|
||||
QtGui.QColor(255, 180, 77),
|
||||
QtGui.QColor(255, 205, 155),
|
||||
QtGui.QColor(255, 230, 205),
|
||||
|
||||
|
@ -312,7 +294,6 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
QtGui.QColor(205, 255, 255),
|
||||
QtGui.QColor(230, 255, 255),
|
||||
]
|
||||
nodeColors = QtCore.Property(list, fget=nodeColors, constant=True)
|
||||
|
||||
@QtCore.Slot(QtGui.QColor)
|
||||
def setColor(self, color: QtGui.QColor) -> None:
|
||||
|
@ -321,7 +302,7 @@ class GraphNode(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
self.__node.color = color
|
||||
|
||||
@QtCore.Slot(result=str)
|
||||
def debugInfo(self):
|
||||
def debugInfo(self) -> str:
|
||||
info = []
|
||||
info.append(str(self))
|
||||
info.append("class: {}".format(type(self).__qualname__))
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
import functools
|
||||
import logging
|
||||
from typing import Any, Dict, Generic, Iterator, List, Sequence, TypeVar
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2 import QtCore
|
||||
|
@ -32,26 +33,26 @@ from .GraphConnection import GraphConnection
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ObjectList(QtCore.QAbstractListModel):
|
||||
def __init__(self, parent=None):
|
||||
OBJ = TypeVar('OBJ')
|
||||
class ObjectList(Generic[OBJ], QtCore.QAbstractListModel):
|
||||
def __init__(self, parent: QtCore.QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.__list = []
|
||||
self.__list = [] # type: List[OBJ]
|
||||
|
||||
def __iter__(self):
|
||||
def __iter__(self) -> Iterator[OBJ]:
|
||||
yield from self.__list
|
||||
|
||||
def __len__(self):
|
||||
def __len__(self) -> int:
|
||||
return len(self.__list)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
def __getitem__(self, idx: int) -> OBJ:
|
||||
return self.__list[idx]
|
||||
|
||||
def rowCount(self, parent=QtCore.QModelIndex()):
|
||||
def rowCount(self, parent: QtCore.QModelIndex = QtCore.QModelIndex()) -> int: # pylint: disable=unused-argument
|
||||
return len(self.__list)
|
||||
|
||||
def data(self, index, role=Qt.DisplayRole):
|
||||
def data(self, index: QtCore.QModelIndex, role: int = Qt.DisplayRole) -> OBJ:
|
||||
if role != Qt.DisplayRole:
|
||||
return None
|
||||
if not index.isValid():
|
||||
|
@ -60,12 +61,12 @@ class ObjectList(QtCore.QAbstractListModel):
|
|||
return None
|
||||
return self.__list[index.row()]
|
||||
|
||||
def insertObject(self, row, obj):
|
||||
def insertObject(self, row: int, obj: OBJ) -> None:
|
||||
self.beginInsertRows(QtCore.QModelIndex(), row, row)
|
||||
self.__list.insert(row, obj)
|
||||
self.endInsertRows()
|
||||
|
||||
def removeObject(self, row):
|
||||
def removeObject(self, row: int) -> OBJ:
|
||||
self.beginRemoveRows(QtCore.QModelIndex(), row, row)
|
||||
obj = self.__list.pop(row)
|
||||
self.endRemoveRows()
|
||||
|
@ -78,16 +79,16 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
offset, offsetChanged = ui_base.Property('offset', QtCore.QPointF)
|
||||
contentRect, contentRectChanged = ui_base.Property('contextRect', QtCore.QRectF)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.__nodes = ObjectList(self)
|
||||
self.__nodeMap = {}
|
||||
self.__nodes = ObjectList[GraphNode](self)
|
||||
self.__nodeMap = {} # type: Dict[int, GraphNode]
|
||||
for idx, node in enumerate(self.model.nodes):
|
||||
self.__addNode(idx, node)
|
||||
self.model.nodesChanged.connect(self.__nodesChanged)
|
||||
|
||||
self.__connections = ObjectList(self)
|
||||
self.__connections = ObjectList[GraphConnection](self)
|
||||
for idx, connection in enumerate(self.model.connections):
|
||||
self.__addConnection(idx, connection)
|
||||
self.model.connectionsChanged.connect(self.__connectionsChanged)
|
||||
|
@ -99,20 +100,20 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
self.__selection = QtCore.QItemSelectionModel(self.__nodes, self)
|
||||
self.__selection.selectionChanged.connect(self.__selectionChanged)
|
||||
|
||||
def cleanup(self):
|
||||
def cleanup(self) -> None:
|
||||
while self.__connections:
|
||||
self.__removeConnection(0)
|
||||
while self.__nodes:
|
||||
self.__removeNode(0)
|
||||
|
||||
def saveState(self):
|
||||
def saveState(self) -> Dict[str, Any]:
|
||||
return {
|
||||
'x': self.offset.x(),
|
||||
'y': self.offset.y(),
|
||||
'zoom': self.zoom,
|
||||
}
|
||||
|
||||
def restoreState(self, state):
|
||||
def restoreState(self, state: Dict[str, Any]) -> None:
|
||||
self.offset = QtCore.QPointF(
|
||||
float(state.get('x', 0.0)), float(state.get('y', 0.0)))
|
||||
self.zoom = float(state.get('zoom', 1.0))
|
||||
|
@ -121,24 +122,24 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
def selection(self) -> QtCore.QItemSelectionModel:
|
||||
return self.__selection
|
||||
|
||||
def hasSelection(self) -> bool:
|
||||
def getHasSelection(self) -> bool:
|
||||
return self.__hasSelection
|
||||
hasSelectionChanged = QtCore.Signal(bool)
|
||||
hasSelection = QtCore.Property(bool, fget=hasSelection, notify=hasSelectionChanged)
|
||||
hasSelection = QtCore.Property(bool, fget=getHasSelection, notify=hasSelectionChanged)
|
||||
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def nodes(self):
|
||||
def nodes(self) -> ObjectList[GraphNode]:
|
||||
return self.__nodes
|
||||
|
||||
@ui_base.ConstProperty(QtCore.QObject)
|
||||
def connections(self):
|
||||
def connections(self) -> ObjectList[GraphConnection]:
|
||||
return self.__connections
|
||||
|
||||
def mapFromGlobal(self, pos: QtCore.QPointF) -> QtCore.QPointF:
|
||||
return (self.__item.mapFromGlobal(pos) - self.offset) / self.zoom
|
||||
|
||||
@QtCore.Slot()
|
||||
def selectAll(self):
|
||||
def selectAll(self) -> None:
|
||||
self.__selection.select(
|
||||
QtCore.QItemSelection(self.__nodes.index(0), self.__nodes.index(self.__nodes.rowCount() - 1)),
|
||||
QtCore.QItemSelectionModel.Select)
|
||||
|
@ -167,7 +168,7 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
node.rect = node.rect.translated(delta)
|
||||
|
||||
@QtCore.Slot(GraphNodePort, result=list)
|
||||
def getTargetPorts(self, src: GraphNodePort) -> None:
|
||||
def getTargetPorts(self, src: GraphNodePort) -> Sequence[GraphNodePort]:
|
||||
ports = []
|
||||
for wrapper in self.__nodes:
|
||||
for port in wrapper.ports:
|
||||
|
@ -198,11 +199,12 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
srcPort = src.port.name
|
||||
destNode = dest.node.node
|
||||
destPort = dest.port.name
|
||||
with self.project.captureChanges("Connect '{}:{}' to '{}:{}'".format(srcNode.title, srcPort, destNode.title, destPort)):
|
||||
with self.project.captureChanges(
|
||||
"Connect '{}:{}' to '{}:{}'".format(srcNode.title, srcPort, destNode.title, destPort)):
|
||||
self.model.connectPorts(srcNode, srcPort, destNode, destPort)
|
||||
|
||||
@QtCore.Slot(GraphConnection)
|
||||
def disconnectPorts(self, wrapper: GraphConnection):
|
||||
def disconnectPorts(self, wrapper: GraphConnection) -> None:
|
||||
self.unhighlightConnection(wrapper)
|
||||
conn = wrapper.connection
|
||||
with self.project.captureChanges("Disconnect '{}:{}' from '{}:{}'".format(
|
||||
|
@ -231,7 +233,7 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
|
||||
self.__updateContentRect()
|
||||
|
||||
def __addNode(self, index, node):
|
||||
def __addNode(self, index: int, node: model.GraphNode) -> None:
|
||||
nodeDesc = self.app.nodeDB.nodeDescription(node.uri)
|
||||
if nodeDesc.ui is not None and nodeDesc.ui.uri is not None:
|
||||
wrapperClass = self.app.uiRegistry.getClass(nodeDesc.ui.uri)
|
||||
|
@ -249,13 +251,13 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
self.__nodeMap[node.id] = wrapper
|
||||
self.__nodes.insertObject(index, wrapper)
|
||||
|
||||
def __removeNode(self, index):
|
||||
def __removeNode(self, index: int) -> None:
|
||||
wrapper = self.__nodes.removeObject(index)
|
||||
wrapper.cleanup()
|
||||
self.__nodeMap.pop(wrapper.node.id)
|
||||
|
||||
@QtCore.Slot(QtCore.QPointF, str)
|
||||
def insertNode(self, pos: QtCore.QPointF, uri: str):
|
||||
def insertNode(self, pos: QtCore.QPointF, uri: str) -> None:
|
||||
desc = self.app.nodeDB.nodeDescription(uri)
|
||||
title = desc.display_name or desc.uri
|
||||
with self.project.captureChanges("Add node '{}'".format(title)):
|
||||
|
@ -265,13 +267,13 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
title=title,
|
||||
rect=QtCore.QRectF(pos, QtCore.QSizeF(100, 100)))
|
||||
|
||||
def __activeNodeChanged(self, node, active):
|
||||
def __activeNodeChanged(self, node: GraphNode, active: bool) -> None:
|
||||
if active:
|
||||
for n in self.__nodes:
|
||||
if n is not node:
|
||||
n.active = False
|
||||
|
||||
def __selectionChanged(self, selected, deselected):
|
||||
def __selectionChanged(self, selected: QtCore.QItemSelection, deselected: QtCore.QItemSelection) -> None:
|
||||
for index in selected.indexes():
|
||||
node = self.__nodes.data(index)
|
||||
node.selected = True
|
||||
|
@ -292,7 +294,7 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
else:
|
||||
raise TypeError(type(change))
|
||||
|
||||
def __addConnection(self, index, connection):
|
||||
def __addConnection(self, index: int, connection: model.Connection) -> None:
|
||||
srcPort = self.__nodeMap[connection.srcNodeId].port(connection.srcPort)
|
||||
srcPort.numConnections += 1
|
||||
destPort = self.__nodeMap[connection.destNodeId].port(connection.destPort)
|
||||
|
@ -304,7 +306,7 @@ class GraphView(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject)
|
|||
context=self.context)
|
||||
self.__connections.insertObject(index, wrapper)
|
||||
|
||||
def __removeConnection(self, index):
|
||||
def __removeConnection(self, index: int) -> None:
|
||||
wrapper = self.__connections.removeObject(index)
|
||||
connection = wrapper.connection
|
||||
srcPort = self.__nodeMap[connection.srcNodeId].port(connection.srcPort)
|
||||
|
|
|
@ -24,7 +24,7 @@ import json
|
|||
import logging
|
||||
import os
|
||||
import os.path
|
||||
from typing import Generator
|
||||
from typing import Any, Dict, Generator, List, Sequence, Tuple
|
||||
|
||||
from PySide2 import QtCore
|
||||
from PySide2 import QtWidgets
|
||||
|
@ -40,14 +40,14 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class Command(QtWidgets.QUndoCommand):
|
||||
def __init__(self, title, project, changes):
|
||||
def __init__(self, title: str, project: 'Project', changes: Sequence[model_lib.ModelChange]) -> None:
|
||||
super().__init__(title)
|
||||
|
||||
self.__project = project
|
||||
self.__changes = changes
|
||||
self.__state = 'created'
|
||||
|
||||
def undo(self):
|
||||
def undo(self) -> None:
|
||||
logger.info("undo '%s'", self.text())
|
||||
assert self.__state == 'done'
|
||||
|
||||
|
@ -69,7 +69,7 @@ class Command(QtWidgets.QUndoCommand):
|
|||
|
||||
self.__state = 'undone'
|
||||
|
||||
def redo(self):
|
||||
def redo(self) -> None:
|
||||
# QUndoStack calls Command.redo() when the command is pushed onto the stack. But we already
|
||||
# executed the actions, so there's nothing to do here.
|
||||
if self.__state == 'created':
|
||||
|
@ -101,7 +101,14 @@ class Command(QtWidgets.QUndoCommand):
|
|||
class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
||||
path, pathChanged = ui_base.Property('path', str)
|
||||
|
||||
def __init__(self, *, path, project, viewState, context: ui_base.CommonContext, **kwargs):
|
||||
def __init__(
|
||||
self, *,
|
||||
path: str,
|
||||
project: model_lib.Project,
|
||||
viewState: Dict[str, Any],
|
||||
context: ui_base.CommonContext,
|
||||
**kwargs: Any
|
||||
) -> None:
|
||||
super().__init__(
|
||||
context=ui_base.ProjectContext(
|
||||
app=context.app,
|
||||
|
@ -145,7 +152,10 @@ class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
|||
for node in self.__project.nodes:
|
||||
self.__nodeAdded(node)
|
||||
for conn in self.__project.connections:
|
||||
self.engine.connect_ports(conn.srcNodeId, conn.srcPort, conn.destNodeId, conn.destPort, engine.PortType.AUDIO)
|
||||
self.engine.connect_ports(
|
||||
conn.srcNodeId, conn.srcPort,
|
||||
conn.destNodeId, conn.destPort,
|
||||
engine.PortType.AUDIO)
|
||||
self.engine.compile_graph()
|
||||
self.__nodesListener = self.__project.nodesChanged.connect(self.__nodesChanged)
|
||||
self.__connectionsListener = self.__project.connectionsChanged.connect(self.__connectionsChanged)
|
||||
|
@ -187,8 +197,8 @@ class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
|||
MAGIC = '# NOISICAA-PROJECT\n'
|
||||
|
||||
@classmethod
|
||||
def load(cls, path: str, classRegistry) -> model_lib.Project:
|
||||
with open(path, 'r') as fp:
|
||||
def load(cls, path: str, classRegistry: model_lib.ClassRegistry) -> Tuple[model_lib.Project, Dict[str, Any]]:
|
||||
with open(path, 'r', encoding='utf-8') as fp:
|
||||
magic = fp.read(len(cls.MAGIC))
|
||||
if magic != cls.MAGIC:
|
||||
raise RuntimeError
|
||||
|
@ -196,9 +206,10 @@ class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
|||
data = json.load(fp)
|
||||
|
||||
assert data['format'] == 1, data
|
||||
return model_lib.Project.deserialize(parent=None, serialized=data['project'], classRegistry=classRegistry), data.get('view', {})
|
||||
return model_lib.Project.deserialize(
|
||||
parent=None, serialized=data['project'], classRegistry=classRegistry), data.get('view', {})
|
||||
|
||||
def __save(self, path=None) -> None:
|
||||
def __save(self, path: str = None) -> None:
|
||||
if path is None:
|
||||
assert self.path is not None
|
||||
path = self.path
|
||||
|
@ -216,7 +227,7 @@ class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
|||
'view': view,
|
||||
'project': self.__project.serialize(),
|
||||
}
|
||||
with open(path, 'w') as fp:
|
||||
with open(path, 'w', encoding='utf-8') as fp:
|
||||
fp.write(self.MAGIC)
|
||||
json.dump(data, fp, sort_keys=True, indent=2)
|
||||
fp.write('\n')
|
||||
|
@ -225,13 +236,13 @@ class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
|||
self.path = path
|
||||
|
||||
@QtCore.Slot()
|
||||
def save(self):
|
||||
def save(self) -> None:
|
||||
assert self.path is not None
|
||||
self.__save(self.path)
|
||||
self.app.addRecentProject(self.path)
|
||||
|
||||
@QtCore.Slot(str)
|
||||
def saveAs(self, path):
|
||||
def saveAs(self, path: str) -> None:
|
||||
logger.info("Save project as...")
|
||||
|
||||
if path.startswith('file://'):
|
||||
|
@ -241,7 +252,7 @@ class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
|||
|
||||
self.__save(path)
|
||||
|
||||
def __modelChanged(self, change: model_lib.ModelChange) -> None:
|
||||
def __modelChanged(self, change: model_lib.ModelChange) -> None: # pylint: disable=unused-argument
|
||||
assert self.__in_model_change
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
@ -249,7 +260,7 @@ class Project(ui_base.ProjectMixin, ui_base.PropertyContainer, QtCore.QObject):
|
|||
with self.applyChanges():
|
||||
logger.info("Beginning change '%s'...", name)
|
||||
|
||||
changes = []
|
||||
changes = [] # type: List[model_lib.ModelChange]
|
||||