mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-17 19:16:26 +02:00
Remove all references to pyCompatibility
- "pyCompatibility.basestring" are replaced by standard Python3 "str" - "Sequence" and "Iterable" from "collections" are now directly imported with "collections.abc"
This commit is contained in:
parent
d2c707f6fa
commit
635f85e7fd
10 changed files with 30 additions and 56 deletions
|
@ -23,7 +23,6 @@ except:
|
||||||
|
|
||||||
from meshroom.core.submitter import BaseSubmitter
|
from meshroom.core.submitter import BaseSubmitter
|
||||||
from . import desc
|
from . import desc
|
||||||
from . import pyCompatibility
|
|
||||||
|
|
||||||
# Setup logging
|
# Setup logging
|
||||||
logging.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', level=logging.INFO)
|
logging.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', level=logging.INFO)
|
||||||
|
@ -150,7 +149,7 @@ class Version(object):
|
||||||
self.components = tuple()
|
self.components = tuple()
|
||||||
elif len(args) == 1:
|
elif len(args) == 1:
|
||||||
versionName = args[0]
|
versionName = args[0]
|
||||||
if isinstance(versionName, pyCompatibility.basestring):
|
if isinstance(versionName, str):
|
||||||
self.components = Version.toComponents(versionName)
|
self.components = Version.toComponents(versionName)
|
||||||
elif isinstance(versionName, (list, tuple)):
|
elif isinstance(versionName, (list, tuple)):
|
||||||
self.components = tuple([int(v) for v in versionName])
|
self.components = tuple([int(v) for v in versionName])
|
||||||
|
|
|
@ -7,9 +7,10 @@ import weakref
|
||||||
import types
|
import types
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from collections.abc import Sequence
|
||||||
from string import Template
|
from string import Template
|
||||||
from meshroom.common import BaseObject, Property, Variant, Signal, ListModel, DictModel, Slot
|
from meshroom.common import BaseObject, Property, Variant, Signal, ListModel, DictModel, Slot
|
||||||
from meshroom.core import desc, pyCompatibility, hashValue
|
from meshroom.core import desc, hashValue
|
||||||
|
|
||||||
|
|
||||||
def attributeFactory(description, value, isOutput, node, root=None, parent=None):
|
def attributeFactory(description, value, isOutput, node, root=None, parent=None):
|
||||||
|
@ -213,7 +214,7 @@ class Attribute(BaseObject):
|
||||||
Return whether the given argument is a link expression.
|
Return whether the given argument is a link expression.
|
||||||
A link expression is a string matching the {nodeName.attrName} pattern.
|
A link expression is a string matching the {nodeName.attrName} pattern.
|
||||||
"""
|
"""
|
||||||
return isinstance(value, pyCompatibility.basestring) and Attribute.stringIsLinkRe.match(value)
|
return isinstance(value, str) and Attribute.stringIsLinkRe.match(value)
|
||||||
|
|
||||||
def getLinkParam(self, recursive=False):
|
def getLinkParam(self, recursive=False):
|
||||||
if not self.isLink:
|
if not self.isLink:
|
||||||
|
@ -264,13 +265,13 @@ class Attribute(BaseObject):
|
||||||
return self._value
|
return self._value
|
||||||
|
|
||||||
def getEvalValue(self):
|
def getEvalValue(self):
|
||||||
if isinstance(self.value, pyCompatibility.basestring):
|
if isinstance(self.value, str):
|
||||||
return Template(self.value).safe_substitute(os.environ)
|
return Template(self.value).safe_substitute(os.environ)
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
def getValueStr(self):
|
def getValueStr(self):
|
||||||
if isinstance(self.attributeDesc, desc.ChoiceParam) and not self.attributeDesc.exclusive:
|
if isinstance(self.attributeDesc, desc.ChoiceParam) and not self.attributeDesc.exclusive:
|
||||||
assert(isinstance(self.value, pyCompatibility.Sequence) and not isinstance(self.value, pyCompatibility.basestring))
|
assert(isinstance(self.value, Sequence) and not isinstance(self.value, str))
|
||||||
return self.attributeDesc.joinChar.join(self.getEvalValue())
|
return self.attributeDesc.joinChar.join(self.getEvalValue())
|
||||||
if isinstance(self.attributeDesc, (desc.StringParam, desc.File)):
|
if isinstance(self.attributeDesc, (desc.StringParam, desc.File)):
|
||||||
return '"{}"'.format(self.getEvalValue())
|
return '"{}"'.format(self.getEvalValue())
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from meshroom.common import BaseObject, Property, Variant, VariantList, JSValue
|
from meshroom.common import BaseObject, Property, Variant, VariantList, JSValue
|
||||||
from meshroom.core import pyCompatibility
|
|
||||||
|
|
||||||
|
from collections.abc import Iterable
|
||||||
from enum import Enum # available by default in python3. For python2: "pip install enum34"
|
from enum import Enum # available by default in python3. For python2: "pip install enum34"
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
@ -85,7 +85,7 @@ class ListAttribute(Attribute):
|
||||||
if JSValue is not None and isinstance(value, JSValue):
|
if JSValue is not None and isinstance(value, JSValue):
|
||||||
# Note: we could use isArray(), property("length").toInt() to retrieve all values
|
# Note: we could use isArray(), property("length").toInt() to retrieve all values
|
||||||
raise ValueError("ListAttribute.validateValue: cannot recognize QJSValue. Please, use JSON.stringify(value) in QML.")
|
raise ValueError("ListAttribute.validateValue: cannot recognize QJSValue. Please, use JSON.stringify(value) in QML.")
|
||||||
if isinstance(value, pyCompatibility.basestring):
|
if isinstance(value, str):
|
||||||
# Alternative solution to set values from QML is to convert values to JSON string
|
# Alternative solution to set values from QML is to convert values to JSON string
|
||||||
# In this case, it works with all data types
|
# In this case, it works with all data types
|
||||||
value = ast.literal_eval(value)
|
value = ast.literal_eval(value)
|
||||||
|
@ -124,7 +124,7 @@ class GroupAttribute(Attribute):
|
||||||
if JSValue is not None and isinstance(value, JSValue):
|
if JSValue is not None and isinstance(value, JSValue):
|
||||||
# Note: we could use isArray(), property("length").toInt() to retrieve all values
|
# Note: we could use isArray(), property("length").toInt() to retrieve all values
|
||||||
raise ValueError("GroupAttribute.validateValue: cannot recognize QJSValue. Please, use JSON.stringify(value) in QML.")
|
raise ValueError("GroupAttribute.validateValue: cannot recognize QJSValue. Please, use JSON.stringify(value) in QML.")
|
||||||
if isinstance(value, pyCompatibility.basestring):
|
if isinstance(value, str):
|
||||||
# Alternative solution to set values from QML is to convert values to JSON string
|
# Alternative solution to set values from QML is to convert values to JSON string
|
||||||
# In this case, it works with all data types
|
# In this case, it works with all data types
|
||||||
value = ast.literal_eval(value)
|
value = ast.literal_eval(value)
|
||||||
|
@ -211,14 +211,14 @@ class File(Attribute):
|
||||||
super(File, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled)
|
super(File, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled)
|
||||||
|
|
||||||
def validateValue(self, value):
|
def validateValue(self, value):
|
||||||
if not isinstance(value, pyCompatibility.basestring):
|
if not isinstance(value, str):
|
||||||
raise ValueError('File only supports string input (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
|
raise ValueError('File only supports string input (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
|
||||||
return os.path.normpath(value).replace('\\', '/') if value else ''
|
return os.path.normpath(value).replace('\\', '/') if value else ''
|
||||||
|
|
||||||
def checkValueTypes(self):
|
def checkValueTypes(self):
|
||||||
# Some File values are functions generating a string: check whether the value is a string or if it
|
# Some File values are functions generating a string: check whether the value is a string or if it
|
||||||
# is a function (but there is no way to check that the function's output is indeed a string)
|
# is a function (but there is no way to check that the function's output is indeed a string)
|
||||||
if not isinstance(self.value, pyCompatibility.basestring) and not callable(self.value):
|
if not isinstance(self.value, str) and not callable(self.value):
|
||||||
return self.name
|
return self.name
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ class BoolParam(Param):
|
||||||
|
|
||||||
def validateValue(self, value):
|
def validateValue(self, value):
|
||||||
try:
|
try:
|
||||||
if isinstance(value, pyCompatibility.basestring):
|
if isinstance(value, str):
|
||||||
# use distutils.util.strtobool to handle (1/0, true/false, on/off, y/n)
|
# use distutils.util.strtobool to handle (1/0, true/false, on/off, y/n)
|
||||||
return bool(distutils.util.strtobool(value))
|
return bool(distutils.util.strtobool(value))
|
||||||
return bool(value)
|
return bool(value)
|
||||||
|
@ -311,10 +311,10 @@ class ChoiceParam(Param):
|
||||||
if self.exclusive:
|
if self.exclusive:
|
||||||
return self.conformValue(value)
|
return self.conformValue(value)
|
||||||
|
|
||||||
if isinstance(value, pyCompatibility.basestring):
|
if isinstance(value, str):
|
||||||
value = value.split(',')
|
value = value.split(',')
|
||||||
|
|
||||||
if not isinstance(value, pyCompatibility.Iterable):
|
if not isinstance(value, Iterable):
|
||||||
raise ValueError('Non exclusive ChoiceParam value should be iterable (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
|
raise ValueError('Non exclusive ChoiceParam value should be iterable (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
|
||||||
return [self.conformValue(v) for v in value]
|
return [self.conformValue(v) for v in value]
|
||||||
|
|
||||||
|
@ -334,12 +334,12 @@ class StringParam(Param):
|
||||||
super(StringParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled)
|
super(StringParam, self).__init__(name=name, label=label, description=description, value=value, uid=uid, group=group, advanced=advanced, semantic=semantic, enabled=enabled)
|
||||||
|
|
||||||
def validateValue(self, value):
|
def validateValue(self, value):
|
||||||
if not isinstance(value, pyCompatibility.basestring):
|
if not isinstance(value, str):
|
||||||
raise ValueError('StringParam value should be a string (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
|
raise ValueError('StringParam value should be a string (param:{}, value:{}, type:{})'.format(self.name, value, type(value)))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def checkValueTypes(self):
|
def checkValueTypes(self):
|
||||||
if not isinstance(self.value, pyCompatibility.basestring):
|
if not isinstance(self.value, str):
|
||||||
return self.name
|
return self.name
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ from enum import Enum
|
||||||
import meshroom
|
import meshroom
|
||||||
import meshroom.core
|
import meshroom.core
|
||||||
from meshroom.common import BaseObject, DictModel, Slot, Signal, Property
|
from meshroom.common import BaseObject, DictModel, Slot, Signal, Property
|
||||||
from meshroom.core import Version, pyCompatibility
|
from meshroom.core import Version
|
||||||
from meshroom.core.attribute import Attribute, ListAttribute
|
from meshroom.core.attribute import Attribute, ListAttribute
|
||||||
from meshroom.core.exception import StopGraphVisit, StopBranchVisit
|
from meshroom.core.exception import StopGraphVisit, StopBranchVisit
|
||||||
from meshroom.core.node import nodeFactory, Status, Node, CompatibilityNode
|
from meshroom.core.node import nodeFactory, Status, Node, CompatibilityNode
|
||||||
|
@ -195,7 +195,7 @@ class Graph(BaseObject):
|
||||||
Returns:
|
Returns:
|
||||||
tuple of Graph.IO.Features: the list of supported features
|
tuple of Graph.IO.Features: the list of supported features
|
||||||
"""
|
"""
|
||||||
if isinstance(fileVersion, pyCompatibility.basestring):
|
if isinstance(fileVersion, str):
|
||||||
fileVersion = Version(fileVersion)
|
fileVersion = Version(fileVersion)
|
||||||
|
|
||||||
features = [Graph.IO.Features.Graph]
|
features = [Graph.IO.Features.Graph]
|
||||||
|
@ -382,11 +382,11 @@ class Graph(BaseObject):
|
||||||
"""
|
"""
|
||||||
for key, val in attributes.items():
|
for key, val in attributes.items():
|
||||||
for corr in nameCorrespondences.keys():
|
for corr in nameCorrespondences.keys():
|
||||||
if isinstance(val, pyCompatibility.basestring) and corr in val:
|
if isinstance(val, str) and corr in val:
|
||||||
attributes[key] = val.replace(corr, nameCorrespondences[corr])
|
attributes[key] = val.replace(corr, nameCorrespondences[corr])
|
||||||
elif isinstance(val, list):
|
elif isinstance(val, list):
|
||||||
for v in val:
|
for v in val:
|
||||||
if isinstance(v, pyCompatibility.basestring):
|
if isinstance(v, str):
|
||||||
if corr in v:
|
if corr in v:
|
||||||
val[val.index(v)] = v.replace(corr, nameCorrespondences[corr])
|
val[val.index(v)] = v.replace(corr, nameCorrespondences[corr])
|
||||||
else: # the list does not contain strings, so there cannot be links to update
|
else: # the list does not contain strings, so there cannot be links to update
|
||||||
|
@ -419,7 +419,7 @@ class Graph(BaseObject):
|
||||||
defaultValue = desc.value
|
defaultValue = desc.value
|
||||||
break
|
break
|
||||||
|
|
||||||
if isinstance(val, pyCompatibility.basestring):
|
if isinstance(val, str):
|
||||||
if Attribute.isLinkExpression(val) and not any(name in val for name in newNames):
|
if Attribute.isLinkExpression(val) and not any(name in val for name in newNames):
|
||||||
if defaultValue is not None: # prevents from not entering condition if defaultValue = ''
|
if defaultValue is not None: # prevents from not entering condition if defaultValue = ''
|
||||||
attributes[key] = defaultValue
|
attributes[key] = defaultValue
|
||||||
|
@ -428,8 +428,7 @@ class Graph(BaseObject):
|
||||||
removedCnt = len(val) # counter to know whether all the list entries will be deemed invalid
|
removedCnt = len(val) # counter to know whether all the list entries will be deemed invalid
|
||||||
tmpVal = list(val) # deep copy to ensure we iterate over the entire list (even if elements are removed)
|
tmpVal = list(val) # deep copy to ensure we iterate over the entire list (even if elements are removed)
|
||||||
for v in tmpVal:
|
for v in tmpVal:
|
||||||
if isinstance(v, pyCompatibility.basestring) and Attribute.isLinkExpression(v) and not any(
|
if isinstance(v, str) and Attribute.isLinkExpression(v) and not any(name in v for name in newNames):
|
||||||
name in v for name in newNames):
|
|
||||||
val.remove(v)
|
val.remove(v)
|
||||||
removedCnt -= 1
|
removedCnt -= 1
|
||||||
if removedCnt == 0 and defaultValue is not None: # if all links were wrong, reset the attribute
|
if removedCnt == 0 and defaultValue is not None: # if all links were wrong, reset the attribute
|
||||||
|
|
|
@ -17,7 +17,7 @@ from enum import Enum
|
||||||
|
|
||||||
import meshroom
|
import meshroom
|
||||||
from meshroom.common import Signal, Variant, Property, BaseObject, Slot, ListModel, DictModel
|
from meshroom.common import Signal, Variant, Property, BaseObject, Slot, ListModel, DictModel
|
||||||
from meshroom.core import desc, stats, hashValue, pyCompatibility, nodeVersion, Version
|
from meshroom.core import desc, stats, hashValue, nodeVersion, Version
|
||||||
from meshroom.core.attribute import attributeFactory, ListAttribute, GroupAttribute, Attribute
|
from meshroom.core.attribute import attributeFactory, ListAttribute, GroupAttribute, Attribute
|
||||||
from meshroom.core.exception import NodeUpgradeError, UnknownNodeTypeError
|
from meshroom.core.exception import NodeUpgradeError, UnknownNodeTypeError
|
||||||
|
|
||||||
|
@ -1264,7 +1264,7 @@ class CompatibilityNode(BaseNode):
|
||||||
return desc.IntParam(range=None, **params)
|
return desc.IntParam(range=None, **params)
|
||||||
elif isinstance(value, float):
|
elif isinstance(value, float):
|
||||||
return desc.FloatParam(range=None, **params)
|
return desc.FloatParam(range=None, **params)
|
||||||
elif isinstance(value, pyCompatibility.basestring):
|
elif isinstance(value, str):
|
||||||
if isOutput or os.path.isabs(value) or Attribute.isLinkExpression(value):
|
if isOutput or os.path.isabs(value) or Attribute.isLinkExpression(value):
|
||||||
return desc.File(**params)
|
return desc.File(**params)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
|
|
||||||
try:
|
|
||||||
unicode = unicode
|
|
||||||
except NameError:
|
|
||||||
# 'unicode' is undefined, must be Python 3
|
|
||||||
str = str
|
|
||||||
unicode = str
|
|
||||||
bytes = bytes
|
|
||||||
basestring = (str, bytes)
|
|
||||||
else:
|
|
||||||
# 'unicode' exists, must be Python 2
|
|
||||||
str = str
|
|
||||||
unicode = unicode
|
|
||||||
bytes = str
|
|
||||||
basestring = basestring
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Import ABC from collections.abc in Python 3.4+
|
|
||||||
from collections.abc import Sequence, Iterable
|
|
||||||
except ImportError:
|
|
||||||
# Import ABC from collections in Python 2 support
|
|
||||||
from collections import Sequence, Iterable
|
|
|
@ -1,6 +1,6 @@
|
||||||
__version__ = "6.0"
|
__version__ = "6.0"
|
||||||
|
|
||||||
from meshroom.core import desc, Version, pyCompatibility
|
from meshroom.core import desc, Version
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
@ -355,7 +355,7 @@ Many cameras are contributing to the low frequencies and only the best ones cont
|
||||||
def upgradeAttributeValues(self, attrValues, fromVersion):
|
def upgradeAttributeValues(self, attrValues, fromVersion):
|
||||||
if fromVersion < Version(6, 0):
|
if fromVersion < Version(6, 0):
|
||||||
outputTextureFileType = attrValues['outputTextureFileType']
|
outputTextureFileType = attrValues['outputTextureFileType']
|
||||||
if isinstance(outputTextureFileType, pyCompatibility.basestring):
|
if isinstance(outputTextureFileType, str):
|
||||||
attrValues['colorMapping'] = {}
|
attrValues['colorMapping'] = {}
|
||||||
attrValues['colorMapping']['colorMappingFileType'] = outputTextureFileType
|
attrValues['colorMapping']['colorMappingFileType'] = outputTextureFileType
|
||||||
return attrValues
|
return attrValues
|
||||||
|
|
|
@ -9,7 +9,6 @@ from PySide2.QtWidgets import QApplication
|
||||||
|
|
||||||
import meshroom
|
import meshroom
|
||||||
from meshroom.core import nodesDesc
|
from meshroom.core import nodesDesc
|
||||||
from meshroom.core import pyCompatibility
|
|
||||||
from meshroom.core.taskManager import TaskManager
|
from meshroom.core.taskManager import TaskManager
|
||||||
|
|
||||||
from meshroom.ui import components
|
from meshroom.ui import components
|
||||||
|
@ -225,7 +224,7 @@ class MeshroomApp(QApplication):
|
||||||
@Slot(str)
|
@Slot(str)
|
||||||
@Slot(QUrl)
|
@Slot(QUrl)
|
||||||
def addRecentProjectFile(self, projectFile):
|
def addRecentProjectFile(self, projectFile):
|
||||||
if not isinstance(projectFile, (QUrl, pyCompatibility.basestring)):
|
if not isinstance(projectFile, (QUrl, str)):
|
||||||
raise TypeError("Unexpected data type: {}".format(projectFile.__class__))
|
raise TypeError("Unexpected data type: {}".format(projectFile.__class__))
|
||||||
if isinstance(projectFile, QUrl):
|
if isinstance(projectFile, QUrl):
|
||||||
projectFileNorm = projectFile.toLocalFile()
|
projectFileNorm = projectFile.toLocalFile()
|
||||||
|
@ -265,7 +264,7 @@ class MeshroomApp(QApplication):
|
||||||
@Slot(str)
|
@Slot(str)
|
||||||
@Slot(QUrl)
|
@Slot(QUrl)
|
||||||
def removeRecentProjectFile(self, projectFile):
|
def removeRecentProjectFile(self, projectFile):
|
||||||
if not isinstance(projectFile, (QUrl, pyCompatibility.basestring)):
|
if not isinstance(projectFile, (QUrl, str)):
|
||||||
raise TypeError("Unexpected data type: {}".format(projectFile.__class__))
|
raise TypeError("Unexpected data type: {}".format(projectFile.__class__))
|
||||||
if isinstance(projectFile, QUrl):
|
if isinstance(projectFile, QUrl):
|
||||||
projectFileNorm = projectFile.toLocalFile()
|
projectFileNorm = projectFile.toLocalFile()
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
from meshroom.core import pyCompatibility
|
|
||||||
|
|
||||||
from PySide2.QtCore import QUrl, QFileInfo
|
from PySide2.QtCore import QUrl, QFileInfo
|
||||||
from PySide2.QtCore import QObject, Slot
|
from PySide2.QtCore import QObject, Slot
|
||||||
|
|
||||||
|
@ -27,7 +25,7 @@ class FilepathHelper(QObject):
|
||||||
Returns:
|
Returns:
|
||||||
str: String representation of 'path'
|
str: String representation of 'path'
|
||||||
"""
|
"""
|
||||||
if not isinstance(path, (QUrl, pyCompatibility.basestring)):
|
if not isinstance(path, (QUrl, str)):
|
||||||
raise TypeError("Unexpected data type: {}".format(path.__class__))
|
raise TypeError("Unexpected data type: {}".format(path.__class__))
|
||||||
if isinstance(path, QUrl):
|
if isinstance(path, QUrl):
|
||||||
path = path.toLocalFile()
|
path = path.toLocalFile()
|
||||||
|
|
|
@ -2,6 +2,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
from collections.abc import Iterable
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from PySide2.QtCore import QObject, Slot, Property, Signal, QUrl, QSizeF
|
from PySide2.QtCore import QObject, Slot, Property, Signal, QUrl, QSizeF
|
||||||
|
@ -13,7 +14,6 @@ from meshroom import multiview
|
||||||
from meshroom.common.qt import QObjectListModel
|
from meshroom.common.qt import QObjectListModel
|
||||||
from meshroom.core import Version
|
from meshroom.core import Version
|
||||||
from meshroom.core.node import Node, CompatibilityNode, Status, Position
|
from meshroom.core.node import Node, CompatibilityNode, Status, Position
|
||||||
from meshroom.core.pyCompatibility import Iterable
|
|
||||||
from meshroom.ui.graph import UIGraph
|
from meshroom.ui.graph import UIGraph
|
||||||
from meshroom.ui.utils import makeProperty
|
from meshroom.ui.utils import makeProperty
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue