diff --git a/.gitignore b/.gitignore
index 00ff34511..3c91dcb67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,6 @@ PyFlow/Configs/
docs/build/
docs/source/_build/
docs/build_docs_env.bat
-__pycache__/
\ No newline at end of file
+.venv
+.idea
+__pycache__/
diff --git a/.vscode/.ropeproject/config.py b/.vscode/.ropeproject/config.py
index dee2d1ae9..0b2a31694 100644
--- a/.vscode/.ropeproject/config.py
+++ b/.vscode/.ropeproject/config.py
@@ -14,8 +14,16 @@ def set_prefs(prefs):
# '.svn': matches 'pkg/.svn' and all of its children
# 'build/*.o': matches 'build/lib.o' but not 'build/sub/lib.o'
# 'build//*.o': matches 'build/lib.o' and 'build/sub/lib.o'
- prefs['ignored_resources'] = ['*.pyc', '*~', '.ropeproject',
- '.hg', '.svn', '_svn', '.git', '.tox']
+ prefs["ignored_resources"] = [
+ "*.pyc",
+ "*~",
+ ".ropeproject",
+ ".hg",
+ ".svn",
+ "_svn",
+ ".git",
+ ".tox",
+ ]
# Specifies which files should be considered python files. It is
# useful when you have scripts inside your project. Only files
@@ -37,66 +45,66 @@ def set_prefs(prefs):
# prefs.add('python_path', '~/python/')
# Should rope save object information or not.
- prefs['save_objectdb'] = True
- prefs['compress_objectdb'] = False
+ prefs["save_objectdb"] = True
+ prefs["compress_objectdb"] = False
# If `True`, rope analyzes each module when it is being saved.
- prefs['automatic_soa'] = True
+ prefs["automatic_soa"] = True
# The depth of calls to follow in static object analysis
- prefs['soa_followed_calls'] = 0
+ prefs["soa_followed_calls"] = 0
# If `False` when running modules or unit tests "dynamic object
# analysis" is turned off. This makes them much faster.
- prefs['perform_doa'] = True
+ prefs["perform_doa"] = True
# Rope can check the validity of its object DB when running.
- prefs['validate_objectdb'] = True
+ prefs["validate_objectdb"] = True
# How many undos to hold?
- prefs['max_history_items'] = 32
+ prefs["max_history_items"] = 32
# Shows whether to save history across sessions.
- prefs['save_history'] = True
- prefs['compress_history'] = False
+ prefs["save_history"] = True
+ prefs["compress_history"] = False
# Set the number spaces used for indenting. According to
# :PEP:`8`, it is best to use 4 spaces. Since most of rope's
# unit-tests use 4 spaces it is more reliable, too.
- prefs['indent_size'] = 4
+ prefs["indent_size"] = 4
# Builtin and c-extension modules that are allowed to be imported
# and inspected by rope.
- prefs['extension_modules'] = []
+ prefs["extension_modules"] = []
# Add all standard c-extensions to extension_modules list.
- prefs['import_dynload_stdmods'] = True
+ prefs["import_dynload_stdmods"] = True
# If `True` modules with syntax errors are considered to be empty.
# The default value is `False`; When `False` syntax errors raise
# `rope.base.exceptions.ModuleSyntaxError` exception.
- prefs['ignore_syntax_errors'] = False
+ prefs["ignore_syntax_errors"] = False
# If `True`, rope ignores unresolvable imports. Otherwise, they
# appear in the importing namespace.
- prefs['ignore_bad_imports'] = False
+ prefs["ignore_bad_imports"] = False
# If `True`, rope will insert new module imports as
# `from import ` by default.
- prefs['prefer_module_from_imports'] = False
+ prefs["prefer_module_from_imports"] = False
# If `True`, rope will transform a comma list of imports into
# multiple separate import statements when organizing
# imports.
- prefs['split_imports'] = False
+ prefs["split_imports"] = False
# If `True`, rope will remove all top-level import statements and
# reinsert them at the top of the module when making changes.
- prefs['pull_imports_to_top'] = True
+ prefs["pull_imports_to_top"] = True
# If `True`, rope will sort imports alphabetically by module name instead
# of alphabetically by import statement, with from imports after normal
# imports.
- prefs['sort_imports_alphabetically'] = False
+ prefs["sort_imports_alphabetically"] = False
# Location of implementation of
# rope.base.oi.type_hinting.interfaces.ITypeHintingFactory In general
@@ -105,8 +113,9 @@ def set_prefs(prefs):
# listed in module rope.base.oi.type_hinting.providers.interfaces
# For example, you can add you own providers for Django Models, or disable
# the search type-hinting in a class hierarchy, etc.
- prefs['type_hinting_factory'] = (
- 'rope.base.oi.type_hinting.factory.default_type_hinting_factory')
+ prefs[
+ "type_hinting_factory"
+ ] = "rope.base.oi.type_hinting.factory.default_type_hinting_factory"
def project_opened(project):
diff --git a/PyFlow/App.py b/PyFlow/App.py
index 150986f07..1440becd1 100644
--- a/PyFlow/App.py
+++ b/PyFlow/App.py
@@ -17,43 +17,40 @@
"""
import os
-import sys
-import subprocess
import json
-import pkgutil
-import uuid
import shutil
from string import ascii_letters
import random
-from Qt import QtGui
-from Qt import QtCore
-from Qt.QtWidgets import *
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import *
from PyFlow import GET_PACKAGES
-from PyFlow.Core.Common import currentProcessorTime
-from PyFlow.Core.Common import SingletonDecorator
from PyFlow.Core.PathsRegistry import PathsRegistry
from PyFlow.Core.version import *
-from PyFlow.Core.GraphBase import GraphBase
from PyFlow.Core.GraphManager import GraphManagerSingleton
-from PyFlow.ConfigManager import ConfigManager
-from PyFlow.UI.Canvas.UICommon import *
+from PyFlow.Core.Common import currentProcessorTime
+from PyFlow.Core.Common import SingletonDecorator
+from PyFlow.Core.Common import validateGraphDataPackages
+from PyFlow.UI.Canvas.UICommon import SessionDescriptor
from PyFlow.UI.Widgets.BlueprintCanvas import BlueprintCanvasWidget
-from PyFlow.UI.Views.NodeBox import NodesBox
-from PyFlow.UI.Canvas.UINodeBase import getUINodeInstance
from PyFlow.UI.Tool.Tool import ShelfTool, DockTool
from PyFlow.UI.EditorHistory import EditorHistory
from PyFlow.UI.Tool import GET_TOOLS
-from PyFlow.UI.Tool import REGISTER_TOOL
from PyFlow.UI.Utils.stylesheet import editableStyleSheet
from PyFlow.UI.ContextMenuGenerator import ContextMenuGenerator
from PyFlow.UI.Widgets.PreferencesWindow import PreferencesWindow
+
try:
from PyFlow.Packages.PyFlowBase.Tools.PropertiesTool import PropertiesTool
-except:
+except ImportError:
pass
+import asyncio
+
+import PyFlow.UI.resources
from PyFlow.Wizards.PackageWizard import PackageWizard
+
from PyFlow import INITIALIZE
from PyFlow.Input import InputAction, InputActionType
from PyFlow.Input import InputManager
@@ -61,12 +58,12 @@
import PyFlow.UI.resources
-EDITOR_TARGET_FPS = 60
+EDITOR_TARGET_FPS = 30
-def generateRandomString(numSymbolds=5):
+def generateRandomString(numbSymbols=5):
result = ""
- for i in range(numSymbolds):
+ for i in range(numbSymbols):
letter = random.choice(ascii_letters)
result += letter
return result
@@ -86,7 +83,7 @@ def winTitle():
return "PyFlow v{0}".format(currentVersion().__str__())
-## App itself
+# App itself
class PyFlow(QMainWindow):
appInstance = None
@@ -102,7 +99,7 @@ def __init__(self, parent=None):
self.edHistory = EditorHistory(self)
self.edHistory.statePushed.connect(self.historyStatePushed)
self.setWindowTitle(winTitle())
- self.undoStack = QUndoStack(self)
+ self.undoStack = QtGui.QUndoStack(self)
self.setContentsMargins(1, 1, 1, 1)
self.graphManager = GraphManagerSingleton()
self.canvasWidget = BlueprintCanvasWidget(self.graphManager.get(), self)
@@ -130,7 +127,7 @@ def __init__(self, parent=None):
self._lastClock = 0.0
self.fps = EDITOR_TARGET_FPS
self.tick_timer = QtCore.QTimer()
- self._currentFileName = ''
+ self._currentFileName = ""
self.currentFileName = None
def historyStatePushed(self, state):
@@ -160,13 +157,13 @@ def updateLabel(self):
def getTempDirectory(self):
"""Returns unique temp directory for application instance.
- This folder and all it's content will be removed from disc on application shutdown.
+ This folder and all its content will be removed from disc on application shutdown.
"""
if self.currentTempDir == "":
# create app folder in documents
# random string used for cases when multiple instances of app are running in the same time
tempDirPath = ConfigManager().getPrefsValue("PREFS", "General/TempFilesDir")
- if tempDirPath[-1:] in ('/', '\\'):
+ if tempDirPath[-1:] in ("/", "\\"):
tempDirPath = tempDirPath[:-1]
self.currentTempDir = "{0}_{1}".format(tempDirPath, generateRandomString())
@@ -181,7 +178,7 @@ def populateMenu(self):
fileMenu = self.menuBar.addMenu("File")
newFileAction = fileMenu.addAction("New file")
newFileAction.setIcon(QtGui.QIcon(":/new_file_icon.png"))
- newFileAction.triggered.connect(self.newFile)
+ newFileAction.triggered.connect(self._clickNewFile)
loadAction = fileMenu.addAction("Load")
loadAction.setIcon(QtGui.QIcon(":/folder_open_icon.png"))
@@ -198,7 +195,6 @@ def populateMenu(self):
IOMenu = fileMenu.addMenu("Custom IO")
for packageName, package in GET_PACKAGES().items():
# exporters
- exporters = None
try:
exporters = package.GetExporters()
except:
@@ -209,10 +205,18 @@ def populateMenu(self):
fileFormatMenu.setToolTip(exporterClass.toolTip())
if exporterClass.createExporterMenu():
exportAction = fileFormatMenu.addAction("Export")
- exportAction.triggered.connect(lambda checked=False, app=self, exporter=exporterClass: exporter.doExport(app))
+ exportAction.triggered.connect(
+ lambda checked=False, app=self, exporter=exporterClass: exporter.doExport(
+ app
+ )
+ )
if exporterClass.createImporterMenu():
importAction = fileFormatMenu.addAction("Import")
- importAction.triggered.connect(lambda checked=False, app=self, exporter=exporterClass: exporter.doImport(app))
+ importAction.triggered.connect(
+ lambda checked=False, app=self, exporter=exporterClass: exporter.doImport(
+ app
+ )
+ )
editMenu = self.menuBar.addMenu("Edit")
preferencesAction = editMenu.addAction("Preferences")
@@ -224,8 +228,16 @@ def populateMenu(self):
packagePlugin.triggered.connect(PackageWizard.run)
helpMenu = self.menuBar.addMenu("Help")
- helpMenu.addAction("Homepage").triggered.connect(lambda _=False, url="https://wonderworks-software.github.io/PyFlow/": QtGui.QDesktopServices.openUrl(url))
- helpMenu.addAction("Docs").triggered.connect(lambda _=False, url="https://pyflow.readthedocs.io/en/latest/": QtGui.QDesktopServices.openUrl(url))
+ helpMenu.addAction("Homepage").triggered.connect(
+ lambda _=False, url="https://wonderworks-software.github.io/PyFlow/": QtGui.QDesktopServices.openUrl(
+ url
+ )
+ )
+ helpMenu.addAction("Docs").triggered.connect(
+ lambda _=False, url="https://pyflow.readthedocs.io/en/latest/": QtGui.QDesktopServices.openUrl(
+ url
+ )
+ )
def showPreferencesWindow(self):
self.preferencesWindow.show()
@@ -264,7 +276,12 @@ def getCanvas(self):
def keyPressEvent(self, event):
modifiers = event.modifiers()
- currentInputAction = InputAction(name="temp", actionType=InputActionType.Keyboard, key=event.key(), modifiers=modifiers)
+ currentInputAction = InputAction(
+ name="temp",
+ actionType=InputActionType.Keyboard,
+ key=event.key(),
+ modifiers=modifiers,
+ )
actionSaveVariants = InputManager()["App.Save"]
actionNewFileVariants = InputManager()["App.NewFile"]
@@ -273,9 +290,9 @@ def keyPressEvent(self, event):
if currentInputAction in actionNewFileVariants:
shouldSave = self.shouldSave()
- if shouldSave == QMessageBox.Yes:
+ if shouldSave == QMessageBox.Save:
self.save()
- elif shouldSave == QMessageBox.Discard:
+ elif shouldSave == QMessageBox.Cancel:
return
EditorHistory().clear()
@@ -291,7 +308,7 @@ def keyPressEvent(self, event):
self.save()
if currentInputAction in actionLoadVariants:
shouldSave = self.shouldSave()
- if shouldSave == QMessageBox.Yes:
+ if shouldSave == QMessageBox.Save:
self.save()
elif shouldSave == QMessageBox.Discard:
return
@@ -301,7 +318,7 @@ def keyPressEvent(self, event):
def loadFromFileChecked(self, filePath):
shouldSave = self.shouldSave()
- if shouldSave == QMessageBox.Yes:
+ if shouldSave == QMessageBox.Save:
self.save()
elif shouldSave == QMessageBox.Discard:
return
@@ -312,7 +329,7 @@ def loadFromFileChecked(self, filePath):
def loadFromData(self, data, clearHistory=False):
# check first if all packages we are trying to load are legal
- missedPackages = set()
+ missedPackages = set() # TODO: nothing fills this, can never report missing package
if not validateGraphDataPackages(data, missedPackages):
msg = "This graph can not be loaded. Following packages not found:\n\n"
index = 1
@@ -346,11 +363,13 @@ def currentFileName(self, value):
self.updateLabel()
def loadFromFile(self, filePath):
- with open(filePath, 'r') as f:
+ with open(filePath, "r") as f:
data = json.load(f)
self.loadFromData(data, clearHistory=True)
self.currentFileName = filePath
- EditorHistory().saveState("Open {}".format(os.path.basename(self.currentFileName)))
+ EditorHistory().saveState(
+ "Open {}".format(os.path.basename(self.currentFileName))
+ )
def load(self):
name_filter = "Graph files (*.pygraph)"
@@ -359,7 +378,7 @@ def load(self):
fpath = savepath[0]
else:
fpath = savepath
- if not fpath == '':
+ if not fpath == "":
self.loadFromFile(fpath)
def save(self, save_as=False):
@@ -370,7 +389,7 @@ def save(self, save_as=False):
pth = savepath[0]
else:
pth = savepath
- if not pth == '':
+ if not pth == "":
self.currentFileName = pth
else:
self.currentFileName = None
@@ -382,7 +401,7 @@ def save(self, save_as=False):
pth = savepath[0]
else:
pth = savepath
- if not pth == '':
+ if not pth == "":
self.currentFileName = pth
else:
self.currentFileName = None
@@ -393,8 +412,8 @@ def save(self, save_as=False):
if not self.currentFileName.endswith(".pygraph"):
self.currentFileName += ".pygraph"
- if not self.currentFileName == '':
- with open(self.currentFileName, 'w') as f:
+ if not self.currentFileName == "":
+ with open(self.currentFileName, "w") as f:
saveData = self.graphManager.get().serialize()
json.dump(saveData, f, indent=4)
print(str("// saved: '{0}'".format(self.currentFileName)))
@@ -402,6 +421,24 @@ def save(self, save_as=False):
self.updateLabel()
return True
+ def _clickNewFile(self):
+ shouldSave = self.shouldSave()
+ if shouldSave == QMessageBox.Save:
+
+ self.save()
+ elif shouldSave == QMessageBox.Cancel:
+ return
+
+ EditorHistory().clear()
+ historyTools = self.getRegisteredTools(classNameFilters=["HistoryTool"])
+ for historyTools in historyTools:
+ historyTools.onClear()
+ self.newFile()
+ EditorHistory().saveState("New file")
+ self.currentFileName = None
+ self.modified = False
+ self.updateLabel()
+
def newFile(self, keepRoot=True):
self.tick_timer.stop()
self.tick_timer.timeout.disconnect()
@@ -422,20 +459,25 @@ def stopMainLoop(self):
self.tick_timer.timeout.disconnect()
def mainLoop(self):
+ asyncio.get_event_loop().run_until_complete(self._tick_asyncio())
+
deltaTime = currentProcessorTime() - self._lastClock
- ds = (deltaTime * 1000.0)
+ ds = deltaTime * 1000.0
if ds > 0:
self.fps = int(1000.0 / ds)
# Tick all graphs
# each graph will tick owning raw nodes
- # each raw node will tick it's ui wrapper if it exists
+ # each raw node will tick its ui wrapper if it exists
self.graphManager.get().Tick(deltaTime)
# Tick canvas. Update ui only stuff such animation etc.
self.canvasWidget.Tick(deltaTime)
self._lastClock = currentProcessorTime()
+
+ async def _tick_asyncio(self):
+ await asyncio.sleep(0.00001)
def createPopupMenu(self):
pass
@@ -461,7 +503,9 @@ def createToolInstanceByClass(self, packageName, toolName, toolClass=DockTool):
return ToolClass()
return None
- def getRegisteredTools(self, classNameFilters=[]):
+ def getRegisteredTools(self, classNameFilters=None):
+ if classNameFilters is None:
+ classNameFilters = []
if len(classNameFilters) == 0:
return self._tools
else:
@@ -486,7 +530,7 @@ def invokeDockToolByName(self, packageName, name, settings=None):
tool.show()
tool.onShow()
# Highlight window
- #print("highlight", tool.uniqueName())
+ # print("highlight", tool.uniqueName())
return tool
ToolInstance = self.createToolInstanceByClass(packageName, name, DockTool)
if ToolInstance:
@@ -504,21 +548,22 @@ def invokeDockToolByName(self, packageName, name, settings=None):
def shouldSave(self):
if self.modified:
- btn = QMessageBox.warning(self, "Confirm?", "Unsaved data will be lost. Save?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Discard)
- if btn == QMessageBox.No:
- return QMessageBox.No
- else:
- return btn
- return QMessageBox.No
+ btn = QMessageBox.warning(
+ self,
+ "Confirm?",
+ "Unsaved data will be lost. Save?",
+ QMessageBox.Save | QMessageBox.Cancel | QMessageBox.Discard,
+ )
+ return btn
+ return QMessageBox.Discard
def closeEvent(self, event):
-
shouldSave = self.shouldSave()
- if shouldSave == QMessageBox.Yes:
+ if shouldSave == QMessageBox.Save:
if not self.save():
event.ignore()
return
- elif shouldSave == QMessageBox.Discard:
+ elif shouldSave == QMessageBox.Cancel:
event.ignore()
return
@@ -534,13 +579,13 @@ def closeEvent(self, event):
settings.clear()
settings.sync()
- settings.beginGroup('Editor')
+ settings.beginGroup("Editor")
settings.setValue("geometry", self.saveGeometry())
settings.setValue("state", self.saveState())
settings.endGroup()
# save tools state
- settings.beginGroup('Tools')
+ settings.beginGroup("Tools")
for tool in self._tools:
if isinstance(tool, ShelfTool):
settings.beginGroup("ShelfTools")
@@ -570,7 +615,9 @@ def closeEvent(self, event):
@staticmethod
def instance(parent=None, software=""):
- assert(software != ""), "Invalid arguments. Please pass you software name as second argument!"
+ assert (
+ software != ""
+ ), "Invalid arguments. Please pass you software name as second argument!"
settings = ConfigManager().getSettings("APP_STATE")
instance = PyFlow(parent)
@@ -582,7 +629,9 @@ def instance(parent=None, software=""):
try:
extraPackagePaths = []
- extraPathsString = ConfigManager().getPrefsValue("PREFS", "General/ExtraPackageDirs")
+ extraPathsString = ConfigManager().getPrefsValue(
+ "PREFS", "General/ExtraPackageDirs"
+ )
if extraPathsString is not None:
extraPathsString = extraPathsString.rstrip(";")
extraPathsRaw = extraPathsString.split(";")
@@ -597,16 +646,15 @@ def instance(parent=None, software=""):
instance.startMainLoop()
# populate tools
- canvas = instance.getCanvas()
toolbar = instance.getToolbar()
# populate menus
instance.populateMenu()
- geo = settings.value('Editor/geometry')
+ geo = settings.value("Editor/geometry")
if geo is not None:
instance.restoreGeometry(geo)
- state = settings.value('Editor/state')
+ state = settings.value("Editor/state")
if state is not None:
instance.restoreState(state)
settings.beginGroup("Tools")
@@ -640,7 +688,9 @@ def instance(parent=None, software=""):
if issubclass(ToolClass, DockTool):
menus = instance.menuBar.findChildren(QMenu)
- pluginsMenuAction = [m for m in menus if m.title() == "Plugins"][0].menuAction()
+ pluginsMenuAction = [m for m in menus if m.title() == "Plugins"][
+ 0
+ ].menuAction()
toolsMenu = getOrCreateMenu(instance.menuBar, "Tools")
instance.menuBar.insertMenu(pluginsMenuAction, toolsMenu)
packageSubMenu = getOrCreateMenu(toolsMenu, packageName)
@@ -649,14 +699,20 @@ def instance(parent=None, software=""):
icon = ToolClass.getIcon()
if icon:
showToolAction.setIcon(icon)
- showToolAction.triggered.connect(lambda pkgName=packageName, toolName=ToolClass.name(): instance.invokeDockToolByName(pkgName, toolName))
+ showToolAction.triggered.connect(
+ lambda pkgName=packageName, toolName=ToolClass.name(): instance.invokeDockToolByName(
+ pkgName, toolName
+ )
+ )
settings.beginGroup("DockTools")
childGroups = settings.childGroups()
for dockToolGroupName in childGroups:
# This dock tool data been saved on last shutdown
settings.beginGroup(dockToolGroupName)
- if dockToolGroupName in [t.uniqueName() for t in instance._tools]:
+ if dockToolGroupName in [
+ t.uniqueName() for t in instance._tools
+ ]:
settings.endGroup()
continue
toolName = dockToolGroupName.split("::")[0]
diff --git a/PyFlow/AppMDI.py b/PyFlow/AppMDI.py
new file mode 100644
index 000000000..7a4b17cc5
--- /dev/null
+++ b/PyFlow/AppMDI.py
@@ -0,0 +1,1416 @@
+## Copyright 2023 David Lario
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import os
+import sys
+import subprocess
+import json
+from time import process_time
+import pkgutil
+import uuid
+import shutil
+from string import ascii_letters
+import random
+
+from qtpy.QtCore import QCoreApplication
+#import mdi_rc
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import *
+
+from PyFlow import GET_PACKAGES
+from PyFlow.Core.Common import SingletonDecorator
+from PyFlow.Core.PathsRegistry import PathsRegistry
+from PyFlow.Core.version import *
+from PyFlow.Core.GraphBase import GraphBase
+from PyFlow.Core.GraphManager import GraphManagerSingleton
+from PyFlow.UI.Canvas.UICommon import *
+from PyFlow.UI.Widgets.BlueprintCanvas import BlueprintCanvasWidget
+from PyFlow.UI.Views.NodeBox import NodesBox
+from PyFlow.UI.Canvas.UINodeBase import getUINodeInstance
+from PyFlow.UI.Tool.Tool import ShelfTool, DockTool, FormTool
+from PyFlow.UI.EditorHistory import EditorHistory
+from PyFlow.UI.Tool import GET_TOOLS
+from PyFlow.UI.Tool import REGISTER_TOOL
+from PyFlow.UI.Utils.stylesheet import editableStyleSheet
+from PyFlow.UI.ContextMenuGenerator import ContextMenuGenerator
+from PyFlow.UI.Widgets.PreferencesWindow import PreferencesWindow
+try:
+ from PyFlow.Packages.PyFlowBase.Tools.PropertiesTool import PropertiesTool
+except:
+ pass
+from PyFlow.UI.Forms.PackageBuilder import PackageBuilder
+from PyFlow.Packages.PyFlowBase.Tools.LoggerTool import LoggerTool
+
+from PyFlow import INITIALIZE
+from PyFlow.Input import InputAction, InputActionType
+from PyFlow.Input import InputManager
+from PyFlow.ConfigManager import ConfigManager
+from PyFlow.UI.Canvas.CanvasBase import CanvasBase
+
+import PyFlow.UI.resources
+
+EDITOR_TARGET_FPS = 60
+
+from qtpy.QtCore import (QSignalMapper, QRect, QSize, Qt, QFile, QFileInfo, QTextStream, QPoint, QSettings)
+from qtpy.QtGui import (QIcon, QKeySequence, QUndoStack)
+from qtpy.QtWidgets import (QAction, QApplication, QFileDialog, QMainWindow, QMdiArea, QMdiSubWindow, QMessageBox, QWidget, QMenuBar)
+
+'''from PyQt5.QtCore import (QFile, QFileInfo, QPoint, QSettings, QSignalMapper, QSize, QTextStream, Qt)
+from PyQt5.QtGui import QIcon, QKeySequence
+from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog, QMainWindow, QMdiArea, QMessageBox, QWidget, QMenuBar)'''
+
+def generateRandomString(numSymbolds=5):
+ result = ""
+ for i in range(numSymbolds):
+ letter = random.choice(ascii_letters)
+ result += letter
+ return result
+
+def getOrCreateMenu(menuBar, title):
+ for child in menuBar.findChildren(QMenu):
+ if child.title() == title:
+ return child
+ menu = QMenu(menuBar)
+ menu.setObjectName(title)
+ menu.setTitle(title)
+ return menu
+
+
+def winTitle():
+ return "PyFlow v{0}".format(currentVersion().__str__())
+
+class pyflowChild(QMdiSubWindow):
+ sequenceNumber = 1
+ newFileExecuted = QtCore.Signal(bool)
+ fileBeenLoaded = QtCore.Signal()
+
+ def __init__(self, parent):
+ super(pyflowChild, self).__init__(parent)
+ self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+ self.isUntitled = True
+ self.guid = uuid.uuid4()
+ self.parent = parent
+ self.graphManager = GraphManagerSingleton()
+ self.canvasWidget = BlueprintCanvasWidget(self.graphManager.get(), self)
+ self.canvasWidget.setObjectName("canvasWidget")
+ self._currentFileName = ""
+
+ self.setWidget(self.canvasWidget)
+ self._tools = set()
+
+ self._lastClock = 0.0
+ self.fps = EDITOR_TARGET_FPS
+ self.tick_timer = QtCore.QTimer(self)
+
+ self.isModified = False
+ self._modified = False
+
+ #self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow)
+
+ self.currentSoftware = ""
+ self.edHistory = EditorHistory(self)
+ self.edHistory.statePushed.connect(self.historyStatePushed)
+
+ self.readSettings()
+ self.currentTempDir = ""
+
+ self.preferencesWindow = PreferencesWindow(self)
+ self.createActions()
+ self.populateToolBar()
+
+ '''self.instanceDict[None] = self
+
+ self.instanceDict = {}
+ self.instanceDict[None] = self'''
+
+ self.startMainLoop()
+
+ def readSettings(self):
+ settings = QSettings('Trolltech', 'MDI Example')
+ pos = settings.value('pos', QPoint(200, 200))
+ size = settings.value('size', QSize(400, 400))
+ self.move(pos)
+ self.resize(size)
+
+ def writeSettings(self):
+ settings = QtCore.QSettings('Trolltech', 'MDI Example')
+ settings.setValue('pos', self.pos())
+ settings.setValue('size', self.size())
+
+ def historyStatePushed(self, state):
+ if state.modifiesData():
+ self.modified = True
+ self.updateLabel()
+ # print(state, state.modifiesData())
+
+ @property
+ def modified(self):
+ return self._modified
+
+ @modified.setter
+ def modified(self, value):
+ self._modified = value
+ self.updateLabel()
+
+ def updateLabel(self):
+ label = self._currentFileName
+ if self.currentFileName is not None:
+ if os.path.isfile(self.currentFileName):
+ label = os.path.basename(self.currentFileName)
+ if self.modified:
+ label += "*"
+ self.setWindowTitle("{0}".format(label))
+
+ def getTempDirectory(self):
+ """Returns unique temp directory for application instance.
+
+ This folder and all it's content will be removed from disc on application shutdown.
+ """
+ if self.currentTempDir == "":
+ # create app folder in documents
+ # random string used for cases when multiple instances of app are running in the same time
+ tempDirPath = ConfigManager().getPrefsValue("PREFS", "General/TempFilesDir")
+ if tempDirPath[-1:] in ('/', '\\'):
+ tempDirPath = tempDirPath[:-1]
+ self.currentTempDir = "{0}_{1}".format(tempDirPath, generateRandomString())
+
+ if not os.path.exists(self.currentTempDir):
+ os.makedirs(self.currentTempDir)
+ return self.currentTempDir
+
+
+ def open(self):
+ fileName, _ = QFileDialog.getOpenFileName(self)
+ if fileName:
+ existing = self.findMdiChild(fileName)
+ if existing:
+ self.mdiArea.setActiveSubWindow(existing)
+ return
+
+ child = self.createMdiChild()
+ if child.loadFile(fileName):
+ self.statusBar().showMessage("File loaded", 2000)
+ child.show()
+ else:
+ child.close()
+
+ def save(self):
+ if self.save():
+ self.statusBar().showMessage("File saved", 2000)
+
+ def saveAs(self):
+ if self.saveAs():
+ self.statusBar().showMessage("File saved", 2000)
+
+ def cut(self):
+ self.cut()
+
+ def copy(self):
+ self.copy()
+
+ def paste(self):
+ self.paste()
+
+ def createActions(self):
+ self.newAct = QAction(QIcon(':/images/new.png'), "&New", self,
+ shortcut=QKeySequence.New, statusTip="Create a new file",
+ triggered=self.parent.newFile)
+
+ self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self,
+ shortcut=QKeySequence.Open, statusTip="Open an existing file",
+ triggered=self.parent.open)
+
+ self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self,
+ shortcut=QKeySequence.Save,
+ statusTip="Save the document to disk", triggered=self.save)
+
+ self.saveAsAct = QAction("Save &As...", self,
+ shortcut=QKeySequence.SaveAs,
+ statusTip="Save the document under a new name",
+ triggered=self.saveAs)
+
+ self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit,
+ statusTip="Exit the application",
+ triggered=QApplication.instance().closeAllWindows)
+
+ self.cutAct = QAction(QIcon(':/images/cut.png'), "Cu&t", self,
+ shortcut=QKeySequence.Cut,
+ statusTip="Cut the current selection's contents to the clipboard",
+ triggered=self.cut)
+
+ self.copyAct = QAction(QIcon(':/images/copy.png'), "&Copy", self,
+ shortcut=QKeySequence.Copy,
+ statusTip="Copy the current selection's contents to the clipboard",
+ triggered=self.copy)
+
+ self.pasteAct = QAction(QIcon(':/images/paste.png'), "&Paste", self,
+ shortcut=QKeySequence.Paste,
+ statusTip="Paste the clipboard's contents into the current selection",
+ triggered=self.paste)
+
+ self.closeAct = QAction("Cl&ose", self,
+ statusTip="Close the active window",
+ triggered=self.parent.mdiArea.closeActiveSubWindow)
+
+ self.closeAllAct = QAction("Close &All", self,
+ statusTip="Close all the windows",
+ triggered=self.parent.mdiArea.closeAllSubWindows)
+
+ self.tileAct = QAction("&Tile", self, statusTip="Tile the windows",
+ triggered=self.parent.mdiArea.tileSubWindows)
+
+ self.cascadeAct = QAction("&Cascade", self,
+ statusTip="Cascade the windows",
+ triggered=self.parent.mdiArea.cascadeSubWindows)
+
+ self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild,
+ statusTip="Move the focus to the next window",
+ triggered=self.parent.mdiArea.activateNextSubWindow)
+
+ self.previousAct = QAction("Pre&vious", self,
+ shortcut=QKeySequence.PreviousChild,
+ statusTip="Move the focus to the previous window",
+ triggered=self.parent.mdiArea.activatePreviousSubWindow)
+
+ self.separatorAct = QAction(self)
+ self.separatorAct.setSeparator(True)
+
+ self.aboutAct = QAction("&About", self,
+ statusTip="Show the application's About box",
+ triggered=self.parent.about)
+
+ self.aboutQtAct = QAction("About &Qt", self,
+ statusTip="Show the Qt library's About box",
+ triggered=QApplication.instance().aboutQt)
+
+ def getMenuBar(self):
+ return self.parent.menuBar
+
+ def getToolBarLayout(self):
+
+ toolBarDict = {}
+ pyFlowToolBar = []
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "NewFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "NewFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "OpenFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "SaveFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "AlignLeft",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "AlignRight",
+ "Active": True})
+
+ toolBarDict["File"] = pyFlowToolBar
+
+ return toolBarDict
+
+ def showPreferencesWindow(self):
+ self.preferencesWindow.show()
+
+ def registerToolInstance(self, instance):
+ """Registers tool instance reference
+
+ This needed to prevent classes from being garbage collected and to save widgets state
+
+ Args:
+
+ instance (ToolBase): Tool to be registered
+ """
+ self._tools.add(instance)
+
+ def unregisterToolInstance(self, instance):
+ if instance in self._tools:
+ self._tools.remove(instance)
+
+ def populateToolBar(self):
+ settings = ConfigManager().getSettings("APP_STATE")
+ toolbar = QToolBar(self)
+
+ #Parent Window Items
+ newFileAction = toolbar.addAction("New file")
+ newFileAction.setIcon(QtGui.QIcon(":/new_file_icon.png"))
+ newFileAction.setToolTip("")
+ newFileAction.triggered.connect(self.parent.newFile)
+
+ loadAction = toolbar.addAction("Load")
+ loadAction.setIcon(QtGui.QIcon(":/folder_open_icon.png"))
+ loadAction.setToolTip("")
+ loadAction.triggered.connect(self.parent.load)
+
+ self.parent.addToolBar(QtCore.Qt.TopToolBarArea, toolbar)
+
+ self.parent.toolBarDict[self.guid] = toolbar
+
+ software = "standalone"
+
+ try:
+ extraPackagePaths = []
+ extraPathsString = ConfigManager().getPrefsValue("PREFS", "General/ExtraPackageDirs")
+ if extraPathsString is not None:
+ extraPathsString = extraPathsString.rstrip(";")
+ extraPathsRaw = extraPathsString.split(";")
+ for rawPath in extraPathsRaw:
+ if os.path.exists(rawPath):
+ extraPackagePaths.append(os.path.normpath(rawPath))
+ INITIALIZE(additionalPackageLocations=extraPackagePaths, software=software)
+ except Exception as e:
+ QMessageBox.critical(None, "Fatal error", str(e))
+ return
+
+ geo = settings.value('Editor/geometry')
+ if geo is not None:
+ self.restoreGeometry(geo)
+ state = settings.value('Editor/state')
+ if state is not None:
+ self.parent.restoreState(state)
+
+ settings.beginGroup("Tools")
+
+ for packageName, registeredToolSet in GET_TOOLS().items():
+ for ToolClass in registeredToolSet:
+ if issubclass(ToolClass, ShelfTool):
+ ToolInstance = ToolClass()
+ # prevent to be garbage collected
+ self.registerToolInstance(ToolInstance)
+ ToolInstance.setAppInstance(self)
+ action = QAction(self)
+ action.setIcon(ToolInstance.getIcon())
+ action.setText(ToolInstance.name())
+ action.setToolTip(ToolInstance.toolTip())
+ action.setObjectName(ToolInstance.name())
+ action.triggered.connect(ToolInstance.do)
+
+ # check if context menu data available
+ menuBuilder = ToolInstance.contextMenuBuilder()
+ if menuBuilder:
+ menuGenerator = ContextMenuGenerator(menuBuilder)
+ menu = menuGenerator.generate()
+ action.setMenu(menu)
+ toolbar.addAction(action)
+
+ # step to ShelfTools/ToolName group and pass settings inside
+ settings.beginGroup("ShelfTools")
+ settings.beginGroup(ToolClass.name())
+ ToolInstance.restoreState(settings)
+ settings.endGroup()
+
+ if issubclass(ToolClass, FormTool):
+ ToolInstance = ToolClass()
+ # prevent to be garbage collected
+ self.registerToolInstance(ToolInstance)
+ ToolInstance.setAppInstance(self.parent)
+ action = QAction(self)
+ action.setIcon(ToolInstance.getIcon())
+ action.setText(ToolInstance.name())
+ action.setToolTip(ToolInstance.toolTip())
+ action.setObjectName(ToolInstance.name())
+ action.triggered.connect(ToolInstance.do)
+
+ menus = self.parent.menuBar.findChildren(QMenu)
+ pluginsMenuAction = [m for m in menus if m.title() == "Tools"][0].menuAction()
+ toolsMenu = getOrCreateMenu(self.parent.menuBar, "Tools")
+ #self.parent.menuBar.insertMenu(pluginsMenuAction, toolsMenu)
+ packageSubMenu = getOrCreateMenu(toolsMenu, packageName)
+ toolsMenu.addMenu(packageSubMenu)
+ showToolAction = packageSubMenu.addAction(action)
+
+ settings.beginGroup("DockTools")
+ childGroups = settings.childGroups()
+ for dockToolGroupName in childGroups:
+ # This dock tool data been saved on last shutdown
+ settings.beginGroup(dockToolGroupName)
+ if dockToolGroupName in [t.uniqueName() for t in self._tools]:
+ settings.endGroup()
+ continue
+ toolName = dockToolGroupName.split("::")[0]
+ self.invokeDockToolByName(packageName, toolName, settings)
+ settings.endGroup()
+ settings.endGroup()
+
+ if issubclass(ToolClass, DockTool):
+ menus = self.parent.menuBar.findChildren(QMenu)
+ pluginsMenuAction = [m for m in menus if m.title() == "Tools"][0].menuAction()
+ toolsMenu = getOrCreateMenu(self.parent.menuBar, "Tools")
+ #self.parent.menuBar.insertMenu(pluginsMenuAction, toolsMenu)
+ packageSubMenu = getOrCreateMenu(toolsMenu, packageName)
+ toolsMenu.addMenu(packageSubMenu)
+ showToolAction = packageSubMenu.addAction(ToolClass.name())
+ icon = ToolClass.getIcon()
+ if icon:
+ showToolAction.setIcon(icon)
+ showToolAction.triggered.connect(
+ lambda pkgName=packageName, toolName=ToolClass.name(): self.invokeDockToolByName(pkgName,
+ toolName))
+ settings.beginGroup("DockTools")
+ childGroups = settings.childGroups()
+ for dockToolGroupName in childGroups:
+ # This dock tool data been saved on last shutdown
+ settings.beginGroup(dockToolGroupName)
+ if dockToolGroupName in [t.uniqueName() for t in self._tools]:
+ settings.endGroup()
+ continue
+ toolName = dockToolGroupName.split("::")[0]
+ self.invokeDockToolByName(packageName, toolName, settings)
+ settings.endGroup()
+ settings.endGroup()
+
+ EditorHistory().saveState("New file")
+
+ for name, package in GET_PACKAGES().items():
+ prefsWidgets = package.PrefsWidgets()
+ if prefsWidgets is not None:
+ for categoryName, widgetClass in prefsWidgets.items():
+ PreferencesWindow().addCategory(categoryName, widgetClass())
+ PreferencesWindow().selectByName("General")
+ return toolbar
+ def getToolBar(self):
+ return self.toolBarDict
+
+ def getCanvas(self):
+ return self.canvasWidget.canvas
+
+ def keyPressEvent(self, event):
+ modifiers = event.modifiers()
+ currentInputAction = InputAction(name="temp", actionType=InputActionType.Keyboard, key=event.key(), modifiers=modifiers)
+
+ actionSaveVariants = InputManager()["App.Save"]
+ actionNewFileVariants = InputManager()["App.NewFile"]
+ actionLoadVariants = InputManager()["App.Load"]
+ actionSaveAsVariants = InputManager()["App.SaveAs"]
+
+ if currentInputAction in actionNewFileVariants:
+ shouldSave = self.shouldSave()
+ if shouldSave == QMessageBox.Yes:
+ self.save()
+ elif shouldSave == QMessageBox.Discard:
+ return
+
+ EditorHistory().clear()
+ historyTools = self.getRegisteredTools(classNameFilters=["HistoryTool"])
+ for historyTools in historyTools:
+ historyTools.onClear()
+ self.newFile()
+ EditorHistory().saveState("New file")
+ self.currentFileName = None
+ self.modified = False
+ self.updateLabel()
+ if currentInputAction in actionSaveVariants:
+ self.save()
+ if currentInputAction in actionLoadVariants:
+ shouldSave = self.shouldSave()
+ if shouldSave == QMessageBox.Yes:
+ self.save()
+ elif shouldSave == QMessageBox.Discard:
+ return
+ self.load()
+ if currentInputAction in actionSaveAsVariants:
+ self.save(True)
+
+ def loadFromFileChecked(self, filePath):
+ shouldSave = self.shouldSave()
+ if shouldSave == QMessageBox.Yes:
+ self.save()
+ elif shouldSave == QMessageBox.Discard:
+ return
+ self.loadFromFile(filePath)
+ self.modified = False
+ self.updateLabel()
+
+
+ def loadFromData(self, data, clearHistory=False):
+
+ # check first if all packages we are trying to load are legal
+ missedPackages = set()
+ if not validateGraphDataPackages(data, missedPackages):
+ msg = "This graph can not be loaded. Following packages not found:\n\n"
+ index = 1
+ for missedPackageName in missedPackages:
+ msg += "{0}. {1}\n".format(index, missedPackageName)
+ index += 1
+ QMessageBox.critical(self, "Missing dependencies", msg)
+ return
+
+ if clearHistory:
+ EditorHistory().clear()
+ historyTools = self.getRegisteredTools(classNameFilters=["HistoryTool"])
+ for historyTools in historyTools:
+ historyTools.onClear()
+
+ self.newFile(keepRoot=False)
+ # load raw data
+ self.graphManager.get().deserialize(data)
+ self.fileBeenLoaded.emit()
+ self.graphManager.get().selectGraphByName(data["activeGraph"])
+ self.updateLabel()
+ PathsRegistry().rebuild()
+
+ @property
+ def currentFileName(self):
+ return self._currentFileName
+
+ @currentFileName.setter
+ def currentFileName(self, value):
+ self._currentFileName = value
+ self.updateLabel()
+
+ def createPopupMenu(self):
+ pass
+
+ def getToolClassByName(self, packageName, toolName, toolClass=DockTool):
+ registeredTools = GET_TOOLS()
+ for ToolClass in registeredTools[packageName]:
+ if issubclass(ToolClass, toolClass):
+ pass
+ if ToolClass.name() == toolName:
+ return ToolClass
+ return None
+
+ def createToolInstanceByClass(self, packageName, toolName, toolClass=DockTool):
+ registeredTools = GET_TOOLS()
+ for ToolClass in registeredTools[packageName]:
+ supportedSoftwares = ToolClass.supportedSoftwares()
+ if "any" not in supportedSoftwares:
+ if self.currentSoftware not in supportedSoftwares:
+ continue
+
+ if issubclass(ToolClass, toolClass):
+ pass
+
+ if ToolClass.name() == toolName:
+ return ToolClass()
+ return None
+
+ def getRegisteredTools(self, classNameFilters=[]):
+ if len(classNameFilters) == 0:
+ return self._tools
+ else:
+ result = []
+ for tool in self._tools:
+ if tool.__class__.__name__ in classNameFilters:
+ result.append(tool)
+ return result
+
+ def invokeFormByName(self, packageName, name, settings=None):
+ # invokeDockToolByName Invokes dock tool by tool name and package name
+ # If settings provided QMainWindow::restoreDockWidget will be called instead QMainWindow::addDockWidget
+ toolClass = self.getToolClassByName(packageName, name, FormTool)
+ if toolClass is None:
+ return
+ isSingleton = toolClass.isSingleton()
+ if isSingleton:
+ # check if already registered
+ if name in [t.name() for t in self._tools]:
+ for tool in self._tools:
+ if tool.name() == name:
+ tool.show()
+ tool.onShow()
+ # Highlight window
+ print("highlight", tool.uniqueName())
+ return
+ ToolInstance = self.createToolInstanceByClass(packageName, name, FormTool)
+ if ToolInstance:
+ self.registerToolInstance(ToolInstance)
+ if settings is not None:
+ ToolInstance.restoreState(settings)
+ if not self.parent.restoreDockWidget(ToolInstance):
+ # handle if ui state was not restored
+ pass
+ ToolInstance.setAppInstance(self.parent)
+ ToolInstance.onShow()
+ return ToolInstance
+
+ def invokeDockToolByName(self, packageName, name, settings=None):
+ # invokeDockToolByName Invokes dock tool by tool name and package name
+ # If settings provided QMainWindow::restoreDockWidget will be called instead QMainWindow::addDockWidget
+ toolClass = self.getToolClassByName(packageName, name, DockTool)
+ if toolClass is None:
+ return
+ isSingleton = toolClass.isSingleton()
+ if isSingleton:
+ # check if already registered
+ if name in [t.name() for t in self._tools]:
+ for tool in self._tools:
+ if tool.name() == name:
+ tool.show()
+ tool.onShow()
+ # Highlight window
+ print("highlight", tool.uniqueName())
+ return
+ ToolInstance = self.createToolInstanceByClass(packageName, name, DockTool)
+ if ToolInstance:
+ self.registerToolInstance(ToolInstance)
+ if settings is not None:
+ ToolInstance.restoreState(settings)
+ if not self.parent.restoreDockWidget(ToolInstance):
+ # handle if ui state was not restored
+ pass
+ else:
+ self.parent.addDockWidget(ToolInstance.defaultDockArea(), ToolInstance)
+ ToolInstance.setAppInstance(self)
+ ToolInstance.onShow()
+ return ToolInstance
+
+ def newFile(self):
+ self.isUntitled = True
+ self.curFile = f"document{pyflowChild.sequenceNumber}.pygraph"
+ self._currentFileName = f"document{pyflowChild.sequenceNumber}.pygraph"
+ pyflowChild.sequenceNumber += 1
+ self.setWindowTitle(self.curFile + '[*]')
+
+ #self.document().contentsChanged.connect(self.documentWasModified)
+
+ def loadFile(self, fileName):
+ file = QFile(fileName)
+ if not file.open(QFile.ReadOnly | QFile.Text):
+ QMessageBox.warning(self, "MDI",
+ "Cannot read file %s:\n%s." % (fileName, file.errorString()))
+ return False
+
+ instr = QTextStream(file)
+ QApplication.setOverrideCursor(Qt.WaitCursor)
+ self.setPlainText(instr.readAll())
+ QApplication.restoreOverrideCursor()
+
+ self.setCurrentFile(fileName)
+
+ #self.contentsChanged.connect(self.documentWasModified)
+
+ return True
+
+ def save(self):
+ if self.isUntitled:
+ return self.saveAs()
+ else:
+ return self.saveFile(self.curFile)
+
+ def saveAs(self):
+ fileName, _ = QFileDialog.getSaveFileName(self, "Save As", self.curFile)
+ if not fileName:
+ return False
+
+ return self.saveFile(fileName)
+
+ def saveFile(self, fileName):
+ file = QFile(fileName)
+
+ if not file.open(QFile.WriteOnly | QFile.Text):
+ QMessageBox.warning(self, "MDI",
+ "Cannot write file %s:\n%s." % (fileName, file.errorString()))
+ return False
+
+ outstr = QTextStream(file)
+ QApplication.setOverrideCursor(Qt.WaitCursor)
+ outstr << self.toPlainText()
+ QApplication.restoreOverrideCursor()
+
+ self.setCurrentFile(fileName)
+ return True
+
+ def userFriendlyCurrentFile(self):
+ return self.strippedName(self.curFile)
+
+ def currentFile(self):
+ return self.curFile
+
+ def closeEvent(self, event):
+ if self.maybeSave():
+ event.accept()
+ else:
+ event.ignore()
+
+ self.parent.toolBarDict[self.guid].setVisible(False)
+ del self.parent.toolBarDict[self.guid]
+
+
+ def documentWasModified(self):
+ self.setWindowModified(self.isModified)
+
+ def maybeSave(self):
+ if self.isModified:
+ ret = QMessageBox.warning(self, "MDI",
+ "'%s' has been modified.\nDo you want to save your "
+ "changes?" % self.userFriendlyCurrentFile(),
+ QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
+
+ if ret == QMessageBox.Save:
+ return self.save()
+
+ if ret == QMessageBox.Cancel:
+ return False
+
+ return True
+
+ def setCurrentFile(self, fileName):
+ self.curFile = QFileInfo(fileName).canonicalFilePath()
+ self.isUntitled = False
+ #self.document().setModified(False)
+ self.setWindowModified(False)
+ self.setWindowTitle(self.userFriendlyCurrentFile() + "[*]")
+
+ def strippedName(self, fullFileName):
+ return QFileInfo(fullFileName).fileName()
+
+ def onRequestFillProperties(self, propertiesFillDelegate):
+ for toolInstance in self._tools:
+ if isinstance(toolInstance, PropertiesTool):
+ toolInstance.clear()
+ toolInstance.assignPropertiesWidget(propertiesFillDelegate)
+
+ def onRequestClearProperties(self):
+ for toolInstance in self._tools:
+ if isinstance(toolInstance, PropertiesTool):
+ toolInstance.clear()
+
+ def startMainLoop(self):
+ self.tick_timer.timeout.connect(self.mainLoop)
+ self.tick_timer.start(1000 / EDITOR_TARGET_FPS)
+ QCoreApplication.processEvents()
+
+ def stopMainLoop(self):
+ self.tick_timer.stop()
+ self.tick_timer.timeout.disconnect()
+
+ def mainLoop(self):
+ deltaTime = process_time() - self._lastClock
+ ds = (deltaTime * 1000.0)
+ if ds > 0:
+ self.fps = int(1000.0 / ds)
+
+ # Tick all graphs
+ # each graph will tick owning raw nodes
+ # each raw node will tick its ui wrapper if it exists
+ self.graphManager.get().Tick(deltaTime)
+
+ # Tick canvas. Update ui only stuff such animation etc.
+ self.canvasWidget.Tick(deltaTime)
+
+ self._lastClock = process_time()
+
+class MDIMain(QMainWindow):
+ appInstance = None
+ def __init__(self, parent=None):
+ super(MDIMain, self).__init__(parent=parent)
+
+ self.mdiArea = QMdiArea()
+
+ '''self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
+ self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)'''
+ self.setCentralWidget(self.mdiArea)
+ self.mdiArea.subWindowActivated.connect(self.updateMenus)
+ self.windowMapper = QSignalMapper(self)
+ self.guid = uuid.uuid4()
+ self.setFocusPolicy(QtCore.Qt.StrongFocus)
+ self.currentSoftware = ""
+ self.undoStack = QUndoStack(self)
+ self.setContentsMargins(1, 1, 1, 1)
+ self.setTabPosition(QtCore.Qt.AllDockWidgetAreas, QTabWidget.North)
+ self.setDockOptions(QMainWindow.AnimatedDocks | QMainWindow.AllowNestedDocks)
+ self.menuBar = QMenuBar(None) #self
+ self.menuBar.setGeometry(QRect(0, 0, 863, 21))
+ self.menuBar.setObjectName("menuBar")
+ self.setMenuBar(self.menuBar)
+ self.windowMapper = QSignalMapper(self)
+ self._tools = set()
+ self.setWindowTitle(winTitle())
+ self.setFocusPolicy(QtCore.Qt.StrongFocus)
+ self.setWindowIcon(QtGui.QIcon(":/LogoBpApp.png"))
+ self.undoStack = QUndoStack(self)
+ self.setMouseTracking(True)
+
+ self.toolBarDict = {}
+ self.populateToolBar()
+ self.refreshToolBar(self)
+ self.createStatusBar()
+ #self.updateMenus()
+ self.populateMenu()
+
+ self.preferencesWindow = PreferencesWindow(self)
+
+ self.createActions()
+
+ def populateMenu(self):
+ fileMenu = self.menuBar.addMenu("&File")
+ newFileAction = fileMenu.addAction("New file")
+ newFileAction.setIcon(QtGui.QIcon(":/new_file_icon.png"))
+ newFileAction.triggered.connect(self.newFile)
+
+ loadAction = fileMenu.addAction("Load")
+ loadAction.setIcon(QtGui.QIcon(":/folder_open_icon.png"))
+ loadAction.triggered.connect(self.load)
+
+ saveAction = fileMenu.addAction("Save")
+ saveAction.setIcon(QtGui.QIcon(":/save_icon.png"))
+ saveAction.triggered.connect(self.save)
+
+ saveAsAction = fileMenu.addAction("Save as")
+ saveAsAction.setIcon(QtGui.QIcon(":/save_as_icon.png"))
+ saveAsAction.triggered.connect(lambda: self.save(True))
+
+ IOMenu = fileMenu.addMenu("Custom IO")
+ for packageName, package in GET_PACKAGES().items():
+ # exporters
+ exporters = None
+ try:
+ exporters = package.GetExporters()
+ except:
+ continue
+ pkgMenu = IOMenu.addMenu(packageName)
+ for exporterName, exporterClass in exporters.items():
+ fileFormatMenu = pkgMenu.addMenu(exporterClass.displayName())
+ fileFormatMenu.setToolTip(exporterClass.toolTip())
+ if exporterClass.createExporterMenu():
+ exportAction = fileFormatMenu.addAction("Export")
+ exportAction.triggered.connect(lambda checked=False, app=self, exporter=exporterClass: exporter.doExport(app))
+ if exporterClass.createImporterMenu():
+ importAction = fileFormatMenu.addAction("Import")
+ importAction.triggered.connect(lambda checked=False, app=self, exporter=exporterClass: exporter.doImport(app))
+
+ editMenu = self.menuBar.addMenu("Edit")
+ preferencesAction = editMenu.addAction("Preferences")
+ preferencesAction.setIcon(QtGui.QIcon(":/options_icon.png"))
+ preferencesAction.triggered.connect(self.showPreferencesWindow)
+
+ pluginsMenu = self.menuBar.addMenu("Tools")
+ packagePlugin = pluginsMenu.addAction("Package Builder")
+ packagePlugin.triggered.connect(self.createPackagebBuilder)
+ packagePlugin2 = pluginsMenu.addAction("Logger")
+ packagePlugin2.triggered.connect(self.createLoggerTool)
+
+
+ self.windowMenu = self.menuBar.addMenu("&Window")
+ self.updateWindowMenu
+ self.windowMenu.aboutToShow.connect(self.updateWindowMenu)
+
+ helpMenu = self.menuBar.addMenu("Help")
+ helpMenu.addAction("Homepage").triggered.connect(lambda _=False, url="https://wonderworks-software.github.io/PyFlow/": QtGui.QDesktopServices.openUrl(url))
+ helpMenu.addAction("Docs").triggered.connect(lambda _=False, url="https://pyflow.readthedocs.io/en/latest/": QtGui.QDesktopServices.openUrl(url))
+
+ def populateToolBar(self):
+ toolbar = QToolBar(self)
+
+ newFileAction = toolbar.addAction("New file")
+ newFileAction.setIcon(QtGui.QIcon(":/new_file_icon.png"))
+ newFileAction.setToolTip("")
+ newFileAction.triggered.connect(self.newFile)
+
+ loadAction = toolbar.addAction("Load")
+ loadAction.setIcon(QtGui.QIcon(":/folder_open_icon.png"))
+ loadAction.setToolTip("")
+ loadAction.triggered.connect(self.load)
+
+ self.addToolBar(QtCore.Qt.TopToolBarArea, toolbar)
+
+ self.toolBarDict[self.guid] = toolbar
+
+ for packageName, registeredToolSet in GET_TOOLS().items():
+ for ToolClass in registeredToolSet:
+ if issubclass(ToolClass, ShelfTool) or issubclass(ToolClass, FormTool):
+ ToolInstance = ToolClass()
+ # prevent to be garbage collected
+ self.registerToolInstance(ToolInstance)
+ ToolInstance.setAppInstance(self)
+ action = QAction(self)
+ action.setIcon(ToolInstance.getIcon())
+ action.setText(ToolInstance.name())
+ action.setToolTip(ToolInstance.toolTip())
+ action.setObjectName(ToolInstance.name())
+ action.triggered.connect(ToolInstance.do)
+ # check if context menu data available
+ menuBuilder = ToolInstance.contextMenuBuilder()
+ if menuBuilder:
+ menuGenerator = ContextMenuGenerator(menuBuilder)
+ menu = menuGenerator.generate()
+ action.setMenu(menu)
+ toolbar.addAction(action)
+
+ if issubclass(ToolClass, DockTool):
+ menus = self.menuBar.findChildren(QMenu)
+ pluginsMenuAction = [m for m in menus if m.title() == "Tools"][0].menuAction()
+ toolsMenu = getOrCreateMenu(self.menuBar, "Tools")
+ self.menuBar.insertMenu(pluginsMenuAction, toolsMenu)
+ packageSubMenu = getOrCreateMenu(toolsMenu, packageName)
+ toolsMenu.addMenu(packageSubMenu)
+ showToolAction = packageSubMenu.addAction(ToolClass.name())
+ icon = ToolClass.getIcon()
+ if icon:
+ showToolAction.setIcon(icon)
+ showToolAction.triggered.connect(
+ lambda pkgName=packageName, toolName=ToolClass.name(): self.invokeDockToolByName(pkgName,
+ toolName))
+
+ EditorHistory(self).saveState("New file")
+
+ for name, package in GET_PACKAGES().items():
+ prefsWidgets = package.PrefsWidgets()
+ if prefsWidgets is not None:
+ for categoryName, widgetClass in prefsWidgets.items():
+ PreferencesWindow().addCategory(categoryName, widgetClass())
+ PreferencesWindow().selectByName("General")
+ return toolbar
+ def getToolBar(self):
+ return self.toolBarDict
+
+ def getToolClassByName(self, packageName, toolName, toolClass=DockTool):
+ registeredTools = GET_TOOLS()
+ for ToolClass in registeredTools[packageName]:
+ if issubclass(ToolClass, toolClass):
+ if ToolClass.name() == toolName:
+ return ToolClass
+ return None
+
+ def createToolInstanceByClass(self, packageName, toolName, toolClass=DockTool):
+ registeredTools = GET_TOOLS()
+ for ToolClass in registeredTools[packageName]:
+ supportedSoftwares = ToolClass.supportedSoftwares()
+ if "any" not in supportedSoftwares:
+ if self.currentSoftware not in supportedSoftwares:
+ continue
+
+ if issubclass(ToolClass, toolClass):
+ if ToolClass.name() == toolName:
+ return ToolClass()
+ return None
+
+ def invokeDockToolByName(self, packageName, name, settings=None):
+ # invokeDockToolByName Invokes dock tool by tool name and package name
+ # If settings provided QMainWindow::restoreDockWidget will be called instead QMainWindow::addDockWidget
+ toolClass = self.getToolClassByName(packageName, name, DockTool)
+ if toolClass is None:
+ return
+ isSingleton = toolClass.isSingleton()
+ if isSingleton:
+ # check if already registered
+ if name in [t.name() for t in self._tools]:
+ for tool in self._tools:
+ if tool.name() == name:
+ tool.show()
+ tool.onShow()
+ # Highlight window
+ print("highlight", tool.uniqueName())
+ return
+ ToolInstance = self.createToolInstanceByClass(packageName, name, DockTool)
+ if ToolInstance:
+ self.registerToolInstance(ToolInstance)
+ if settings is not None:
+ ToolInstance.restoreState(settings)
+ if not self.parent.restoreDockWidget(ToolInstance):
+ # handle if ui state was not restored
+ pass
+ else:
+ self.parent.addDockWidget(ToolInstance.defaultDockArea(), ToolInstance)
+ ToolInstance.setAppInstance(self)
+ ToolInstance.onShow()
+ return ToolInstance
+
+ def loadFromFile(self, filePath):
+ with open(filePath, 'r') as f:
+ data = json.load(f)
+ self.loadFromData(data, clearHistory=True)
+ self.currentFileName = filePath
+ EditorHistory().saveState("Open {}".format(os.path.basename(self.currentFileName)))
+
+
+ def loadFromData(self, data, clearHistory=False):
+
+ # check first if all packages we are trying to load are legal
+ missedPackages = set()
+ if not validateGraphDataPackages(data, missedPackages):
+ msg = "This graph can not be loaded. Following packages not found:\n\n"
+ index = 1
+ for missedPackageName in missedPackages:
+ msg += "{0}. {1}\n".format(index, missedPackageName)
+ index += 1
+ QMessageBox.critical(self, "Missing dependencies", msg)
+ return
+
+ if clearHistory:
+ EditorHistory().clear()
+ historyTools = self.getRegisteredTools(classNameFilters=["HistoryTool"])
+ for historyTools in historyTools:
+ historyTools.onClear()
+
+ self.newFile(keepRoot=False)
+ # load raw data
+ self.graphManager.get().deserialize(data)
+ self.fileBeenLoaded.emit()
+ self.graphManager.get().selectGraphByName(data["activeGraph"])
+ self.updateLabel()
+ PathsRegistry().rebuild()
+
+
+ def load(self):
+ name_filter = "Graph files (*.pygraph)"
+ savepath = QFileDialog.getOpenFileName(filter=name_filter)
+ if type(savepath) in [tuple, list]:
+ fpath = savepath[0]
+ else:
+ fpath = savepath
+ if not fpath == '':
+ self.loadFromFile(fpath)
+
+ def closeEvent(self, event):
+ self.mdiArea.closeAllSubWindows()
+ if self.mdiArea.currentSubWindow():
+ event.ignore()
+ else:
+ self.writeSettings()
+ event.accept()
+
+ def newFile(self):
+ child = self.createMdiChild()
+ child.newFile()
+ child.show()
+
+ #toolbar = self.populateToolBar()
+ self.hidealltool(child.guid)
+ # self.instanceDict[child.guid] = child.instance
+ # self.toolBarDict[child.guid] = toolbar
+ # self.addToolBar(Qt.TopToolBarArea, toolbar)
+
+ def hidealltool(self, VisibleToolBar=None):
+ if VisibleToolBar is None:
+ VisibleToolBar = self.guid
+
+ for toolbar in self.toolBarDict:
+ if toolbar != VisibleToolBar:
+ self.toolBarDict[toolbar].setVisible(False)
+ else:
+ self.toolBarDict[toolbar].setVisible(True)
+
+ def open(self):
+ fileName, _ = QFileDialog.getOpenFileName(self)
+ if fileName:
+ existing = self.findMdiChild(fileName)
+ if existing:
+ self.mdiArea.setActiveSubWindow(existing)
+ return
+
+ child = self.createMdiChild()
+ if child.loadFile(fileName):
+ self.statusBar().showMessage("File loaded", 2000)
+ child.show()
+ else:
+ child.close()
+
+ def save(self):
+ if self.activeMdiChild() and self.activeMdiChild().save():
+ self.statusBar().showMessage("File saved", 2000)
+
+ def saveAs(self):
+ if self.activeMdiChild() and self.activeMdiChild().saveAs():
+ self.statusBar().showMessage("File saved", 2000)
+
+ def cut(self):
+ if self.activeMdiChild():
+ self.activeMdiChild().cut()
+
+ def copy(self):
+ if self.activeMdiChild():
+ self.activeMdiChild().copy()
+
+ def paste(self):
+ if self.activeMdiChild():
+ self.activeMdiChild().paste()
+
+ def about(self):
+ QMessageBox.about(self, "About MDI",
+ "The MDI example demonstrates how to write multiple "
+ "document interface applications using Qt.")
+
+ def updateMenus(self):
+ hasMdiChild = (self.activeMdiChild() is not None)
+ self.saveAct.setEnabled(hasMdiChild)
+ self.saveAsAct.setEnabled(hasMdiChild)
+ self.pasteAct.setEnabled(hasMdiChild)
+ self.closeAct.setEnabled(hasMdiChild)
+ self.closeAllAct.setEnabled(hasMdiChild)
+ self.tileAct.setEnabled(hasMdiChild)
+ self.cascadeAct.setEnabled(hasMdiChild)
+ self.nextAct.setEnabled(hasMdiChild)
+ self.previousAct.setEnabled(hasMdiChild)
+ self.separatorAct.setVisible(hasMdiChild)
+
+ '''hasSelection = (self.activeMdiChild() is not None and
+ self.activeMdiChild().textCursor().hasSelection())
+ self.cutAct.setEnabled(hasSelection)
+ self.copyAct.setEnabled(hasSelection)'''
+
+ def updateWindowMenu(self):
+ self.windowMenu.clear()
+ self.windowMenu.addAction(self.closeAct)
+ self.windowMenu.addAction(self.closeAllAct)
+ self.windowMenu.addSeparator()
+ self.windowMenu.addAction(self.tileAct)
+ self.windowMenu.addAction(self.cascadeAct)
+ self.windowMenu.addSeparator()
+ self.windowMenu.addAction(self.nextAct)
+ self.windowMenu.addAction(self.previousAct)
+ self.windowMenu.addAction(self.separatorAct)
+
+ windows = self.mdiArea.subWindowList()
+ self.separatorAct.setVisible(len(windows) != 0)
+
+ for i, window in enumerate(windows):
+ child = window.widget()
+
+ text = "%d %s" % (i + 1, window.userFriendlyCurrentFile())
+ if i < 9:
+ text = '&' + text
+
+ action = self.windowMenu.addAction(text)
+ action.setCheckable(True)
+ action.setChecked(child is self.activeMdiChild())
+ action.triggered.connect(self.windowMapper.map)
+ self.windowMapper.setMapping(action, window)
+
+
+ def createMdiChild(self):
+ #instance = self.instance(software="standalone")
+ child = pyflowChild(self)
+ self.mdiArea.addSubWindow(child)
+ self.mdiArea.setBaseSize(200, 200)
+ '''child.copyAvailable.connect(self.cutAct.setEnabled)
+ child.copyAvailable.connect(self.copyAct.setEnabled)'''
+ return child
+
+ def createPackagebBuilder(self):
+ self.newFileFromUi(PackageBuilder.PackageBuilder(self))
+
+ def createLoggerTool(self):
+ self.Logger = LoggerTool()
+ self.addDockWidget(self.Logger.defaultDockArea(), self.Logger)
+
+ def newFileFromUi(self, MDIClass):
+ child = MDIClass.ui
+ MDIClass.uuid = uuid.uuid4()
+ self.mdiArea.addSubWindow(child)
+ child.show()
+
+ def createActions(self):
+ self.newAct = QAction(QIcon(':/images/new.png'), "&New", self,
+ shortcut=QKeySequence.New, statusTip="Create a new file",
+ triggered=self.newFile)
+
+ self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self,
+ shortcut=QKeySequence.Open, statusTip="Open an existing file",
+ triggered=self.open)
+
+ self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self,
+ shortcut=QKeySequence.Save,
+ statusTip="Save the document to disk", triggered=self.save)
+
+ self.saveAsAct = QAction("Save &As...", self,
+ shortcut=QKeySequence.SaveAs,
+ statusTip="Save the document under a new name",
+ triggered=self.saveAs)
+
+ self.exitAct = QAction("E&xit", self, shortcut=QKeySequence.Quit,
+ statusTip="Exit the application",
+ triggered=QApplication.instance().closeAllWindows)
+
+ self.cutAct = QAction(QIcon(':/images/cut.png'), "Cu&t", self,
+ shortcut=QKeySequence.Cut,
+ statusTip="Cut the current selection's contents to the clipboard",
+ triggered=self.cut)
+
+ self.copyAct = QAction(QIcon(':/images/copy.png'), "&Copy", self,
+ shortcut=QKeySequence.Copy,
+ statusTip="Copy the current selection's contents to the clipboard",
+ triggered=self.copy)
+
+ self.pasteAct = QAction(QIcon(':/images/paste.png'), "&Paste", self,
+ shortcut=QKeySequence.Paste,
+ statusTip="Paste the clipboard's contents into the current selection",
+ triggered=self.paste)
+
+ self.closeAct = QAction("Cl&ose", self,
+ statusTip="Close the active window",
+ triggered=self.mdiArea.closeActiveSubWindow)
+
+ self.closeAllAct = QAction("Close &All", self,
+ statusTip="Close all the windows",
+ triggered=self.mdiArea.closeAllSubWindows)
+
+ self.tileAct = QAction("&Tile", self, statusTip="Tile the windows",
+ triggered=self.mdiArea.tileSubWindows)
+
+ self.cascadeAct = QAction("&Cascade", self,
+ statusTip="Cascade the windows",
+ triggered=self.mdiArea.cascadeSubWindows)
+
+ self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild,
+ statusTip="Move the focus to the next window",
+ triggered=self.mdiArea.activateNextSubWindow)
+
+ self.previousAct = QAction("Pre&vious", self,
+ shortcut=QKeySequence.PreviousChild,
+ statusTip="Move the focus to the previous window",
+ triggered=self.mdiArea.activatePreviousSubWindow)
+
+ self.separatorAct = QAction(self)
+ self.separatorAct.setSeparator(True)
+
+ self.aboutAct = QAction("&About", self,
+ statusTip="Show the application's About box",
+ triggered=self.about)
+
+ self.aboutQtAct = QAction("About &Qt", self,
+ statusTip="Show the Qt library's About box",
+ triggered=QApplication.instance().aboutQt)
+
+ def createStatusBar(self):
+ self.statusBar().showMessage("Ready")
+
+ def readSettings(self):
+ settings = QSettings('Trolltech', 'MDI Example')
+ pos = settings.value('pos', QPoint(200, 200))
+ size = settings.value('size', QSize(400, 400))
+ self.move(pos)
+ self.resize(size)
+
+ def writeSettings(self):
+ settings = QtCore.QSettings('Trolltech', 'MDI Example')
+ settings.setValue('pos', self.pos())
+ settings.setValue('size', self.size())
+
+ def activeMdiChild(self):
+ activeSubWindow = self.mdiArea.activeSubWindow()
+
+ if activeSubWindow:
+ try:
+ guid = activeSubWindow.widget().guid
+ self.hidealltool(guid)
+ return activeSubWindow.widget()
+ except:
+ pass
+ return None
+
+ def findMdiChild(self, fileName):
+ canonicalFilePath = QFileInfo(fileName).canonicalFilePath()
+
+ for window in self.mdiArea.subWindowList():
+ if window.widget().currentFile() == canonicalFilePath:
+ return window
+ return None
+
+ def switchLayoutDirection(self):
+ if self.layoutDirection() == Qt.LeftToRight:
+ QApplication.setLayoutDirection(Qt.RightToLeft)
+ else:
+ QApplication.setLayoutDirection(Qt.LeftToRight)
+
+ def setActiveSubWindow(self, window):
+ if window:
+ self.mdiArea.setActiveSubWindow(window)
+
+ def refreshToolBar(self, instance):
+
+ if instance is None: #The Package is selected but a file has not be loaded/started
+
+ package = "Root"
+ toolDict = self.getToolBarLayout()
+ toolOrder = ["Packages", "Tools"]
+
+ else: #There is an active instance
+ package = "PyFlow"
+ #toolDict = instance.getToolBarLayout()
+
+ def getToolBarLayout(self):
+
+ toolBarDict = {}
+ pyFlowToolBar = []
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "NewFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "NewFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "OpenFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "SaveFile",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "AlignLeft",
+ "Active": True})
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action",
+ "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "AlignRight",
+ "Active": True})
+
+ toolBarDict["File"] = pyFlowToolBar
+
+ return toolBarDict
+
+ def updateToolBar(self, tBar, tDict):
+ if tDict["Action"] == "Add Action":
+ toolAction = self.actionRegisterDict[tDict["Package"]].getAction(tDict["PackageGroup"], tDict["Command"])
+ try:
+ toolAction.setInstance(tDict["Instance"])
+ tBar.addAction(toolAction)
+ except:
+ pass
+ if tDict["Action"] == "Add Separator":
+ pass
+ '''menuAction.setSeparator(True)
+ mBar.addAction(menuAction)'''
+ if tDict["Action"] == "Add Children":
+ pass
+
+ def showPreferencesWindow(self):
+ self.preferencesWindow.show()
+
+ def instance(self, parent=None, software=""):
+ assert(software != ""), "Invalid arguments. Please pass you software name as second argument!"
+ settings = ConfigManager().getSettings("APP_STATE")
+
+ instance = pyflowChild(self)
+ instance.currentSoftware = software
+ SessionDescriptor().software = instance.currentSoftware
+ return instance
+
+ def unregisterToolInstance(self, instance):
+ if instance in self._tools:
+ self._tools.remove(instance)
+
+ def registerToolInstance(self, instance):
+ """Registers tool instance reference
+
+ This needed to prevent classes from being garbage collected and to save widgets state
+
+ Args:
+
+ instance (ToolBase): Tool to be registered
+ """
+ self._tools.add(instance)
+
+if __name__ == '__main__':
+
+ import sys
+
+ app = QApplication(sys.argv)
+ mainWin = MDIMain()
+ mainWin.show()
+ sys.exit(app.exec_())
diff --git a/PyFlow/ConfigManager.py b/PyFlow/ConfigManager.py
index bccb6f3cb..7f74b2414 100644
--- a/PyFlow/ConfigManager.py
+++ b/PyFlow/ConfigManager.py
@@ -16,7 +16,7 @@
import os
import json
-from Qt import QtCore, QtGui
+from qtpy import QtCore, QtGui
from PyFlow.Core.Common import *
from PyFlow.Input import InputAction, InputManager, InputActionType
@@ -24,7 +24,10 @@
@SingletonDecorator
class ConfigManager(object):
- """Responsible for registering configuration files, reading/writing values to registered config files by aliases, providing QSettings from registered aliases."""
+ """
+ Responsible for registering configuration files, reading/writing values to registered config files by
+ aliases, providing QSettings from registered aliases.
+ """
CONFIGS_STORAGE = {}
@@ -33,7 +36,9 @@ class ConfigManager(object):
def __init__(self, *args, **kwargs):
self.registerConfigFile("PREFS", os.path.join(self.CONFIGS_DIR, "prefs.ini"))
- self.registerConfigFile("APP_STATE", os.path.join(self.CONFIGS_DIR, "config.ini"))
+ self.registerConfigFile(
+ "APP_STATE", os.path.join(self.CONFIGS_DIR, "config.ini")
+ )
if not os.path.exists(self.INPUT_CONFIG_PATH):
self.createDefaultInput()
@@ -49,7 +54,9 @@ def __init__(self, *args, **kwargs):
@staticmethod
def shouldRedirectOutput():
- return ConfigManager().getPrefsValue("PREFS", "General/RedirectOutput") == "true"
+ return (
+ ConfigManager().getPrefsValue("PREFS", "General/RedirectOutput") == "true"
+ )
def registerConfigFile(self, alias, absPath):
if alias not in self.CONFIGS_STORAGE:
@@ -59,7 +66,9 @@ def registerConfigFile(self, alias, absPath):
def getSettings(self, alias):
if alias in self.CONFIGS_STORAGE:
- settings = QtCore.QSettings(self.CONFIGS_STORAGE[alias], QtCore.QSettings.IniFormat)
+ settings = QtCore.QSettings(
+ self.CONFIGS_STORAGE[alias], QtCore.QSettings.IniFormat
+ )
return settings
def getPrefsValue(self, configAlias, valueKey):
@@ -69,35 +78,259 @@ def getPrefsValue(self, configAlias, valueKey):
return settings.value(valueKey)
def createDefaultInput(self):
- InputManager().registerAction(InputAction(name="Canvas.Pan", actionType=InputActionType.Mouse, group="Navigation", mouse=QtCore.Qt.MouseButton.MiddleButton))
- InputManager().registerAction(InputAction(name="Canvas.Pan", actionType=InputActionType.Mouse, group="Navigation", mouse=QtCore.Qt.MouseButton.LeftButton, modifiers=QtCore.Qt.AltModifier))
- InputManager().registerAction(InputAction(name="Canvas.Zoom", actionType=InputActionType.Mouse, group="Navigation", mouse=QtCore.Qt.MouseButton.RightButton))
- InputManager().registerAction(InputAction(name="Canvas.FrameSelected", actionType=InputActionType.Keyboard, group="Navigation", key=QtCore.Qt.Key_F))
- InputManager().registerAction(InputAction(name="Canvas.FrameAll", actionType=InputActionType.Keyboard, group="Navigation", key=QtCore.Qt.Key_H))
- InputManager().registerAction(InputAction(name="Canvas.ZoomIn", actionType=InputActionType.Keyboard, group="Navigation", key=QtCore.Qt.Key_Equal, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="Canvas.ZoomOut", actionType=InputActionType.Keyboard, group="Navigation", key=QtCore.Qt.Key_Minus, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="Canvas.ResetScale", actionType=InputActionType.Keyboard, group="Navigation", key=QtCore.Qt.Key_R, modifiers=QtCore.Qt.ControlModifier))
-
- InputManager().registerAction(InputAction(name="Canvas.AlignLeft", actionType=InputActionType.Keyboard, group="Refactoring", modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier, key=QtCore.Qt.Key_Left))
- InputManager().registerAction(InputAction(name="Canvas.AlignTop", actionType=InputActionType.Keyboard, group="Refactoring", modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier, key=QtCore.Qt.Key_Up))
- InputManager().registerAction(InputAction(name="Canvas.AlignRight", actionType=InputActionType.Keyboard, group="Refactoring", modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier, key=QtCore.Qt.Key_Right))
- InputManager().registerAction(InputAction(name="Canvas.AlignBottom", actionType=InputActionType.Keyboard, group="Refactoring", modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier, key=QtCore.Qt.Key_Down))
-
- InputManager().registerAction(InputAction(name="Canvas.Undo", actionType=InputActionType.Keyboard, group="Editing", modifiers=QtCore.Qt.ControlModifier, key=QtCore.Qt.Key_Z))
- InputManager().registerAction(InputAction(name="Canvas.Redo", actionType=InputActionType.Keyboard, group="Editing", modifiers=QtCore.Qt.ControlModifier, key=QtCore.Qt.Key_Y))
- InputManager().registerAction(InputAction(name="Canvas.KillSelected", actionType=InputActionType.Keyboard, group="Editing", key=QtCore.Qt.Key_Delete))
- InputManager().registerAction(InputAction(name="Canvas.CopyNodes", actionType=InputActionType.Keyboard, group="Editing", key=QtCore.Qt.Key_C, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="Canvas.CutNodes", actionType=InputActionType.Keyboard, group="Editing", key=QtCore.Qt.Key_X, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="Canvas.DragCopyNodes", actionType=InputActionType.Mouse, group="Editing", mouse=QtCore.Qt.MouseButton.LeftButton, modifiers=QtCore.Qt.AltModifier))
- InputManager().registerAction(InputAction(name="Canvas.DragCopyNodes", actionType=InputActionType.Mouse, group="Editing", mouse=QtCore.Qt.MouseButton.MiddleButton, modifiers=QtCore.Qt.AltModifier))
- InputManager().registerAction(InputAction(name="Canvas.DragNodes", actionType=InputActionType.Mouse, group="Editing", mouse=QtCore.Qt.MouseButton.MiddleButton))
- InputManager().registerAction(InputAction(name="Canvas.DragNodes", actionType=InputActionType.Mouse, group="Editing", mouse=QtCore.Qt.MouseButton.LeftButton))
- InputManager().registerAction(InputAction(name="Canvas.DragChainedNodes", actionType=InputActionType.Mouse, group="Editing", mouse=QtCore.Qt.MouseButton.MiddleButton))
- InputManager().registerAction(InputAction(name="Canvas.PasteNodes", actionType=InputActionType.Keyboard, group="Editing", key=QtCore.Qt.Key_V, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="Canvas.DuplicateNodes", actionType=InputActionType.Keyboard, group="Editing", key=QtCore.Qt.Key_D, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="Canvas.DisconnectPin", actionType=InputActionType.Mouse, group="Editing", mouse=QtCore.Qt.MouseButton.LeftButton, modifiers=QtCore.Qt.AltModifier))
-
- InputManager().registerAction(InputAction(name="App.NewFile", actionType=InputActionType.Keyboard, group="IO", key=QtCore.Qt.Key_N, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="App.Save", actionType=InputActionType.Keyboard, group="IO", key=QtCore.Qt.Key_S, modifiers=QtCore.Qt.ControlModifier))
- InputManager().registerAction(InputAction(name="App.SaveAs", actionType=InputActionType.Keyboard, group="IO", key=QtCore.Qt.Key_S, modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier))
- InputManager().registerAction(InputAction(name="App.Load", actionType=InputActionType.Keyboard, group="IO", key=QtCore.Qt.Key_O, modifiers=QtCore.Qt.ControlModifier))
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.Pan",
+ actionType=InputActionType.Mouse,
+ group="Navigation",
+ mouse=QtCore.Qt.MouseButton.MiddleButton,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.Pan",
+ actionType=InputActionType.Mouse,
+ group="Navigation",
+ mouse=QtCore.Qt.MouseButton.LeftButton,
+ modifiers=QtCore.Qt.AltModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.Zoom",
+ actionType=InputActionType.Mouse,
+ group="Navigation",
+ mouse=QtCore.Qt.MouseButton.RightButton,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.FrameSelected",
+ actionType=InputActionType.Keyboard,
+ group="Navigation",
+ key=QtCore.Qt.Key_F,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.FrameAll",
+ actionType=InputActionType.Keyboard,
+ group="Navigation",
+ key=QtCore.Qt.Key_H,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.ZoomIn",
+ actionType=InputActionType.Keyboard,
+ group="Navigation",
+ key=QtCore.Qt.Key_Equal,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.ZoomOut",
+ actionType=InputActionType.Keyboard,
+ group="Navigation",
+ key=QtCore.Qt.Key_Minus,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.ResetScale",
+ actionType=InputActionType.Keyboard,
+ group="Navigation",
+ key=QtCore.Qt.Key_R,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.AlignLeft",
+ actionType=InputActionType.Keyboard,
+ group="Refactoring",
+ modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ key=QtCore.Qt.Key_Left,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.AlignTop",
+ actionType=InputActionType.Keyboard,
+ group="Refactoring",
+ modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ key=QtCore.Qt.Key_Up,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.AlignRight",
+ actionType=InputActionType.Keyboard,
+ group="Refactoring",
+ modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ key=QtCore.Qt.Key_Right,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.AlignBottom",
+ actionType=InputActionType.Keyboard,
+ group="Refactoring",
+ modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ key=QtCore.Qt.Key_Down,
+ )
+ )
+
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.Undo",
+ actionType=InputActionType.Keyboard,
+ group="Editing",
+ modifiers=QtCore.Qt.ControlModifier,
+ key=QtCore.Qt.Key_Z,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.Redo",
+ actionType=InputActionType.Keyboard,
+ group="Editing",
+ modifiers=QtCore.Qt.ControlModifier,
+ key=QtCore.Qt.Key_Y,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.KillSelected",
+ actionType=InputActionType.Keyboard,
+ group="Editing",
+ key=QtCore.Qt.Key_Delete,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.CopyNodes",
+ actionType=InputActionType.Keyboard,
+ group="Editing",
+ key=QtCore.Qt.Key_C,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.CutNodes",
+ actionType=InputActionType.Keyboard,
+ group="Editing",
+ key=QtCore.Qt.Key_X,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.DragCopyNodes",
+ actionType=InputActionType.Mouse,
+ group="Editing",
+ mouse=QtCore.Qt.MouseButton.LeftButton,
+ modifiers=QtCore.Qt.AltModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.DragCopyNodes",
+ actionType=InputActionType.Mouse,
+ group="Editing",
+ mouse=QtCore.Qt.MouseButton.MiddleButton,
+ modifiers=QtCore.Qt.AltModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.DragNodes",
+ actionType=InputActionType.Mouse,
+ group="Editing",
+ mouse=QtCore.Qt.MouseButton.MiddleButton,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.DragNodes",
+ actionType=InputActionType.Mouse,
+ group="Editing",
+ mouse=QtCore.Qt.MouseButton.LeftButton,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.DragChainedNodes",
+ actionType=InputActionType.Mouse,
+ group="Editing",
+ mouse=QtCore.Qt.MouseButton.MiddleButton,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.PasteNodes",
+ actionType=InputActionType.Keyboard,
+ group="Editing",
+ key=QtCore.Qt.Key_V,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.DuplicateNodes",
+ actionType=InputActionType.Keyboard,
+ group="Editing",
+ key=QtCore.Qt.Key_D,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="Canvas.DisconnectPin",
+ actionType=InputActionType.Mouse,
+ group="Editing",
+ mouse=QtCore.Qt.MouseButton.LeftButton,
+ modifiers=QtCore.Qt.AltModifier,
+ )
+ )
+
+ InputManager().registerAction(
+ InputAction(
+ name="App.NewFile",
+ actionType=InputActionType.Keyboard,
+ group="IO",
+ key=QtCore.Qt.Key_N,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="App.Save",
+ actionType=InputActionType.Keyboard,
+ group="IO",
+ key=QtCore.Qt.Key_S,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="App.SaveAs",
+ actionType=InputActionType.Keyboard,
+ group="IO",
+ key=QtCore.Qt.Key_S,
+ modifiers=QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ )
+ )
+ InputManager().registerAction(
+ InputAction(
+ name="App.Load",
+ actionType=InputActionType.Keyboard,
+ group="IO",
+ key=QtCore.Qt.Key_O,
+ modifiers=QtCore.Qt.ControlModifier,
+ )
+ )
diff --git a/PyFlow/Core/Common.py b/PyFlow/Core/Common.py
index 80712a17e..4289daf94 100644
--- a/PyFlow/Core/Common.py
+++ b/PyFlow/Core/Common.py
@@ -23,27 +23,17 @@
import re
import math
import time
-import inspect
import struct
import weakref
-try:
- from queue import Queue
-except:
- from Queue import Queue
-import uuid
import sys
-from nine import IS_PYTHON2, str
-if IS_PYTHON2:
- from aenum import IntEnum, Flag, auto, Enum
-else:
- from enum import IntEnum, Flag, auto, Enum
+from enum import IntEnum, Flag, auto
from PyFlow import findPinClassByType
from PyFlow.Core.version import Version
+from PyFlow import GET_PACKAGES
-
-maxint = 2 ** (struct.Struct('i').size * 8 - 1) - 1
+maxint = 2 ** (struct.Struct("i").size * 8 - 1) - 1
FLOAT_RANGE_MIN = 0.1 + (-maxint - 1.0)
@@ -51,11 +41,60 @@
INT_RANGE_MIN = -maxint + 0
INT_RANGE_MAX = maxint + 0
-DEFAULT_IN_EXEC_NAME = str('inExec')
-DEFAULT_OUT_EXEC_NAME = str('outExec')
-DEFAULT_WIDGET_VARIANT = str('DefaultWidget')
-REF = str('Reference')
+DEFAULT_IN_EXEC_NAME = "inExec"
+DEFAULT_OUT_EXEC_NAME = "outExec"
+DEFAULT_WIDGET_VARIANT = "DefaultWidget"
+REF = "Reference"
+
+global GlobalVariables
+GlobalVariables = {}
+
+
+def fetchPackageNames(graphJson):
+ """Parses serialized graph and returns all package names it uses
+
+ :param graphJson: Serialized graph
+ :type graphJson: dict
+ :rtyoe: list(str)
+ """
+ packages = set()
+
+ def worker(graphData):
+ for node in graphData["nodes"]:
+ packages.add(node["package"])
+
+ for inpJson in node["inputs"]:
+ packages.add(inpJson["package"])
+ for outJson in node["inputs"]:
+ packages.add(outJson["package"])
+
+ if "graphData" in node:
+ worker(node["graphData"])
+
+ worker(graphJson)
+ return packages
+
+
+def validateGraphDataPackages(graphData, missedPackages=None):
+ """Checks if packages used in serialized data accessible
+
+ Missed packages will be added to output set
+
+ :param graphData: Serialized graph
+ :type graphData: dict
+ :param missedPackages: Package names that missed
+ :type missedPackages: set
+ :rtype: bool
+ """
+ if missedPackages is None:
+ missedPackages = set()
+ existingPackages = GET_PACKAGES().keys()
+ graphPackages = fetchPackageNames(graphData)
+ for pkg in graphPackages:
+ if pkg not in existingPackages:
+ missedPackages.add(pkg)
+ return len(missedPackages) == 0
def lerp(start, end, alpha):
"""Performs a linear interpolation
@@ -67,7 +106,7 @@ def lerp(start, end, alpha):
:param alpha: alpha how far to interpolate
:returns: The result of the linear interpolation
"""
- return (start + alpha * (end - start))
+ return start + alpha * (end - start)
def GetRangePct(MinValue, MaxValue, Value):
@@ -80,6 +119,7 @@ def GetRangePct(MinValue, MaxValue, Value):
"""
return (Value - MinValue) / (MaxValue - MinValue)
+
def mapRangeClamped(Value, InRangeA, InRangeB, OutRangeA, OutRangeB):
"""Returns Value mapped from one range into another where the Value is clamped to the Input Range.
(e.g. 0.5 normalized from the range 0->1 to 0->50 would result in 25)
@@ -88,11 +128,13 @@ def mapRangeClamped(Value, InRangeA, InRangeB, OutRangeA, OutRangeB):
ClampedPct = clamp(GetRangePct(InRangeA, InRangeB, Value), 0.0, 1.0)
return lerp(OutRangeA, OutRangeB, ClampedPct)
+
def mapRangeUnclamped(Value, InRangeA, InRangeB, OutRangeA, OutRangeB):
"""Returns Value mapped from one range into another where the Value is clamped to the Input Range.
(e.g. 0.5 normalized from the range 0->1 to 0->50 would result in 25)"""
return lerp(OutRangeA, OutRangeB, GetRangePct(InRangeA, InRangeB, Value))
+
def sign(x):
"""Returns sign of x. -1 if x is negative, 1 if positive and zero if 0.
@@ -102,10 +144,7 @@ def sign(x):
def currentProcessorTime():
- if IS_PYTHON2:
- return time.clock()
- else:
- return time.process_time()
+ return time.process_time()
def clamp(n, vmin, vmax):
@@ -140,6 +179,7 @@ def roundup(x, to):
_currentVersion = Version(sys.version_info.major, sys.version_info.minor, 0)
python32 = Version(3, 2, 0)
if _currentVersion <= python32:
+
def clearList(list):
"""Clears python list
@@ -149,7 +189,10 @@ def clearList(list):
:rtype: list
"""
del list[:]
+
+
else:
+
def clearList(list):
"""Clears python list
@@ -160,6 +203,7 @@ def clearList(list):
"""
list.clear()
+
def findGoodId(ids):
"""
Finds good minimum unique int from iterable. Starting from 1
@@ -182,7 +226,6 @@ def findGoodId(ids):
diff = ID - lastID
if diff > 1:
return lastID + 1
- break
lastID = ID
else:
return ID + 1
@@ -212,9 +255,9 @@ def test(a=5):
result = "def {0}({1}):\n".format(functionName, kwargsString)
- for scriptLine in scriptString.split('\n'):
+ for scriptLine in scriptString.split("\n"):
result += "\t{}".format(scriptLine)
- result += '\n'
+ result += "\n"
return result
@@ -222,9 +265,9 @@ def cycleCheck(src, dst):
"""Check for cycle connected nodes
:param src: hand side pin
- :type src: :class:`PyFlow.Core.PinBase`
+ :type src: :class:`PyFlow.Core.PinBase.PinBase`
:param dst: hand side pin
- :type dst: :class:`PyFlow.Core.PinBase`
+ :type dst: :class:`PyFlow.Core.PinBase.PinBase`
:returns: True if cycle deleted
:rtype: bool
"""
@@ -245,9 +288,9 @@ def arePinsConnected(src, dst):
.. note:: Pins can be passed in any order if **src** pin is :py:class:`PyFlow.Core.Common.PinDirection`, they will be swapped
:param src: left hand side pin
- :type src: :py:class:`PyFlow.Core.PinBase`
+ :type src: :py:class:`PyFlow.Core.PinBase.PinBase`
:param dst: right hand side pin
- :type dst: :py:class:`PyFlow.Core.PinBase`
+ :type dst: :py:class:`PyFlow.Core.PinBase.PinBase`
:returns: True if Pins are connected
:rtype: bool
"""
@@ -281,7 +324,7 @@ def getConnectedPins(pin):
def pinAffects(lhs, rhs):
- """This function for establish dependencies bitween pins
+ """This function for establish dependencies between pins
.. warning:: Used internally, users will hardly need this
@@ -290,7 +333,7 @@ def pinAffects(lhs, rhs):
:param rhs: Second Pin to connect
:type rhs: :py:class:`PyFlow.Core.PinBase.PinBase`
"""
- assert(lhs is not rhs), "pin can not affect itself"
+ assert lhs is not rhs, "pin can not affect itself"
lhs.affects.add(rhs)
rhs.affected_by.add(lhs)
@@ -334,39 +377,62 @@ def canConnectPins(src, dst):
if not src.isDict() and dst.isDict():
if dst.optionEnabled(PinOptions.SupportsOnlyArrays):
- if not (src.canChangeStructure(dst._currStructure, []) or dst.canChangeStructure(src._currStructure, [], selfCheck=False)):
+ if not (
+ src.canChangeStructure(dst._currStructure, [])
+ or dst.canChangeStructure(src._currStructure, [], selfCheck=False)
+ ):
return False
- elif not src.supportDictElement([], src.optionEnabled(PinOptions.DictElementSupported)) and dst.optionEnabled(PinOptions.SupportsOnlyArrays) and not dst.canChangeStructure(src._currStructure, [], selfCheck=False):
+ elif (
+ not src.supportDictElement(
+ [], src.optionEnabled(PinOptions.DictElementSupported)
+ )
+ and dst.optionEnabled(PinOptions.SupportsOnlyArrays)
+ and not dst.canChangeStructure(src._currStructure, [], selfCheck=False)
+ ):
return False
else:
- DictElement = src.getDictElementNode([])
+ DictElementNode = src.getDictElementNode([])
dictNode = dst.getDictNode([])
nodeFree = False
if dictNode:
nodeFree = dictNode.KeyType.checkFree([])
- if DictElement:
- if not DictElement.key.checkFree([]) and not nodeFree:
- if dst._data.keyType != DictElement.key.dataType:
+ if DictElementNode:
+ if not DictElementNode.key.checkFree([]) and not nodeFree:
+ if dst._data.keyType != DictElementNode.key.dataType:
return False
if src.isArray() and not dst.isArray():
srcCanChangeStruct = src.canChangeStructure(dst._currStructure, [])
- dstCanChangeStruct = dst.canChangeStructure(src._currStructure, [], selfCheck=False)
- if not dst.optionEnabled(PinOptions.ArraySupported) and not (srcCanChangeStruct or dstCanChangeStruct):
+ dstCanChangeStruct = dst.canChangeStructure(
+ src._currStructure, [], selfCheck=False
+ )
+ if not dst.optionEnabled(PinOptions.ArraySupported) and not (
+ srcCanChangeStruct or dstCanChangeStruct
+ ):
return False
if src.isDict() and not dst.isDict():
srcCanChangeStruct = src.canChangeStructure(dst._currStructure, [])
- dstCanChangeStruct = dst.canChangeStructure(src._currStructure, [], selfCheck=False)
- if not dst.optionEnabled(PinOptions.DictSupported) and not (srcCanChangeStruct or dstCanChangeStruct):
+ dstCanChangeStruct = dst.canChangeStructure(
+ src._currStructure, [], selfCheck=False
+ )
+ if not dst.optionEnabled(PinOptions.DictSupported) and not (
+ srcCanChangeStruct or dstCanChangeStruct
+ ):
return False
if dst.hasConnections():
- if not dst.optionEnabled(PinOptions.AllowMultipleConnections) and dst.reconnectionPolicy == PinReconnectionPolicy.ForbidConnection:
+ if (
+ not dst.optionEnabled(PinOptions.AllowMultipleConnections)
+ and dst.reconnectionPolicy == PinReconnectionPolicy.ForbidConnection
+ ):
return False
if src.hasConnections():
- if not src.optionEnabled(PinOptions.AllowMultipleConnections) and src.reconnectionPolicy == PinReconnectionPolicy.ForbidConnection:
+ if (
+ not src.optionEnabled(PinOptions.AllowMultipleConnections)
+ and src.reconnectionPolicy == PinReconnectionPolicy.ForbidConnection
+ ):
return False
if src.owningNode().graph() is None or dst.owningNode().graph() is None:
@@ -386,25 +452,72 @@ def canConnectPins(src, dst):
return False
if src.IsValuePin() and dst.IsValuePin():
- if src.dataType in dst.allowedDataTypes([], dst._supportedDataTypes) or dst.dataType in src.allowedDataTypes([], src._supportedDataTypes):
- a = src.dataType == "AnyPin" and not src.canChangeTypeOnConnection([], src.optionEnabled(PinOptions.ChangeTypeOnConnection), [])
- b = dst.canChangeTypeOnConnection([], dst.optionEnabled(PinOptions.ChangeTypeOnConnection), []) and not dst.optionEnabled(PinOptions.AllowAny)
- c = not dst.canChangeTypeOnConnection([], dst.optionEnabled(PinOptions.ChangeTypeOnConnection), []) and not dst.optionEnabled(PinOptions.AllowAny)
+ if src.dataType in dst.allowedDataTypes(
+ [], dst._supportedDataTypes
+ ) or dst.dataType in src.allowedDataTypes([], src._supportedDataTypes):
+ a = src.dataType == "AnyPin" and not src.canChangeTypeOnConnection(
+ [], src.optionEnabled(PinOptions.ChangeTypeOnConnection), []
+ )
+ b = dst.canChangeTypeOnConnection(
+ [], dst.optionEnabled(PinOptions.ChangeTypeOnConnection), []
+ ) and not dst.optionEnabled(PinOptions.AllowAny)
+ c = not dst.canChangeTypeOnConnection(
+ [], dst.optionEnabled(PinOptions.ChangeTypeOnConnection), []
+ ) and not dst.optionEnabled(PinOptions.AllowAny)
if all([a, b or c]):
return False
- if not src.isDict() and dst.supportOnlyDictElement([], dst.isDict()) and not (dst.checkFree([], selfCheck=False) and dst.canChangeStructure(src._currStructure, [], selfCheck=False)):
- if not src.supportDictElement([], src.optionEnabled(PinOptions.DictElementSupported)) and dst.supportOnlyDictElement([], dst.isDict()):
+ if (
+ not src.isDict()
+ and dst.supportOnlyDictElement([], dst.isDict())
+ and not (
+ dst.checkFree([], selfCheck=False)
+ and dst.canChangeStructure(src._currStructure, [], selfCheck=False)
+ )
+ ):
+ if not src.supportDictElement(
+ [], src.optionEnabled(PinOptions.DictElementSupported)
+ ) and dst.supportOnlyDictElement([], dst.isDict()):
return False
return True
else:
if src.dataType not in dst.supportedDataTypes():
return False
- if all([src.dataType in list(dst.allowedDataTypes([], dst._defaultSupportedDataTypes, selfCheck=dst.optionEnabled(PinOptions.AllowMultipleConnections), defaults=True)) + ["AnyPin"],
- dst.checkFree([], selfCheck=dst.optionEnabled(PinOptions.AllowMultipleConnections))]):
+ if all(
+ [
+ src.dataType
+ in list(
+ dst.allowedDataTypes(
+ [],
+ dst._defaultSupportedDataTypes,
+ selfCheck=dst.optionEnabled(
+ PinOptions.AllowMultipleConnections
+ ),
+ defaults=True,
+ )
+ )
+ + ["AnyPin"],
+ dst.checkFree(
+ [],
+ selfCheck=dst.optionEnabled(
+ PinOptions.AllowMultipleConnections
+ ),
+ ),
+ ]
+ ):
return True
- if all([dst.dataType in list(src.allowedDataTypes([], src._defaultSupportedDataTypes, defaults=True)) + ["AnyPin"],
- src.checkFree([])]):
+ if all(
+ [
+ dst.dataType
+ in list(
+ src.allowedDataTypes(
+ [], src._defaultSupportedDataTypes, defaults=True
+ )
+ )
+ + ["AnyPin"],
+ src.checkFree([]),
+ ]
+ ):
return True
return False
@@ -413,10 +526,11 @@ def canConnectPins(src, dst):
return True
+
def connectPins(src, dst):
"""**Connects two pins**
- This are the rules how pins connect:
+ These are the rules how pins connect:
* Input value pins can have one output connection if :py:class:`PyFlow.Core.Common.PinOptions.AllowMultipleConnections` flag is disabled
* Output value pins can have any number of connections
@@ -463,7 +577,7 @@ def connectPins(src, dst):
dst.pinConnected(src)
src.pinConnected(dst)
- push(dst)
+ #push(dst)
return True
@@ -498,6 +612,7 @@ def connectPinsByIndexes(lhsNode=None, lhsOutPinIndex=0, rhsNode=None, rhsInPinI
return connectPins(lhsPin, rhsPin)
+
def traverseConstrainedPins(startFrom, callback):
"""Iterate over constrained and connected pins
@@ -564,6 +679,7 @@ def push(start_from):
:param start_from: pin from which recursion begins
:type start_from: :py:class:`~PyFlow.Core.PinBase.PinBase`
"""
+ #print("push", start_from.name, start_from.owningNode().name)
if not len(start_from.affects) == 0:
start_from.setDirty()
for i in start_from.affects:
@@ -585,7 +701,7 @@ def extractDigitsFromEndOfString(string):
:returns: Numbers in the final of the string
:rtype: int
"""
- result = re.search('(\d+)$', string)
+ result = re.search("(\d+)$", string)
if result is not None:
return int(result.group(0))
@@ -600,7 +716,7 @@ def removeDigitsFromEndOfString(string):
:returns: Modified string
:rtype: string
"""
- return re.sub(r'\d+$', '', string)
+ return re.sub(r"\d+$", "", string)
def getUniqNameFromList(existingNames, name):
@@ -608,11 +724,11 @@ def getUniqNameFromList(existingNames, name):
Iterates over **existingNames** and extracts the end digits to find a new unique id
- :param existingNames: List of strings where to search for existing indexes
- :type existingNames: list
+ :param existingNames: List or set of strings where to search for existing indexes
+ :type existingNames: list[str]|set[str]
:param name: Name to obtain a unique version from
:type name: str
- :returns: New name non overlapin with any in existingNames
+ :returns: New name non overlapping with any in existingNames
:rtype: str
"""
if name not in existingNames:
@@ -643,6 +759,7 @@ def clearSignal(signal):
class SingletonDecorator:
"""Decorator to make class unique, so each time called same object returned
"""
+
allInstances = []
@staticmethod
@@ -656,13 +773,16 @@ def __init__(self, cls):
self.allInstances.append(self)
def destroy(self):
+ if ('destroy' in dir(self.instance)):
+ self.instance.destroy()
del self.instance
self.instance = None
def __call__(self, *args, **kwds):
if self.instance is None:
self.instance = self.cls(*args, **kwds)
-
+ if hasattr(self.instance, 'instanceCount'):
+ self.instance.instanceCount += 1
return self.instance
@@ -671,17 +791,18 @@ class DictElement(tuple):
This subclass of python's :class:`tuple` is to represent dict elements to construct typed dicts
"""
- def __new__(self, a=None, b=None):
+
+ def __new__(cls, a=None, b=None):
if a is None and b is None:
new = ()
elif b is None:
if isinstance(a, tuple) and len(a) <= 2:
new = a
else:
- raise Exception("non Valid Input")
+ raise Exception("Invalid Input")
else:
new = (a, b)
- return super(DictElement, self).__new__(self, new)
+ return super(DictElement, cls).__new__(cls, new)
class PFDict(dict):
@@ -707,7 +828,7 @@ def __eq__(self, other):
return (self.__class__ == other.__class__ and self.x == other.x)
"""
- def __init__(self, keyType, valueType='AnyPin', inp={}):
+ def __init__(self, keyType, valueType="AnyPin", inp=None):
"""
:param keyType: Key dataType
:param valueType: value dataType, defaults to None
@@ -715,6 +836,8 @@ def __init__(self, keyType, valueType='AnyPin', inp={}):
:param inp: Construct from another dict, defaults to {}
:type inp: dict, optional
"""
+ if inp is None:
+ inp = {}
super(PFDict, self).__init__(inp)
self.keyType = keyType
self.valueType = valueType
@@ -722,15 +845,17 @@ def __init__(self, keyType, valueType='AnyPin', inp={}):
def __setitem__(self, key, item):
"""Re implements Python Dict __setitem__ to only allow Typed Keys.
- Will throw an Exception if non Valid KeyType
+ Will throw an Exception if non-Valid KeyType
"""
if type(key) == self.getClassFromType(self.keyType):
super(PFDict, self).__setitem__(key, item)
else:
raise Exception(
- "Valid key should be a {0}".format(self.getClassFromType(self.keyType)))
+ "Valid key should be a {0}".format(self.getClassFromType(self.keyType))
+ )
- def getClassFromType(self, pinType):
+ @staticmethod
+ def getClassFromType(pinType):
"""
Gets the internal data structure for a defined pin type
@@ -762,17 +887,29 @@ class PinOptions(Flag):
ArraySupported = auto() #: Pin can hold array data structure
DictSupported = auto() #: Pin can hold dict data structure
- SupportsOnlyArrays = auto() #: Pin will only support other pins with array data structure
+ SupportsOnlyArrays = (
+ auto()
+ ) #: Pin will only support other pins with array data structure
- AllowMultipleConnections = auto() #: This enables pin to allow more that one input connection. See :func:`~PyFlow.Core.Common.connectPins`
+ AllowMultipleConnections = (
+ auto()
+ ) #: This enables pin to allow more than one input connection. See :func:`~PyFlow.Core.Common.connectPins`
- ChangeTypeOnConnection = auto() #: Used by :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` to determine if it can change its data type on new connection.
+ ChangeTypeOnConnection = (
+ auto()
+ ) #: Used by :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` to determine if it can change its data type on new connection.
RenamingEnabled = auto() #: Determines if pin can be renamed
- Dynamic = auto() #: Specifies if pin was created dynamically (during program runtime)
+ Dynamic = (
+ auto()
+ ) #: Specifies if pin was created dynamically (during program runtime)
AlwaysPushDirty = auto() #: Pin will always be seen as dirty (computation needed)
Storable = auto() #: Determines if pin data can be stored when pin serialized
- AllowAny = auto() #: Special flag that allow a pin to be :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin`, which means non typed without been marked as error. By default a :py:class:`PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` need to be initialized with some data type, other defined pin. This flag overrides that. Used in lists and non typed nodes
- DictElementSupported = auto() #: Dicts are constructed with :class:`DictElement` objects. So dict pins will only allow other dicts until this flag enabled. Used in :class:`~PyFlow.Packages.PyFlowBase.Nodes.makeDict` node
+ AllowAny = (
+ auto()
+ ) #: Special flag that allow a pin to be :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin`, which means non typed without been marked as error. By default, a :py:class:`PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` need to be initialized with some data type, other defined pin. This flag overrides that. Used in lists and non typed nodes
+ DictElementSupported = (
+ auto()
+ ) #: Dicts are constructed with :class:`DictElement` objects. So dict pins will only allow other dicts until this flag enabled. Used in :class:`~PyFlow.Packages.PyFlowBase.Nodes.makeDict` node
class StructureType(IntEnum):
@@ -780,9 +917,13 @@ class StructureType(IntEnum):
"""
Single = 0 #: Single data structure
- Array = 1 #: Python list structure, represented as arrays -> typed and lists -> non typed
+ Array = (
+ 1
+ ) #: Python list structure, represented as arrays -> typed and lists -> non typed
Dict = 2 #: :py:class:`PFDict` structure, is basically a rey typed python dict
- Multi = 3 #: This means it can became any of the previous ones on connection/user action
+ Multi = (
+ 3
+ ) #: This means it can become any of the previous ones on connection/user action
def findStructFromValue(value):
@@ -844,8 +985,8 @@ class Direction(IntEnum):
Down = 3 #: Down
-class PinSpecifires:
- """Pin specifires constants
+class PinSpecifiers:
+ """Pin specifiers constants
:var SUPPORTED_DATA_TYPES: To specify supported data types list
:var CONSTRAINT: To specify type constraint key
@@ -878,6 +1019,7 @@ class NodeMeta:
:var KEYWORDS: To specify list of additional keywords, used in node box search field
:var CACHE_ENABLED: To specify if node is cached or not
"""
+
CATEGORY = "Category"
KEYWORDS = "Keywords"
CACHE_ENABLED = "CacheEnabled"
diff --git a/PyFlow/Core/EvaluationEngine.py b/PyFlow/Core/EvaluationEngine.py
index 595f0450b..3fbe8bffb 100644
--- a/PyFlow/Core/EvaluationEngine.py
+++ b/PyFlow/Core/EvaluationEngine.py
@@ -25,7 +25,7 @@ def __init__(self):
super(DefaultEvaluationEngine_Impl, self).__init__()
@staticmethod
- def getPinData(pin):
+ def old_getPinData(pin):
if not pin.hasConnections():
return pin.currentData()
@@ -42,7 +42,25 @@ def getPinData(pin):
return pin.currentData()
@staticmethod
- def getEvaluationOrderIterative(node,forward=False):
+ def getPinData(pin):
+ if not pin.hasConnections():
+ return pin.currentData()
+
+ bOwningNodeCallable = pin.owningNode().bCallable
+
+ if not pin.dirty:
+ return pin.currentData()
+
+ order = DefaultEvaluationEngine_Impl.getEvaluationOrderIterative(pin.owningNode())
+ [node.processNode() for node in order]
+
+ #if pin.dirty:
+ # pin.owningNode().processNode()
+
+ return pin.currentData()
+
+ @staticmethod
+ def getEvaluationOrderIterative(node, forward=False):
visited = set()
stack = [node]
order = []
@@ -75,6 +93,7 @@ def dfsWalk(n):
if lhsNode not in visited:
dfsWalk(lhsNode)
order.append(n)
+
dfsWalk(node)
order.pop()
return order
@@ -99,7 +118,7 @@ def getNextLayerNodes(node):
for outPin in affectedByPins:
outPinNode = outPin.owningNode()
if not outPinNode.bCallable:
- #if node.isDirty():
+ # if node.isDirty():
nodes.add(outPinNode)
elif node.__class__.__name__ == "graphInputs":
# graph inputs node
@@ -138,6 +157,7 @@ def getForwardNextLayerNodes(node):
nodes.add(owningNode)
return nodes
+
@SingletonDecorator
class EvaluationEngine(object):
def __init__(self):
diff --git a/PyFlow/Core/FunctionLibrary.py b/PyFlow/Core/FunctionLibrary.py
index f0846faf6..7dbde5bbd 100644
--- a/PyFlow/Core/FunctionLibrary.py
+++ b/PyFlow/Core/FunctionLibrary.py
@@ -41,7 +41,7 @@
Value of this argument is tuple with 2 or 3 elements or None.
First element is pin data type.
Second - default value.
- Third element is :term:`pin specifires`
+ Third element is :term:`pin specifiers`
.. seealso:: :meth:`~PyFlow.Core.NodeBase.NodeBase.createInputPin`
:meth:`~PyFlow.Core.NodeBase.NodeBase.createOutputPin`
@@ -63,28 +63,28 @@
def makeInt(i=('IntPin', 0)):
return i
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0, {PinSpecifires.ENABLED_OPTIONS: PinOptions.AlwaysPushDirty}))
+ @IMPLEMENT_NODE(returns=('FloatPin', 0.0, {PinSpecifiers.ENABLED_OPTIONS: PinOptions.AlwaysPushDirty}))
def clock():
return time.processor_time()
.. glossary::
- pin specifires
+ pin specifiers
dict that describes different pin options and attributes to be considered on generation
Following key-value pairs allowed:
- >>> (PinSpecifires.SUPPORTED_DATA_TYPES : list)
- >>> (PinSpecifires.CONSTRAINT: None)
- >>> (PinSpecifires.STRUCT_CONSTRAINT: None)
- >>> (PinSpecifires.ENABLED_OPTIONS: None)
- >>> (PinSpecifires.DISABLED_OPTIONS: None)
- >>> (PinSpecifires.INPUT_WIDGET_VARIANT: "DefaultWidget")
- >>> (PinSpecifires.DESCRIPTION: str)
- >>> (PinSpecifires.VALUE_LIST: [str])
- >>> (PinSpecifires.VALUE_RANGE: (int|float, int|float))
- >>> (PinSpecifires.DRAGGER_STEPS: [int|float])
+ >>> (PinSpecifiers.SUPPORTED_DATA_TYPES : list)
+ >>> (PinSpecifiers.CONSTRAINT: None)
+ >>> (PinSpecifiers.STRUCT_CONSTRAINT: None)
+ >>> (PinSpecifiers.ENABLED_OPTIONS: None)
+ >>> (PinSpecifiers.DISABLED_OPTIONS: None)
+ >>> (PinSpecifiers.INPUT_WIDGET_VARIANT: "DefaultWidget")
+ >>> (PinSpecifiers.DESCRIPTION: str)
+ >>> (PinSpecifiers.VALUE_LIST: [str])
+ >>> (PinSpecifiers.VALUE_RANGE: (int|float, int|float))
+ >>> (PinSpecifiers.DRAGGER_STEPS: [int|float])
Value list is specific for string pins. If Specified - enum input widget will be created for this pin.
If value range is specified, slider will be created in property view instead of value box.
@@ -102,31 +102,33 @@ def clock():
"""
-try:
- from inspect import getfullargspec as getargspec
-except:
- from inspect import getargspec
+from inspect import getfullargspec, getmembers, isfunction
from PyFlow.Core.Common import *
empty = {}
-def IMPLEMENT_NODE(func=None, returns=empty, meta={NodeMeta.CATEGORY: 'Default', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Pure):
+def IMPLEMENT_NODE(
+ func=None,
+ returns=empty,
+ meta={NodeMeta.CATEGORY: "Default", NodeMeta.KEYWORDS: []},
+ nodeType=NodeTypes.Pure,
+):
def wrapper(func):
- func.__annotations__ = getattr(func, '__annotations__', {})
- func.__annotations__['nodeType'] = nodeType
+ func.__annotations__ = getattr(func, "__annotations__", {})
+ func.__annotations__["nodeType"] = nodeType
if not meta == empty:
- func.__annotations__['meta'] = meta
+ func.__annotations__["meta"] = meta
if not returns == empty:
- func.__annotations__['return'] = returns
+ func.__annotations__["return"] = returns
defaults = func.__defaults__
if defaults:
- spec = getargspec(func)
- for (i, name) in enumerate(spec.args[-len(defaults):]):
+ spec = getfullargspec(func)
+ for i, name in enumerate(spec.args[-len(defaults):]):
if len(defaults[i]) < 1 or defaults[i][0] is empty:
continue
func.__annotations__[name] = defaults[i]
@@ -138,13 +140,12 @@ def wrapper(func):
class FunctionLibraryBase(object):
- """Base class fo function libraries
- """
+ """Base class fo function libraries"""
def __init__(self, packageName):
super(FunctionLibraryBase, self).__init__()
self.__foos = {}
- for name, function in inspect.getmembers(self, inspect.isfunction):
+ for name, function in getmembers(self, isfunction):
function.__annotations__["packageName"] = packageName
function.__annotations__["lib"] = self.__class__.__name__
self.__foos[name] = function
diff --git a/PyFlow/Core/GraphBase.py b/PyFlow/Core/GraphBase.py
index 1c7230e54..a1e46d7cc 100644
--- a/PyFlow/Core/GraphBase.py
+++ b/PyFlow/Core/GraphBase.py
@@ -13,15 +13,14 @@
## limitations under the License.
-import weakref
+import uuid
+
from blinker import Signal
from collections import Counter
from PyFlow.Core.Common import *
from PyFlow.Core.NodeBase import NodeBase
-from PyFlow import CreateRawPin
from PyFlow import getRawNodeInstance
-from PyFlow import findPinClassByType
from PyFlow import getPinDefaultValueByType
from PyFlow.Core.Variable import Variable
from PyFlow.Core.Interfaces import ISerializable
@@ -42,8 +41,8 @@ class GraphBase(ISerializable):
:var childGraphs: a set of child graphs
:vartype childGraphs: :class:`set`
- :var nodes: nodes storage. Dictionary with :class:`uuid.UUID` as key and :class:`~PyFlow.Core.NodeBase.NodeBase` as value
- :vartype nodes: :class:`dict`
+ :var _nodes: nodes storage. Dictionary with :class:`uuid.UUID` as key and :class:`~PyFlow.Core.NodeBase.NodeBase` as value
+ :vartype _nodes: :class:`dict`
:var uid: Unique identifier
:vartype uid: :class:`uuid.UUID`
@@ -76,7 +75,9 @@ class GraphBase(ISerializable):
:rtype: dict
"""
- def __init__(self, name, manager, parentGraph=None, category='', uid=None, *args, **kwargs):
+
+ def __init__(
+ self, name, manager, parentGraph=None, category="", uid=None, *args, **kwargs):
super(GraphBase, self).__init__(*args, **kwargs)
self.graphManager = manager
self._isRoot = False
@@ -173,13 +174,15 @@ def serialize(self, *args, **kwargs):
:rtype: dict
"""
result = {
- 'name': self.name,
- 'category': self.category,
- 'vars': [v.serialize() for v in self._vars.values()],
- 'nodes': [n.serialize() for n in self._nodes.values()],
- 'depth': self.depth(),
- 'isRoot': self.isRoot(),
- 'parentGraphName': str(self._parentGraph.name) if self._parentGraph is not None else str(None)
+ "name": self.name,
+ "category": self.category,
+ "vars": [v.serialize() for v in self._vars.values()],
+ "nodes": [n.serialize() for n in self._nodes.values()],
+ "depth": self.depth(),
+ "isRoot": self.isRoot(),
+ "parentGraphName": str(self._parentGraph.name)
+ if self._parentGraph is not None
+ else str(None),
}
return result
@@ -190,30 +193,34 @@ def populateFromJson(self, jsonData):
:type jsonData: dict
"""
self.clear()
- self.name = self.graphManager.getUniqGraphName(jsonData['name'])
- self.category = jsonData['category']
- self.setIsRoot(jsonData['isRoot'])
+ self.name = self.graphManager.getUniqGraphName(jsonData["name"])
+ self.category = jsonData["category"]
+ self.setIsRoot(jsonData["isRoot"])
if self.isRoot():
self.name = "root"
# restore vars
- for varJson in jsonData['vars']:
+ for varJson in jsonData["vars"]:
var = Variable.deserialize(self, varJson)
self._vars[var.uid] = var
# restore nodes
- for nodeJson in jsonData['nodes']:
+ for nodeJson in jsonData["nodes"]:
# check if variable getter or setter and pass variable
- nodeArgs = ()
nodeKwargs = {}
- if nodeJson['type'] in ('getVar', 'setVar'):
- nodeKwargs['var'] = self._vars[uuid.UUID(nodeJson['varUid'])]
- nodeJson['owningGraphName'] = self.name
- node = getRawNodeInstance(nodeJson['type'], packageName=nodeJson['package'], libName=nodeJson['lib'], *nodeArgs, **nodeKwargs)
+ if nodeJson["type"] in ("getVar", "setVar"):
+ nodeKwargs["var"] = self._vars[uuid.UUID(nodeJson["varUid"])]
+ nodeJson["owningGraphName"] = self.name
+ node = getRawNodeInstance(
+ nodeJson["type"],
+ packageName=nodeJson["package"],
+ libName=nodeJson["lib"],
+ **nodeKwargs,
+ )
self.addNode(node, nodeJson)
# restore connection
- for nodeJson in jsonData['nodes']:
- for nodeOutputJson in nodeJson['outputs']:
- for linkData in nodeOutputJson['linkedTo']:
+ for nodeJson in jsonData["nodes"]:
+ for nodeOutputJson in nodeJson["outputs"]:
+ for linkData in nodeOutputJson["linkedTo"]:
try:
lhsNode = self._nodes[uuid.UUID(linkData["lhsNodeUid"])]
except Exception as e:
@@ -304,7 +311,13 @@ def pins(self):
result[pin.uid] = pin
return result
- def createVariable(self, dataType=str('AnyPin'), accessLevel=AccessLevel.public, uid=None, name=str("var")):
+ def createVariable(
+ self,
+ dataType=str("AnyPin"),
+ accessLevel=AccessLevel.public,
+ uid=None,
+ name=str("var"),
+ ):
"""Creates variable inside this graph scope
:param dataType: Variable data type
@@ -317,7 +330,14 @@ def createVariable(self, dataType=str('AnyPin'), accessLevel=AccessLevel.public,
:type name: str
"""
name = self.graphManager.getUniqVariableName(name)
- var = Variable(self, getPinDefaultValueByType(dataType), name, dataType, accessLevel=accessLevel, uid=uid)
+ var = Variable(
+ self,
+ getPinDefaultValueByType(dataType),
+ name,
+ dataType,
+ accessLevel=accessLevel,
+ uid=uid,
+ )
self._vars[var.uid] = var
return var
@@ -329,7 +349,7 @@ def killVariable(self, var):
:param var: Variable to remove
:type var: :class:`~PyFlow.Core.Variable.Variable`
"""
- assert(isinstance(var, Variable))
+ assert isinstance(var, Variable)
if var.uid in self._vars:
popped = self._vars.pop(var.uid)
popped.killed.send()
@@ -341,12 +361,18 @@ def getNodes(self):
"""
return self._nodes
- def getNodesList(self, classNameFilters=[]):
+ def getNodesList(self, classNameFilters=None):
"""Returns this graph's nodes list
:rtype: list(:class:`~PyFlow.Core.NodeBase.NodeBase`)
"""
+ if classNameFilters is None:
+ classNameFilters = []
if len(classNameFilters) > 0:
- return [n for n in self._nodes.values() if n.__class__.__name__ in classNameFilters]
+ return [
+ n
+ for n in self._nodes.values()
+ if n.__class__.__name__ in classNameFilters
+ ]
else:
return [n for n in self._nodes.values()]
@@ -430,13 +456,13 @@ def addNode(self, node, jsonTemplate=None):
"""
from PyFlow.Core.PathsRegistry import PathsRegistry
- assert(node is not None), "failed to add node, None is passed"
+ assert node is not None, "failed to add node, None is passed"
if node.uid in self._nodes:
return False
# Check if this node is variable get/set. Variables created in child graphs are not visible to parent ones
# Do not disrupt variable scope
- if node.__class__.__name__ in ['getVar', 'setVar']:
+ if node.__class__.__name__ in ["getVar", "setVar"]:
var = self.graphManager.findVariableByUid(node.variableUid())
variableLocation = var.location()
if len(variableLocation) > len(self.location()):
@@ -447,7 +473,9 @@ def addNode(self, node, jsonTemplate=None):
node.graph = weakref.ref(self)
if jsonTemplate is not None:
- jsonTemplate['name'] = self.graphManager.getUniqNodeName(jsonTemplate['name'])
+ jsonTemplate["name"] = self.graphManager.getUniqNodeName(
+ jsonTemplate["name"]
+ )
else:
node.setName(self.graphManager.getUniqNodeName(node.name))
@@ -489,11 +517,13 @@ def plot(self):
"""Prints graph to console. May be useful for debugging
"""
depth = self.depth()
- prefix = "".join(['-'] * depth) if depth > 1 else ''
- parentGraphString = str(None) if self.parentGraph is None else self.parentGraph.name
+ prefix = "".join(["-"] * depth) if depth > 1 else ""
+ parentGraphString = (
+ str(None) if self.parentGraph is None else self.parentGraph.name
+ )
print(prefix + "GRAPH:" + self.name + ", parent:{0}".format(parentGraphString))
- assert(self not in self.childGraphs)
+ assert self not in self.childGraphs
for child in self.childGraphs:
child.plot()
diff --git a/PyFlow/Core/GraphManager.py b/PyFlow/Core/GraphManager.py
index e0ffbfca2..9a5004d12 100644
--- a/PyFlow/Core/GraphManager.py
+++ b/PyFlow/Core/GraphManager.py
@@ -13,23 +13,23 @@
## limitations under the License.
-from nine import str
from blinker import Signal
from PyFlow.Core.GraphBase import GraphBase
from PyFlow.Core.Common import *
from PyFlow.Core import version
-ROOT_GRAPH_NAME = str('root')
+ROOT_GRAPH_NAME = "root"
class GraphManager(object):
"""Data structure that holds graph tree
This class switches active graph. Can insert or remove graphs to tree,
- can search nodes and variables across all graphs. Also this class responsible
+ can search nodes and variables across all graphs. Also, this class responsible
for giving unique names.
"""
+
def __init__(self):
super(GraphManager, self).__init__()
self.terminationRequested = False #: used by cli only
@@ -48,7 +48,7 @@ def findRootGraph(self):
for graph in self.getAllGraphs():
if graph.isRoot():
roots.append(graph)
- assert(len(roots) == 1), "Fatal! Multiple roots!"
+ assert len(roots) == 1, "Fatal! Multiple roots!"
return roots[0]
def selectRootGraph(self):
@@ -105,12 +105,12 @@ def deserialize(self, data):
:type data: dict
"""
if "fileVersion" in data:
- fileVersion = version.Version.fromString(data["fileVersion"])
+ fileVersion = version.Version.fromString(data["fileVersion"]) # TODO: find purpose
else:
# handle older version
pass
self.clear(keepRoot=False)
- self._activeGraph = GraphBase(str('root'), self)
+ self._activeGraph = GraphBase(str("root"), self)
self._activeGraph.populateFromJson(data)
self._activeGraph.setIsRoot(True)
self.selectGraph(self._activeGraph)
@@ -149,7 +149,7 @@ def findVariableRefs(self, variable):
:rtype: list(:class:`~PyFlow.Core.NodeBase.NodeBase`)
"""
result = []
- for node in self.getAllNodes(classNameFilters=['getVar', 'setVar']):
+ for node in self.getAllNodes(classNameFilters=["getVar", "setVar"]):
if node.variableUid() == variable.uid:
result.append(node)
return result
@@ -265,7 +265,6 @@ def selectGraphByName(self, name):
graphs = self.getGraphsDict()
if name in graphs:
if name != self.activeGraph().name:
- oldGraph = self.activeGraph()
newGraph = graphs[name]
self._activeGraph = newGraph
self.graphChanged.send(self.activeGraph())
@@ -279,7 +278,6 @@ def selectGraph(self, graph):
for newGraph in self.getAllGraphs():
if newGraph.name == graph.name:
if newGraph.name != self.activeGraph().name:
- oldGraph = self.activeGraph()
self._activeGraph = newGraph
self.graphChanged.send(self.activeGraph())
break
@@ -291,19 +289,27 @@ def getAllGraphs(self):
"""
return [g for g in self._graphs.values()]
- def getAllNodes(self, classNameFilters=[]):
+ def getAllNodes(self, classNameFilters=None):
"""Returns all nodes across all graphs
:param classNameFilters: If class name filters specified, only those node classes will be considered
:type classNameFilters: list(str)
:rtype: list(:class:`~PyFlow.Core.NodeBase.NodeBase`)
"""
+ if classNameFilters is None:
+ classNameFilters = []
allNodes = []
for graph in self.getAllGraphs():
if len(classNameFilters) == 0:
allNodes.extend(list(graph.getNodes().values()))
else:
- allNodes.extend([node for node in graph.getNodes().values() if node.__class__.__name__ in classNameFilters])
+ allNodes.extend(
+ [
+ node
+ for node in graph.getNodes().values()
+ if node.__class__.__name__ in classNameFilters
+ ]
+ )
return allNodes
def getAllVariables(self):
@@ -316,7 +322,8 @@ def getAllVariables(self):
result.extend(list(graph.getVars().values()))
return result
- def getUniqGraphPinName(self, graph, name):
+ @staticmethod
+ def getUniqGraphPinName(graph, name):
"""Returns unique pin name for graph
Used by compound node and graphInputs graphOutputs nodes.
@@ -330,7 +337,9 @@ def getUniqGraphPinName(self, graph, name):
:rtype: str
"""
existingNames = []
- for node in graph.getNodesList(classNameFilters=['graphInputs', 'graphOutputs']):
+ for node in graph.getNodesList(
+ classNameFilters=["graphInputs", "graphOutputs"]
+ ):
existingNames.extend([pin.name for pin in node.pins])
return getUniqNameFromList(existingNames, name)
@@ -392,7 +401,11 @@ def plot(self):
"""Prints all data to console. May be useful for debugging
"""
root = self.findRootGraph()
- print("Active graph: {0}".format(str(self.activeGraph().name)), "All graphs:", [g.name for g in self._graphs.values()])
+ print(
+ "Active graph: {0}".format(str(self.activeGraph().name)),
+ "All graphs:",
+ [g.name for g in self._graphs.values()],
+ )
root.plot()
@@ -400,6 +413,7 @@ def plot(self):
class GraphManagerSingleton(object):
"""Singleton class that holds graph manager instance inside. Used by app as main graph manager
"""
+
def __init__(self):
self.man = GraphManager()
diff --git a/PyFlow/Core/Interfaces.py b/PyFlow/Core/Interfaces.py
index 717bd23aa..72d27a6d2 100644
--- a/PyFlow/Core/Interfaces.py
+++ b/PyFlow/Core/Interfaces.py
@@ -20,6 +20,7 @@ class ISerializable(object):
"""
Interface for serialization and deserialization
"""
+
def __init__(self):
super(ISerializable, self).__init__()
@@ -28,14 +29,18 @@ def serialize(self, *args, **Kwargs):
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('serialize method of ISerializable is not implemented')
+ raise NotImplementedError(
+ "serialize method of ISerializable is not implemented"
+ )
def deserialize(self, jsonData):
"""Implements how item will be deserialized
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('deserialize method of ISerializable is not implemented')
+ raise NotImplementedError(
+ "deserialize method of ISerializable is not implemented"
+ )
class IItemBase(ISerializable):
@@ -73,15 +78,15 @@ def setWrapper(self, wrapper):
@property
def uid(self):
- raise NotImplementedError('uid property of IItemBase should be implemented')
+ raise NotImplementedError("uid property of IItemBase should be implemented")
@uid.setter
def uid(self, value):
- raise NotImplementedError('uid setter of IItemBase should be implemented')
+ raise NotImplementedError("uid setter of IItemBase should be implemented")
@uid.deleter
def uid(self):
- raise NotImplementedError('uid property of IItemBase can not be deleted')
+ raise NotImplementedError("uid property of IItemBase can not be deleted")
def getName(self):
"""Returns item's name
@@ -90,7 +95,7 @@ def getName(self):
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('getName method of IItemBase is not implemented')
+ raise NotImplementedError("getName method of IItemBase is not implemented")
def setName(self, name):
"""Sets item name
@@ -99,21 +104,22 @@ def setName(self, name):
:type name: str
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('setName method of IItemBase is not implemented')
+ raise NotImplementedError("setName method of IItemBase is not implemented")
def kill(self):
"""Removes item
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('kill method of IItemBase is not implemented')
+ raise NotImplementedError("kill method of IItemBase is not implemented")
def path(self):
"""Returns path to item
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('path method of IItemBase is not implemented')
+ raise NotImplementedError("path method of IItemBase is not implemented")
+
class IPin(IItemBase):
"""Pin interface
@@ -131,7 +137,7 @@ def IsValuePin():
:rtype: bool
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('IsValuePin method of IPin is not implemented')
+ raise NotImplementedError("IsValuePin method of IPin is not implemented")
@staticmethod
def color():
@@ -142,7 +148,7 @@ def color():
:returns: Rgba tuple
:rtype: tuple(0, 0, 0, 255)
"""
- return (255, 0, 0, 255)
+ return 255, 0, 0, 255
def isExec(self):
"""Is this pin executable or not
@@ -150,15 +156,15 @@ def isExec(self):
:rtype: bool
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('isExec method of IPin is not implemented')
+ raise NotImplementedError("isExec method of IPin is not implemented")
def isArray(self):
- """Is this pin holds an list of values or not
+ """Is this pin holds a list of values or not
:rtype: bool
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('isArray method of IPin is not implemented')
+ raise NotImplementedError("isArray method of IPin is not implemented")
def isAny(self):
"""Is this pin of type Any or not
@@ -166,7 +172,7 @@ def isAny(self):
:rtype: bool
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('isAny method of IPin is not implemented')
+ raise NotImplementedError("isAny method of IPin is not implemented")
@staticmethod
def internalDataStructure():
@@ -175,7 +181,9 @@ def internalDataStructure():
:rtype: object
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('internalDataStructure method of IPin is not implemented')
+ raise NotImplementedError(
+ "internalDataStructure method of IPin is not implemented"
+ )
@staticmethod
def processData(data):
@@ -185,18 +193,20 @@ def processData(data):
:rtype: object
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('processData method of IPin is not implemented')
+ raise NotImplementedError("processData method of IPin is not implemented")
@staticmethod
def supportedDataTypes():
"""List of supported data types
- List of data types that can be casted to this type. For example - int can support float, or vector3 can support vector4 etc.
+ List of data types that can be cast to this type. For example - int can support float, or vector3 can support vector4 etc.
:rtype: list(object)
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('supportedDataTypes method of IPin is not implemented')
+ raise NotImplementedError(
+ "supportedDataTypes method of IPin is not implemented"
+ )
def defaultValue(self):
"""Default value for this pin
@@ -204,14 +214,14 @@ def defaultValue(self):
:rtype: object
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('defaultValue method of IPin is not implemented')
+ raise NotImplementedError("defaultValue method of IPin is not implemented")
def getData(self):
"""How to return data for this pin
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('getData method of IPin is not implemented')
+ raise NotImplementedError("getData method of IPin is not implemented")
def setData(self, value):
"""How to set data to pin
@@ -220,7 +230,7 @@ def setData(self, value):
:type value: object
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('setData method of IPin is not implemented')
+ raise NotImplementedError("setData method of IPin is not implemented")
def call(self, *args, **kwargs):
"""How to execute. What this should do is execute `call` on another pin,
@@ -236,57 +246,77 @@ def dataType(self):
:setter: How to set this pin data type
"""
- raise NotImplementedError('dataType getter method of IPin is not implemented')
+ raise NotImplementedError("dataType getter method of IPin is not implemented")
@dataType.setter
def dataType(self, value):
- raise NotImplementedError('dataType setter method of IPin is not implemented')
+ raise NotImplementedError("dataType setter method of IPin is not implemented")
@staticmethod
def jsonEncoderClass():
- raise NotImplementedError('jsonEncoderClass method of IPin is not implemented')
+ raise NotImplementedError("jsonEncoderClass method of IPin is not implemented")
@staticmethod
def jsonDecoderClass():
- raise NotImplementedError('jsonEncoderClass method of IPin is not implemented')
+ raise NotImplementedError("jsonEncoderClass method of IPin is not implemented")
def setAsArray(self, bIsArray):
- raise NotImplementedError('setAsArray method of IPin is not implemented')
+ raise NotImplementedError("setAsArray method of IPin is not implemented")
class INode(IItemBase):
-
def __init__(self):
super(INode, self).__init__()
def compute(self, *args, **kwargs):
- raise NotImplementedError('compute method of INode is not implemented')
+ raise NotImplementedError("compute method of INode is not implemented")
def isCallable(self):
- raise NotImplementedError('isCallable method of INode is not implemented')
+ raise NotImplementedError("isCallable method of INode is not implemented")
def call(self, outPinName, *args, **kwargs):
"""call out exec pin by name
"""
- raise NotImplementedError('call method of INode is not implemented')
-
- def createInputPin(self, pinName, dataType, defaultValue=None, foo=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group=""):
- raise NotImplementedError('createInputPin method of INode is not implemented')
-
- def createOutputPin(self, pinName, dataType, defaultValue=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group=""):
- raise NotImplementedError('createOutputPin method of INode is not implemented')
+ raise NotImplementedError("call method of INode is not implemented")
+
+ def createInputPin(
+ self,
+ pinName,
+ dataType,
+ defaultValue=None,
+ callback=None,
+ structure=StructureType.Single,
+ constraint=None,
+ structConstraint=None,
+ supportedPinDataTypes=None,
+ group="",
+ ):
+ raise NotImplementedError("createInputPin method of INode is not implemented")
+
+ def createOutputPin(
+ self,
+ pinName,
+ dataType,
+ defaultValue=None,
+ structure=StructureType.Single,
+ constraint=None,
+ structConstraint=None,
+ supportedPinDataTypes=None,
+ group="",
+ ):
+ raise NotImplementedError("createOutputPin method of INode is not implemented")
def getUniqPinName(self, name):
- raise NotImplementedError('getUniqPinName method of INode is not implemented')
+ raise NotImplementedError("getUniqPinName method of INode is not implemented")
def postCreate(self, jsonTemplate=None):
- raise NotImplementedError('postCreate method of INode is not implemented')
+ raise NotImplementedError("postCreate method of INode is not implemented")
def setData(self, pinName, data, pinSelectionGroup):
- raise NotImplementedError('setData method of INode is not implemented')
+ raise NotImplementedError("setData method of INode is not implemented")
def getData(self, pinName, pinSelectionGroup):
- raise NotImplementedError('getData method of INode is not implemented')
+ raise NotImplementedError("getData method of INode is not implemented")
class ICodeCompiler(object):
@@ -294,14 +324,17 @@ def __init__(self, *args, **kwargs):
super(ICodeCompiler, self).__init__(*args, **kwargs)
def compile(self, code):
- raise NotImplementedError('compile method of ICodeCompiler is not implemented')
+ raise NotImplementedError("compile method of ICodeCompiler is not implemented")
class IEvaluationEngine(object):
"""docstring for IEvaluationEngine."""
+
def __init__(self):
super(IEvaluationEngine, self).__init__()
@staticmethod
def getPinData(pin):
- raise NotImplementedError('getPinData method of IEvaluationEngine is not implemented')
+ raise NotImplementedError(
+ "getPinData method of IEvaluationEngine is not implemented"
+ )
diff --git a/PyFlow/Core/NodeBase.py b/PyFlow/Core/NodeBase.py
index 62988b561..548a11506 100644
--- a/PyFlow/Core/NodeBase.py
+++ b/PyFlow/Core/NodeBase.py
@@ -14,36 +14,32 @@
from blinker import Signal
-import weakref
-import functools
import uuid
-import keyword
-import json
from collections import OrderedDict
from copy import copy
-try:
- from inspect import getfullargspec as getargspec
-except:
- from inspect import getargspec
+
+from inspect import getfullargspec
from types import MethodType
-import collections
import traceback
from PyFlow import getPinDefaultValueByType
-from PyFlow import getRawNodeInstance
from PyFlow.Core.Common import *
from PyFlow.Core.Interfaces import INode
from PyFlow import CreateRawPin
from datetime import datetime
+
class NodePinsSuggestionsHelper(object):
"""Describes node's pins types and structs for inputs and outputs
separately. Used by nodebox to suggest good nodes.
"""
+
def __init__(self):
super(NodePinsSuggestionsHelper, self).__init__()
- self.template = {'types': {'inputs': [], 'outputs': []},
- 'structs': {'inputs': [], 'outputs': []}}
+ self.template = {
+ "types": {"inputs": [], "outputs": []},
+ "structs": {"inputs": [], "outputs": []},
+ }
self.inputTypes = set()
self.outputTypes = set()
self.inputStructs = set()
@@ -76,7 +72,7 @@ def __init__(self, name, uid=None):
self.setDirty = Signal()
self.computing = Signal()
self.computed = Signal()
- self.errorOccured = Signal(object)
+ self.errorOccurred = Signal(object)
self.errorCleared = Signal()
self.dirty = True
@@ -104,7 +100,9 @@ def __init__(self, name, uid=None):
def setDeprecated(self, message):
self._deprecated = True
- self._deprecationMessage = "This node will be removed in later releases! {}".format(message)
+ self._deprecationMessage = "This node will be removed in later releases! {}".format(
+ message
+ )
def isDeprecated(self):
return self._deprecated
@@ -151,7 +149,7 @@ def clearError(self):
def setError(self, err):
self._lastError = str(err)
- self.errorOccured.send(self._lastError)
+ self.errorOccurred.send(self._lastError)
def checkForErrors(self):
failedPins = {}
@@ -198,7 +196,9 @@ def __getitem__(self, pinName):
except:
raise Exception("Could not find pin with name:{0}".format(pinName))
else:
- raise Exception("Could not find signature for __getitem__:{0}".format(type(pinName)))
+ raise Exception(
+ "Could not find signature for __getitem__:{0}".format(type(pinName))
+ )
@property
def pins(self):
@@ -288,17 +288,18 @@ def uid(self, value):
@staticmethod
def jsonTemplate():
- template = {'package': None,
- 'lib': None,
- 'type': None,
- 'owningGraphName': None,
- 'name': None,
- 'uuid': None,
- 'inputs': [],
- 'outputs': [],
- 'meta': {'var': {}},
- 'wrapper': {}
- }
+ template = {
+ "package": None,
+ "lib": None,
+ "type": None,
+ "owningGraphName": None,
+ "name": None,
+ "uuid": None,
+ "inputs": [],
+ "outputs": [],
+ "meta": {"var": {}},
+ "wrapper": {},
+ }
return template
def serialize(self):
@@ -307,22 +308,22 @@ def serialize(self):
uidString = str(self.uid)
nodeName = self.name
- template['package'] = self.packageName
- template['lib'] = self.lib
- template['type'] = self.__class__.__name__
- template['name'] = nodeName
- template['owningGraphName'] = self.graph().name
- template['uuid'] = uidString
- template['inputs'] = [i.serialize() for i in self.inputs.values()]
- template['outputs'] = [o.serialize() for o in self.outputs.values()]
- template['meta']['label'] = self.name
- template['x'] = self.x
- template['y'] = self.y
+ template["package"] = self.packageName
+ template["lib"] = self.lib
+ template["type"] = self.__class__.__name__
+ template["name"] = nodeName
+ template["owningGraphName"] = self.graph().name
+ template["uuid"] = uidString
+ template["inputs"] = [i.serialize() for i in self.inputs.values()]
+ template["outputs"] = [o.serialize() for o in self.outputs.values()]
+ template["meta"]["label"] = self.name
+ template["x"] = self.x
+ template["y"] = self.y
# if running with ui get ui wrapper data to save
wrapper = self.getWrapper()
if wrapper:
- template['wrapper'] = wrapper.serializationHook()
+ template["wrapper"] = wrapper.serializationHook()
return template
def isUnderActiveGraph(self):
@@ -381,8 +382,8 @@ def afterCompute(self):
pin.setClean()
def processNode(self, *args, **kwargs):
- start=datetime.now()
- #if not self.isValid():
+ start = datetime.now()
+ # if not self.isValid():
# return
self.computing.send()
if self.bCacheEnabled:
@@ -392,17 +393,17 @@ def processNode(self, *args, **kwargs):
self.clearError()
self.checkForErrors()
self.afterCompute()
- except Exception as e:
- self.setError(traceback.format_exc() )
+ except Exception as e:
+ self.setError(traceback.format_exc())
else:
try:
self.compute()
self.clearError()
self.checkForErrors()
- except Exception as e:
- self.setError(traceback.format_exc() )
- delta = (datetime.now()-start)
- self._computingTime =delta
+ except Exception as e:
+ self.setError(traceback.format_exc())
+ delta = datetime.now() - start
+ self._computingTime = delta
self.computed.send()
# INode interface
@@ -463,14 +464,25 @@ def autoAffectPins(self):
"""
for i in self.inputs.values():
for o in self.outputs.values():
- assert(i is not o)
+ assert i is not o
if not i.IsValuePin() and o.IsValuePin():
continue
if i.IsValuePin() and not o.IsValuePin():
continue
pinAffects(i, o)
- def createInputPin(self, pinName, dataType, defaultValue=None, foo=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group=""):
+ def createInputPin(
+ self,
+ pinName,
+ dataType,
+ defaultValue=None,
+ callback=None,
+ structure=StructureType.Single,
+ constraint=None,
+ structConstraint=None,
+ supportedPinDataTypes=None,
+ group="",
+ ):
"""Creates input pin
:param pinName: Pin name
@@ -479,13 +491,13 @@ def createInputPin(self, pinName, dataType, defaultValue=None, foo=None, structu
:type dataType: str
:param defaultValue: Pin default value
:type defaultValue: object
- :param foo: Pin callback. used for exec pins
- :type foo: function
+ :param callback: Pin callback. used for exec pins
+ :type callback: function
:param structure: Pin structure
:type structure: :class:`~PyFlow.Core.Common.StructureType.Single`
:param constraint: Pin constraint. Should be any hashable type. We use str
:type constraint: object
- :param structConstraint: Pin struct constraint. Also should be hashable type
+ :param structConstraint: Pin struct constraint. Also, should be hashable type
:type structConstraint: object
:param supportedPinDataTypes: List of allowed pin data types to be connected. Used by AnyPin
:type supportedPinDataTypes: list(str)
@@ -504,8 +516,8 @@ def createInputPin(self, pinName, dataType, defaultValue=None, foo=None, structu
elif structure == StructureType.Multi:
p.enableOptions(PinOptions.ArraySupported)
- if foo:
- p.onExecute.connect(foo, weak=False)
+ if callback:
+ p.onExecute.connect(callback, weak=False)
if defaultValue is not None or dataType == "AnyPin":
p.setDefaultValue(defaultValue)
@@ -516,9 +528,13 @@ def createInputPin(self, pinName, dataType, defaultValue=None, foo=None, structu
p.setDefaultValue(getPinDefaultValueByType(dataType))
if dataType == "AnyPin" and supportedPinDataTypes:
+
def supportedDataTypes():
return supportedPinDataTypes
- p._supportedDataTypes = p._defaultSupportedDataTypes = tuple(supportedPinDataTypes)
+
+ p._supportedDataTypes = p._defaultSupportedDataTypes = tuple(
+ supportedPinDataTypes
+ )
p.supportedDataTypes = supportedDataTypes
if constraint is not None:
p.updateConstraint(constraint)
@@ -528,7 +544,17 @@ def supportedDataTypes():
p.markedAsDirty.connect(self.setDirty.send)
return p
- def createOutputPin(self, pinName, dataType, defaultValue=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group=""):
+ def createOutputPin(
+ self,
+ pinName,
+ dataType,
+ defaultValue=None,
+ structure=StructureType.Single,
+ constraint=None,
+ structConstraint=None,
+ supportedPinDataTypes=None,
+ group="",
+ ):
"""Creates output pin
:param pinName: Pin name
@@ -541,7 +567,7 @@ def createOutputPin(self, pinName, dataType, defaultValue=None, structure=Struct
:type structure: :class:`~PyFlow.Core.Common.StructureType.Single`
:param constraint: Pin constraint. Should be any hashable type. We use str
:type constraint: object
- :param structConstraint: Pin struct constraint. Also should be hashable type
+ :param structConstraint: Pin struct constraint. Also, should be hashable type
:type structConstraint: object
:param supportedPinDataTypes: List of allowed pin data types to be connected. Used by AnyPin
:type supportedPinDataTypes: list(str)
@@ -569,8 +595,10 @@ def createOutputPin(self, pinName, dataType, defaultValue=None, structure=Struct
p.setDefaultValue(getPinDefaultValueByType(dataType))
if dataType == "AnyPin" and supportedPinDataTypes:
+
def supportedDataTypes():
return supportedPinDataTypes
+
p.supportedDataTypes = supportedDataTypes
if constraint is not None:
p.updateConstraint(constraint)
@@ -589,7 +617,7 @@ def setData(self, pinName, data, pinSelectionGroup=PinSelectionGroup.BothSides):
:type pinSelectionGroup: :class:`~PyFlow.Core.Common.PinSelectionGroup`
"""
p = self.getPinSG(str(pinName), pinSelectionGroup)
- assert(p is not None), "Failed to find pin by name: {}".format(pinName)
+ assert p is not None, "Failed to find pin by name: {}".format(pinName)
p.setData(data)
def getData(self, pinName, pinSelectionGroup=PinSelectionGroup.BothSides):
@@ -602,7 +630,7 @@ def getData(self, pinName, pinSelectionGroup=PinSelectionGroup.BothSides):
:rtype: object
"""
p = self.getPinSG(str(pinName), pinSelectionGroup)
- assert(p is not None), "Failed to find pin by name: {}".format(pinName)
+ assert p is not None, "Failed to find pin by name: {}".format(pinName)
return p.getData()
def getUniqPinName(self, name):
@@ -612,12 +640,18 @@ def getUniqPinName(self, name):
:type name: str
:rtype: str
"""
- pinNames = [i.name for i in list(list(self.inputs.values())) + list(list(self.outputs.values()))]
+ pinNames = [
+ i.name
+ for i in list(list(self.inputs.values()))
+ + list(list(self.outputs.values()))
+ ]
return getUniqNameFromList(pinNames, name)
def __repr__(self):
graphName = self.graph().name if self.graph is not None else str(None)
- return "".format(self.__class__.__name__, self.getName(), graphName)
+ return "".format(
+ self.__class__.__name__, self.getName(), graphName
+ )
def call(self, name, *args, **kwargs):
"""Call exec pin by name
@@ -680,30 +714,34 @@ def postCreate(self, jsonTemplate=None):
:type jsonTemplate: dict or None
"""
if jsonTemplate is not None:
- self.uid = uuid.UUID(jsonTemplate['uuid'])
- self.setName(jsonTemplate['name'])
- self.x = jsonTemplate['x']
- self.y = jsonTemplate['y']
+ self.uid = uuid.UUID(jsonTemplate["uuid"])
+ self.setName(jsonTemplate["name"])
+ self.x = jsonTemplate["x"]
+ self.y = jsonTemplate["y"]
# set pins data
- sortedInputs = sorted(jsonTemplate['inputs'], key=lambda pinDict: pinDict["pinIndex"])
+ sortedInputs = sorted(
+ jsonTemplate["inputs"], key=lambda pinDict: pinDict["pinIndex"]
+ )
for inpJson in sortedInputs:
dynamicEnabled = PinOptions.Dynamic.value in inpJson["options"]
- if dynamicEnabled or inpJson['name'] not in self.namePinInputsMap:
+ if dynamicEnabled or inpJson["name"] not in self.namePinInputsMap:
# create custom dynamically created pins in derived classes
continue
- pin = self.getPinSG(str(inpJson['name']), PinSelectionGroup.Inputs)
+ pin = self.getPinSG(str(inpJson["name"]), PinSelectionGroup.Inputs)
pin.deserialize(inpJson)
- sortedOutputs = sorted(jsonTemplate['outputs'], key=lambda pinDict: pinDict["pinIndex"])
+ sortedOutputs = sorted(
+ jsonTemplate["outputs"], key=lambda pinDict: pinDict["pinIndex"]
+ )
for outJson in sortedOutputs:
dynamicEnabled = PinOptions.Dynamic.value in outJson["options"]
- if dynamicEnabled or outJson['name'] not in self.namePinOutputsMap:
+ if dynamicEnabled or outJson["name"] not in self.namePinOutputsMap:
# create custom dynamically created pins in derived classes
continue
- pin = self.getPinSG(str(outJson['name']), PinSelectionGroup.Outputs)
+ pin = self.getPinSG(str(outJson["name"]), PinSelectionGroup.Outputs)
pin.deserialize(outJson)
# store data for wrapper
@@ -714,7 +752,7 @@ def postCreate(self, jsonTemplate=None):
self.bCallable = True
# make no sense cache nodes without inputs
- #if len(self.inputs) == 0:
+ # if len(self.inputs) == 0:
# self.bCacheEnabled = False
self.autoAffectPins()
@@ -732,36 +770,46 @@ def initializeFromFunction(foo):
"""
retAnyOpts = None
retConstraint = None
- meta = foo.__annotations__['meta']
+ meta = foo.__annotations__["meta"]
returnType = returnDefaultValue = None
returnPinOptionsToEnable = None
returnPinOptionsToDisable = None
returnWidgetVariant = "DefaultWidget"
retStructConstraint = None
returnAnnotationDict = None
- if foo.__annotations__['return'] is not None:
- returnType = foo.__annotations__['return'][0]
- returnDefaultValue = foo.__annotations__['return'][1]
- if len(foo.__annotations__['return']) == 3:
- returnAnnotationDict = foo.__annotations__['return'][2]
-
- if PinSpecifires.SUPPORTED_DATA_TYPES in returnAnnotationDict:
- retAnyOpts = returnAnnotationDict[PinSpecifires.SUPPORTED_DATA_TYPES]
- if PinSpecifires.CONSTRAINT in returnAnnotationDict:
- retConstraint = returnAnnotationDict[PinSpecifires.CONSTRAINT]
- if PinSpecifires.STRUCT_CONSTRAINT in returnAnnotationDict:
- retStructConstraint = returnAnnotationDict[PinSpecifires.STRUCT_CONSTRAINT]
- if PinSpecifires.ENABLED_OPTIONS in returnAnnotationDict:
- returnPinOptionsToEnable = returnAnnotationDict[PinSpecifires.ENABLED_OPTIONS]
- if PinSpecifires.DISABLED_OPTIONS in returnAnnotationDict:
- returnPinOptionsToDisable = returnAnnotationDict[PinSpecifires.DISABLED_OPTIONS]
- if PinSpecifires.INPUT_WIDGET_VARIANT in returnAnnotationDict:
- returnWidgetVariant = returnAnnotationDict[PinSpecifires.INPUT_WIDGET_VARIANT]
-
- nodeType = foo.__annotations__['nodeType']
- _packageName = foo.__annotations__['packageName']
- libName = foo.__annotations__['lib']
- fooArgNames = getargspec(foo).args
+ if foo.__annotations__["return"] is not None:
+ returnType = foo.__annotations__["return"][0]
+ returnDefaultValue = foo.__annotations__["return"][1]
+ if len(foo.__annotations__["return"]) == 3:
+ returnAnnotationDict = foo.__annotations__["return"][2]
+
+ if PinSpecifiers.SUPPORTED_DATA_TYPES in returnAnnotationDict:
+ retAnyOpts = returnAnnotationDict[
+ PinSpecifiers.SUPPORTED_DATA_TYPES
+ ]
+ if PinSpecifiers.CONSTRAINT in returnAnnotationDict:
+ retConstraint = returnAnnotationDict[PinSpecifiers.CONSTRAINT]
+ if PinSpecifiers.STRUCT_CONSTRAINT in returnAnnotationDict:
+ retStructConstraint = returnAnnotationDict[
+ PinSpecifiers.STRUCT_CONSTRAINT
+ ]
+ if PinSpecifiers.ENABLED_OPTIONS in returnAnnotationDict:
+ returnPinOptionsToEnable = returnAnnotationDict[
+ PinSpecifiers.ENABLED_OPTIONS
+ ]
+ if PinSpecifiers.DISABLED_OPTIONS in returnAnnotationDict:
+ returnPinOptionsToDisable = returnAnnotationDict[
+ PinSpecifiers.DISABLED_OPTIONS
+ ]
+ if PinSpecifiers.INPUT_WIDGET_VARIANT in returnAnnotationDict:
+ returnWidgetVariant = returnAnnotationDict[
+ PinSpecifiers.INPUT_WIDGET_VARIANT
+ ]
+
+ nodeType = foo.__annotations__["nodeType"]
+ _packageName = foo.__annotations__["packageName"]
+ libName = foo.__annotations__["lib"]
+ fooArgNames = getfullargspec(foo).args
@staticmethod
def description():
@@ -778,11 +826,16 @@ def keywords():
def constructor(self, name, **kwargs):
NodeBase.__init__(self, name, **kwargs)
- nodeClass = type(foo.__name__, (NodeBase,), {'__init__': constructor,
- 'category': category,
- 'keywords': keywords,
- 'description': description
- })
+ nodeClass = type(
+ foo.__name__,
+ (NodeBase,),
+ {
+ "__init__": constructor,
+ "category": category,
+ "keywords": keywords,
+ "description": description,
+ },
+ )
nodeClass._packageName = _packageName
@@ -806,32 +859,46 @@ def compute(self, *args, **kwargs):
foo.owningNode = self
result = foo(**kwds)
if returnType is not None:
- self.setData(str('out'), result)
+ self.setData(str("out"), result)
if nodeType == NodeTypes.Callable:
outExec.call(*args, **kwargs)
raw_inst.compute = MethodType(compute, raw_inst)
raw_inst._nodeMetaData = meta
- if 'CacheEnabled' in meta:
- raw_inst.bCacheEnabled = meta['CacheEnabled']
+ if "CacheEnabled" in meta:
+ raw_inst.bCacheEnabled = meta["CacheEnabled"]
# create execs if callable
if nodeType == NodeTypes.Callable:
- inputExec = raw_inst.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, raw_inst.compute)
- outExec = raw_inst.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin')
+ raw_inst.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, raw_inst.compute
+ )
+ outExec = raw_inst.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin")
raw_inst.bCallable = True
raw_inst.bCacheEnabled = False
if returnType is not None:
- p = raw_inst.createOutputPin('out', returnType, returnDefaultValue, supportedPinDataTypes=retAnyOpts, constraint=retConstraint, structConstraint=retStructConstraint)
+ p = raw_inst.createOutputPin(
+ "out",
+ returnType,
+ returnDefaultValue,
+ supportedPinDataTypes=retAnyOpts,
+ constraint=retConstraint,
+ structConstraint=retStructConstraint,
+ )
p.setData(returnDefaultValue)
p.setDefaultValue(returnDefaultValue)
p.initAsArray(isinstance(returnDefaultValue, list))
p.initAsDict(isinstance(returnDefaultValue, dict))
p.setInputWidgetVariant(returnWidgetVariant)
- p.annotationDescriptionDict = copy(returnAnnotationDict) if returnAnnotationDict is not None else None
- if p.annotationDescriptionDict is not None and "Description" in p.annotationDescriptionDict:
+ p.annotationDescriptionDict = (
+ copy(returnAnnotationDict) if returnAnnotationDict is not None else None
+ )
+ if (
+ p.annotationDescriptionDict is not None
+ and "Description" in p.annotationDescriptionDict
+ ):
p.description = p.annotationDescriptionDict["Description"]
if returnPinOptionsToEnable is not None:
p.enableOptions(returnPinOptionsToEnable)
@@ -861,22 +928,33 @@ def compute(self, *args, **kwargs):
pinDict = pinDescriptionTuple[1][2]
if pinDict is not None:
- if PinSpecifires.SUPPORTED_DATA_TYPES in pinDict:
- anyOpts = pinDict[PinSpecifires.SUPPORTED_DATA_TYPES]
- if PinSpecifires.CONSTRAINT in pinDict:
- constraint = pinDict[PinSpecifires.CONSTRAINT]
- if PinSpecifires.STRUCT_CONSTRAINT in pinDict:
- structConstraint = pinDict[PinSpecifires.STRUCT_CONSTRAINT]
- if PinSpecifires.ENABLED_OPTIONS in pinDict:
- pinOptionsToEnable = pinDict[PinSpecifires.ENABLED_OPTIONS]
- if PinSpecifires.DISABLED_OPTIONS in pinDict:
- pinOptionsToDisable = pinDict[PinSpecifires.DISABLED_OPTIONS]
- if PinSpecifires.INPUT_WIDGET_VARIANT in pinDict:
- inputWidgetVariant = pinDict[PinSpecifires.INPUT_WIDGET_VARIANT]
-
- outRef = raw_inst.createOutputPin(argName, pinDataType, supportedPinDataTypes=anyOpts, constraint=constraint, structConstraint=structConstraint)
- outRef.annotationDescriptionDict = copy(pinDict) if pinDict is not None else None
- if outRef.annotationDescriptionDict is not None and "Description" in outRef.annotationDescriptionDict:
+ if PinSpecifiers.SUPPORTED_DATA_TYPES in pinDict:
+ anyOpts = pinDict[PinSpecifiers.SUPPORTED_DATA_TYPES]
+ if PinSpecifiers.CONSTRAINT in pinDict:
+ constraint = pinDict[PinSpecifiers.CONSTRAINT]
+ if PinSpecifiers.STRUCT_CONSTRAINT in pinDict:
+ structConstraint = pinDict[PinSpecifiers.STRUCT_CONSTRAINT]
+ if PinSpecifiers.ENABLED_OPTIONS in pinDict:
+ pinOptionsToEnable = pinDict[PinSpecifiers.ENABLED_OPTIONS]
+ if PinSpecifiers.DISABLED_OPTIONS in pinDict:
+ pinOptionsToDisable = pinDict[PinSpecifiers.DISABLED_OPTIONS]
+ if PinSpecifiers.INPUT_WIDGET_VARIANT in pinDict:
+ inputWidgetVariant = pinDict[PinSpecifiers.INPUT_WIDGET_VARIANT]
+
+ outRef = raw_inst.createOutputPin(
+ argName,
+ pinDataType,
+ supportedPinDataTypes=anyOpts,
+ constraint=constraint,
+ structConstraint=structConstraint,
+ )
+ outRef.annotationDescriptionDict = (
+ copy(pinDict) if pinDict is not None else None
+ )
+ if (
+ outRef.annotationDescriptionDict is not None
+ and "Description" in outRef.annotationDescriptionDict
+ ):
outRef.description = outRef.annotationDescriptionDict["Description"]
outRef.initAsArray(isinstance(pinDefaultValue, list))
outRef.initAsDict(isinstance(pinDefaultValue, dict))
@@ -887,7 +965,9 @@ def compute(self, *args, **kwargs):
outRef.enableOptions(pinOptionsToEnable)
if pinOptionsToDisable is not None:
outRef.disableOptions(pinOptionsToDisable)
- if not outRef.isArray() and outRef.optionEnabled(PinOptions.ArraySupported):
+ if not outRef.isArray() and outRef.optionEnabled(
+ PinOptions.ArraySupported
+ ):
outRef.structureType = StructureType.Multi
elif outRef.isArray():
outRef.structureType = StructureType.Array
@@ -900,22 +980,33 @@ def compute(self, *args, **kwargs):
pinDict = pinDescriptionTuple[2]
if pinDict is not None:
- if PinSpecifires.SUPPORTED_DATA_TYPES in pinDict:
- anyOpts = pinDict[PinSpecifires.SUPPORTED_DATA_TYPES]
- if PinSpecifires.CONSTRAINT in pinDict:
- constraint = pinDict[PinSpecifires.CONSTRAINT]
- if PinSpecifires.STRUCT_CONSTRAINT in pinDict:
- structConstraint = pinDict[PinSpecifires.STRUCT_CONSTRAINT]
- if PinSpecifires.ENABLED_OPTIONS in pinDict:
- pinOptionsToEnable = pinDict[PinSpecifires.ENABLED_OPTIONS]
- if PinSpecifires.DISABLED_OPTIONS in pinDict:
- pinOptionsToDisable = pinDict[PinSpecifires.DISABLED_OPTIONS]
- if PinSpecifires.INPUT_WIDGET_VARIANT in pinDict:
- inputWidgetVariant = pinDict[PinSpecifires.INPUT_WIDGET_VARIANT]
-
- inp = raw_inst.createInputPin(argName, pinDataType, supportedPinDataTypes=anyOpts, constraint=constraint, structConstraint=structConstraint)
- inp.annotationDescriptionDict = copy(pinDict) if pinDict is not None else None
- if inp.annotationDescriptionDict is not None and "Description" in inp.annotationDescriptionDict:
+ if PinSpecifiers.SUPPORTED_DATA_TYPES in pinDict:
+ anyOpts = pinDict[PinSpecifiers.SUPPORTED_DATA_TYPES]
+ if PinSpecifiers.CONSTRAINT in pinDict:
+ constraint = pinDict[PinSpecifiers.CONSTRAINT]
+ if PinSpecifiers.STRUCT_CONSTRAINT in pinDict:
+ structConstraint = pinDict[PinSpecifiers.STRUCT_CONSTRAINT]
+ if PinSpecifiers.ENABLED_OPTIONS in pinDict:
+ pinOptionsToEnable = pinDict[PinSpecifiers.ENABLED_OPTIONS]
+ if PinSpecifiers.DISABLED_OPTIONS in pinDict:
+ pinOptionsToDisable = pinDict[PinSpecifiers.DISABLED_OPTIONS]
+ if PinSpecifiers.INPUT_WIDGET_VARIANT in pinDict:
+ inputWidgetVariant = pinDict[PinSpecifiers.INPUT_WIDGET_VARIANT]
+
+ inp = raw_inst.createInputPin(
+ argName,
+ pinDataType,
+ supportedPinDataTypes=anyOpts,
+ constraint=constraint,
+ structConstraint=structConstraint,
+ )
+ inp.annotationDescriptionDict = (
+ copy(pinDict) if pinDict is not None else None
+ )
+ if (
+ inp.annotationDescriptionDict is not None
+ and "Description" in inp.annotationDescriptionDict
+ ):
inp.description = inp.annotationDescriptionDict["Description"]
inp.initAsArray(isinstance(pinDefaultValue, list))
inp.initAsDict(isinstance(pinDefaultValue, dict))
diff --git a/PyFlow/Core/PathsRegistry.py b/PyFlow/Core/PathsRegistry.py
index cd0d75432..108f0a530 100644
--- a/PyFlow/Core/PathsRegistry.py
+++ b/PyFlow/Core/PathsRegistry.py
@@ -5,6 +5,7 @@
@SingletonDecorator
class PathsRegistry(object):
"""Holds paths to nodes and pins. Can rebuild paths and return entities by paths."""
+
def __init__(self):
self._data = {}
diff --git a/PyFlow/Core/PinBase.py b/PyFlow/Core/PinBase.py
index 0cbb4c9df..e18e50c29 100644
--- a/PyFlow/Core/PinBase.py
+++ b/PyFlow/Core/PinBase.py
@@ -13,18 +13,15 @@
## limitations under the License.
-from blinker import Signal
+import json
import uuid
from copy import copy
-import weakref
-import json
-from nine import str
-from PyFlow.Core.Interfaces import IPin
+from blinker import Signal
+
from PyFlow.Core.Common import *
-from PyFlow.Core.PathsRegistry import PathsRegistry
from PyFlow.Core.EvaluationEngine import EvaluationEngine
-from PyFlow import getPinDefaultValueByType
+from PyFlow.Core.Interfaces import IPin
class PinBase(IPin):
@@ -40,7 +37,7 @@ class PinBase(IPin):
:type _packageName: str
Signals:
- * **serializationHook** : Fired when Serialize Pin called, so Ui wrapers can append data to the serialization object
+ * **serializationHook** : Fired when Serialize Pin called, so Ui wrappers can append data to the serialization object
* **onPinConnected** : Fired when a new connection is made to this Pin, sends other Pin
* **onPinDisconnected** : Fired when some disconnection is made to this Pin, sends other Pin
* **nameChanged** : Fired when pin.setName() called, sends New Name
@@ -67,6 +64,7 @@ class PinBase(IPin):
:ivar pinIndex: Position of this pin on node
:ivar description: Text description of this pin
"""
+
_packageName = ""
def __init__(self, name, owningNode, direction):
@@ -81,13 +79,13 @@ def __init__(self, name, owningNode, direction):
self.containerTypeChanged = Signal()
self.dataBeenSet = Signal(object)
self.dictChanged = Signal(str)
- self.markedAsDirty =Signal()
+ self.markedAsDirty = Signal()
- self.errorOccured = Signal(object)
+ self.errorOccurred = Signal(object)
self.errorCleared = Signal()
self._lastError = None
- ## Access to the node
+ # Access to the node
self.owningNode = weakref.ref(owningNode)
self._uid = uuid.uuid4()
@@ -176,7 +174,7 @@ def group(self):
@group.setter
def group(self, value):
- self._group = str(value)
+ self._group = value
def enableOptions(self, *options):
"""Enables flags on pin instance
@@ -249,24 +247,16 @@ def linkedTo(self):
result = list()
if self.direction == PinDirection.Output:
for i in getConnectedPins(self):
- connection = {"lhsNodeName": "", "outPinId": 0, "rhsNodeName": "", "inPinId": 0}
- connection["lhsNodeName"] = self.owningNode().getName()
- connection["lhsNodeUid"] = str(self.owningNode().uid)
- connection["outPinId"] = self.pinIndex
- connection["rhsNodeName"] = i.owningNode().getName()
- connection["rhsNodeUid"] = str(i.owningNode().uid)
- connection["inPinId"] = i.pinIndex
+ connection = {"lhsNodeName": self.owningNode().getName(), "outPinId": self.pinIndex,
+ "rhsNodeName": i.owningNode().getName(), "inPinId": i.pinIndex,
+ "lhsNodeUid": str(self.owningNode().uid), "rhsNodeUid": str(i.owningNode().uid)}
result.append(connection)
if self.direction == PinDirection.Input:
for i in getConnectedPins(self):
- connection = {"lhsNodeName": "", "outPinId": 0, "rhsNodeName": "", "inPinId": 0}
- connection["lhsNodeName"] = i.owningNode().getName()
- connection["lhsNodeUid"] = str(i.owningNode().uid)
- connection["outPinId"] = i.pinIndex
- connection["rhsNodeName"] = self.owningNode().getName()
- connection["rhsNodeUid"] = str(self.owningNode().uid)
- connection["inPinId"] = self.pinIndex
+ connection = {"lhsNodeName": i.owningNode().getName(), "outPinId": i.pinIndex,
+ "rhsNodeName": self.owningNode().getName(), "inPinId": self.pinIndex,
+ "lhsNodeUid": str(i.owningNode().uid), "rhsNodeUid": str(self.owningNode().uid)}
result.append(connection)
return result
@@ -294,8 +284,8 @@ def initAsArray(self, bIsArray):
def initAsDict(self, bIsDict):
"""Sets this pins to be a dict always
- :param bIsArray: Define as dict
- :type bIsArray: bool
+ :param bIsDict: Define as dict
+ :type bIsDict: bool
"""
self._alwaysDict = bool(bIsDict)
if bool(bIsDict):
@@ -328,8 +318,8 @@ def setAsArray(self, bIsArray):
def setAsDict(self, bIsDict):
"""Sets this pins to be a dict
- :param bIsArray: Define as Array
- :type bIsArray: bool
+ :param bIsDict: Define as Array
+ :type bIsDict: bool
"""
bIsDict = bool(bIsDict)
if self._isDict == bIsDict:
@@ -383,7 +373,7 @@ def deserialize(self, jsonData):
:type jsonData: dict
"""
self.setName(jsonData["name"])
- self.uid = uuid.UUID(jsonData['uuid'])
+ self.uid = uuid.UUID(jsonData["uuid"])
for opt in PinOptions:
if opt.value in jsonData["options"]:
@@ -392,12 +382,12 @@ def deserialize(self, jsonData):
self.disableOptions(opt)
self.changeStructure(jsonData["structure"])
- self._alwaysList = jsonData['alwaysList']
- self._alwaysSingle = jsonData['alwaysSingle']
- self._alwaysDict = jsonData['alwaysDict']
+ self._alwaysList = jsonData["alwaysList"]
+ self._alwaysSingle = jsonData["alwaysSingle"]
+ self._alwaysDict = jsonData["alwaysDict"]
try:
- self.setData(json.loads(jsonData['value'], cls=self.jsonDecoderClass()))
+ self.setData(json.loads(jsonData["value"], cls=self.jsonDecoderClass()))
except Exception as e:
self.setData(self.defaultValue())
@@ -414,25 +404,26 @@ def serialize(self):
serializedData = None
if not self.dataType == "AnyPin":
if storable:
- serializedData = json.dumps(self.currentData(), cls=self.jsonEncoderClass())
- #else:
+ serializedData = json.dumps(self.currentData(),
+ cls=self.jsonEncoderClass())
+ # else:
# serializedData = json.dumps(self.defaultValue(), cls=self.jsonEncoderClass())
data = {
- 'name': self.name,
- 'package': self.packageName,
- 'fullName': self.getFullName(),
- 'dataType': self.__class__.__name__,
- 'direction': int(self.direction),
- 'value': serializedData,
- 'uuid': str(self.uid),
- 'linkedTo': list(self.linkedTo),
- 'pinIndex': self.pinIndex,
- 'options': [i.value for i in PinOptions if self.optionEnabled(i)],
- 'structure': int(self._currStructure),
- 'alwaysList': self._alwaysList,
- 'alwaysSingle': self._alwaysSingle,
- 'alwaysDict': self._alwaysDict
+ "name": self.name,
+ "package": self.packageName,
+ "fullName": self.getFullName(),
+ "dataType": self.__class__.__name__,
+ "direction": int(self.direction),
+ "value": serializedData,
+ "uuid": str(self.uid),
+ "linkedTo": list(self.linkedTo),
+ "pinIndex": self.pinIndex,
+ "options": [i.value for i in PinOptions if self.optionEnabled(i)],
+ "structure": int(self._currStructure),
+ "alwaysList": self._alwaysList,
+ "alwaysSingle": self._alwaysSingle,
+ "alwaysDict": self._alwaysDict,
}
# Wrapper class can subscribe to this signal and return
@@ -442,7 +433,7 @@ def serialize(self):
if wrapperData is not None:
if len(wrapperData) > 0:
# We take return value from one wrapper
- data['wrapper'] = wrapperData[0][1]
+ data["wrapper"] = wrapperData[0][1]
return data
@property
@@ -481,12 +472,12 @@ def getFullName(self):
:rtype: str
"""
- return self.owningNode().name + '_' + self.name
+ return self.owningNode().name + "_" + self.name
- def allowedDataTypes(self, checked=[], dataTypes=[], selfCheck=True, defaults=False):
+ def allowedDataTypes(self, checked=None, dataTypes=None, selfCheck=True, defaults=False):
return list(self.supportedDataTypes())
- def checkFree(self, checked=[], selfCheck=True):
+ def checkFree(self, checked=None, selfCheck=True):
return False
def defaultValue(self):
@@ -521,8 +512,8 @@ def setError(self, err):
:param err: Error message
:type err: str
"""
- self._lastError = str(err)
- self.errorOccured.send(self._lastError)
+ self._lastError = err
+ self.errorOccurred.send(self._lastError)
def validateArray(self, array, func):
valid = True
@@ -555,9 +546,9 @@ def setData(self, data):
self._data = self.super.processData(data)
elif self.isArray():
if isinstance(data, list):
- #if self.validateArray(data, self.super.processData):
+ # if self.validateArray(data, self.super.processData):
self._data = data
- #else:
+ # else:
# raise Exception("Some Array Input is not valid Data")
else:
self._data = [self.super.processData(data)]
@@ -577,7 +568,7 @@ def setData(self, data):
elif self.direction == PinDirection.Input and self.owningNode().__class__.__name__ == "compound":
for i in self.affects:
i.setData(self.currentData())
-
+
if self.direction == PinDirection.Input or self.optionEnabled(PinOptions.AlwaysPushDirty):
push(self)
self.clearError()
@@ -707,20 +698,22 @@ def changeStructure(self, newStruct, init=False):
if free:
self.updateConstrainedPins(set(), newStruct, init, connecting=True)
- def canChangeStructure(self, newStruct, checked=[], selfCheck=True, init=False):
+ def canChangeStructure(self, newStruct, checked=None, selfCheck=True, init=False):
"""Recursive function to determine if pin can change its structure
:param newStruct: New structure we want to apply
- :type newStruct: string
+ :type newStruct: :class:`~PyFlow.Core.Common.StructureType`
:param checked: Already visited pins, defaults to []
:type checked: list, optional
:param selfCheck: Define if check pin itself for connected pins, defaults to True
:type selfCheck: bool, optional
- :param init: Initialization flag, if set multi pins can became other structure and don't be able to change after new call with init=True, defaults to False
+ :param init: Initialization flag, if set multi pins can become other structure and don't be able to change after new call with init=True, defaults to False
:type init: bool, optional
:returns: True if pin can change structure to newStruct
:rtype: bool
"""
+ if checked is None:
+ checked = []
if not init and (self._alwaysList or self._alwaysSingle or self._alwaysDict):
return False
if self.structConstraint is None or self.structureType == StructureType.Multi:
@@ -736,10 +729,10 @@ def canChangeStructure(self, newStruct, checked=[], selfCheck=True, init=False):
if c not in checked:
con.append(c)
else:
- free = True
checked.append(self)
free = True
if selfCheck:
+
def testfree():
free = False
for pin in getConnectedPins(self):
@@ -749,15 +742,36 @@ def testfree():
free = False
break
return free
- if any([self._currStructure == StructureType.Single and newStruct == StructureType.Array and not self.optionEnabled(PinOptions.ArraySupported) and self.hasConnections(),
- self._currStructure == StructureType.Single and newStruct == StructureType.Dict and not self.optionEnabled(PinOptions.DictSupported) and self.hasConnections(),
- self._currStructure == StructureType.Array and newStruct == StructureType.Single and self.optionEnabled(PinOptions.SupportsOnlyArrays) and self.hasConnections(),
- self._currStructure == StructureType.Dict and newStruct == StructureType.Single and self.optionEnabled(PinOptions.SupportsOnlyArrays) and self.hasConnections(),
- self._currStructure == StructureType.Array and newStruct == StructureType.Dict and self.hasConnections(),
- self._currStructure == StructureType.Dict and newStruct == StructureType.Array and self.hasConnections()]):
+
+ if any(
+ [
+ self._currStructure == StructureType.Single
+ and newStruct == StructureType.Array
+ and not self.optionEnabled(PinOptions.ArraySupported)
+ and self.hasConnections(),
+ self._currStructure == StructureType.Single
+ and newStruct == StructureType.Dict
+ and not self.optionEnabled(PinOptions.DictSupported)
+ and self.hasConnections(),
+ self._currStructure == StructureType.Array
+ and newStruct == StructureType.Single
+ and self.optionEnabled(PinOptions.SupportsOnlyArrays)
+ and self.hasConnections(),
+ self._currStructure == StructureType.Dict
+ and newStruct == StructureType.Single
+ and self.optionEnabled(PinOptions.SupportsOnlyArrays)
+ and self.hasConnections(),
+ self._currStructure == StructureType.Array
+ and newStruct == StructureType.Dict
+ and self.hasConnections(),
+ self._currStructure == StructureType.Dict
+ and newStruct == StructureType.Array
+ and self.hasConnections(),
+ ]
+ ):
free = testfree()
if free:
- for port in self.owningNode().structConstraints[self.structConstraint] + con:
+ for port in (self.owningNode().structConstraints[self.structConstraint] + con):
if port not in checked:
checked.append(port)
free = port.canChangeStructure(newStruct, checked, True, init=init)
@@ -766,11 +780,10 @@ def testfree():
return free
def updateConstrainedPins(self, traversed, newStruct, init=False, connecting=False):
- nodePins = set()
if self.structConstraint is not None:
nodePins = set(self.owningNode().structConstraints[self.structConstraint])
else:
- nodePins = set([self])
+ nodePins = {self}
for connectedPin in getConnectedPins(self):
if connectedPin.structureType == StructureType.Multi:
if connectedPin.canChangeStructure(self._currStructure, init=init):
@@ -790,7 +803,7 @@ def updateConstrainedPins(self, traversed, newStruct, init=False, connecting=Fal
if newStruct == StructureType.Array:
neighbor.enableOptions(PinOptions.ArraySupported)
elif newStruct == StructureType.Dict:
- neighbor.enableOptions(PinOptions.DictSupported)
+ neighbor.enableOptions(PinOptions.DictSupported)
elif newStruct == StructureType.Multi:
neighbor.enableOptions(PinOptions.ArraySupported)
neighbor.enableOptions(PinOptions.DictSupported)
@@ -800,32 +813,39 @@ def updateConstrainedPins(self, traversed, newStruct, init=False, connecting=Fal
neighbor._currStructure = neighbor._structure
neighbor._data = neighbor.defaultValue()
traversed.add(neighbor)
- neighbor.setData(neighbor.defaultValue())
+ try:
+ neighbor.setData(neighbor.getData())
+ except:
+ neighbor.setData(neighbor.defaultValue())
neighbor.updateConstrainedPins(traversed, newStruct, init, connecting=connecting)
def pinConnected(self, other):
- push(self)
+ #push(self)
if self.isDict():
self.updateConnectedDicts([], self._data.keyType)
def pinDisconnected(self, other):
self.onPinDisconnected.send(other)
- push(other)
+ #push(other)
- def canChangeTypeOnConnection(self, checked=[], can=True, extraPins=[], selfCheck=True):
+ def canChangeTypeOnConnection(self, checked=None, can=True, extraPins=None, selfCheck=True):
"""Recursive function to determine if pin can change its dataType
:param checked: Already visited pins, defaults to []
:type checked: list, optional
:param can: Variable Updated during iteration, defaults to True
:type can: bool, optional
- :param extraPins: extra pins, non constrained or connected to this pin but that want to check also, defaults to []
+ :param extraPins: extra pins, non-constrained or connected to this pin but that want to check also, defaults to []
:type extraPins: list, optional
:param selfCheck: Define if check pin itself for connected pins, defaults to True
:type selfCheck: bool, optional
- :returns: True if pin can becabe other dataType
+ :returns: True if pin can become other dataType
:rtype: bool
"""
+ if checked is None:
+ checked = []
+ if extraPins is None:
+ extraPins = []
if not self.optionEnabled(PinOptions.ChangeTypeOnConnection):
return False
con = []
@@ -845,14 +865,17 @@ def canChangeTypeOnConnection(self, checked=[], can=True, extraPins=[], selfChec
can = port.canChangeTypeOnConnection(checked, can, selfCheck=True)
return can
- def getDictElementNode(self, checked=[], node=None):
- """Get the connected :py:class:`PyFlow.Packages.PyFlowBase.Nodes.makeDictElement.makeDictElement` to this pin recursively
+ def getDictElementNode(self, checked=None, node=None):
+ """Get the connected :py:class:`PyFlow.Packages.PyFlowBase.Nodes.makeDictElement.makeDictElement` to this
+ pin recursively
:param checked: Currently visited pins, defaults to []
:type checked: list, optional
:param node: founded node, defaults to None
:rtype: :class:`~PyFlow.Core.NodeBase.NodeBase` or None
"""
+ if checked is None:
+ checked = []
if self.owningNode().__class__.__name__ == "makeDictElement":
return self.owningNode()
con = []
@@ -864,12 +887,12 @@ def getDictElementNode(self, checked=[], node=None):
if self.constraint:
neis = self.owningNode().constraints[self.constraint]
for port in con + neis:
- if port not in checked and node == None:
+ if port not in checked and node is None:
checked.append(port)
node = port.getDictElementNode(checked, node)
return node
- def getDictNode(self, checked=[], node=None):
+ def getDictNode(self, checked=None, node=None):
"""Get the connected :py:class:`PyFlow.Packages.PyFlowBase.Nodes.makeDict.makeDict` or
:py:class:`PyFlow.Packages.PyFlowBase.Nodes.makeAnyDict.makeAnyDict` to this pin recursively
@@ -878,6 +901,8 @@ def getDictNode(self, checked=[], node=None):
:param node: founded node, defaults to None
:returns: founded node or None if not found
"""
+ if checked is None:
+ checked = []
if self.owningNode().__class__.__name__ in ["makeDict", "makeAnyDict"]:
return self.owningNode()
con = []
@@ -889,12 +914,12 @@ def getDictNode(self, checked=[], node=None):
if self.constraint:
neis = self.owningNode().constraints[self.constraint]
for port in con + neis:
- if port not in checked and node == None:
+ if port not in checked and node is None:
checked.append(port)
node = port.getDictNode(checked, node)
return node
- def supportDictElement(self, checked=[], can=True, selfCheck=True):
+ def supportDictElement(self, checked=None, can=True, selfCheck=True):
"""Iterative functions that search in all connected pins to see if they support DictElement nodes.
:param checked: Already visited pins, defaults to []
@@ -906,6 +931,8 @@ def supportDictElement(self, checked=[], can=True, selfCheck=True):
:returns: True if can connect DictElement nodes to this pin
:rtype: bool
"""
+ if checked is None:
+ checked = []
if not self.optionEnabled(PinOptions.DictElementSupported):
return False
con = []
@@ -925,9 +952,9 @@ def supportDictElement(self, checked=[], can=True, selfCheck=True):
can = port.supportDictElement(checked, can, selfCheck=True)
return can
- def supportOnlyDictElement(self, checked=[], can=False, selfCheck=True):
+ def supportOnlyDictElement(self, checked=None, can=False, selfCheck=True):
"""Iterative Functions that search in all connected pins to see if they support only DictElement nodes, this
- is done for nodes like makeDict and simmilars.
+ is done for nodes like makeDict and similar.
:param checked: Already Visited Pins, defaults to []
:type checked: list, optional
@@ -938,6 +965,8 @@ def supportOnlyDictElement(self, checked=[], can=False, selfCheck=True):
:returns: True if can connect only DictElement and Dicts nodes to this Pin
:rtype: bool
"""
+ if checked is None:
+ checked = []
if self.isDict():
return True
con = []
@@ -957,7 +986,7 @@ def supportOnlyDictElement(self, checked=[], can=False, selfCheck=True):
can = port.supportOnlyDictElement(checked, can, selfCheck=True)
return can
- def updateConnectedDicts(self, checked=[], keyType=None):
+ def updateConnectedDicts(self, checked=None, keyType=None):
"""Iterate over connected dicts pins and DictElements pins updating key data type
:param checked: Already visited pins, defaults to []
@@ -965,6 +994,8 @@ def updateConnectedDicts(self, checked=[], keyType=None):
:param keyType: KeyDataType to set, defaults to None
:type keyType: string, optional
"""
+ if checked is None:
+ checked = []
if not self.isDict():
return
con = []
@@ -990,7 +1021,7 @@ def setClean(self):
"""Sets dirty flag to True
"""
self.dirty = False
- #if self.direction == PinDirection.Output:
+ # if self.direction == PinDirection.Output:
# for i in self.affects:
# i.dirty = False
@@ -1057,7 +1088,7 @@ def pinDataTypeHint():
:rtype: tuple(str, object)
:raises NotImplementedError: If not implemented
"""
- raise NotImplementedError('pinDataTypeHint method of PinBase is not implemented')
+ raise NotImplementedError("pinDataTypeHint method of PinBase is not implemented")
@staticmethod
def supportedDataTypes():
diff --git a/PyFlow/Core/PyCodeCompiler.py b/PyFlow/Core/PyCodeCompiler.py
index aa9b33e0f..be6ff1226 100644
--- a/PyFlow/Core/PyCodeCompiler.py
+++ b/PyFlow/Core/PyCodeCompiler.py
@@ -15,12 +15,14 @@
from PyFlow.Core.Interfaces import ICodeCompiler
+
class Py3FunctionCompiler(ICodeCompiler):
"""Compiles string to python function
"""
+
def __init__(self, fooName=None, *args, **kwargs):
super(Py3FunctionCompiler, self).__init__(*args, **kwargs)
- assert(isinstance(fooName, str))
+ assert isinstance(fooName, str)
self._fooName = fooName
def compile(self, code):
@@ -32,9 +34,9 @@ def compile(self, code):
:rtype: :class:`function`
"""
foo = "def {}(self):".format(self._fooName)
- lines = [i for i in code.split('\n') if len(i) > 0]
+ lines = [i for i in code.split("\n") if len(i) > 0]
for line in lines:
- foo += '\n\t{}'.format(line)
+ foo += "\n\t{}".format(line)
if len(lines) == 0:
foo += "\n\tpass"
codeObject = compile(foo, "PyFlowCodeCompiler", "exec")
@@ -45,10 +47,11 @@ def compile(self, code):
class Py3CodeCompiler(ICodeCompiler):
"""Generic python code compiler"""
+
def __init__(self):
super(Py3CodeCompiler, self).__init__()
- def compile(self, code, moduleName="PyFlowCodeCompiler", scope={}):
+ def compile(self, code, moduleName="PyFlowCodeCompiler", scope=None):
"""Evaluates supplied string
Used by python node
@@ -60,6 +63,8 @@ def compile(self, code, moduleName="PyFlowCodeCompiler", scope={}):
:param scope: Storage where symbols will be placed
:type scope: dict
"""
+ if scope is None:
+ scope = {}
codeObject = compile(code, moduleName, "exec")
exec(codeObject, scope)
return scope
diff --git a/PyFlow/Core/Variable.py b/PyFlow/Core/Variable.py
index 50250a513..0eb7fa93d 100644
--- a/PyFlow/Core/Variable.py
+++ b/PyFlow/Core/Variable.py
@@ -13,11 +13,10 @@
## limitations under the License.
-from nine import str
from blinker import Signal
import json
+import uuid
-from PyFlow import findPinClassByType
from PyFlow import getPinDefaultValueByType
from PyFlow.Core.Common import *
from PyFlow.Core.Interfaces import IItemBase
@@ -41,7 +40,17 @@ class Variable(IItemBase):
:var graph: Reference to owning graph
:vartype graph: :class:`~PyFlow.Core.GraphBase.GraphBase`
"""
- def __init__(self, graph, value, name, dataType, accessLevel=AccessLevel.public, structure=StructureType.Single, uid=None):
+
+ def __init__(
+ self,
+ graph,
+ value,
+ name,
+ dataType,
+ accessLevel=AccessLevel.public,
+ structure=StructureType.Single,
+ uid=None,
+ ):
"""Constructor
:param graph: Owning graph
@@ -76,7 +85,7 @@ def __init__(self, graph, value, name, dataType, accessLevel=AccessLevel.public,
self._accessLevel = accessLevel
self._packageName = None
self._uid = uuid.uuid4() if uid is None else uid
- assert(isinstance(self._uid, uuid.UUID))
+ assert isinstance(self._uid, uuid.UUID)
self.updatePackageName()
self._uiWrapper = None
@@ -114,9 +123,9 @@ def packageName(self):
@packageName.setter
def packageName(self, value):
- assert(isinstance(value, str))
+ assert isinstance(value, str)
self._packageName = value
- self.packageNameChanged.send(value)
+ self.packageNameChanged.send(value) # TODO: nonexistent, single use
@property
def accessLevel(self):
@@ -128,7 +137,7 @@ def accessLevel(self):
@accessLevel.setter
def accessLevel(self, value):
- assert(isinstance(value, AccessLevel))
+ assert isinstance(value, AccessLevel)
self._accessLevel = value
self.accessLevelChanged.send(value)
@@ -138,7 +147,7 @@ def name(self):
@name.setter
def name(self, value):
- assert(isinstance(value, str))
+ assert isinstance(value, str)
self._name = value
self.nameChanged.send(value)
@@ -153,7 +162,7 @@ def value(self):
@value.setter
def value(self, value):
# type checking if variable is not of any type
- if not self.dataType == 'AnyPin':
+ if not self.dataType == "AnyPin":
supportedDataTypes = findPinClassByType(self.dataType).supportedDataTypes()
if self.dataType not in supportedDataTypes:
return
@@ -176,7 +185,7 @@ def dataType(self):
@dataType.setter
def dataType(self, value):
- assert(isinstance(value, str))
+ assert isinstance(value, str)
if value != self._dataType:
self._dataType = value
self.updatePackageName()
@@ -193,7 +202,7 @@ def structure(self):
@structure.setter
def structure(self, value):
- assert(isinstance(value, StructureType))
+ assert isinstance(value, StructureType)
if value != self._structure:
self._structure = value
if self._structure == StructureType.Array:
@@ -208,7 +217,7 @@ def uid(self):
@uid.setter
def uid(self, value):
- assert(isinstance(value, uuid.UUID))
+ assert isinstance(value, uuid.UUID)
self.graph.getVars()[value] = self.graph.getVars().pop(self._uid)
self._uid = value
@@ -219,45 +228,45 @@ def serialize(self):
uidString = str(self.uid)
- template['name'] = self.name
- if self.dataType == 'AnyPin':
- template['value'] = None
+ template["name"] = self.name
+ if self.dataType == "AnyPin":
+ template["value"] = None
else:
- template['value'] = json.dumps(self.value, cls=pinClass.jsonEncoderClass())
+ template["value"] = json.dumps(self.value, cls=pinClass.jsonEncoderClass())
if self.structure == StructureType.Dict:
template["dictKeyType"] = self.value.keyType
template["dictValueType"] = self.value.valueType
else:
- template['dataType'] = self.dataType
- template['structure'] = self.structure.name
- template['accessLevel'] = self.accessLevel.name
- template['package'] = self._packageName
- template['uuid'] = uidString
+ template["dataType"] = self.dataType
+ template["structure"] = self.structure.name
+ template["accessLevel"] = self.accessLevel.name
+ template["package"] = self._packageName
+ template["uuid"] = uidString
return template
@staticmethod
def deserialize(graph, jsonData, *args, **kwargs):
- name = jsonData['name']
+ name = jsonData["name"]
dataType = "BoolPin"
if jsonData["structure"] == StructureType.Dict.name:
- keyDataType = jsonData['dictKeyType']
- valueDataType = jsonData['dictValueType']
+ keyDataType = jsonData["dictKeyType"]
+ valueDataType = jsonData["dictValueType"]
value = PFDict(keyDataType, valueDataType)
else:
- dataType = jsonData['dataType']
+ dataType = jsonData["dataType"]
if dataType != "AnyPin":
pinClass = findPinClassByType(dataType)
- value = json.loads(jsonData['value'], cls=pinClass.jsonDecoderClass())
+ value = json.loads(jsonData["value"], cls=pinClass.jsonDecoderClass())
else:
value = getPinDefaultValueByType("AnyPin")
- accessLevel = AccessLevel[jsonData['accessLevel']]
- structure = StructureType[jsonData['structure']]
- uid = uuid.UUID(jsonData['uuid'])
+ accessLevel = AccessLevel[jsonData["accessLevel"]]
+ structure = StructureType[jsonData["structure"]]
+ uid = uuid.UUID(jsonData["uuid"])
return Variable(graph, value, name, dataType, accessLevel, structure, uid)
@staticmethod
@@ -267,11 +276,11 @@ def jsonTemplate():
:rtype: dict
"""
template = {
- 'name': None,
- 'value': None,
- 'dataType': None,
- 'accessLevel': None,
- 'package': None,
- 'uuid': None
+ "name": None,
+ "value": None,
+ "dataType": None,
+ "accessLevel": None,
+ "package": None,
+ "uuid": None,
}
return template
diff --git a/PyFlow/Core/structs.py b/PyFlow/Core/structs.py
index a67cae073..4e7f23b55 100644
--- a/PyFlow/Core/structs.py
+++ b/PyFlow/Core/structs.py
@@ -15,6 +15,7 @@
class Tick(object):
""" Element For Ramp Widget Basic U and V Attribute holder """
+
def __init__(self):
self._u = 0
self._v = 0
@@ -41,6 +42,7 @@ def isSelected(self):
class splineRamp(object):
""" Ramp/Curve Editor with evaluateAt support , clamped to 0-1 in both x and y"""
+
def __init__(self):
self.items = []
@@ -71,7 +73,7 @@ def addItem(self, u, v):
item.setU(u)
item.setV(v)
self.items.append(item)
- return(item)
+ return item
def removeItem(self, item=None, index=-1):
if item:
@@ -100,9 +102,15 @@ def evaluateAt(self, value, bezier=False):
if isinstance(items[0].getV(), list):
v = []
for i in range(len(items[0].getV())):
- v.append(self.interpolateBezier([p.getV()[i] for p in items], 0, len(items) - 1, value))
+ v.append(
+ self.interpolateBezier(
+ [p.getV()[i] for p in items], 0, len(items) - 1, value
+ )
+ )
else:
- v = self.interpolateBezier([p.getV() for p in items], 0, len(items) - 1, value)
+ v = self.interpolateBezier(
+ [p.getV() for p in items], 0, len(items) - 1, value
+ )
else:
interval = len(items) - 1
for i, x in enumerate(items):
@@ -110,12 +118,25 @@ def evaluateAt(self, value, bezier=False):
interval = i
break
- u = max(0, min(1, (((value - items[interval - 1].getU()) * (1.0 - 0.0)) / (
- items[interval].getU() - items[interval - 1].getU())) + 0.0))
+ u = max(
+ 0,
+ min(
+ 1,
+ (
+ ((value - items[interval - 1].getU()) * (1.0 - 0.0))
+ / (items[interval].getU() - items[interval - 1].getU())
+ )
+ + 0.0,
+ ),
+ )
start = items[interval].getV()
end = items[interval - 1].getV()
- if isinstance(start, list) and isinstance(end, list) and len(start) == len(end):
+ if (
+ isinstance(start, list)
+ and isinstance(end, list)
+ and len(start) == len(end)
+ ):
v = []
for i, element in enumerate(start):
v.append(self.interpolateLinear(start[i], end[i], u))
@@ -131,12 +152,17 @@ def evaluateAt(self, value, bezier=False):
def interpolateBezier(self, coorArr, i, j, t):
if j == 0:
return coorArr[i]
- return self.interpolateBezier(coorArr, i, j - 1, t) * (1 - t) + self.interpolateBezier(coorArr, i + 1, j - 1, t) * t
+ return (
+ self.interpolateBezier(coorArr, i, j - 1, t) * (1 - t)
+ + self.interpolateBezier(coorArr, i + 1, j - 1, t) * t
+ )
+
+ @staticmethod
+ def interpolateLinear(start, end, ratio):
+ return ratio * start + (1 - ratio) * end
- def interpolateLinear(self, start, end, ratio):
- return (ratio * start + (1 - ratio) * end)
-if __name__ == '__main__':
+if __name__ == "__main__":
ramp = splineRamp()
ramp.addItem(0.1, [0.0, 0.0, 0.0])
ramp.addItem(1.0, [1.0, 1.0, 1.0])
diff --git a/PyFlow/Core/version.py b/PyFlow/Core/version.py
index dff56996d..e6de1527c 100644
--- a/PyFlow/Core/version.py
+++ b/PyFlow/Core/version.py
@@ -18,11 +18,12 @@ class Version(object):
Comparison operators overloaded
"""
+
def __init__(self, major, minor, patch):
super(Version, self).__init__()
- assert(isinstance(major, int))
- assert(isinstance(minor, int))
- assert(isinstance(patch, int))
+ assert isinstance(major, int)
+ assert isinstance(minor, int)
+ assert isinstance(patch, int)
self._major = major
self._minor = minor
self._patch = patch
@@ -36,7 +37,7 @@ def fromString(string):
:param string: Version as string
:type string: str
"""
- major, minor, patch = string.split('.')
+ major, minor, patch = string.split(".")
return Version(int(major), int(minor), int(patch))
def __str__(self):
@@ -55,9 +56,13 @@ def patch(self):
return self._patch
def __eq__(self, other):
- return all([self.major == other.major,
- self.minor == other.minor,
- self.patch == other.patch])
+ return all(
+ [
+ self.major == other.major,
+ self.minor == other.minor,
+ self.patch == other.patch,
+ ]
+ )
def __ge__(self, other):
lhs = int("".join([str(self.major), str(self.minor), str(self.patch)]))
diff --git a/PyFlow/Input.py b/PyFlow/Input.py
index b63713e0e..58cabfb86 100644
--- a/PyFlow/Input.py
+++ b/PyFlow/Input.py
@@ -13,10 +13,10 @@
## limitations under the License.
-from collections import Counter
from collections import defaultdict
+from enum import Enum
-from Qt import QtCore, QtGui
+from qtpy import QtCore, QtGui
from PyFlow.Core.Common import *
@@ -27,16 +27,26 @@ class InputActionType(Enum):
class InputAction(object):
- def __init__(self, name="defaultName", actionType=InputActionType.Keyboard, group="default", mouse=QtCore.Qt.NoButton, key=None, modifiers=QtCore.Qt.NoModifier):
+ def __init__(
+ self,
+ name="defaultName",
+ actionType=InputActionType.Keyboard,
+ group="default",
+ mouse=QtCore.Qt.NoButton,
+ key=None,
+ modifiers=QtCore.Qt.NoModifier,
+ ):
self.__actionType = actionType
self._name = name
self._group = group
self.__data = {"mouse": mouse, "key": key, "modifiers": modifiers}
def __str__(self):
- return "{0} {1} {2}".format(QtGui.QKeySequence(self.getModifiers()).toString(),
- self.getMouseButton().name.decode('utf=8'),
- QtGui.QKeySequence(self.getKey()).toString())
+ return "{0} {1} {2}".format(
+ QtGui.QKeySequence(self.getModifiers()).toString(),
+ self.getMouseButton().name.decode("utf=8"),
+ QtGui.QKeySequence(self.getKey()).toString(),
+ )
@property
def group(self):
@@ -53,10 +63,7 @@ def __eq__(self, other):
om = other.getData()["mouse"]
ok = other.getData()["key"]
omod = other.getData()["modifiers"]
- smod == omod
- return all([sm == om,
- sk == ok,
- smod == omod])
+ return all([sm == om, sk == ok, smod == omod])
def __ne__(self, other):
sm = self.__data["mouse"]
@@ -65,9 +72,7 @@ def __ne__(self, other):
om = other.getData()["mouse"]
ok = other.getData()["key"]
omod = other.getData()["modifiers"]
- return not all([sm == om,
- sk == ok,
- smod == omod])
+ return not all([sm == om, sk == ok, smod == omod])
def getName(self):
return self._name
@@ -76,14 +81,16 @@ def getData(self):
return self.__data
def setMouseButton(self, btn):
- assert(isinstance(btn, QtCore.Qt.MouseButton))
+ assert isinstance(btn, QtCore.Qt.MouseButton)
self.__data["mouse"] = btn
def getMouseButton(self):
return self.__data["mouse"]
- def setKey(self, key=[]):
- assert(isinstance(key, QtCore.Qt.Key))
+ def setKey(self, key=None):
+ if key is None:
+ key = []
+ assert isinstance(key, QtCore.Qt.Key)
self.__data["key"] = key
def getKey(self):
@@ -112,24 +119,24 @@ def _modifiersToList(mods):
result.append(QtCore.Qt.GroupSwitchModifier)
return result
- def _listOfModifiersToEnum(self, modifiersList):
+ @staticmethod
+ def _listOfModifiersToEnum(modifiersList):
result = QtCore.Qt.NoModifier
for mod in modifiersList:
result = result | mod
return result
def toJson(self):
- saveData = {}
- saveData["name"] = self._name
- saveData["group"] = self._group
- saveData["mouse"] = int(self.__data["mouse"])
- saveData["actionType"] = self.actionType.value
-
+ saveData = {"name": self._name,
+ "group": self._group,
+ "mouse": int(self.__data["mouse"].value),
+ "actionType": self.actionType.value}
key = self.__data["key"]
saveData["key"] = int(key) if key is not None else None
modifiersList = self._modifiersToList(self.__data["modifiers"])
- saveData["modifiers"] = [int(i) for i in modifiersList]
+
+ saveData["modifiers"] = [i.value for i in modifiersList]
return saveData
def fromJson(self, jsonData):
@@ -138,8 +145,12 @@ def fromJson(self, jsonData):
self._group = jsonData["group"]
self.__data["mouse"] = QtCore.Qt.MouseButton(jsonData["mouse"])
keyJson = jsonData["key"]
- self.__data["key"] = QtCore.Qt.Key(keyJson) if isinstance(keyJson, int) else None
- self.__data["modifiers"] = self._listOfModifiersToEnum([QtCore.Qt.KeyboardModifier(i) for i in jsonData["modifiers"]])
+ self.__data["key"] = (
+ QtCore.Qt.Key(keyJson) if isinstance(keyJson, int) else None
+ )
+ self.__data["modifiers"] = self._listOfModifiersToEnum(
+ [QtCore.Qt.KeyboardModifier(i) for i in jsonData["modifiers"]]
+ )
self.__actionType = InputActionType(jsonData["actionType"])
return self
except:
diff --git a/PyFlow/Packages/PyFlowBase/Exporters/PythonScriptExporter.py b/PyFlow/Packages/PyFlowBase/Exporters/PythonScriptExporter.py
index dd8b29024..5a62b1fd2 100644
--- a/PyFlow/Packages/PyFlowBase/Exporters/PythonScriptExporter.py
+++ b/PyFlow/Packages/PyFlowBase/Exporters/PythonScriptExporter.py
@@ -14,13 +14,11 @@
from datetime import datetime
-from Qt.QtWidgets import QFileDialog
-from Qt.QtWidgets import QMessageBox
+from qtpy.QtWidgets import QFileDialog
+from qtpy.QtWidgets import QMessageBox
-from PyFlow import getRawNodeInstance
from PyFlow.Core.Common import *
from PyFlow.UI.UIInterfaces import IDataExporter
-from PyFlow import getRawNodeInstance
from PyFlow.Core.version import Version
from PyFlow.Core.PyCodeCompiler import Py3CodeCompiler
@@ -28,27 +26,53 @@
def nodeToScript(node, supportedDataTypes, supportedStructures):
script = ""
lib = None if node.lib is None else "'{0}'".format(node.lib)
- script += "{0} = getRawNodeInstance('{1}', packageName='{2}', libName={3})\n".format(node.name, node.__class__.__name__, node.packageName, lib)
+ script += "{0} = getRawNodeInstance('{1}', packageName='{2}', libName={3})\n".format(
+ node.name, node.__class__.__name__, node.packageName, lib
+ )
script += "ROOT_GRAPH.addNode({0})\n".format(node.name)
script += "{0}.setPosition({1}, {2})\n".format(node.name, node.x, node.y)
script += "{0}.setName('{0}')\n".format(node.name)
for inPin in node.inputs.values():
if inPin.dataType not in supportedDataTypes:
- raise Exception("Data type {0} of pin {1} is not supported!".format(inPin.dataType, inPin.getFullName()))
+ raise Exception(
+ "Data type {0} of pin {1} is not supported!".format(
+ inPin.dataType, inPin.getFullName()
+ )
+ )
if inPin.structureType not in supportedStructures:
- raise Exception("Structure {0} of pin {1} is not supported!".format(str(inPin.structureType), inPin.getFullName()))
-
- data = "'{}'".format(inPin.currentData()) if isinstance(inPin.currentData(), str) else inPin.currentData()
+ raise Exception(
+ "Structure {0} of pin {1} is not supported!".format(
+ str(inPin.structureType), inPin.getFullName()
+ )
+ )
+
+ data = (
+ "'{}'".format(inPin.currentData())
+ if isinstance(inPin.currentData(), str)
+ else inPin.currentData()
+ )
script += "{0}['{1}'].setData({2})\n".format(node.name, inPin.name, data)
for outPin in node.outputs.values():
if outPin.dataType not in supportedDataTypes:
- raise Exception("Data type {0} of pin {1} is not supported!".format(outPin.dataType, outPin.getFullName()))
+ raise Exception(
+ "Data type {0} of pin {1} is not supported!".format(
+ outPin.dataType, outPin.getFullName()
+ )
+ )
if outPin.structureType not in supportedStructures:
- raise Exception("Structure {0} of pin {1} is not supported!".format(str(outPin.structureType), outPin.getFullName()))
-
- data = "'{}'".format(outPin.currentData()) if isinstance(outPin.currentData(), str) else outPin.currentData()
+ raise Exception(
+ "Structure {0} of pin {1} is not supported!".format(
+ str(outPin.structureType), outPin.getFullName()
+ )
+ )
+
+ data = (
+ "'{}'".format(outPin.currentData())
+ if isinstance(outPin.currentData(), str)
+ else outPin.currentData()
+ )
script += "{0}['{1}'].setData({2})\n".format(node.name, outPin.name, data)
return script
@@ -83,14 +107,18 @@ def displayName():
@staticmethod
def doImport(pyFlowInstance):
- name_filter = "Graph files (*.json)"
- openFilename, filterString = QFileDialog.getOpenFileName(filter=PythonScriptExporter.name_filter)
+ openFilename, filterString = QFileDialog.getOpenFileName(
+ filter=PythonScriptExporter.name_filter
+ )
if openFilename != "":
- with open(openFilename, 'r') as f:
+ with open(openFilename, "r") as f:
script = f.read()
mem = Py3CodeCompiler().compile(code=script, scope=globals())
fileVersion = Version.fromString(mem["EXPORTER_VERSION"])
- if fileVersion >= PythonScriptExporter.version() and PythonScriptExporter.displayName() == mem["EXPORTER_NAME"]:
+ if (
+ fileVersion >= PythonScriptExporter.version()
+ and PythonScriptExporter.displayName() == mem["EXPORTER_NAME"]
+ ):
pyFlowInstance.newFile()
ROOT_GRAPH = pyFlowInstance.graphManager.get().findRootGraph()
mem["createScene"](ROOT_GRAPH)
@@ -103,10 +131,16 @@ def doExport(pyFlowInstance):
supportedStructures = {StructureType.Single}
script = "# -*- coding: utf-8 -*-\n\n"
- script += "# This file was auto-generated by PyFlow exporter '{0} v{1}'\n".format(PythonScriptExporter.displayName(), str(PythonScriptExporter.version()))
- script += "#\tCreated: {0}\n\n".format(PythonScriptExporter.creationDateString())
+ script += "# This file was auto-generated by PyFlow exporter '{0} v{1}'\n".format(
+ PythonScriptExporter.displayName(), str(PythonScriptExporter.version())
+ )
+ script += "#\tCreated: {0}\n\n".format(
+ PythonScriptExporter.creationDateString()
+ )
script += "EXPORTER_NAME = '{}'\n".format(PythonScriptExporter.displayName())
- script += "EXPORTER_VERSION = '{}'\n\n".format(str(PythonScriptExporter.version()))
+ script += "EXPORTER_VERSION = '{}'\n\n".format(
+ str(PythonScriptExporter.version())
+ )
rootGraph = pyFlowInstance.graphManager.get().findRootGraph()
@@ -118,7 +152,9 @@ def doExport(pyFlowInstance):
# create root level nodes
graphScript = ""
for node in rootGraph.getNodesList():
- graphScript += nodeToScript(node, supportedDataTypes, supportedStructures)
+ graphScript += nodeToScript(
+ node, supportedDataTypes, supportedStructures
+ )
graphScript += "\n# connect pins\n"
@@ -131,13 +167,17 @@ def doExport(pyFlowInstance):
# graphScript += "{0} = ROOT_GRAPH.graphManager.findPinByName('{1}')\n".format(inPin.getFullName(), inPin.getFullName())
# graphScript += "connectPins({0}, {1})\n".format(outPin.getFullName(), inPin.getFullName())
- wrappedGraphScript = wrapStringToFunctionDef("createScene", graphScript, {"ROOT_GRAPH": None})
+ wrappedGraphScript = wrapStringToFunctionDef(
+ "createScene", graphScript, {"ROOT_GRAPH": None}
+ )
script += wrappedGraphScript + "\n"
- outFilePath, filterString = QFileDialog.getSaveFileName(filter=PythonScriptExporter.name_filter)
+ outFilePath, filterString = QFileDialog.getSaveFileName(
+ filter=PythonScriptExporter.name_filter
+ )
if outFilePath != "":
- with open(outFilePath, 'w') as f:
+ with open(outFilePath, "w") as f:
f.write(script)
print("saved!")
except Exception as e:
diff --git a/PyFlow/Packages/PyFlowBase/Factories/PinInputWidgetFactory.py b/PyFlow/Packages/PyFlowBase/Factories/PinInputWidgetFactory.py
index 1f80464d2..be366ee9d 100644
--- a/PyFlow/Packages/PyFlowBase/Factories/PinInputWidgetFactory.py
+++ b/PyFlow/Packages/PyFlowBase/Factories/PinInputWidgetFactory.py
@@ -15,13 +15,11 @@
# Input widgets for pins
-from copy import copy
-from Qt import QtCore
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy.QtWidgets import *
from PyFlow.Packages.PyFlowBase import PACKAGE_NAME
from PyFlow import GET_PACKAGES
-from PyFlow.Core.Common import *
from PyFlow.Core.PathsRegistry import PathsRegistry
from PyFlow.UI.Widgets.EnumComboBox import EnumComboBox
from PyFlow.UI.Widgets.InputWidgets import *
@@ -29,17 +27,16 @@
from PyFlow.UI.Widgets.FileDialog import FileDialog
-
class ExecInputWidget(InputWidgetSingle):
"""docstring for ExecInputWidget"""
- def __init__(self, parent=None, **kwds):
- super(ExecInputWidget, self).__init__(parent=parent, **kwds)
- self.pb = QPushButton('execute', self)
+ def __init__(self, parent=None, **kwargs):
+ super(ExecInputWidget, self).__init__(parent=parent, **kwargs)
+ self.pb = QPushButton("execute", self)
self.setWidget(self.pb)
self.pb.clicked.connect(self.dataSetCallback)
- def blockWidgetSignals(self, bLocked):
+ def blockWidgetSignals(self, bLock=False):
pass
@@ -48,16 +45,16 @@ class FloatInputWidgetSimple(InputWidgetSingle):
Floating point data input widget without enhancements
"""
- def __init__(self, parent=None, **kwds):
- super(FloatInputWidgetSimple, self).__init__(parent=parent, **kwds)
+ def __init__(self, parent=None, **kwargs):
+ super(FloatInputWidgetSimple, self).__init__(parent=parent, **kwargs)
self.sb = valueBox(type="float", buttons=True)
self.sb.setRange(FLOAT_RANGE_MIN, FLOAT_RANGE_MAX)
self.sb.setSingleStep(0.01)
self.setWidget(self.sb)
self.sb.valueChanged.connect(self.dataSetCallback)
- def blockWidgetSignals(self, bLocked):
- self.sb.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.sb.blockSignals(bLock)
def setWidgetValue(self, val):
self.sb.setValue(float(val))
@@ -74,26 +71,28 @@ class FloatInputWidget(InputWidgetSingle):
Floating point data input widget
"""
- def __init__(self, parent=None, **kwds):
- super(FloatInputWidget, self).__init__(parent=parent, **kwds)
+ def __init__(self, parent=None, **kwargs):
+ super(FloatInputWidget, self).__init__(parent=parent, **kwargs)
valueRange = (FLOAT_RANGE_MIN, FLOAT_RANGE_MAX)
- if "pinAnnotations" in kwds:
- if PinSpecifires.VALUE_RANGE in kwds["pinAnnotations"]:
- valueRange = kwds["pinAnnotations"][PinSpecifires.VALUE_RANGE]
+ if "pinAnnotations" in kwargs:
+ if PinSpecifiers.VALUE_RANGE in kwargs["pinAnnotations"]:
+ valueRange = kwargs["pinAnnotations"][PinSpecifiers.VALUE_RANGE]
steps = copy(FLOAT_SLIDER_DRAG_STEPS)
- if "pinAnnotations" in kwds:
- if PinSpecifires.DRAGGER_STEPS in kwds["pinAnnotations"]:
- steps = kwds["pinAnnotations"][PinSpecifires.DRAGGER_STEPS]
+ if "pinAnnotations" in kwargs:
+ if PinSpecifiers.DRAGGER_STEPS in kwargs["pinAnnotations"]:
+ steps = kwargs["pinAnnotations"][PinSpecifiers.DRAGGER_STEPS]
- self.sb = pyf_Slider(self, "float", style=0, sliderRange=valueRange, draggerSteps=steps)
+ self.sb = pyf_Slider(
+ self, "float", style=0, sliderRange=valueRange, draggerSteps=steps
+ )
self.setWidget(self.sb)
# when spin box updated call setter function
self.sb.valueChanged.connect(lambda val: self.dataSetCallback(val))
- def blockWidgetSignals(self, bLocked):
- self.sb.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.sb.blockSignals(bLock)
def setWidgetValue(self, val):
self.sb.setValue(float(val))
@@ -110,15 +109,15 @@ class IntInputWidgetSimple(InputWidgetSingle):
Decimal number input widget without enhancements
"""
- def __init__(self, parent=None, **kwds):
- super(IntInputWidgetSimple, self).__init__(parent=parent, **kwds)
+ def __init__(self, parent=None, **kwargs):
+ super(IntInputWidgetSimple, self).__init__(parent=parent, **kwargs)
self.sb = valueBox(type="int", buttons=True)
self.sb.setRange(INT_RANGE_MIN, INT_RANGE_MAX)
self.setWidget(self.sb)
self.sb.valueChanged.connect(self.dataSetCallback)
- def blockWidgetSignals(self, bLocked):
- self.sb.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.sb.blockSignals(bLock)
def setWidgetValue(self, val):
self.sb.setValue(int(val))
@@ -129,18 +128,18 @@ class IntInputWidget(InputWidgetSingle):
Decimal number input widget
"""
- def __init__(self, parent=None, **kwds):
- super(IntInputWidget, self).__init__(parent=parent, **kwds)
+ def __init__(self, parent=None, **kwargs):
+ super(IntInputWidget, self).__init__(parent=parent, **kwargs)
valueRange = (INT_RANGE_MIN, INT_RANGE_MAX)
- if "pinAnnotations" in kwds:
- if "ValueRange" in kwds["pinAnnotations"]:
- valueRange = kwds["pinAnnotations"]["ValueRange"]
+ if "pinAnnotations" in kwargs:
+ if "ValueRange" in kwargs["pinAnnotations"]:
+ valueRange = kwargs["pinAnnotations"]["ValueRange"]
self.sb = pyf_Slider(self, "int", style=1, sliderRange=valueRange)
self.setWidget(self.sb)
self.sb.valueChanged.connect(self.dataSetCallback)
- def blockWidgetSignals(self, bLocked):
- self.sb.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.sb.blockSignals(bLock)
def setWidgetValue(self, val):
self.sb.setValue(int(val))
@@ -151,15 +150,15 @@ class StringInputWidget(InputWidgetSingle):
String data input widget
"""
- def __init__(self, parent=None, **kwds):
- super(StringInputWidget, self).__init__(parent=parent, **kwds)
+ def __init__(self, parent=None, **kwargs):
+ super(StringInputWidget, self).__init__(parent=parent, **kwargs)
self.le = QLineEdit(self)
self.le.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
self.setWidget(self.le)
self.le.editingFinished.connect(lambda: self.dataSetCallback(self.le.text()))
- def blockWidgetSignals(self, bLocked):
- self.le.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.le.blockSignals(bLock)
def setWidgetValue(self, val):
self.le.setText(str(val))
@@ -167,15 +166,16 @@ def setWidgetValue(self, val):
class EnumInputWidget(InputWidgetSingle):
"""docstring for EnumInputWidget."""
- def __init__(self, parent=None, **kwds):
- super(EnumInputWidget, self).__init__(parent=parent, **kwds)
+
+ def __init__(self, parent=None, **kwargs):
+ super(EnumInputWidget, self).__init__(parent=parent, **kwargs)
values = []
- if PinSpecifires.VALUE_LIST in kwds["pinAnnotations"]:
- values = kwds["pinAnnotations"][PinSpecifires.VALUE_LIST]
+ if PinSpecifiers.VALUE_LIST in kwargs["pinAnnotations"]:
+ values = kwargs["pinAnnotations"][PinSpecifiers.VALUE_LIST]
self.enumBox = EnumComboBox(values)
self.enumBox.setEditable(False)
- if "editable" in kwds["pinAnnotations"]:
- self.enumBox.setEditable(kwds["pinAnnotations"]["editable"])
+ if "editable" in kwargs["pinAnnotations"]:
+ self.enumBox.setEditable(kwargs["pinAnnotations"]["editable"])
self.setWidget(self.enumBox)
self.enumBox.changeCallback.connect(self.dataSetCallback)
@@ -188,11 +188,11 @@ def setWidgetValue(self, value):
self.enumBox.setCurrentIndex(index)
-class ObjectPathWIdget(InputWidgetSingle):
- """docstring for ObjectPathWIdget."""
- def __init__(self, parent=None, **kwds):
- super(ObjectPathWIdget, self).__init__(parent=parent, **kwds)
- values = []
+class ObjectPathWidget(InputWidgetSingle):
+ """docstring for ObjectPathWidget."""
+
+ def __init__(self, parent=None, **kwargs):
+ super(ObjectPathWidget, self).__init__(parent=parent, **kwargs)
self.enumBox = EnumComboBox(PathsRegistry().getAllPaths())
self.setWidget(self.enumBox)
self.enumBox.changeCallback.connect(self.dataSetCallback)
@@ -209,8 +209,8 @@ class PathInputWidget(InputWidgetSingle):
Path input widget
"""
- def __init__(self, mode="all", parent=None, **kwds):
- super(PathInputWidget, self).__init__(parent=parent, **kwds)
+ def __init__(self, mode="all", parent=None, **kwargs):
+ super(PathInputWidget, self).__init__(parent=parent, **kwargs)
self.mode = mode
self.content = QWidget()
self.content.setContentsMargins(0, 0, 0, 0)
@@ -227,27 +227,27 @@ def __init__(self, mode="all", parent=None, **kwds):
def getPath(self):
dlg = FileDialog(self.mode)
- if dlg.exec_() == QFileDialog.Accepted and len(dlg.selectedFiles())>0:
+ if dlg.exec_() == QFileDialog.Accepted and len(dlg.selectedFiles()) > 0:
self.le.setText(dlg.selectedFiles()[0])
- def blockWidgetSignals(self, bLocked):
- self.le.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.le.blockSignals(bLock)
def setWidgetValue(self, val):
self.le.setText(str(val))
+
class BoolInputWidget(InputWidgetSingle):
"""Boolean data input widget"""
- def __init__(self, parent=None, **kwds):
- super(BoolInputWidget, self).__init__(parent=parent, **kwds)
+ def __init__(self, parent=None, **kwargs):
+ super(BoolInputWidget, self).__init__(parent=parent, **kwargs)
self.cb = QCheckBox(self)
self.setWidget(self.cb)
- self.cb.stateChanged.connect(
- lambda val: self.dataSetCallback(bool(val)))
+ self.cb.stateChanged.connect(lambda val: self.dataSetCallback(bool(val)))
- def blockWidgetSignals(self, bLocked):
- self.cb.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.cb.blockSignals(bLock)
def setWidgetValue(self, val):
if bool(val):
@@ -261,25 +261,27 @@ class NoneInputWidget(InputWidgetSingle):
String data input widget
"""
- def __init__(self, parent=None, **kwds):
- super(NoneInputWidget, self).__init__(parent=parent, **kwds)
+ def __init__(self, parent=None, **kwargs):
+ super(NoneInputWidget, self).__init__(parent=parent, **kwargs)
self.le = QLineEdit(self)
self.le.setContextMenuPolicy(QtCore.Qt.NoContextMenu)
self.setWidget(self.le)
self.le.textChanged.connect(lambda val: self.dataSetCallback(val))
self.le.setEnabled(False)
- def blockWidgetSignals(self, bLocked):
- self.le.blockSignals(bLocked)
+ def blockWidgetSignals(self, bLock=False):
+ self.le.blockSignals(bLock)
def setWidgetValue(self, val):
self.le.setText(str(val))
-def getInputWidget(dataType, dataSetter, defaultValue, widgetVariant=DEFAULT_WIDGET_VARIANT, **kwds):
- '''
+def getInputWidget(
+ dataType, dataSetter, defaultValue, widgetVariant=DEFAULT_WIDGET_VARIANT, **kwds
+):
+ """
factory method
- '''
+ """
# try to find factory in other packages first
for pkgName, pkg in GET_PACKAGES().items():
@@ -288,39 +290,83 @@ def getInputWidget(dataType, dataSetter, defaultValue, widgetVariant=DEFAULT_WID
continue
try:
- widget = pkg.PinsInputWidgetFactory()(dataType, dataSetter, defaultValue, widgetVariant=widgetVariant, **kwds)
+ widget = pkg.PinsInputWidgetFactory()(
+ dataType, dataSetter, defaultValue, widgetVariant=widgetVariant, **kwds
+ )
if widget is not None:
return widget
except Exception as e:
- print("Failed to override input widget.{0} Package - {1}".format(dataType,pkgName), e)
+ print(
+ "Failed to override input widget.{0} Package - {1}".format(
+ dataType, pkgName
+ ),
+ e,
+ )
continue
- if dataType == 'FloatPin':
+ if dataType == "FloatPin":
if kwds is not None and "pinAnnotations" in kwds:
- if kwds["pinAnnotations"] is not None and "ValueRange" in kwds["pinAnnotations"]:
- return FloatInputWidget(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
- return FloatInputWidgetSimple(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
- if dataType == 'IntPin':
+ if (
+ kwds["pinAnnotations"] is not None
+ and "ValueRange" in kwds["pinAnnotations"]
+ ):
+ return FloatInputWidget(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
+ return FloatInputWidgetSimple(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
+ if dataType == "IntPin":
if kwds is not None and "pinAnnotations" in kwds:
- if kwds["pinAnnotations"] is not None and "ValueRange" in kwds["pinAnnotations"]:
- return IntInputWidget(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
- return IntInputWidgetSimple(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
- if dataType == 'StringPin':
+ if (
+ kwds["pinAnnotations"] is not None
+ and "ValueRange" in kwds["pinAnnotations"]
+ ):
+ return IntInputWidget(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
+ return IntInputWidgetSimple(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
+ if dataType == "StringPin":
if widgetVariant == DEFAULT_WIDGET_VARIANT:
- return StringInputWidget(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
+ return StringInputWidget(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
elif widgetVariant == "PathWidget":
- return PathInputWidget(mode="all", dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
+ return PathInputWidget(
+ mode="all",
+ dataSetCallback=dataSetter,
+ defaultValue=defaultValue,
+ **kwds,
+ )
elif widgetVariant == "FilePathWidget":
- return PathInputWidget(mode="file", dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
+ return PathInputWidget(
+ mode="file",
+ dataSetCallback=dataSetter,
+ defaultValue=defaultValue,
+ **kwds,
+ )
elif widgetVariant == "FolderPathWidget":
- return PathInputWidget(mode="directory", dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
+ return PathInputWidget(
+ mode="directory",
+ dataSetCallback=dataSetter,
+ defaultValue=defaultValue,
+ **kwds,
+ )
elif widgetVariant == "EnumWidget":
- return EnumInputWidget(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
- elif widgetVariant == "ObjectPathWIdget":
- return ObjectPathWIdget(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
- if dataType == 'BoolPin':
- return BoolInputWidget(dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds)
- if dataType == 'ExecPin':
+ return EnumInputWidget(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
+ elif widgetVariant == "ObjectPathWidget":
+ return ObjectPathWidget(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
+ if dataType == "BoolPin":
+ return BoolInputWidget(
+ dataSetCallback=dataSetter, defaultValue=defaultValue, **kwds
+ )
+ if dataType == "ExecPin":
return ExecInputWidget(dataSetCallback=dataSetter, defaultValue=None, **kwds)
- if dataType == 'AnyPin':
+ if dataType == "AnyPin":
return NoneInputWidget(dataSetCallback=dataSetter, defaultValue=None, **kwds)
diff --git a/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py b/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py
index 2efc94d5b..f3bd600dc 100644
--- a/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py
+++ b/PyFlow/Packages/PyFlowBase/Factories/UINodeFactory.py
@@ -13,7 +13,10 @@
## limitations under the License.
-from PyFlow.Packages.PyFlowBase.Nodes.switchOnString import switchOnString
+from PyFlow.Packages.PyFlowBase.Nodes.storeArgs import storeArgs
+from PyFlow.Packages.PyFlowBase.Nodes.combineArgs import combineArgs
+from PyFlow.Packages.PyFlowBase.Nodes.subProcess import subProcess
+from PyFlow.Packages.PyFlowBase.Nodes.switch import switch
from PyFlow.Packages.PyFlowBase.Nodes.getVar import getVar
from PyFlow.Packages.PyFlowBase.Nodes.setVar import setVar
from PyFlow.Packages.PyFlowBase.Nodes.sequence import sequence
@@ -22,10 +25,7 @@
from PyFlow.Packages.PyFlowBase.Nodes.stickyNote import stickyNote
from PyFlow.Packages.PyFlowBase.Nodes.reroute import reroute
from PyFlow.Packages.PyFlowBase.Nodes.rerouteExecs import rerouteExecs
-from PyFlow.Packages.PyFlowBase.Nodes.graphNodes import (
- graphInputs,
- graphOutputs
-)
+from PyFlow.Packages.PyFlowBase.Nodes.graphNodes import graphInputs, graphOutputs
from PyFlow.Packages.PyFlowBase.Nodes.floatRamp import floatRamp
from PyFlow.Packages.PyFlowBase.Nodes.colorRamp import colorRamp
@@ -41,7 +41,10 @@
from PyFlow.Packages.PyFlowBase.Nodes.imageDisplay import imageDisplay
from PyFlow.Packages.PyFlowBase.UI.UIImageDisplayNode import UIImageDisplayNode
-from PyFlow.Packages.PyFlowBase.UI.UISwitchOnStringNode import UISwitchOnString
+from PyFlow.Packages.PyFlowBase.UI.UIStoreArgsNode import UIStoreArgs
+from PyFlow.Packages.PyFlowBase.UI.UICombineArgsNode import UICombineArgs
+from PyFlow.Packages.PyFlowBase.UI.UISubProcessNode import UISubProcess
+from PyFlow.Packages.PyFlowBase.UI.UISwitchNode import UISwitch
from PyFlow.Packages.PyFlowBase.UI.UIGetVarNode import UIGetVarNode
from PyFlow.Packages.PyFlowBase.UI.UISetVarNode import UISetVarNode
from PyFlow.Packages.PyFlowBase.UI.UISequenceNode import UISequenceNode
@@ -49,10 +52,7 @@
from PyFlow.Packages.PyFlowBase.UI.UIStickyNote import UIStickyNote
from PyFlow.Packages.PyFlowBase.UI.UIRerouteNodeSmall import UIRerouteNodeSmall
from PyFlow.Packages.PyFlowBase.UI.UIPythonNode import UIPythonNode
-from PyFlow.Packages.PyFlowBase.UI.UIGraphNodes import (
- UIGraphInputs,
- UIGraphOutputs
-)
+from PyFlow.Packages.PyFlowBase.UI.UIGraphNodes import UIGraphInputs, UIGraphOutputs
from PyFlow.Packages.PyFlowBase.UI.UIFloatRamp import UIFloatRamp
from PyFlow.Packages.PyFlowBase.UI.UIColorRamp import UIColorRamp
@@ -71,8 +71,14 @@ def createUINode(raw_instance):
return UIGetVarNode(raw_instance)
if isinstance(raw_instance, setVar):
return UISetVarNode(raw_instance)
- if isinstance(raw_instance, switchOnString):
- return UISwitchOnString(raw_instance)
+ if isinstance(raw_instance, subProcess):
+ return UISubProcess(raw_instance)
+ if isinstance(raw_instance, storeArgs):
+ return UIStoreArgs(raw_instance)
+ if isinstance(raw_instance, combineArgs):
+ return UICombineArgs(raw_instance)
+ if isinstance(raw_instance, switch):
+ return UISwitch(raw_instance)
if isinstance(raw_instance, sequence):
return UISequenceNode(raw_instance)
if isinstance(raw_instance, commentNode):
@@ -103,8 +109,8 @@ def createUINode(raw_instance):
return UIColorRamp(raw_instance)
if isinstance(raw_instance, imageDisplay):
return UIImageDisplayNode(raw_instance)
- if isinstance(raw_instance,forLoopBegin):
+ if isinstance(raw_instance, forLoopBegin):
return UIForLoopBeginNode(raw_instance)
- if isinstance(raw_instance,whileLoopBegin):
+ if isinstance(raw_instance, whileLoopBegin):
return UIWhileLoopBeginNode(raw_instance)
return UINodeBase(raw_instance)
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/ArrayLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/ArrayLib.py
index 6022cb16f..f12169afd 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/ArrayLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/ArrayLib.py
@@ -13,42 +13,101 @@
## limitations under the License.
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
class ArrayLib(FunctionLibraryBase):
- '''doc string for ArrayLib'''
+ """doc string for ArrayLib"""
+
def __init__(self, packageName):
super(ArrayLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1'}), meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: []})
- def extendArray(lhs=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- rhs=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny})):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", [], {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: []},
+ )
+ def extendArray(
+ lhs=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ rhs=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ ):
"""Extend the list by appending all the items from the iterable."""
lhs.extend(rhs)
return lhs
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: []})
- def insertToArray(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- elem=('AnyPin', None, {PinSpecifires.CONSTRAINT: '1'}),
- index=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: []},
+ )
+ def insertToArray(
+ ls=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ elem=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ index=("IntPin", 0),
+ ):
"""Insert an item at a given position. The first argument is the index of the element before which to insert."""
ls.insert(index, elem)
return ls
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: []})
- def removeFromArray(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- elem=('AnyPin', None, {PinSpecifires.CONSTRAINT: '1'}),
- removed=(REF, ('BoolPin', False))):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: []},
+ )
+ def removeFromArray(
+ ls=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ elem=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ removed=(REF, ("BoolPin", False)),
+ ):
"""Remove the first item from the list whose value is equal to x."""
if elem not in ls:
removed(False)
@@ -58,11 +117,35 @@ def removeFromArray(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifi
return ls
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", None, {PinSpecifires.CONSTRAINT: '1'}), meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: []})
- def popFromArray(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- index=('IntPin', -1),
- popped=(REF, ('BoolPin', False)),
- outLs=(REF, ('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}))):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: []},
+ )
+ def popFromArray(
+ ls=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ index=("IntPin", -1),
+ popped=(REF, ("BoolPin", False)),
+ outLs=(
+ REF,
+ (
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ ),
+ ):
"""Remove the item at the given position in the array, and return it. If no index is specified, ``a.pop()`` removes and returns the last item in the list."""
poppedElem = None
try:
@@ -75,17 +158,62 @@ def popFromArray(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires
return poppedElem if poppedElem is not None else 0
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: []})
- def clearArray(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny})):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: []},
+ )
+ def clearArray(
+ ls=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ )
+ ):
"""Remove all items from the list."""
return clearList(ls)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: []})
+ def arraySize(ls=('AnyPin', [], {PinSpecifiers.CONSTRAINT: '1', PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
+ size=(REF, ("BoolPin", 0)),
+ is_empty=(REF, ("BoolPin", False)),
+ not_empty=(REF, ("BoolPin", False))):
+ l = len(ls)
+ size(l)
+ is_empty(True if l > 0 else False)
+ not_empty(not is_empty)
+ return clearList(ls)
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: ['in']})
- def arrayElementIndex(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- element=("AnyPin", None, {PinSpecifires.CONSTRAINT: '1'}),
- result=(REF, ("BoolPin", False))):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: ["in"]},
+ )
+ def arrayElementIndex(
+ ls=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ element=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Returns index of array element if it present. If element is not in array -1 will be returned."""
if element in ls:
result(True)
@@ -95,10 +223,30 @@ def arrayElementIndex(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpeci
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: ['in']})
- def arrayElementCount(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- element=("AnyPin", None, {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.AllowAny}),
- result=(REF, ("BoolPin", False))):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: ["in"]},
+ )
+ def arrayElementCount(
+ ls=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ element=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.AllowAny,
+ },
+ ),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Returns len of passed array."""
if element in ls:
result(True)
@@ -108,18 +256,50 @@ def arrayElementCount(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpeci
return 0
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', None, {PinSpecifires.CONSTRAINT: '1'}), meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: []})
- def arraySum(Value=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]})):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: []},
+ )
+ def arraySum(
+ Value=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ )
+ ):
"""Python **sum()** function."""
return sum(Value)
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- meta={NodeMeta.CATEGORY: 'Array', NodeMeta.KEYWORDS: ['in']})
- def arraySlice(ls=('AnyPin', [], {PinSpecifires.CONSTRAINT: '1', PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- start=("IntPin", 0),
- end=("IntPin", 1),
- result=(REF, ("BoolPin", False))):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "Array", NodeMeta.KEYWORDS: ["in"]},
+ )
+ def arraySlice(
+ ls=(
+ "AnyPin",
+ [],
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ start=("IntPin", 0),
+ end=("IntPin", 1),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Array slice."""
try:
result(True)
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/BoolLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/BoolLib.py
index 35050cba2..6787dfc40 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/BoolLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/BoolLib.py
@@ -13,50 +13,66 @@
## limitations under the License.
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
class BoolLib(FunctionLibraryBase):
- '''doc string for BoolLib'''
+ """doc string for BoolLib"""
+
def __init__(self, packageName):
super(BoolLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Math|Bool', NodeMeta.KEYWORDS: []})
- def boolAnd(a=('BoolPin', False), b=('BoolPin', False)):
- '''Returns the logical `AND` of two values `(A AND B)`.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Bool", NodeMeta.KEYWORDS: []},
+ )
+ def boolAnd(a=("BoolPin", False), b=("BoolPin", False)):
+ """Returns the logical `AND` of two values `(A AND B)`."""
return a and b
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Math|Bool', NodeMeta.KEYWORDS: []})
- def boolNot(a=('BoolPin', False)):
- '''Returns the logical complement of the Boolean value `(NOT A)`.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Bool", NodeMeta.KEYWORDS: []},
+ )
+ def boolNot(a=("BoolPin", False)):
+ """Returns the logical complement of the Boolean value `(NOT A)`."""
return not a
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Math|Bool', NodeMeta.KEYWORDS: []})
- def boolNand(a=('BoolPin', False), b=('BoolPin', False)):
- '''Returns the logical `NAND` of two values `(A AND B)`.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Bool", NodeMeta.KEYWORDS: []},
+ )
+ def boolNand(a=("BoolPin", False), b=("BoolPin", False)):
+ """Returns the logical `NAND` of two values `(A AND B)`."""
return not (a and b)
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Math|Bool', NodeMeta.KEYWORDS: []})
- def boolNor(a=('BoolPin', False), b=('BoolPin', False)):
- '''Returns the logical `Not OR` of two values `(A NOR B)`.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Bool", NodeMeta.KEYWORDS: []},
+ )
+ def boolNor(a=("BoolPin", False), b=("BoolPin", False)):
+ """Returns the logical `Not OR` of two values `(A NOR B)`."""
return not (a or b)
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Math|Bool', NodeMeta.KEYWORDS: []})
- def boolOr(a=('BoolPin', False), b=('BoolPin', False)):
- '''Returns the logical `OR` of two values `(A OR B)`.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Bool", NodeMeta.KEYWORDS: []},
+ )
+ def boolOr(a=("BoolPin", False), b=("BoolPin", False)):
+ """Returns the logical `OR` of two values `(A OR B)`."""
return a or b
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Math|Bool', NodeMeta.KEYWORDS: []})
- def boolXor(a=('BoolPin', False), b=('BoolPin', False)):
- '''Returns the logical `eXclusive OR` of two values `(A XOR B)`.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Bool", NodeMeta.KEYWORDS: []},
+ )
+ def boolXor(a=("BoolPin", False), b=("BoolPin", False)):
+ """Returns the logical `eXclusive OR` of two values `(A XOR B)`."""
return a ^ b
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/DefaultLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/DefaultLib.py
index b0301f35e..601079d69 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/DefaultLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/DefaultLib.py
@@ -16,17 +16,17 @@
import os
import platform
from copy import copy, deepcopy
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow import getHashableDataTypes
from PyFlow.Core.Common import *
from PyFlow.Core.PathsRegistry import PathsRegistry
-from nine import IS_PYTHON2
-PIN_ALLOWS_ANYTHING = {PinSpecifires.ENABLED_OPTIONS: PinOptions.AllowAny | PinOptions.ArraySupported | PinOptions.DictSupported}
+PIN_ALLOWS_ANYTHING = {
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.AllowAny
+ | PinOptions.ArraySupported
+ | PinOptions.DictSupported
+}
class DefaultLib(FunctionLibraryBase):
@@ -37,106 +37,243 @@ def __init__(self, packageName):
super(DefaultLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', None, {PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported, PinSpecifires.CONSTRAINT: "1", PinSpecifires.STRUCT_CONSTRAINT: "1"}),
- meta={NodeMeta.CATEGORY: 'Utils', NodeMeta.KEYWORDS: ['id']})
- def copyObject(obj=('AnyPin', None, {PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported, PinSpecifires.CONSTRAINT: "1", PinSpecifires.STRUCT_CONSTRAINT: "1"}), deepCopy=("BoolPin", False)):
- '''Shallow or deep copy of an object.'''
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported,
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.STRUCT_CONSTRAINT: "1",
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "Utils", NodeMeta.KEYWORDS: ["id"]},
+ )
+ def copyObject(
+ obj=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported,
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.STRUCT_CONSTRAINT: "1",
+ },
+ ),
+ deepCopy=("BoolPin", False),
+ ):
+ """Shallow or deep copy of an object."""
copyFunction = deepcopy if deepCopy else copy
return copyFunction(obj)
@staticmethod
- @IMPLEMENT_NODE(returns=None, nodeType=NodeTypes.Callable, meta={NodeMeta.CATEGORY: 'Common', NodeMeta.KEYWORDS: []})
+ @IMPLEMENT_NODE(
+ returns=None,
+ nodeType=NodeTypes.Callable,
+ meta={NodeMeta.CATEGORY: "Common", NodeMeta.KEYWORDS: []},
+ )
def clearConsole():
- '''Cross platform clears console.'''
+ """Cross-platform clears console."""
system = platform.system()
if system != "":
system = system.lower()
if system in ("windows", "win32"):
- os.system('cls')
+ os.system("cls")
if system in ("linux", "darwin", "linux2"):
- os.system('clear')
+ os.system("clear")
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'GenericTypes', NodeMeta.KEYWORDS: []})
- def makeInt(i=('IntPin', 0)):
- '''Make integer.'''
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "GenericTypes", NodeMeta.KEYWORDS: []},
+ )
+ def makeInt(i=("IntPin", 0)):
+ """Make integer."""
return i
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'GenericTypes', NodeMeta.KEYWORDS: []})
- def makeFloat(f=('FloatPin', 0.0)):
- '''Make floating point number.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "GenericTypes", NodeMeta.KEYWORDS: []},
+ )
+ def makeFloat(f=("FloatPin", 0.0)):
+ """Make floating point number."""
return f
@staticmethod
- @IMPLEMENT_NODE(returns=('StringPin', ''), meta={NodeMeta.CATEGORY: 'GenericTypes', NodeMeta.KEYWORDS: []})
- def makeString(s=('StringPin', '')):
- '''Make string.'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={NodeMeta.CATEGORY: "GenericTypes", NodeMeta.KEYWORDS: []},
+ )
+ def makeString(s=("StringPin", "")):
+ """Make string."""
return s
@staticmethod
- @IMPLEMENT_NODE(returns=None, nodeType=NodeTypes.Callable, meta={NodeMeta.CATEGORY: 'Common', NodeMeta.KEYWORDS: []})
- def setGlobalVar(name=('StringPin', 'var1'), value=('AnyPin', None, PIN_ALLOWS_ANYTHING.copy())):
- '''Sets value to globals() dict'''
+ @IMPLEMENT_NODE(
+ returns=None,
+ nodeType=NodeTypes.Callable,
+ meta={NodeMeta.CATEGORY: "Common", NodeMeta.KEYWORDS: []},
+ )
+ def setGlobalVar(
+ name=("StringPin", "var1"), value=("AnyPin", None, PIN_ALLOWS_ANYTHING.copy())
+ ):
+ """Sets value to globals() dict"""
globals()[name] = value
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', None, PIN_ALLOWS_ANYTHING.copy()), meta={NodeMeta.CATEGORY: 'Common', NodeMeta.KEYWORDS: []})
- def getAttribute(obj=('AnyPin', None, PIN_ALLOWS_ANYTHING.copy()), name=('StringPin', 'attrName')):
- '''Returns attribute from object using "getattr(name)"'''
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, PIN_ALLOWS_ANYTHING.copy()),
+ meta={NodeMeta.CATEGORY: "Common", NodeMeta.KEYWORDS: []},
+ )
+ def getAttribute(
+ obj=("AnyPin", None, PIN_ALLOWS_ANYTHING.copy()), name=("StringPin", "attrName")
+ ):
+ """Returns attribute from object using "getattr(name)\""""
return getattr(obj, name)
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', None, PIN_ALLOWS_ANYTHING.copy()), meta={NodeMeta.CATEGORY: 'Common', NodeMeta.KEYWORDS: [],NodeMeta.CACHE_ENABLED: False})
- def getGlobalVar(name=('StringPin', 'var1')):
- '''Retrieves value from globals()'''
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, PIN_ALLOWS_ANYTHING.copy()),
+ meta={
+ NodeMeta.CATEGORY: "Common",
+ NodeMeta.KEYWORDS: [],
+ NodeMeta.CACHE_ENABLED: False,
+ },
+ )
+ def getGlobalVar(name=("StringPin", "var1")):
+ """Retrieves value from globals()"""
if name in globals():
return globals()[name]
else:
return None
@staticmethod
- @IMPLEMENT_NODE(returns=('StringPin', ''), meta={NodeMeta.CATEGORY: 'GenericTypes', NodeMeta.KEYWORDS: []})
- def makePath(path=('StringPin', '', {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Make path.'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={NodeMeta.CATEGORY: "GenericTypes", NodeMeta.KEYWORDS: []},
+ )
+ def makePath(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Make path."""
return path
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'GenericTypes', NodeMeta.KEYWORDS: []})
- def makeBool(b=('BoolPin', False)):
- '''Make boolean.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "GenericTypes", NodeMeta.KEYWORDS: []},
+ )
+ def makeBool(b=("BoolPin", False)):
+ """Make boolean."""
return b
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0, {PinSpecifires.ENABLED_OPTIONS: PinOptions.AlwaysPushDirty}), meta={NodeMeta.CATEGORY: 'Utils', NodeMeta.KEYWORDS: [], NodeMeta.CACHE_ENABLED: False})
+ @IMPLEMENT_NODE(
+ returns=(
+ "FloatPin",
+ 0.0,
+ {PinSpecifiers.ENABLED_OPTIONS: PinOptions.AlwaysPushDirty},
+ ),
+ meta={
+ NodeMeta.CATEGORY: "Utils",
+ NodeMeta.KEYWORDS: [],
+ NodeMeta.CACHE_ENABLED: False,
+ },
+ )
def clock():
- '''Returns the CPU time or real time since the start of the process or since the first call of process_time().'''
+ """Returns the CPU time or real time since the start of the process or since the first call of process_time()."""
return currentProcessorTime()
@staticmethod
- @IMPLEMENT_NODE(returns=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported}), meta={NodeMeta.CATEGORY: 'DefaultLib', NodeMeta.KEYWORDS: []})
- def select(A=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported}),
- B=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported}),
- PickA=('BoolPin', False),
- aPicked=(REF, ("BoolPin", False))):
- '''
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported,
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "DefaultLib", NodeMeta.KEYWORDS: []},
+ )
+ def select(
+ A=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported,
+ },
+ ),
+ B=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported,
+ },
+ ),
+ PickA=("BoolPin", False),
+ aPicked=(REF, ("BoolPin", False)),
+ ):
+ """
If bPickA is true, A is returned, otherwise B.
- '''
+ """
aPicked(PickA)
return A if PickA else B
@staticmethod
- @IMPLEMENT_NODE(returns=('StringPin', ""), meta={NodeMeta.CATEGORY: 'GenericTypes', NodeMeta.KEYWORDS: []})
- def objectType(obj=("AnyPin", None, {PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported})):
- '''Returns ``type(obj).__name__``'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={NodeMeta.CATEGORY: "GenericTypes", NodeMeta.KEYWORDS: []},
+ )
+ def objectType(
+ obj=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported
+ },
+ )
+ ):
+ """Returns ``type(obj).__name__``"""
t = type(obj).__name__
if t == "DictElement":
- t += ",key:{0},value:{1}".format(type(obj[1]).__name__, type(obj[0]).__name__)
+ t += ",key:{0},value:{1}".format(
+ type(obj[1]).__name__, type(obj[0]).__name__
+ )
return t
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'DefaultLib', NodeMeta.KEYWORDS: ['in']})
- def contains(obj=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported}), element=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "DefaultLib", NodeMeta.KEYWORDS: ["in"]},
+ )
+ def contains(
+ obj=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported,
+ },
+ ),
+ element=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ ):
"""Python's **in** keyword. `element in obj` will be executed"""
try:
return element in obj
@@ -144,8 +281,26 @@ def contains(obj=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.
return False
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0, {PinSpecifires.DESCRIPTION: "Number of elements of iterable"}), meta={NodeMeta.CATEGORY: 'DefaultLib', NodeMeta.KEYWORDS: ['len']})
- def len(obj=('AnyPin', None, {PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported, PinSpecifires.DESCRIPTION: "Iterable object"})):
+ @IMPLEMENT_NODE(
+ returns=(
+ "IntPin",
+ 0,
+ {PinSpecifiers.DESCRIPTION: "Number of elements of iterable"},
+ ),
+ meta={NodeMeta.CATEGORY: "DefaultLib", NodeMeta.KEYWORDS: ["len"]},
+ )
+ def len(
+ obj=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported,
+ PinSpecifiers.DESCRIPTION: "Iterable object",
+ },
+ )
+ ):
"""Python's **len** function."""
try:
return len(obj)
@@ -153,11 +308,35 @@ def len(obj=('AnyPin', None, {PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySup
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- meta={NodeMeta.CATEGORY: 'DefaultLib', NodeMeta.KEYWORDS: ['get']})
- def getItem(obj=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- element=("AnyPin", None, {PinSpecifires.SUPPORTED_DATA_TYPES: getHashableDataTypes()}),
- result=(REF, ("BoolPin", False))):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "DefaultLib", NodeMeta.KEYWORDS: ["get"]},
+ )
+ def getItem(
+ obj=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ element=(
+ "AnyPin",
+ None,
+ {PinSpecifiers.SUPPORTED_DATA_TYPES: getHashableDataTypes()},
+ ),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Python's ``[]`` operator. ``obj[element]`` will be executed."""
try:
result(True)
@@ -167,10 +346,33 @@ def getItem(obj=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.E
return None
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.STRUCT_CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}), meta={NodeMeta.CATEGORY: 'DefaultLib', NodeMeta.KEYWORDS: ['get']})
- def appendTo(obj=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.STRUCT_CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- element=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- result=(REF, ('BoolPin', False))):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.STRUCT_CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "DefaultLib", NodeMeta.KEYWORDS: ["get"]},
+ )
+ def appendTo(
+ obj=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.STRUCT_CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ element=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Calls ``obj.append(element)``. And returns object. If failed - object is unchanged"""
try:
obj.append(element)
@@ -181,10 +383,36 @@ def appendTo(obj=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.
return obj
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'DefaultLib', NodeMeta.KEYWORDS: ['get']})
- def addTo(obj=('AnyPin', None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.STRUCT_CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}),
- element=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- result=(REF, ("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.STRUCT_CONSTRAINT: "1", PinSpecifires.ENABLED_OPTIONS: PinOptions.ArraySupported | PinOptions.AllowAny}))):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "DefaultLib", NodeMeta.KEYWORDS: ["get"]},
+ )
+ def addTo(
+ obj=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.STRUCT_CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ element=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ result=(
+ REF,
+ (
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.STRUCT_CONSTRAINT: "1",
+ PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported
+ | PinOptions.AllowAny,
+ },
+ ),
+ ),
+ ):
"""Calls ``obj.add(element)``. And returns object. If failed - object is unchanged"""
try:
obj.add(element)
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/FloatLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/FloatLib.py
index 87eafc286..c37196c77 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/FloatLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/FloatLib.py
@@ -15,46 +15,71 @@
from math import pi
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
class FloatLib(FunctionLibraryBase):
- '''doc string for FloatLib'''
+ """doc string for FloatLib"""
+
def __init__(self, packageName):
super(FloatLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Math|Float', NodeMeta.KEYWORDS: ['lerp']})
- ## Linear interpolate
- def lerpf(a=('FloatPin', 0.0), b=('FloatPin', 0.0), alpha=('FloatPin', 0.0, {PinSpecifires.VALUE_RANGE: (0.0, 1.0), PinSpecifires.DRAGGER_STEPS: [0.1, 0.01, 0.001]})):
- '''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Math|Float", NodeMeta.KEYWORDS: ["lerp"]},
+ )
+ # Linear interpolate
+ def lerpf(
+ a=("FloatPin", 0.0),
+ b=("FloatPin", 0.0),
+ alpha=(
+ "FloatPin",
+ 0.0,
+ {
+ PinSpecifiers.VALUE_RANGE: (0.0, 1.0),
+ PinSpecifiers.DRAGGER_STEPS: [0.1, 0.01, 0.001],
+ },
+ ),
+ ):
+ """
Linear interpolate
- '''
+ """
return lerp(a, b, clamp(alpha, 0.0, 1.0))
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Math|Float', NodeMeta.KEYWORDS: []})
- def nearlyEqual(a=('FloatPin', 0.0), b=('FloatPin', 0.0), abs_tol=('FloatPin', 0.0)):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Float", NodeMeta.KEYWORDS: []},
+ )
+ def nearlyEqual(
+ a=("FloatPin", 0.0), b=("FloatPin", 0.0), abs_tol=("FloatPin", 0.0)
+ ):
return abs(a - b) < abs_tol
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Math|Float', NodeMeta.KEYWORDS: []})
- def multByPi(a=('FloatPin', 0.0)):
- '''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Math|Float", NodeMeta.KEYWORDS: []},
+ )
+ def multByPi(a=("FloatPin", 0.0)):
+ """
Multiplies the input value by pi.
- '''
+ """
return a * pi
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Math|Float', NodeMeta.KEYWORDS: []})
- def normalizeToRange(Value=('FloatPin', 0.0), RangeMin=('FloatPin', 0.0), RangeMax=('FloatPin', 0.0)):
- '''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Math|Float", NodeMeta.KEYWORDS: []},
+ )
+ def normalizeToRange(
+ Value=("FloatPin", 0.0), RangeMin=("FloatPin", 0.0), RangeMax=("FloatPin", 0.0)
+ ):
+ """
Returns Value normalized to the given range. (e.g. 20 normalized to the range 10->50 would result in 0.25)
- '''
+ """
if RangeMin == RangeMax:
return RangeMin
@@ -63,9 +88,15 @@ def normalizeToRange(Value=('FloatPin', 0.0), RangeMin=('FloatPin', 0.0), RangeM
return (Value - RangeMin) / (RangeMax - RangeMin)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Math|Float', NodeMeta.KEYWORDS: []})
- def roundf(Value=('FloatPin', 0.0), Digits=('IntPin', 0, {PinSpecifires.VALUE_RANGE: (0, 323)})):
- '''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Math|Float", NodeMeta.KEYWORDS: []},
+ )
+ def roundf(
+ Value=("FloatPin", 0.0),
+ Digits=("IntPin", 0, {PinSpecifiers.VALUE_RANGE: (0, 323)}),
+ ):
+ """
Round a number to a given precision in decimal digits.
- '''
+ """
return round(Value, Digits)
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/IOLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/IOLib.py
new file mode 100644
index 000000000..7c7c8e834
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/IOLib.py
@@ -0,0 +1,175 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+import codecs
+import shutil
+import os
+import requests
+import pathlib
+
+from PyFlow.Core import(
+ FunctionLibraryBase,
+ IMPLEMENT_NODE
+)
+from PyFlow.Core.Common import *
+
+
+class IOLib(FunctionLibraryBase):
+ """doc string for IOLib"""
+
+ def __init__(self, packageName):
+ super(IOLib, self).__init__(packageName)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Callable)
+ def readAllText( #inExec=("ExecPin", None), outExec=(REF, ("ExecPin", None)),
+ file=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "FilePathWidget"}),
+ encoding=('StringPin', 'utf-8'),
+ text=(REF, ('StringPin', "")),
+ error_msg=(REF, ('StringPin', ""))):
+ err_msg = ""
+ all_text = ""
+ try:
+ with codecs.open(file, encoding=encoding) as f:
+ all_text = f.read()
+ except Exception as e:
+ err_msg = str(e)
+ error_msg(err_msg)
+ text(all_text)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Callable)
+ def readAllLines(file=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "FilePathWidget"}),
+ encoding=('StringPin', 'utf-8'),
+ lines=(REF, ('StringPin', [], {PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported})),
+ error_msg=(REF, ('StringPin', ""))):
+ err_msg = ""
+ try:
+ with codecs.open(file, encoding) as f:
+ all_lines = list(f.readlines())
+ except Exception as e:
+ err_msg = str(e)
+ error_msg(err_msg)
+ lines(all_lines if None != all_lines else [])
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Callable)
+ def copyFile(src=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "FilePathWidget"}),
+ dst=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "FilePathWidget"}),
+ ok=(REF, ('BoolPin', False)),
+ error_msg=(REF, ('StringPin', ""))):
+ ret = True
+ err_msg = ""
+ try:
+ if os.path.exists(src):
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
+ shutil.copy(src, dst)
+ except Exception as e:
+ ret = False
+ err_msg = str(e)
+ error_msg(err_msg)
+ ok(ret)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Callable)
+ def removeFile(file=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "FilePathWidget"}),
+ ok=(REF, ('BoolPin', False)),
+ error_msg=(REF, ('StringPin', ""))):
+ ret = True
+ err_msg = ""
+ try:
+ if os.path.exists(file):
+ os.remove(file)
+ except Exception as e:
+ ret = False
+ err_msg = str(e)
+ error_msg(err_msg)
+ ok(ret)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Callable)
+ def copyDir(src=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ dst=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ ok=(REF, ('BoolPin', False)),
+ error_msg=(REF, ('StringPin', False))):
+ ret = True
+ err_msg = ""
+ try:
+ shutil.copytree(src, dst, dirs_exist_ok=True)
+ except Exception as e:
+ ret = False
+ err_msg = str(e)
+ error_msg(err_msg)
+ ok(ret)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Pure)
+ def pathCombine(p1=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}), p2=("StringPin", "")):
+ ret: pathlib.PurePath = pathlib.PurePath(p1, p2)
+ # ret = os.path.normpath(os.path.join(str(p1), str(p2)))
+ return ret.as_posix()
+
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Callable)
+ def removeDir(dir=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ ignore_error=('BoolPin', False),
+ ok=(REF, ('BoolPin', False)),
+ error_msg=(REF, ('StringPin', ""))):
+ ret = True
+ err_msg = ""
+ try:
+ if os.path.isdir(dir):
+ shutil.rmtree(dir, ignore_error)
+ except Exception as e:
+ ret = False
+ err_msg = str(e)
+ error_msg(err_msg)
+ ok(ret)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'IO', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Callable)
+ def httpUploadFile(url=('StringPin', ""),
+ store_path=('StringPin', ""),
+ file=('StringPin', "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "FilePathWidget"}),
+ ok=(REF, ('BoolPin', False)),
+ error_msg=(REF, ('StringPin', ""))):
+ ret = True
+ err_msg = ""
+ try:
+ if not os.path.isfile(file):
+ ret = False
+ err_msg = "file not exist"
+ else:
+ rsp: requests.Response = requests.post(url, {"file_path": store_path}, files={"file": open(file, 'rb')})
+ if 200 != rsp.status_code:
+ ret = False
+ err_msg = rsp.content
+ except Exception as e:
+ ret = False
+ err_msg = str(e)
+ error_msg(err_msg)
+ ok(ret)
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/IntLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/IntLib.py
index cb9ae603c..5bab87572 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/IntLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/IntLib.py
@@ -13,84 +13,115 @@
## limitations under the License.
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
class IntLib(FunctionLibraryBase):
"""doc string for IntLib"""
+
def __init__(self, packageName):
super(IntLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def bitwiseAnd(a=('IntPin', 0), b=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def bitwiseAnd(a=("IntPin", 0), b=("IntPin", 0)):
"""Bitwise AND ``(A & B)``"""
return a & b
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def bitwiseNot(a=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def bitwiseNot(a=("IntPin", 0)):
"""Bitwise NOT ``(~A)``"""
return ~a
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def bitwiseOr(a=('IntPin', 0), b=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def bitwiseOr(a=("IntPin", 0), b=("IntPin", 0)):
"""Bitwise OR ``(A | B)``"""
return a | b
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def bitwiseXor(a=('IntPin', 0), b=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def bitwiseXor(a=("IntPin", 0), b=("IntPin", 0)):
"""Bitwise XOR ``(A ^ B)``"""
return a ^ b
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def binaryLeftShift(a=('IntPin', 0), b=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def binaryLeftShift(a=("IntPin", 0), b=("IntPin", 0)):
"""Binary left shift ``a << b``"""
return a << b
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def binaryRightShift(a=('IntPin', 0), b=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def binaryRightShift(a=("IntPin", 0), b=("IntPin", 0)):
"""Binary right shift ``a << b``"""
return a >> b
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def testBit(intType=('IntPin', 0), offset=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def testBit(intType=("IntPin", 0), offset=("IntPin", 0)):
"""Returns a nonzero result, 2**offset, if the bit at 'offset' is one"""
mask = 1 << offset
- return(intType & mask)
+ return intType & mask
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def setBit(intType=('IntPin', 0), offset=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def setBit(intType=("IntPin", 0), offset=("IntPin", 0)):
"""Returns an integer with the bit at 'offset' set to 1."""
mask = 1 << offset
- return(intType | mask)
+ return intType | mask
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def clearBit(intType=('IntPin', 0), offset=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def clearBit(intType=("IntPin", 0), offset=("IntPin", 0)):
"""Returns an integer with the bit at 'offset' cleared."""
mask = ~(1 << offset)
- return(intType & mask)
+ return intType & mask
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Bits manipulation', NodeMeta.KEYWORDS: []})
- def toggleBit(intType=('IntPin', 0), offset=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Bits manipulation", NodeMeta.KEYWORDS: []},
+ )
+ def toggleBit(intType=("IntPin", 0), offset=("IntPin", 0)):
"""Returns an integer with the bit at 'offset' inverted, 0 -> 1 and 1 -> 0."""
mask = 1 << offset
- return(intType ^ mask)
+ return intType ^ mask
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Math|Int', NodeMeta.KEYWORDS: []})
- def sign(x=('IntPin', 0)):
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={NodeMeta.CATEGORY: "Math|Int", NodeMeta.KEYWORDS: []},
+ )
+ def sign(x=("IntPin", 0)):
"""Sign (integer, returns -1 if A < 0, 0 if A is zero, and +1 if A > 0)"""
return int(sign(x))
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathAbstractLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathAbstractLib.py
index 0194aae61..9109bdce4 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathAbstractLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathAbstractLib.py
@@ -13,136 +13,297 @@
## limitations under the License.
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
class MathAbstractLib(FunctionLibraryBase):
"""doc string for MathAbstractLib"""
+
def __init__(self, packageName):
super(MathAbstractLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ["=", "operator"]})
- ## Is a equal b
- def isEqual(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: ["=", "operator"]},
+ )
+ # Is a equal b
+ def isEqual(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ ):
"""Is a equal b."""
return a == b
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ["!=", "operator"]})
+ ## Is a equal b
+ def notEqual(a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"})):
+ """Is a equal b."""
+ return a != b
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: [">", "operator"]})
- def isGreater(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- result=(REF, ("BoolPin", False))):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: [">", "operator"]},
+ )
+ def isGreater(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Operator **>**."""
return a > b
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: [">", "operator"]})
- def isGreaterOrEqual(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- result=(REF, ("BoolPin", False))):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: [">", "operator"]},
+ )
+ def isGreaterOrEqual(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Operator **>=**."""
return a >= b
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ["<", "operator"]})
- def isLess(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}),
- result=(REF, ("BoolPin", False))):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: ["<", "operator"]},
+ )
+ def isLess(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ result=(REF, ("BoolPin", False)),
+ ):
"""Operator **<**."""
return a < b
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ["<", "operator"]})
- def isLessOrEqual(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: ["<", "operator"]},
+ )
+ def isLessOrEqual(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ ):
"""Operator **<=**."""
return a <= b
@staticmethod
- @IMPLEMENT_NODE(returns=(("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ['+', 'append', "sum", "operator"]})
- def add(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={
+ NodeMeta.CATEGORY: "Math|Basic",
+ NodeMeta.KEYWORDS: ["+", "append", "sum", "operator"],
+ },
+ )
+ def add(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ ):
"""Operator **+**."""
return a + b
@staticmethod
- @IMPLEMENT_NODE(returns=(("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ['-', "operator", "minus"]})
- def subtract(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={
+ NodeMeta.CATEGORY: "Math|Basic",
+ NodeMeta.KEYWORDS: ["-", "operator", "minus"],
+ },
+ )
+ def subtract(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ ):
"""Operator **-**."""
return a - b
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ['/', "divide", "operator"]})
- def divide(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={
+ NodeMeta.CATEGORY: "Math|Basic",
+ NodeMeta.KEYWORDS: ["/", "divide", "operator"],
+ },
+ )
+ def divide(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ ):
"""Operator **/**."""
return a / b
@staticmethod
- @IMPLEMENT_NODE(returns=(("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ['*', "multiply", "operator"]})
- def multiply(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={
+ NodeMeta.CATEGORY: "Math|Basic",
+ NodeMeta.KEYWORDS: ["*", "multiply", "operator"],
+ },
+ )
+ def multiply(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ b=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ ):
"""Operator *****."""
return a * b
@staticmethod
- @IMPLEMENT_NODE(returns=(("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"})), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ['*', "multiply", "operator"]})
- def multiply_by_float(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1"}), b=("FloatPin", 1.0)):
+ @IMPLEMENT_NODE(
+ returns=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}),
+ meta={
+ NodeMeta.CATEGORY: "Math|Basic",
+ NodeMeta.KEYWORDS: ["*", "multiply", "operator"],
+ },
+ )
+ def multiply_by_float(
+ a=("AnyPin", None, {PinSpecifiers.CONSTRAINT: "1"}), b=("FloatPin", 1.0)
+ ):
"""Operator *****."""
return a * b
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ["in", "range"]})
- def inRange(Value=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}),
- RangeMin=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}),
- RangeMax=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}),
- InclusiveMin=("BoolPin", False),
- InclusiveMax=("BoolPin", False)):
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: ["in", "range"]},
+ )
+ def inRange(
+ Value=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ RangeMin=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ RangeMax=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ InclusiveMin=("BoolPin", False),
+ InclusiveMax=("BoolPin", False),
+ ):
"""Returns true if value is between Min and Max (V >= Min && V <= Max) If InclusiveMin is true, value needs to be equal or larger than Min,\
else it needs to be larger If InclusiveMax is true, value needs to be smaller or equal than Max, else it needs to be smaller
"""
- return ((Value >= RangeMin) if InclusiveMin else (Value > RangeMin)) and ((Value <= RangeMax) if InclusiveMax else (Value < RangeMax))
+ return ((Value >= RangeMin) if InclusiveMin else (Value > RangeMin)) and (
+ (Value <= RangeMax) if InclusiveMax else (Value < RangeMax)
+ )
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: []})
- def mapRangeClamped(Value=("FloatPin", 0.0),
- InRangeA=("FloatPin", 0.0),
- InRangeB=("FloatPin", 0.0),
- OutRangeA=("FloatPin", 0.0),
- OutRangeB=("FloatPin", 0.0)):
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: []},
+ )
+ def mapRangeClamped(
+ Value=("FloatPin", 0.0),
+ InRangeA=("FloatPin", 0.0),
+ InRangeB=("FloatPin", 0.0),
+ OutRangeA=("FloatPin", 0.0),
+ OutRangeB=("FloatPin", 0.0),
+ ):
"""Returns Value mapped from one range into another where the Value is clamped to the Input Range.\
(e.g. 0.5 normalized from the range 0->1 to 0->50 would result in 25)"""
return mapRangeClamped(Value, InRangeA, InRangeB, OutRangeA, OutRangeB)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: []})
- def mapRangeUnclamped(Value=("FloatPin", 0.0),
- InRangeA=("FloatPin", 0.0),
- InRangeB=("FloatPin", 0.0),
- OutRangeA=("FloatPin", 0.0),
- OutRangeB=("FloatPin", 0.0)):
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: []},
+ )
+ def mapRangeUnclamped(
+ Value=("FloatPin", 0.0),
+ InRangeA=("FloatPin", 0.0),
+ InRangeB=("FloatPin", 0.0),
+ OutRangeA=("FloatPin", 0.0),
+ OutRangeB=("FloatPin", 0.0),
+ ):
"""Returns Value mapped from one range into another where the Value is clamped to the Input Range.\
(e.g. 0.5 normalized from the range 0->1 to 0->50 would result in 25)"""
return mapRangeUnclamped(Value, InRangeA, InRangeB, OutRangeA, OutRangeB)
@staticmethod
- @IMPLEMENT_NODE(returns=("FloatPin", 0.0), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ['clamp']})
- def clamp(i=("FloatPin", 0.0),
- imin=("FloatPin", 0.0),
- imax=("FloatPin", 0.0)):
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: ["clamp"]},
+ )
+ def clamp(i=("FloatPin", 0.0), imin=("FloatPin", 0.0), imax=("FloatPin", 0.0)):
"""Clamp."""
return clamp(i, imin, imax)
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: ["operator"]})
- def modulo(a=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}),
- b=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]})):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: ["operator"]},
+ )
+ def modulo(
+ a=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ b=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ ):
"""Modulo (A % B)."""
return a % b
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}), meta={NodeMeta.CATEGORY: 'Math|Basic', NodeMeta.KEYWORDS: []})
- def abs(inp=("AnyPin", None, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]})):
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ meta={NodeMeta.CATEGORY: "Math|Basic", NodeMeta.KEYWORDS: []},
+ )
+ def abs(
+ inp=(
+ "AnyPin",
+ None,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ )
+ ):
"""Return the absolute value of a number."""
return abs(inp)
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathLib.py
index 10da679f3..f7b88dea0 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/MathLib.py
@@ -13,13 +13,7 @@
## limitations under the License.
-import math
-import random
-
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
@@ -35,35 +29,115 @@ def __init__(self, packageName):
# builtin python math
# ###################
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", 0, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def copysign(x=("AnyPin", 0, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}), y=("AnyPin", 0, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]})):
- '''Return `x` with the sign of `y`. On a platform that supports signed zeros, `copysign(1.0, -0.0)` returns `-1.0`.'''
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ 0,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def copysign(
+ x=(
+ "AnyPin",
+ 0,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ y=(
+ "AnyPin",
+ 0,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ ):
+ """Return `x` with the sign of `y`. On a platform that supports signed zeros, `copysign(1.0, -0.0)` returns `-1.0`."""
return math.copysign(x, y)
@staticmethod
- @IMPLEMENT_NODE(returns=("AnyPin", 0, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def fmod(x=("AnyPin", 0, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]}), y=("AnyPin", 0, {PinSpecifires.CONSTRAINT: "1", PinSpecifires.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"]})):
- '''Return `fmod(x, y)`, as defined by the platform C library.'''
+ @IMPLEMENT_NODE(
+ returns=(
+ "AnyPin",
+ 0,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def fmod(
+ x=(
+ "AnyPin",
+ 0,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ y=(
+ "AnyPin",
+ 0,
+ {
+ PinSpecifiers.CONSTRAINT: "1",
+ PinSpecifiers.SUPPORTED_DATA_TYPES: ["FloatPin", "IntPin"],
+ },
+ ),
+ ):
+ """Return `fmod(x, y)`, as defined by the platform C library."""
return math.fmod(x, y)
@staticmethod
- @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def modf(x=("FloatPin", 0.0), f=(REF, ('FloatPin', 0.0)), i=(REF, ('FloatPin', 0.0))):
- '''Return the fractional and integer parts of `x`. Both results carry the sign of `x` and are floats.'''
+ @IMPLEMENT_NODE(
+ returns=None,
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def modf(
+ x=("FloatPin", 0.0), f=(REF, ("FloatPin", 0.0)), i=(REF, ("FloatPin", 0.0))
+ ):
+ """Return the fractional and integer parts of `x`. Both results carry the sign of `x` and are floats."""
t = math.modf(x)
f(t[0])
i(t[1])
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def ceil(x=('FloatPin', 0.0)):
- '''Return the ceiling of `x` as a float, the smallest integer value greater than or equal to `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def ceil(x=("FloatPin", 0.0)):
+ """Return the ceiling of `x` as a float, the smallest integer value greater than or equal to `x`."""
return math.ceil(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def factorial(x=('IntPin', 0), result=(REF, ('BoolPin', False))):
- '''Return `x` factorial. Raises ValueError if `x` is not integral or is negative.'''
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def factorial(x=("IntPin", 0), result=(REF, ("BoolPin", False))):
+ """Return `x` factorial. Raises ValueError if `x` is not integral or is negative."""
try:
f = math.factorial(x)
result(True)
@@ -73,23 +147,41 @@ def factorial(x=('IntPin', 0), result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def floor(x=('FloatPin', 0.0)):
- '''Return the floor of x as an Integral.'''
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def floor(x=("FloatPin", 0.0)):
+ """Return the floor of x as an Integral."""
return math.floor(x)
@staticmethod
- @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def frexp(x=('FloatPin', 0.0), m=(REF, ('FloatPin', 0.0)), e=(REF, ('IntPin', 0))):
- '''Return the mantissa and exponent of `x` as the pair (m, e). m is `x` float and e is an integer such that `x == m * 2**e` exactly.'''
+ @IMPLEMENT_NODE(
+ returns=None,
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def frexp(x=("FloatPin", 0.0), m=(REF, ("FloatPin", 0.0)), e=(REF, ("IntPin", 0))):
+ """Return the mantissa and exponent of `x` as the pair (m, e). m is `x` float and e is an integer such that `x == m * 2**e` exactly."""
t = math.frexp(x)
m(t[0])
e(t[1])
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def fsum(arr=('FloatPin', []), result=(REF, ('BoolPin', False))):
- '''Return an accurate floating point sum of values in the iterable. Avoids loss of precision by tracking multiple intermediate partial sums.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def fsum(arr=("FloatPin", []), result=(REF, ("BoolPin", False))):
+ """Return an accurate floating point sum of values in the iterable. Avoids loss of precision by tracking multiple intermediate partial sums."""
try:
s = math.fsum([i for i in arr])
result(True)
@@ -99,45 +191,89 @@ def fsum(arr=('FloatPin', []), result=(REF, ('BoolPin', False))):
return 0.0
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def isinf(x=('FloatPin', 0.0)):
- '''Check if the float `x` is positive or negative infinity.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def isinf(x=("FloatPin", 0.0)):
+ """Check if the float `x` is positive or negative infinity."""
return math.isinf(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('BoolPin', False), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def isnan(x=('FloatPin', 0.0)):
- '''Check if the float `x` is a NaN (not a number).'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def isnan(x=("FloatPin", 0.0)):
+ """Check if the float `x` is a NaN (not a number)."""
return math.isnan(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def ldexp(x=('FloatPin', 0.0), i=('IntPin', 0)):
- '''Return `x * (2**i)`. This is essentially the inverse of function `frexp()`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def ldexp(x=("FloatPin", 0.0), i=("IntPin", 0)):
+ """Return `x * (2**i)`. This is essentially the inverse of function `frexp()`."""
return math.ldexp(x, i)
@staticmethod
- @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'Python|math|Number-theoretic and representation functions', NodeMeta.KEYWORDS: []})
- def trunc(x=('FloatPin', 0.0)):
- '''Return the Real value `x` truncated to an Integral (usually a long integer).'''
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Number-theoretic and representation functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def trunc(x=("FloatPin", 0.0)):
+ """Return the Real value `x` truncated to an Integral (usually a long integer)."""
return math.trunc(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Power and logarithmic functions', NodeMeta.KEYWORDS: []})
- def exp(x=('FloatPin', 0.0)):
- '''Return e**x.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Power and logarithmic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def exp(x=("FloatPin", 0.0)):
+ """Return e**x."""
return math.exp(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Power and logarithmic functions', NodeMeta.KEYWORDS: []})
- def expm1(x=('FloatPin', 0.1)):
- '''Return `e**x - 1`. For small floats `x`, the subtraction in `exp(x) - 1` can result in a significant loss of precision.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Power and logarithmic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def expm1(x=("FloatPin", 0.1)):
+ """Return `e**x - 1`. For small floats `x`, the subtraction in `exp(x) - 1` can result in a significant loss of precision."""
return math.expm1(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Power and logarithmic functions', NodeMeta.KEYWORDS: []})
- def log(x=('FloatPin', 1.0), base=('FloatPin', math.e), result=(REF, ('BoolPin', False))):
- '''Return the logarithm of `x` to the given base, calculated as `log(x)/log(base)`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Power and logarithmic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def log(
+ x=("FloatPin", 1.0), base=("FloatPin", math.e), result=(REF, ("BoolPin", False))
+ ):
+ """Return the logarithm of `x` to the given base, calculated as `log(x)/log(base)`."""
try:
result(True)
return math.log(x, base)
@@ -146,9 +282,15 @@ def log(x=('FloatPin', 1.0), base=('FloatPin', math.e), result=(REF, ('BoolPin',
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Power and logarithmic functions', NodeMeta.KEYWORDS: []})
- def log1p(x=('FloatPin', 1.0), result=(REF, ('BoolPin', False))):
- '''Return the natural logarithm of `1+x` (base e). The result is calculated in a way which is accurate for `x` near zero.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Power and logarithmic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def log1p(x=("FloatPin", 1.0), result=(REF, ("BoolPin", False))):
+ """Return the natural logarithm of `1+x` (base e). The result is calculated in a way which is accurate for `x` near zero."""
try:
result(True)
return math.log1p(x)
@@ -157,9 +299,15 @@ def log1p(x=('FloatPin', 1.0), result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Power and logarithmic functions', NodeMeta.KEYWORDS: []})
- def log10(x=('FloatPin', 1.0), result=(REF, ('BoolPin', False))):
- '''Return the base-10 logarithm of `x`. This is usually more accurate than `log(x, 10)`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Power and logarithmic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def log10(x=("FloatPin", 1.0), result=(REF, ("BoolPin", False))):
+ """Return the base-10 logarithm of `x`. This is usually more accurate than `log(x, 10)`."""
try:
result(True)
return math.log10(x)
@@ -168,9 +316,17 @@ def log10(x=('FloatPin', 1.0), result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Power and logarithmic functions', NodeMeta.KEYWORDS: []})
- def power(x=('FloatPin', 0.0), y=('FloatPin', 0.0), result=(REF, ('BoolPin', False))):
- '''Return `x` raised to the power `y`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Power and logarithmic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def power(
+ x=("FloatPin", 0.0), y=("FloatPin", 0.0), result=(REF, ("BoolPin", False))
+ ):
+ """Return `x` raised to the power `y`."""
try:
result(True)
return math.pow(x, y)
@@ -179,9 +335,15 @@ def power(x=('FloatPin', 0.0), y=('FloatPin', 0.0), result=(REF, ('BoolPin', Fal
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Power and logarithmic functions', NodeMeta.KEYWORDS: []})
- def sqrt(x=('FloatPin', 0.0), result=(REF, ('BoolPin', False))):
- '''Return the square root of `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Power and logarithmic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def sqrt(x=("FloatPin", 0.0), result=(REF, ("BoolPin", False))):
+ """Return the square root of `x`."""
try:
result(True)
return math.sqrt(x)
@@ -190,69 +352,111 @@ def sqrt(x=('FloatPin', 0.0), result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def cos(rad=('FloatPin', 0.0)):
- '''Return the cosine of `x` radians.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def cos(rad=("FloatPin", 0.0)):
+ """Return the cosine of `x` radians."""
return math.cos(rad)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def acos(rad=('FloatPin', 0.0)):
- '''Return the arc cosine of `x`, in radians.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def acos(rad=("FloatPin", 0.0)):
+ """Return the arc cosine of `x`, in radians."""
return math.acos(rad)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def sin(rad=('FloatPin', 0.0)):
- '''Return the sine of `x` radians.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def sin(rad=("FloatPin", 0.0)):
+ """Return the sine of `x` radians."""
return math.sin(rad)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def asin(rad=('FloatPin', 0.0)):
- '''Return the arc sine of `x`, in radians.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def asin(rad=("FloatPin", 0.0)):
+ """Return the arc sine of `x`, in radians."""
return math.asin(rad)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def tan(rad=('FloatPin', 0.0)):
- '''Return the tangent of `x` radians.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def tan(rad=("FloatPin", 0.0)):
+ """Return the tangent of `x` radians."""
return math.tan(rad)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def atan(rad=('FloatPin', 0.0)):
- '''Return the arc tangent of `x`, in radians.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def atan(rad=("FloatPin", 0.0)):
+ """Return the arc tangent of `x`, in radians."""
return math.atan(rad)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def atan2(x=('FloatPin', 0.0), y=('FloatPin', 0.0)):
- '''Return `atan(a / b)`, in radians. The result is between `-pi` and `pi`.\nThe vector in the plane from the origin to point (x, y) makes this angle with the positive X axis. The point of `atan2()` is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle.\nFor example, `atan(1)` and `atan2(1, 1)` are both `pi/4`, but `atan2(-1, -1)` is `-3*pi/4`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def atan2(x=("FloatPin", 0.0), y=("FloatPin", 0.0)):
+ """Return `atan(a / b)`, in radians. The result is between `-pi` and `pi`.\nThe vector in the plane from the origin to point (x, y) makes this angle with the positive X axis. The point of `atan2()` is that the signs of both inputs are known to it, so it can compute the correct quadrant for the angle.\nFor example, `atan(1)` and `atan2(1, 1)` are both `pi/4`, but `atan2(-1, -1)` is `-3*pi/4`."""
return math.atan2(x, y)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Trigonometry', NodeMeta.KEYWORDS: []})
- def hypot(x=('FloatPin', 0.0), y=('FloatPin', 0.0)):
- '''Return the Euclidean norm, `sqrt(x*x + y*y)`. This is the length of the vector from the origin to point (x, y).'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Trigonometry", NodeMeta.KEYWORDS: []},
+ )
+ def hypot(x=("FloatPin", 0.0), y=("FloatPin", 0.0)):
+ """Return the Euclidean norm, `sqrt(x*x + y*y)`. This is the length of the vector from the origin to point (x, y)."""
return math.hypot(x, y)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Angular conversion', NodeMeta.KEYWORDS: []})
- def degtorad(deg=('FloatPin', 0.0)):
- '''Convert angle `x` from degrees to radians.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Angular conversion",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def degtorad(deg=("FloatPin", 0.0)):
+ """Convert angle `x` from degrees to radians."""
return math.radians(deg)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Angular conversion', NodeMeta.KEYWORDS: []})
- def radtodeg(rad=('FloatPin', 0.0)):
- '''Convert angle `x` from radians to degrees.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Angular conversion",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def radtodeg(rad=("FloatPin", 0.0)):
+ """Convert angle `x` from radians to degrees."""
return math.degrees(rad)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Hyperbolic functions', NodeMeta.KEYWORDS: []})
- def acosh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
- '''Return the inverse hyperbolic cosine of `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Hyperbolic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def acosh(x=("FloatPin", 0.0), Result=(REF, ("BoolPin", False))):
+ """Return the inverse hyperbolic cosine of `x`."""
try:
Result(True)
return math.acosh(x)
@@ -261,15 +465,27 @@ def acosh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Hyperbolic functions', NodeMeta.KEYWORDS: []})
- def asinh(x=('FloatPin', 0.0)):
- '''Return the inverse hyperbolic sine of x.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Hyperbolic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def asinh(x=("FloatPin", 0.0)):
+ """Return the inverse hyperbolic sine of x."""
return math.asinh(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Hyperbolic functions', NodeMeta.KEYWORDS: []})
- def atanh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
- '''Return the inverse hyperbolic tangent of `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Hyperbolic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def atanh(x=("FloatPin", 0.0), Result=(REF, ("BoolPin", False))):
+ """Return the inverse hyperbolic tangent of `x`."""
try:
Result(True)
return math.atanh(x)
@@ -278,9 +494,15 @@ def atanh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Hyperbolic functions', NodeMeta.KEYWORDS: []})
- def cosh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
- '''Return the hyperbolic cosine of `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Hyperbolic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def cosh(x=("FloatPin", 0.0), Result=(REF, ("BoolPin", False))):
+ """Return the hyperbolic cosine of `x`."""
try:
Result(True)
return math.cosh(x)
@@ -289,9 +511,15 @@ def cosh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Hyperbolic functions', NodeMeta.KEYWORDS: []})
- def sinh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
- '''Return the hyperbolic sine of `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Hyperbolic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def sinh(x=("FloatPin", 0.0), Result=(REF, ("BoolPin", False))):
+ """Return the hyperbolic sine of `x`."""
try:
Result(True)
return math.sinh(x)
@@ -300,27 +528,51 @@ def sinh(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Hyperbolic functions', NodeMeta.KEYWORDS: []})
- def tanh(x=('FloatPin', 0.0)):
- '''Return the hyperbolic tangent of `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Hyperbolic functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def tanh(x=("FloatPin", 0.0)):
+ """Return the hyperbolic tangent of `x`."""
return math.tanh(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Special functions', NodeMeta.KEYWORDS: []})
- def erf(x=('FloatPin', 0.0)):
- '''Return the error function at `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Special functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def erf(x=("FloatPin", 0.0)):
+ """Return the error function at `x`."""
return math.erf(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Special functions', NodeMeta.KEYWORDS: []})
- def erfc(x=('FloatPin', 0.0)):
- '''Return the complementary error function at `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Special functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def erfc(x=("FloatPin", 0.0)):
+ """Return the complementary error function at `x`."""
return math.erfc(x)
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Special functions', NodeMeta.KEYWORDS: []})
- def gamma(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
- '''Return the Gamma function at `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Special functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def gamma(x=("FloatPin", 0.0), Result=(REF, ("BoolPin", False))):
+ """Return the Gamma function at `x`."""
try:
Result(True)
return math.gamma(x)
@@ -329,9 +581,15 @@ def gamma(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Special functions', NodeMeta.KEYWORDS: []})
- def lgamma(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
- '''Return the natural logarithm of the absolute value of the Gamma function at `x`.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|math|Special functions",
+ NodeMeta.KEYWORDS: [],
+ },
+ )
+ def lgamma(x=("FloatPin", 0.0), Result=(REF, ("BoolPin", False))):
+ """Return the natural logarithm of the absolute value of the Gamma function at `x`."""
try:
Result(True)
return math.lgamma(x)
@@ -340,13 +598,19 @@ def lgamma(x=('FloatPin', 0.0), Result=(REF, ('BoolPin', False))):
return -1
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Constants', NodeMeta.KEYWORDS: []})
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Constants", NodeMeta.KEYWORDS: []},
+ )
def e():
- '''The mathematical constant `e = 2.718281`, to available precision.'''
+ """The mathematical constant `e = 2.718281`, to available precision."""
return math.e
@staticmethod
- @IMPLEMENT_NODE(returns=('FloatPin', 0.0), meta={NodeMeta.CATEGORY: 'Python|math|Constants', NodeMeta.KEYWORDS: []})
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={NodeMeta.CATEGORY: "Python|math|Constants", NodeMeta.KEYWORDS: []},
+ )
def pi():
- '''The mathematical constant = `3.141592`, to available precision.'''
+ """The mathematical constant = `3.141592`, to available precision."""
return math.pi
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/PathLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/PathLib.py
index b1ae61646..e817d1652 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/PathLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/PathLib.py
@@ -14,166 +14,349 @@
## limitations under the License.
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
import os
import os.path as osPath
class PathLib(FunctionLibraryBase):
- '''
+ """
Os.Path Library wrap
- '''
+ """
def __init__(self, packageName):
super(PathLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def abspath(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return a absolute version of a path. On most platforms, this is equivalent to calling the function normpath()'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def abspath(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return an absolute version of a path. On most platforms, this is equivalent to calling the function normpath()"""
return osPath.abspath(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Extract', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def basename(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the base name of pathname path. This is the second element of the pair returned by passing path to the function split(). Note that the result of this function is different from the Unix basename program; where basename for '/foo/bar/' returns 'bar', the basename() function returns an empty string ('')'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Extract",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def basename(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the base name of pathname path. This is the second element of the pair returned by passing path to the function split(). Note that the result of this function is different from the Unix basename program; where basename for '/foo/bar/' returns 'bar', the basename() function returns an empty string ('')"""
return osPath.basename(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Extract', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Extract",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
def commonprefix(path=("StringPin", [])):
- '''Return the longest path prefix (taken character-by-character) that is a prefix of all paths in list. If list is empty, return the empty string (''). Note that this may return invalid paths because it works a character at a time'''
+ """Return the longest path prefix (taken character-by-character) that is a prefix of all paths in list. If list is empty, return the empty string (''). Note that this may return invalid paths because it works a character at a time"""
return osPath.commonprefix(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Extract', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def dirname(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the directory name of pathname path. This is the first element of the pair returned by passing path to the function split().'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Extract",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def dirname(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the directory name of pathname path. This is the first element of the pair returned by passing path to the function split()."""
return osPath.dirname(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Test', NodeMeta.KEYWORDS: ["test", "file", "folder", "path"]})
- def exists(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return True if path refers to an existing path. Returns False for broken symbolic links. On some platforms, this function may return False if permission is not granted to execute os.stat() on the requested file, even if the path physically exists.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Test",
+ NodeMeta.KEYWORDS: ["test", "file", "folder", "path"],
+ },
+ )
+ def exists(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return True if path refers to an existing path. Returns False for broken symbolic links. On some platforms, this function may return False if permission is not granted to execute os.stat() on the requested file, even if the path physically exists."""
return osPath.exists(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Test', NodeMeta.KEYWORDS: ["test", "file", "folder", "path"]})
- def lexists(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return True if path refers to an existing path. Returns True for broken symbolic links. Equivalent to exists() on platforms lacking os.lstat().'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Test",
+ NodeMeta.KEYWORDS: ["test", "file", "folder", "path"],
+ },
+ )
+ def lexists(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return True if path refers to an existing path. Returns True for broken symbolic links. Equivalent to exists() on platforms lacking os.lstat()."""
return osPath.lexists(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def expanduser(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''On Unix and Windows, return the argument with an initial component of ~ or ~user replaced by that user’s home directory.
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def expanduser(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """On Unix and Windows, return the argument with an initial component of ~ or ~user replaced by that user’s home directory.
On Unix, an initial ~ is replaced by the environment variable HOME if it is set; otherwise the current user’s home directory is looked up in the password directory through the built-in module pwd. An initial ~user is looked up directly in the password directory.
On Windows, HOME and USERPROFILE will be used if set, otherwise a combination of HOMEPATH and HOMEDRIVE will be used. An initial ~user is handled by stripping the last directory component from the created user path derived above.
- If the expansion fails or if the path does not begin with a tilde, the path is returned unchanged.'''
+ If the expansion fails or if the path does not begin with a tilde, the path is returned unchanged."""
return osPath.expanduser(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def expandvars(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the argument with environment variables expanded. Substrings of the form $name or ${name} are replaced by the value of environment variable name. Malformed variable names and references to non-existing variables are left unchanged.
- On Windows, %name% expansions are supported in addition to $name and ${name}.'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def expandvars(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the argument with environment variables expanded. Substrings of the form $name or ${name} are replaced by the value of environment variable name. Malformed variable names and references to non-existing variables are left unchanged.
+ On Windows, %name% expansions are supported in addition to $name and ${name}."""
return osPath.expandvars(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("FloatPin", 0.0), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Time', NodeMeta.KEYWORDS: ["time", "file", "path"]})
- def getatime(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the time of last access of path. The return value is a number giving the number of seconds since the epoch (see the time module). Raise os.error if the file does not exist or is inaccessible.'''
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Time",
+ NodeMeta.KEYWORDS: ["time", "file", "path"],
+ },
+ )
+ def getatime(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the time of last access of path. The return value is a number giving the number of seconds since the epoch (see the time module). Raise os.error if the file does not exist or is inaccessible."""
return osPath.getatime(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("FloatPin", 0.0), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Time', NodeMeta.KEYWORDS: ["time", "file", "path"]})
- def getmtime(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the time of last modification of path. The return value is a number giving the number of seconds since the epoch (see the time module). Raise os.error if the file does not exist or is inaccessible.'''
- return osPath.getmtime(path, path2)
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Time",
+ NodeMeta.KEYWORDS: ["time", "file", "path"],
+ },
+ )
+ def getmtime(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the time of last modification of path. The return value is a number giving the number of seconds since the epoch (see the time module). Raise os.error if the file does not exist or is inaccessible."""
+ return osPath.getmtime(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("FloatPin", 0.0), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Time', NodeMeta.KEYWORDS: ["time", "file", "path"]})
- def getctime(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the system’s ctime which, on some systems (like Unix) is the time of the last metadata change, and, on others (like Windows), is the creation time for path. The return value is a number giving the number of seconds since the epoch (see the time module). Raise os.error if the file does not exist or is inaccessible.'''
- return osPath.getctime(path, path2)
+ @IMPLEMENT_NODE(
+ returns=("FloatPin", 0.0),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Time",
+ NodeMeta.KEYWORDS: ["time", "file", "path"],
+ },
+ )
+ def getctime(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the system’s ctime which, on some systems (like Unix) is the time of the last metadata change, and, on others (like Windows), is the creation time for path. The return value is a number giving the number of seconds since the epoch (see the time module). Raise os.error if the file does not exist or is inaccessible."""
+ return osPath.getctime(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("IntPin", 0), meta={NodeMeta.CATEGORY: 'Python|OS|Path', NodeMeta.KEYWORDS: ["size", "file", "path"]})
- def getsize(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the size, in bytes, of path. Raise os.error if the file does not exist or is inaccessible.'''
- return osPath.getctime(path, path2)
+ @IMPLEMENT_NODE(
+ returns=("IntPin", 0),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path",
+ NodeMeta.KEYWORDS: ["size", "file", "path"],
+ },
+ )
+ def getsize(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the size, in bytes, of path. Raise os.error if the file does not exist or is inaccessible."""
+ return osPath.getctime(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Test', NodeMeta.KEYWORDS: ["test", "file", "path"]})
- def isabs(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return True if path is an absolute pathname. On Unix, that means it begins with a slash, on Windows that it begins with a (back)slash after chopping off a potential drive letter.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Test",
+ NodeMeta.KEYWORDS: ["test", "file", "path"],
+ },
+ )
+ def isabs(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return True if path is an absolute pathname. On Unix, that means it begins with a slash, on Windows that it begins with a (back)slash after chopping off a potential drive letter."""
return osPath.isabs(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Test', NodeMeta.KEYWORDS: ["test", "file", "path"]})
- def isFile(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return True if path is an existing regular file. This follows symbolic links, so both islink() and isfile() can be true for the same path.'''
- return osPath.isFile(path)
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Test",
+ NodeMeta.KEYWORDS: ["test", "file", "path"],
+ },
+ )
+ def isFile(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return True if path is an existing regular file. This follows symbolic links, so both islink() and isfile() can be true for the same path."""
+ return osPath.isfile(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Test', NodeMeta.KEYWORDS: ["test", "folder", "Directory", "dir", "path"]})
- def isDir(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return True if path is an existing directory. This follows symbolic links, so both islink() and isdir() can be true for the same path.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Test",
+ NodeMeta.KEYWORDS: ["test", "folder", "Directory", "dir", "path"],
+ },
+ )
+ def isDir(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return True if path is an existing directory. This follows symbolic links, so both islink() and isdir() can be true for the same path."""
return osPath.isdir(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Test', NodeMeta.KEYWORDS: ["test", "file", "path"]})
- def islink(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return True if path refers to a directory entry that is a symbolic link. Always False if symbolic links are not supported by the Python runtime.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Test",
+ NodeMeta.KEYWORDS: ["test", "file", "path"],
+ },
+ )
+ def islink(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return True if path refers to a directory entry that is a symbolic link. Always False if symbolic links are not supported by the Python runtime."""
return osPath.islink(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Test', NodeMeta.KEYWORDS: ["test", "file", "path"]})
- def ismount(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return True if pathname path is a mount point: a point in a file system where a different file system has been mounted. The function checks whether path’s parent, path/.., is on a different device than path, or whether path/.. and path point to the same i-node on the same device — this should detect mount points for all Unix and POSIX variants.'''
+ @IMPLEMENT_NODE(
+ returns=("BoolPin", False),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Test",
+ NodeMeta.KEYWORDS: ["test", "file", "path"],
+ },
+ )
+ def ismount(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return True if pathname path is a mount point: a point in a file system where a different file system has been mounted. The function checks whether path’s parent, path/.., is on a different device than path, or whether path/.. and path point to the same i-node on the same device — this should detect mount points for all Unix and POSIX variants."""
return osPath.ismount(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["test", "file", "path"]})
- def join(base=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"}), paths=("StringPin", [])):
- '''Join one or more path components intelligently. The return value is the concatenation of path and any members of *paths with exactly one directory separator (os.sep) following each non-empty part except the last, meaning that the result will only end in a separator if the last part is empty. If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.
- On Windows, the drive letter is not reset when an absolute path component (e.g., r'\foo') is encountered. If a component contains a drive letter, all previous components are thrown away and the drive letter is reset. Note that since there is a current directory for each drive, os.path.join("c:", "foo") represents a path relative to the current directory on drive C: (c:foo), not c:\foo.'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["test", "file", "path"],
+ },
+ )
+ def join(
+ base=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ paths=("StringPin", []),
+ ):
+ """Join one or more path components intelligently. The return value is the concatenation of path and any members of *paths with exactly one directory separator (os.sep) following each non-empty part except the last, meaning that the result will only end in a separator if the last part is empty. If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.
+ On Windows, the drive letter is not reset when an absolute path component (e.g., r'\foo') is encountered. If a component contains a drive letter, all previous components are thrown away and the drive letter is reset. Note that since there is a current directory for each drive, os.path.join("c:", "foo") represents a path relative to the current directory on drive C: (c:foo), not c:\foo."""
return osPath.join(base, paths)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def normcase(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Normalize the case of a pathname. On Unix and Mac OS X, this returns the path unchanged; on case-insensitive filesystems, it converts the path to lowercase. On Windows, it also converts forward slashes to backward slashes.'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def normcase(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Normalize the case of a pathname. On Unix and Mac OS X, this returns the path unchanged; on case-insensitive filesystems, it converts the path to lowercase. On Windows, it also converts forward slashes to backward slashes."""
return osPath.normcase(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def normpath(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Normalize a pathname by collapsing redundant separators and up-level references so that A//B, A/B/, A/./B and A/foo/../B all become A/B. This string manipulation may change the meaning of a path that contains symbolic links. On Windows, it converts forward slashes to backward slashes. To normalize case, use normcase().'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def normpath(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Normalize a pathname by collapsing redundant separators and up-level references so that A//B, A/B/, A/./B and A/foo/../B all become A/B. This string manipulation may change the meaning of a path that contains symbolic links. On Windows, it converts forward slashes to backward slashes. To normalize case, use normcase()."""
return osPath.normpath(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def realpath(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path (if they are supported by the operating system).'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def realpath(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path (if they are supported by the operating system)."""
return osPath.realpath(path)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Convert', NodeMeta.KEYWORDS: ["file", "folder", "path"]})
- def relpath(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"}), start=("StringPin", os.curdir)):
- '''Return a relative filepath to path either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the existence or nature of path or start.
+ @IMPLEMENT_NODE(
+ returns=("StringPin", ""),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Convert",
+ NodeMeta.KEYWORDS: ["file", "folder", "path"],
+ },
+ )
+ def relpath(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ start=("StringPin", os.curdir),
+ ):
+ """Return a relative filepath to path either from the current directory or from an optional start directory. This is a path computation: the filesystem is not accessed to confirm the existence or nature of path or start.
start defaults to os.curdir.
- Availability: Windows, Unix.'''
- return osPath.relpath(path)
+ Availability: Windows, Unix."""
+ return osPath.relpath(path, start)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Split', NodeMeta.KEYWORDS: ["file", "path"]})
- def split(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"}), head=(REF, ("StringPin", "")), tail=(REF, ("StringPin", ""))):
- '''Split the pathname path into a pair, (head, tail) where tail is the last pathname component and head is everything leading up to that. The tail part will never contain a slash; if path ends in a slash, tail will be empty. If there is no slash in path, head will be empty. If path is empty, both head and tail are empty. Trailing slashes are stripped from head unless it is the root (one or more slashes only). In all cases, join(head, tail) returns a path to the same location as path (but the strings may differ). Also see the functions dirname() and basename().'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Split",
+ NodeMeta.KEYWORDS: ["file", "path"],
+ },
+ )
+ def split(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ head=(REF, ("StringPin", "")),
+ tail=(REF, ("StringPin", "")),
+ ):
+ """Split the pathname path into a pair, (head, tail) where tail is the last pathname component and head is everything leading up to that. The tail part will never contain a slash; if path ends in a slash, tail will be empty. If there is no slash in path, head will be empty. If path is empty, both head and tail are empty. Trailing slashes are stripped from head unless it is the root (one or more slashes only). In all cases, join(head, tail) returns a path to the same location as path (but the strings may differ). Also see the functions dirname() and basename()."""
splited = osPath.split(path)
if len(splited):
head(splited[0])
@@ -184,9 +367,19 @@ def split(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidge
return list(splited)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Split', NodeMeta.KEYWORDS: ["file", "path"]})
- def splitdrive(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"}), drive=(REF, ("StringPin", "")), tail=(REF, ("StringPin", ""))):
- '''Split the pathname path into a pair (drive, tail) where drive is either a drive specification or the empty string. On systems which do not use drive specifications, drive will always be the empty string. In all cases, drive + tail will be the same as path.'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Split",
+ NodeMeta.KEYWORDS: ["file", "path"],
+ },
+ )
+ def splitdrive(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ drive=(REF, ("StringPin", "")),
+ tail=(REF, ("StringPin", "")),
+ ):
+ """Split the pathname path into a pair (drive, tail) where drive is either a drive specification or the empty string. On systems which do not use drive specifications, drive will always be the empty string. In all cases, drive + tail will be the same as path."""
splited = osPath.splitdrive(path)
if len(splited):
drive(splited[0])
@@ -197,9 +390,19 @@ def splitdrive(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "Path
return list(splited)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Split', NodeMeta.KEYWORDS: ["file", "path"]})
- def splitext(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"}), root=(REF, ("StringPin", "")), ext=(REF, ("StringPin", ""))):
- '''Split the pathname path into a pair (root, ext) such that root + ext == path, and ext is empty or begins with a period and contains at most one period. Leading periods on the basename are ignored; splitext('.cshrc') returns ('.cshrc', '').'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Split",
+ NodeMeta.KEYWORDS: ["file", "path"],
+ },
+ )
+ def splitext(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ root=(REF, ("StringPin", "")),
+ ext=(REF, ("StringPin", "")),
+ ):
+ """Split the pathname path into a pair (root, ext) such that root + ext == path, and ext is empty or begins with a period and contains at most one period. Leading periods on the basename are ignored; splitext('.cshrc') returns ('.cshrc', '')."""
splited = osPath.splitext(path)
if len(splited):
root(splited[0])
@@ -210,10 +413,20 @@ def splitext(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWi
return list(splited)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|Split', NodeMeta.KEYWORDS: ["file", "path"]})
- def splitunc(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"}), unc=(REF, ("StringPin", "")), rest=(REF, ("StringPin", ""))):
- '''Split the pathname path into a pair (unc, rest) so that unc is the UNC mount point (such as r'\\host\mount'), if present, and rest the rest of the path (such as r'\path\file.ext'). For paths containing drive letters, unc will always be the empty string.'''
- splited = osPath.splitunc(path)
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|Split",
+ NodeMeta.KEYWORDS: ["file", "path"],
+ },
+ )
+ def splitunc(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ unc=(REF, ("StringPin", "")),
+ rest=(REF, ("StringPin", "")),
+ ):
+ """Split the pathname path into a pair (unc, rest) so that unc is the UNC mount point (such as r'\\host\mount'), if present, and rest the rest of the path (such as r'\path\file.ext'). For paths containing drive letters, unc will always be the empty string."""
+ splited = osPath.splitdrive(path)
if len(splited):
unc(splited[0])
rest(splited[1])
@@ -223,9 +436,20 @@ def splitunc(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWi
return list(splited)
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|GetFiles', NodeMeta.KEYWORDS: ["get", "folder", "file", "Directory", "dir", "path"]})
- def walk(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"}), topdown=("BoolPin", False), files=(REF, ("StringPin", [])), folders=(REF, ("StringPin", []))):
- '''Lists files and directories on Path recursive'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|GetFiles",
+ NodeMeta.KEYWORDS: ["get", "folder", "file", "Directory", "dir", "path"],
+ },
+ )
+ def walk(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"}),
+ topdown=("BoolPin", False),
+ files=(REF, ("StringPin", [])),
+ folders=(REF, ("StringPin", [])),
+ ):
+ """Lists files and directories on Path recursive"""
paths = []
pfiles = []
pfolders = []
@@ -241,19 +465,51 @@ def walk(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget
return paths
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|GetFiles', NodeMeta.KEYWORDS: ["get", "folder", "dir", "Directory", "path"]})
- def getFolders(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Lists directories on Path'''
- return [osPath.join(path, x) for x in os.listdir(path) if osPath.isdir(osPath.join(path, x))]
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|GetFiles",
+ NodeMeta.KEYWORDS: ["get", "folder", "dir", "Directory", "path"],
+ },
+ )
+ def getFolders(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Lists directories on Path"""
+ return [
+ osPath.join(path, x)
+ for x in os.listdir(path)
+ if osPath.isdir(osPath.join(path, x))
+ ]
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|GetFiles', NodeMeta.KEYWORDS: ["get", "File", "dir", "Directory", "path"]})
- def getFiles(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Lists files on Path'''
- return [osPath.join(path, x) for x in os.listdir(path) if osPath.isfile(osPath.join(path, x))]
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|GetFiles",
+ NodeMeta.KEYWORDS: ["get", "File", "dir", "Directory", "path"],
+ },
+ )
+ def getFiles(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Lists files on Path"""
+ return [
+ osPath.join(path, x)
+ for x in os.listdir(path)
+ if osPath.isfile(osPath.join(path, x))
+ ]
@staticmethod
- @IMPLEMENT_NODE(returns=("StringPin", []), meta={NodeMeta.CATEGORY: 'Python|OS|Path|GetFiles', NodeMeta.KEYWORDS: ["get", "folder", "file", "Directory", "dir", "path"]})
- def listDir(path=("StringPin", "", {PinSpecifires.INPUT_WIDGET_VARIANT: "PathWidget"})):
- '''Lists files and directories on Path'''
+ @IMPLEMENT_NODE(
+ returns=("StringPin", []),
+ meta={
+ NodeMeta.CATEGORY: "Python|OS|Path|GetFiles",
+ NodeMeta.KEYWORDS: ["get", "folder", "file", "Directory", "dir", "path"],
+ },
+ )
+ def listDir(
+ path=("StringPin", "", {PinSpecifiers.INPUT_WIDGET_VARIANT: "PathWidget"})
+ ):
+ """Lists files and directories on Path"""
return [osPath.join(path, x) for x in os.listdir(path)]
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/RandomLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/RandomLib.py
index 02017939e..13d0309c3 100644
--- a/PyFlow/Packages/PyFlowBase/FunctionLibraries/RandomLib.py
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/RandomLib.py
@@ -15,10 +15,7 @@
import random
-from PyFlow.Core import(
- FunctionLibraryBase,
- IMPLEMENT_NODE
-)
+from PyFlow.Core import FunctionLibraryBase, IMPLEMENT_NODE
from PyFlow.Core.Common import *
@@ -29,16 +26,28 @@ def __init__(self, packageName):
super(RandomLib, self).__init__(packageName)
@staticmethod
- @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'Math|random', NodeMeta.KEYWORDS: [], NodeMeta.CACHE_ENABLED: False})
+ @IMPLEMENT_NODE(
+ returns=None,
+ meta={
+ NodeMeta.CATEGORY: "Math|random",
+ NodeMeta.KEYWORDS: [],
+ NodeMeta.CACHE_ENABLED: False,
+ },
+ )
# Return a random integer N such that a <= N <= b
- def randint(start=('IntPin', 0), end=('IntPin', 10), Result=(REF, ('IntPin', 0))):
+ def randint(start=("IntPin", 0), end=("IntPin", 10), Result=(REF, ("IntPin", 0))):
"""Return a random integer N such that a <= N <= b."""
Result(random.randint(start, end))
@staticmethod
- @IMPLEMENT_NODE(returns=None, meta={NodeMeta.CATEGORY: 'Math|random', NodeMeta.KEYWORDS: []})
+ @IMPLEMENT_NODE(
+ returns=None, meta={NodeMeta.CATEGORY: "Math|random", NodeMeta.KEYWORDS: []}
+ )
# Shuffle the sequence x in place
- def shuffle(seq=('AnyPin', [], {PinSpecifires.CONSTRAINT: "1"}), Result=(REF, ('AnyPin', [], {PinSpecifires.CONSTRAINT: "1"}))):
+ def shuffle(
+ seq=("AnyPin", [], {PinSpecifiers.CONSTRAINT: "1"}),
+ Result=(REF, ("AnyPin", [], {PinSpecifiers.CONSTRAINT: "1"})),
+ ):
"""Shuffle the sequence x in place."""
random.shuffle(seq)
Result(seq)
diff --git a/PyFlow/Packages/PyFlowBase/FunctionLibraries/StringLib.py b/PyFlow/Packages/PyFlowBase/FunctionLibraries/StringLib.py
new file mode 100644
index 000000000..6dc59e26d
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/FunctionLibraries/StringLib.py
@@ -0,0 +1,115 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import random
+
+from PyFlow.Core import(
+ FunctionLibraryBase,
+ IMPLEMENT_NODE
+)
+from PyFlow.Core.Common import *
+
+
+class StringLib(FunctionLibraryBase):
+ """doc string for StringLib"""
+
+ def __init__(self, packageName):
+ super(StringLib, self).__init__(packageName)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def lower(s=('StringPin', "")):
+ return str.lower(s)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def upper(s=('StringPin', "")):
+ return str.upper(s)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def tostring(s=('AnyPin', 0)):
+ return str(s)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def isEmpty(s=('StringPin', "")):
+ return len(s) <= 0
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def notEmpty(s=('StringPin', "")):
+ return len(s) > 0
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("BoolPin", False), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def equal(l=('StringPin', ""), r=('StringPin', "")):
+ return l == r
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def strimp(s=('StringPin', ""), chars=('StringPin', "")):
+ return s.strip(chars)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def lstrip(s=('StringPin', ""), chars=('StringPin', "")):
+ return s.lstrip(chars)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=("StringPin", ""), meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def rstrip(s=('StringPin', ""), chars=('StringPin', "")):
+ return s.rstrip(chars)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=('StringPin', [], {PinSpecifiers.CONSTRAINT: '1', PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported}),
+ meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def split(s=('StringPin', ""), sep=('StringPin', "")):
+ return str.split(s, sep)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=('StringPin', [], {PinSpecifiers.CONSTRAINT: '1', PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported}),
+ meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def starstWith(s=('StringPin', ""), prefix=('StringPin', "", { PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported|PinOptions.ChangeTypeOnConnection })):
+ return s.startswith(prefix)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=('StringPin', [], {PinSpecifiers.CONSTRAINT: '1', PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported}),
+ meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def endsWith(s=('StringPin', ""), suffix=('StringPin', "", { PinSpecifiers.ENABLED_OPTIONS: PinOptions.ArraySupported|PinOptions.ChangeTypeOnConnection })):
+ return s.endswith(suffix)
+
+ @staticmethod
+ @IMPLEMENT_NODE(returns=('StringPin', ""),
+ meta={NodeMeta.CATEGORY: 'String', NodeMeta.KEYWORDS: []})
+ def concat(s1=('AnyPin', ""), s2=('AnyPin', "")):
+ return str(s1) + str(s2)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/address.py b/PyFlow/Packages/PyFlowBase/Nodes/address.py
index d4408f1dd..4dff39346 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/address.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/address.py
@@ -23,28 +23,28 @@ def __init__(self, name):
super(address, self).__init__(name)
self.obj = self.createInputPin("obj", "AnyPin", structure=StructureType.Multi)
self.obj.enableOptions(PinOptions.AllowAny)
- self.addr = self.createOutputPin('out', 'StringPin')
+ self.addr = self.createOutputPin("out", "StringPin")
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('StringPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("StringPin")
helper.addInputStruct(StructureType.Multi)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'Utils'
+ return "Utils"
@staticmethod
def keywords():
- return ['id']
+ return ["id"]
@staticmethod
def description():
- return 'Returns address of an object in memory'
+ return "Returns address of an object in memory"
def compute(self, *args, **kwargs):
self.addr.setData(hex(id(self.obj.getData())))
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/branch.py b/PyFlow/Packages/PyFlowBase/Nodes/branch.py
index ae3d203e3..24064f3e8 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/branch.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/branch.py
@@ -22,10 +22,12 @@
class branch(NodeBase):
def __init__(self, name):
super(branch, self).__init__(name)
- self.trueExec = self.createOutputPin("True", 'ExecPin')
- self.falseExec = self.createOutputPin("False", 'ExecPin')
- self.inExec = self.createInputPin("In", 'ExecPin', defaultValue=None, foo=self.compute)
- self.condition = self.createInputPin("Condition", 'BoolPin')
+ self.trueExec = self.createOutputPin("True", "ExecPin")
+ self.falseExec = self.createOutputPin("False", "ExecPin")
+ self.inExec = self.createInputPin(
+ "In", "ExecPin", defaultValue=None, callback=self.compute
+ )
+ self.condition = self.createInputPin("Condition", "BoolPin")
self.headerColor = FLOW_CONTROL_COLOR
@staticmethod
@@ -35,16 +37,16 @@ def description():
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('BoolPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("BoolPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
def compute(self, *args, **kwargs):
data = self.condition.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/charge.py b/PyFlow/Packages/PyFlowBase/Nodes/charge.py
index 0278e9008..5e68b32d1 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/charge.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/charge.py
@@ -22,30 +22,30 @@
class charge(NodeBase):
def __init__(self, name):
super(charge, self).__init__(name)
- self.inExec = self.createInputPin('inExec', 'ExecPin', None, self.compute)
- self.amount = self.createInputPin('Amount', 'FloatPin')
+ self.inExec = self.createInputPin("inExec", "ExecPin", None, self.compute)
+ self.amount = self.createInputPin("Amount", "FloatPin")
self.amount.setDefaultValue(1.0)
- self.step = self.createInputPin('Step', 'FloatPin')
+ self.step = self.createInputPin("Step", "FloatPin")
self.step.setDefaultValue(0.1)
- self.completed = self.createOutputPin('completed', 'ExecPin')
+ self.completed = self.createOutputPin("completed", "ExecPin")
self._currentAmount = 0
self.headerColor = FLOW_CONTROL_COLOR
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('FloatPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("FloatPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
@@ -53,9 +53,11 @@ def keywords():
@staticmethod
def description():
- return 'Each time node called it accumulates the step value.' +\
- 'When accumulated value reaches **amount** - **completed** pin called.' +\
- 'Useful when you need to wait some time inside some tick function.'
+ return (
+ "Each time node called it accumulates the step value."
+ + "When accumulated value reaches **amount** - **completed** pin called."
+ + "Useful when you need to wait some time inside some tick function."
+ )
def compute(self, *args, **kwargs):
step = abs(self.step.getData())
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/cliexit.py b/PyFlow/Packages/PyFlowBase/Nodes/cliexit.py
index 31a9fa4a9..2013d48d4 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/cliexit.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/cliexit.py
@@ -23,18 +23,20 @@
class cliexit(NodeBase):
def __init__(self, name):
super(cliexit, self).__init__(name)
- self.inp0 = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
+ self.inp0 = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'CLI'
+ return "CLI"
@staticmethod
def keywords():
@@ -42,7 +44,7 @@ def keywords():
@staticmethod
def description():
- return 'Stops cli program loop'
+ return "Stops cli program loop"
def compute(self, *args, **kwargs):
man = GraphManagerSingleton().get()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/colorRamp.py b/PyFlow/Packages/PyFlowBase/Nodes/colorRamp.py
index cd5eff1b9..1b23ee926 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/colorRamp.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/colorRamp.py
@@ -18,13 +18,18 @@
from PyFlow.Core.Common import *
from PyFlow.Core.structs import splineRamp
+
class colorRamp(NodeBase):
def __init__(self, name):
super(colorRamp, self).__init__(name)
self.bCacheEnabled = False
- self.input = self.createInputPin('input', 'FloatPin', structure=StructureType.Multi)
+ self.input = self.createInputPin(
+ "input", "FloatPin", structure=StructureType.Multi
+ )
self.input.enableOptions(PinOptions.AlwaysPushDirty)
- self.output = self.createOutputPin('result', 'FloatPin', structure=StructureType.Array)
+ self.output = self.createOutputPin(
+ "result", "FloatPin", structure=StructureType.Array
+ )
self.output.enableOptions(PinOptions.AlwaysPushDirty)
self.ramp = splineRamp()
self._curveTypes = ["linear", "bezier"]
@@ -33,8 +38,8 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('FloatPin')
- helper.addOutputDataType('FloatPin')
+ helper.addInputDataType("FloatPin")
+ helper.addOutputDataType("FloatPin")
helper.addInputStruct(StructureType.Multi)
helper.addOutputStruct(StructureType.Array)
return helper
@@ -49,29 +54,29 @@ def postCreate(self, jsonTemplate=None):
super(colorRamp, self).postCreate(jsonTemplate)
if "ramp" in jsonTemplate:
for x in jsonTemplate["ramp"]:
- self.ramp.addItem(x[0],x[1])
+ self.ramp.addItem(x[0], x[1])
if "curveType" in jsonTemplate:
- self._curveType = jsonTemplate["curveType"]
+ self._curveType = jsonTemplate["curveType"]
@staticmethod
def category():
- return 'Common'
+ return "Common"
@staticmethod
def keywords():
- return ["fcurve","ramp","curve"]
+ return ["fcurve", "ramp", "curve"]
@staticmethod
def description():
- return 'Editable fCurve mapped from 0 to 1, click on empty Space to add point, and right Click to delete point'
+ return "Editable fCurve mapped from 0 to 1, click on empty Space to add point, and right Click to delete point"
def compute(self, *args, **kwargs):
bezier = self._curveTypes[self._curveType] == "bezier"
if not self.input.isArray():
- self.output.setData(self.ramp.evaluateAt(self.input.getData(),bezier))
+ self.output.setData(self.ramp.evaluateAt(self.input.getData(), bezier))
else:
result = []
for i in self.input.getData():
- result.append(self.ramp.evaluateAt(i,bezier))
+ result.append(self.ramp.evaluateAt(i, bezier))
self.output.setData(result)
push(self.output)
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/combineArgs.py b/PyFlow/Packages/PyFlowBase/Nodes/combineArgs.py
new file mode 100644
index 000000000..b75b18159
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/Nodes/combineArgs.py
@@ -0,0 +1,86 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import asyncio
+import time
+
+from PyFlow.Core import NodeBase, PinBase
+from PyFlow.Core.Common import *
+from PyFlow.Core.NodeBase import NodePinsSuggestionsHelper
+from PyFlow.Packages.PyFlowBase.Nodes import FLOW_CONTROL_COLOR
+import json
+
+
+class combineArgs(NodeBase):
+ def __init__(self, name):
+ super(combineArgs, self).__init__(name)
+ self.bCacheEnabled = False
+ self.combine_result = self.createOutputPin('combine_result', 'StringPin', "")
+
+ def addInPin(self, name, dataType):
+ p = self.createInputPin(name, dataType)
+ p.enableOptions(PinOptions.RenamingEnabled | PinOptions.Dynamic
+ | PinOptions.AllowMultipleConnections | PinOptions.Storable)
+ return p
+
+ def postCreate(self, jsonTemplate=None):
+ super(combineArgs, self).postCreate(jsonTemplate=jsonTemplate)
+ # recreate dynamically created pins
+ existingPins = self.namePinInputsMap
+ if jsonTemplate is not None:
+ sortedInputs = sorted(jsonTemplate["inputs"], key=lambda x: x["pinIndex"])
+ for inPinJson in sortedInputs:
+ if inPinJson['name'] not in existingPins:
+ inDyn = self.addInPin(inPinJson['name'], inPinJson["dataType"])
+ inDyn.uid = uuid.UUID(inPinJson['uuid'])
+ try:
+ val = json.loads(inPinJson['value'], cls=inDyn.jsonDecoderClass())
+ inDyn.setData(val)
+ except:
+ inDyn.setData(inDyn.defaultValue())
+
+ @staticmethod
+ def pinTypeHints():
+ helper = NodePinsSuggestionsHelper()
+ helper.addOutputDataType('StringPin')
+ return helper
+
+ @staticmethod
+ def category():
+ return 'Cmd'
+
+ @staticmethod
+ def keywords():
+ return []
+
+ @staticmethod
+ def description():
+ return 'combine opts to one cmd line'
+
+ def compute(self, *args, **kwargs):
+ cmd_line = ""
+ for elem in self.orderedInputs.values():
+ name = elem.name.lstrip(" ")
+ if 0 == len(name) or name.isdigit():
+ cmd_line += " {0} ".format(elem.getData())
+ continue
+ if 1 == len(name) and name.isalpha():
+ cmd_line += " -{0} {1} ".format(name ,elem.getData())
+ continue
+ if name[:1] == "-":
+ cmd_line += " {0} {1} ".format(name ,elem.getData())
+ continue
+ cmd_line += " --{0} {1} ".format(name ,elem.getData())
+ self.combine_result.setData(cmd_line)
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/commentNode.py b/PyFlow/Packages/PyFlowBase/Nodes/commentNode.py
index ce8a66e09..608ae1b64 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/commentNode.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/commentNode.py
@@ -22,7 +22,7 @@ def __init__(self, name):
@staticmethod
def category():
- return 'UI'
+ return "UI"
@staticmethod
def keywords():
@@ -30,4 +30,4 @@ def keywords():
@staticmethod
def description():
- return 'Can drag intersected nodes. You can also specify color and resize it.'
+ return "Can drag intersected nodes. You can also specify color and resize it."
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/compound.py b/PyFlow/Packages/PyFlowBase/Nodes/compound.py
index ec76f6dae..7b8059a42 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/compound.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/compound.py
@@ -13,10 +13,7 @@
## limitations under the License.
-import os
-import json
-import weakref
-from copy import deepcopy
+import uuid
from blinker import Signal
@@ -30,6 +27,7 @@ class compound(NodeBase):
pins can be edited only from inside the compound
"""
+
def __init__(self, name):
super(compound, self).__init__(name)
self.isCompoundNode = True
@@ -54,7 +52,7 @@ def rawGraph(self):
@rawGraph.setter
def rawGraph(self, newGraph):
- assert(newGraph is not None)
+ assert newGraph is not None
self._rawGraph = newGraph
def syncPins(self):
@@ -62,7 +60,7 @@ def syncPins(self):
nodeInputPins = self.namePinInputsMap
nodeOutputPins = self.namePinOutputsMap
- graphInputsNodes = self.rawGraph.getNodesList(classNameFilters=['graphInputs'])
+ graphInputsNodes = self.rawGraph.getNodesList(classNameFilters=["graphInputs"])
graphInputPins = {}
for graphInputNode in graphInputsNodes:
for outPin in graphInputNode.orderedOutputs.values():
@@ -71,7 +69,7 @@ def syncPins(self):
if outPin.name not in nodeInputPins:
self.onGraphInputPinCreated(outPin)
- graphOutputNodes = self.rawGraph.getNodesList(classNameFilters=['graphOutputs'])
+ graphOutputNodes = self.rawGraph.getNodesList(classNameFilters=["graphOutputs"])
graphOutputPins = {}
for graphOutputNode in graphOutputNodes:
for inPin in graphOutputNode.orderedInputs.values():
@@ -106,7 +104,7 @@ def setName(self, name):
@staticmethod
def category():
- return 'SubGraphs'
+ return "SubGraphs"
@staticmethod
def keywords():
@@ -114,11 +112,11 @@ def keywords():
@staticmethod
def description():
- return 'Encapsulate a graph inside a node'
+ return "Encapsulate a graph inside a node"
def serialize(self):
default = NodeBase.serialize(self)
- default['graphData'] = self.rawGraph.serialize()
+ default["graphData"] = self.rawGraph.serialize()
return default
def onGraphInputPinCreated(self, outPin):
@@ -129,22 +127,28 @@ def onGraphInputPinCreated(self, outPin):
"""
# add companion pin for graphInputs node's output pin
- subgraphInputPin = self.createInputPin(outPin.name,
- outPin.__class__.__name__,
- outPin.defaultValue(),
- outPin.call,
- outPin.structureType,
- outPin.constraint,
- outPin.structConstraint,
- group=outPin.owningNode().name)
+ subgraphInputPin = self.createInputPin(
+ outPin.name,
+ outPin.__class__.__name__,
+ outPin.defaultValue(),
+ outPin.call,
+ outPin.structureType,
+ outPin.constraint,
+ outPin.structConstraint,
+ group=outPin.owningNode().name,
+ )
if subgraphInputPin.isAny():
subgraphInputPin.supportedDataTypes = outPin.supportedDataTypes
- subgraphInputPin.enableOptions(PinOptions.AllowAny | PinOptions.DictElementSupported)
+ subgraphInputPin.enableOptions(
+ PinOptions.AllowAny | PinOptions.DictElementSupported
+ )
outPin.owningNode().constraints[outPin.constraint].append(subgraphInputPin)
self.constraints[outPin.constraint].append(outPin)
- outPin.owningNode().structConstraints[outPin.structConstraint].append(subgraphInputPin)
+ outPin.owningNode().structConstraints[outPin.structConstraint].append(
+ subgraphInputPin
+ )
self.structConstraints[outPin.structConstraint].append(outPin)
self.__inputsMap[subgraphInputPin] = outPin
@@ -153,6 +157,7 @@ def onGraphInputPinCreated(self, outPin):
def forceRename(name):
subgraphInputPin.setName(name, force=True)
+
outPin.nameChanged.connect(forceRename, weak=False)
# broadcast for UI wrapper class
@@ -166,16 +171,20 @@ def onGraphOutputPinCreated(self, inPin):
"""
# add companion pin for graphOutputs node's input pin
- subgraphOutputPin = self.createOutputPin(inPin.name,
- inPin.__class__.__name__,
- inPin.defaultValue(),
- inPin.structureType,
- inPin.constraint,
- inPin.structConstraint,
- group=inPin.owningNode().name)
+ subgraphOutputPin = self.createOutputPin(
+ inPin.name,
+ inPin.__class__.__name__,
+ inPin.defaultValue(),
+ inPin.structureType,
+ inPin.constraint,
+ inPin.structConstraint,
+ group=inPin.owningNode().name,
+ )
if subgraphOutputPin.isAny():
subgraphOutputPin.supportedDataTypes = inPin.supportedDataTypes
- subgraphOutputPin.enableOptions(PinOptions.AllowAny | PinOptions.DictElementSupported)
+ subgraphOutputPin.enableOptions(
+ PinOptions.AllowAny | PinOptions.DictElementSupported
+ )
if subgraphOutputPin.isExec():
inPin.onExecute.connect(subgraphOutputPin.call)
@@ -183,7 +192,9 @@ def onGraphOutputPinCreated(self, inPin):
inPin.owningNode().constraints[inPin.constraint].append(subgraphOutputPin)
self.constraints[inPin.constraint].append(inPin)
- inPin.owningNode().structConstraints[inPin.structConstraint].append(subgraphOutputPin)
+ inPin.owningNode().structConstraints[inPin.structConstraint].append(
+ subgraphOutputPin
+ )
self.structConstraints[inPin.structConstraint].append(inPin)
self.__outputsMap[subgraphOutputPin] = inPin
@@ -192,6 +203,7 @@ def onGraphOutputPinCreated(self, inPin):
# connect
def forceRename(name):
subgraphOutputPin.setName(name, force=True)
+
inPin.nameChanged.connect(forceRename, weak=False)
# broadcast for UI wrapper class
@@ -204,24 +216,30 @@ def kill(self, *args, **kwargs):
def postCreate(self, jsonTemplate=None):
super(compound, self).postCreate(jsonTemplate=jsonTemplate)
- if jsonTemplate is not None and 'graphData' in jsonTemplate:
- parentGraph = self.graph().graphManager.findGraph(jsonTemplate['owningGraphName'])
+ if jsonTemplate is not None and "graphData" in jsonTemplate:
+ parentGraph = self.graph().graphManager.findGraph(
+ jsonTemplate["owningGraphName"]
+ )
self.rawGraph = GraphBase(self.name, self.graph().graphManager, parentGraph)
# recreate graph contents
- jsonTemplate['graphData']['name'] = self.getName()
- self.rawGraph.populateFromJson(jsonTemplate['graphData'])
+ jsonTemplate["graphData"]["name"] = self.getName()
+ self.rawGraph.populateFromJson(jsonTemplate["graphData"])
self.syncPins()
inputsMap = self.namePinInputsMap
- for inpJson in jsonTemplate['inputs']:
- inputsMap[inpJson['name']].uid = uuid.UUID(inpJson['uuid'])
+ for inpJson in jsonTemplate["inputs"]:
+ inputsMap[inpJson["name"]].uid = uuid.UUID(inpJson["uuid"])
outputsMap = self.namePinOutputsMap
- for outJson in jsonTemplate['outputs']:
- outputsMap[outJson['name']].uid = uuid.UUID(outJson['uuid'])
+ for outJson in jsonTemplate["outputs"]:
+ outputsMap[outJson["name"]].uid = uuid.UUID(outJson["uuid"])
else:
- self.rawGraph = GraphBase(self.name, self.graph().graphManager, self.graph().graphManager.activeGraph())
+ self.rawGraph = GraphBase(
+ self.name,
+ self.graph().graphManager,
+ self.graph().graphManager.activeGraph(),
+ )
def addNode(self, node):
self.rawGraph.addNode(node)
@@ -233,4 +251,4 @@ def compute(self, *args, **kwargs):
# put data from inner graph pins to outer compound node output companions
for outputPin, innerPin in self.__outputsMap.items():
outputPin.setData(innerPin.getData())
- outputPin.setClean()
\ No newline at end of file
+ outputPin.setClean()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/consoleOutput.py b/PyFlow/Packages/PyFlowBase/Nodes/consoleOutput.py
index 005ca2969..4f34a8545 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/consoleOutput.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/consoleOutput.py
@@ -13,7 +13,6 @@
## limitations under the License.
-from nine import *
import logging
from PyFlow.Core import NodeBase
@@ -25,17 +24,21 @@
class consoleOutput(NodeBase):
def __init__(self, name):
super(consoleOutput, self).__init__(name)
- self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.entity = self.createInputPin('entity', 'AnyPin', structure=StructureType.Multi)
+ self.inExec = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.entity = self.createInputPin(
+ "entity", "AnyPin", structure=StructureType.Multi
+ )
self.entity.enableOptions(PinOptions.AllowAny)
- self.outExec = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin')
+ self.outExec = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin")
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Multi)
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
@@ -43,11 +46,11 @@ def pinTypeHints():
@staticmethod
def category():
- return 'Common'
+ return "Common"
@staticmethod
def keywords():
- return ['print']
+ return ["print"]
@staticmethod
def description():
@@ -58,15 +61,15 @@ def compute(self, *args, **kwargs):
if self.getWrapper() is not None and redirectionEnabled:
data = str(self.entity.getData())
if self.entity.dataType != "StringPin":
- data = data.encode('unicode-escape')
- if IS_PYTHON2:
- data = data.replace("\\n", "
")
- else:
- if isinstance(data, bytes):
- data = data.decode("utf-8")
- data = str(data).replace("\\n", "
")
+ data = data.encode("unicode-escape")
+ if isinstance(data, bytes):
+ data = data.decode("utf-8")
+ data = str(data).replace("\\n", "
")
- errorLink = """%s
""" % (self.name, "
%s" % data)
+ errorLink = (
+ """%s"""
+ % (self.name, "
%s" % data)
+ )
logging.getLogger(None).consoleoutput(errorLink)
else:
print(self.entity.getData())
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/constant.py b/PyFlow/Packages/PyFlowBase/Nodes/constant.py
index 45c9f8515..88d1a70cc 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/constant.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/constant.py
@@ -16,15 +16,28 @@
from PyFlow.Core import NodeBase
from PyFlow.Core.NodeBase import NodePinsSuggestionsHelper
from PyFlow.Core.Common import *
-from PyFlow import findPinClassByType, getAllPinClasses
-from PyFlow import CreateRawPin
-from copy import copy
+from PyFlow import getAllPinClasses
+
class constant(NodeBase):
def __init__(self, name):
super(constant, self).__init__(name)
- self.input = self.createInputPin("in", 'AnyPin', defaultValue=0.0, structure=StructureType.Multi, constraint="1", structConstraint="1")
- self.output = self.createOutputPin("out", 'AnyPin', defaultValue=0.0, structure=StructureType.Multi, constraint="1", structConstraint="1")
+ self.input = self.createInputPin(
+ "in",
+ "AnyPin",
+ defaultValue=0.0,
+ structure=StructureType.Multi,
+ constraint="1",
+ structConstraint="1",
+ )
+ self.output = self.createOutputPin(
+ "out",
+ "AnyPin",
+ defaultValue=0.0,
+ structure=StructureType.Multi,
+ constraint="1",
+ structConstraint="1",
+ )
self.input.disableOptions(PinOptions.ChangeTypeOnConnection)
self.output.disableOptions(PinOptions.ChangeTypeOnConnection)
pinAffects(self.input, self.output)
@@ -38,22 +51,21 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('AnyPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("AnyPin")
helper.addInputStruct(StructureType.Multi)
helper.addOutputStruct(StructureType.Multi)
return helper
@staticmethod
def category():
- return 'GenericTypes'
-
+ return "GenericTypes"
+
@staticmethod
def keywords():
return ["Make"]
-
- def serialize(self):
+ def serialize(self):
orig = super(constant, self).serialize()
orig["currDataType"] = self.input.dataType
return orig
@@ -63,32 +75,31 @@ def postCreate(self, jsonTemplate=None):
if "currDataType" in jsonTemplate:
self.updateType(self.pinTypes.index(jsonTemplate["currDataType"]))
- def onPinConected(self,other):
+ def onPinConected(self, other):
self.changeType(other.dataType)
- def overrideTypeChanged(self,toogle):
+ def overrideTypeChanged(self, toogle):
if bool(toogle):
self.input.enableOptions(PinOptions.ChangeTypeOnConnection)
self.output.enableOptions(PinOptions.ChangeTypeOnConnection)
else:
self.input.disableOptions(PinOptions.ChangeTypeOnConnection)
- self.output.disableOptions(PinOptions.ChangeTypeOnConnection)
+ self.output.disableOptions(PinOptions.ChangeTypeOnConnection)
- def updateType(self,dataTypeIndex):
+ def updateType(self, dataTypeIndex):
self.input.enableOptions(PinOptions.ChangeTypeOnConnection)
- self.output.enableOptions(PinOptions.ChangeTypeOnConnection)
- self.changeType(self.pinTypes[dataTypeIndex],True)
+ self.output.enableOptions(PinOptions.ChangeTypeOnConnection)
+ self.changeType(self.pinTypes[dataTypeIndex], True)
self.input.disableOptions(PinOptions.ChangeTypeOnConnection)
- self.output.disableOptions(PinOptions.ChangeTypeOnConnection)
-
- def changeType(self,dataType,init=False):
- a = self.input.initType(dataType,init)
- b = self.output.initType(dataType,init)
+ self.output.disableOptions(PinOptions.ChangeTypeOnConnection)
- def selectStructure(self,name):
- self.input.changeStructure(StructureType(name),True)
- self.output.changeStructure(StructureType(name),True)
+ def changeType(self, dataType, init=False):
+ a = self.input.initType(dataType, init)
+ b = self.output.initType(dataType, init)
+ def selectStructure(self, name):
+ self.input.changeStructure(StructureType(name), True)
+ self.output.changeStructure(StructureType(name), True)
def compute(self, *args, **kwargs):
self.output.setData(self.input.getData())
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/convertTo.py b/PyFlow/Packages/PyFlowBase/Nodes/convertTo.py
index 80602f17f..8f8aa2fab 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/convertTo.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/convertTo.py
@@ -24,8 +24,8 @@
class convertTo(NodeBase):
def __init__(self, name):
super(convertTo, self).__init__(name)
- self.input = self.createInputPin("in", 'AnyPin', defaultValue=None)
- self.output = self.createOutputPin("result", 'AnyPin', defaultValue=None)
+ self.input = self.createInputPin("in", "AnyPin", defaultValue=None)
+ self.output = self.createOutputPin("result", "AnyPin", defaultValue=None)
pinAffects(self.input, self.output)
self.input.enableOptions(PinOptions.AllowAny)
self.pinTypes = []
@@ -36,17 +36,17 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('AnyPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("AnyPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'GenericTypes'
-
- def serialize(self):
+ return "GenericTypes"
+
+ def serialize(self):
orig = super(convertTo, self).serialize()
orig["currDataType"] = self.output.dataType
return orig
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/delay.py b/PyFlow/Packages/PyFlowBase/Nodes/delay.py
index d0432ddcd..357bad862 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/delay.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/delay.py
@@ -22,10 +22,12 @@
class delay(NodeBase):
def __init__(self, name):
super(delay, self).__init__(name)
- self.inp0 = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.delay = self.createInputPin('Delay(s)', 'FloatPin')
+ self.inp0 = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.delay = self.createInputPin("Delay(s)", "FloatPin")
self.delay.setDefaultValue(0.2)
- self.out0 = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin')
+ self.out0 = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin")
self.process = False
self._total = 0.0
self._currentDelay = 0.0
@@ -34,16 +36,16 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('FloatPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("FloatPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
@@ -51,7 +53,7 @@ def keywords():
@staticmethod
def description():
- return 'Delayed call'
+ return "Delayed call"
def callAndReset(self):
self.process = False
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/deltaTime.py b/PyFlow/Packages/PyFlowBase/Nodes/deltaTime.py
index f2f9c4547..582e35b1b 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/deltaTime.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/deltaTime.py
@@ -23,18 +23,18 @@ def __init__(self, name):
super(deltaTime, self).__init__(name)
self.bCachedEnabled = False
self._deltaTime = 0.0
- self._out0 = self.createOutputPin('out0', 'FloatPin')
+ self._out0 = self.createOutputPin("out0", "FloatPin")
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addOutputDataType('FloatPin')
+ helper.addOutputDataType("FloatPin")
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'Utils'
+ return "Utils"
@staticmethod
def keywords():
@@ -42,7 +42,7 @@ def keywords():
@staticmethod
def description():
- return 'Editor delta time.'
+ return "Editor delta time."
def Tick(self, deltaTime):
self._deltaTime = deltaTime
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/dictKeys.py b/PyFlow/Packages/PyFlowBase/Nodes/dictKeys.py
index a06b47e1d..1c6066450 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/dictKeys.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/dictKeys.py
@@ -25,7 +25,9 @@ def __init__(self, name):
self.dict.enableOptions(PinOptions.DictSupported)
self.dict.onPinConnected.connect(self.dictConnected)
self.dict.dictChanged.connect(self.dictChanged)
- self.keys = self.createOutputPin("keys", "AnyPin", structure=StructureType.Array)
+ self.keys = self.createOutputPin(
+ "keys", "AnyPin", structure=StructureType.Array
+ )
self.keys.disableOptions(PinOptions.ChangeTypeOnConnection)
def dictConnected(self, other):
@@ -41,15 +43,15 @@ def dictChanged(self, dataType):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('AnyPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("AnyPin")
helper.addInputStruct(StructureType.Dict)
helper.addOutputStruct(StructureType.Array)
return helper
@staticmethod
def category():
- return 'Dictionary'
+ return "Dictionary"
@staticmethod
def keywords():
@@ -57,7 +59,7 @@ def keywords():
@staticmethod
def description():
- return 'Returns an array of dict keys.'
+ return "Returns an array of dict keys."
def compute(self, *args, **kwargs):
self.keys.setData(list(self.dict.getData().keys()))
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/doN.py b/PyFlow/Packages/PyFlowBase/Nodes/doN.py
index 589135e91..5d477fd4b 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/doN.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/doN.py
@@ -22,13 +22,13 @@
class doN(NodeBase):
def __init__(self, name):
super(doN, self).__init__(name)
- self.enter = self.createInputPin('Enter', 'ExecPin', None, self.compute)
- self._N = self.createInputPin('N', 'IntPin')
+ self.enter = self.createInputPin("Enter", "ExecPin", None, self.compute)
+ self._N = self.createInputPin("N", "IntPin")
self._N.setData(10)
- self.reset = self.createInputPin('Reset', 'ExecPin', None, self.OnReset)
+ self.reset = self.createInputPin("Reset", "ExecPin", None, self.OnReset)
- self.completed = self.createOutputPin('Exit', 'ExecPin')
- self.counter = self.createOutputPin('Counter', 'IntPin')
+ self.completed = self.createOutputPin("Exit", "ExecPin")
+ self.counter = self.createOutputPin("Counter", "IntPin")
self.bClosed = False
self._numCalls = 0
self.headerColor = FLOW_CONTROL_COLOR
@@ -40,17 +40,17 @@ def OnReset(self):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('IntPin')
- helper.addOutputDataType('ExecPin')
- helper.addOutputDataType('IntPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("IntPin")
+ helper.addOutputDataType("ExecPin")
+ helper.addOutputDataType("IntPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
@@ -58,8 +58,8 @@ def keywords():
@staticmethod
def description():
- return 'The DoN node will fire off an execution pin N times. After the limit has been reached,\
- it will cease all outgoing execution until a pulse is sent into its Reset input.'
+ return "The DoN node will fire off an execution pin N times. After the limit has been reached,\
+ it will cease all outgoing execution until a pulse is sent into its Reset input."
def compute(self, *args, **kwargs):
maxCalls = self._N.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/doOnce.py b/PyFlow/Packages/PyFlowBase/Nodes/doOnce.py
index af3cc33dc..46743019f 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/doOnce.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/doOnce.py
@@ -22,10 +22,10 @@
class doOnce(NodeBase):
def __init__(self, name):
super(doOnce, self).__init__(name)
- self.inExec = self.createInputPin('inExec', 'ExecPin', None, self.compute)
- self.reset = self.createInputPin('Reset', 'ExecPin', None, self.OnReset)
- self.bStartClosed = self.createInputPin('Start closed', 'BoolPin')
- self.completed = self.createOutputPin('Completed', 'ExecPin')
+ self.inExec = self.createInputPin("inExec", "ExecPin", None, self.compute)
+ self.reset = self.createInputPin("Reset", "ExecPin", None, self.OnReset)
+ self.bStartClosed = self.createInputPin("Start closed", "BoolPin")
+ self.completed = self.createOutputPin("Completed", "ExecPin")
self.bClosed = False
self.headerColor = FLOW_CONTROL_COLOR
@@ -36,16 +36,16 @@ def OnReset(self):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('BoolPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("BoolPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
@@ -53,7 +53,7 @@ def keywords():
@staticmethod
def description():
- return 'Will fire off an execution pin just once. But can reset.'
+ return "Will fire off an execution pin just once. But can reset."
def compute(self, *args, **kwargs):
bStartClosed = self.bStartClosed.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/flipFlop.py b/PyFlow/Packages/PyFlowBase/Nodes/flipFlop.py
index 67e3613b7..cd71eee8e 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/flipFlop.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/flipFlop.py
@@ -23,25 +23,27 @@ class flipFlop(NodeBase):
def __init__(self, name):
super(flipFlop, self).__init__(name)
self.bState = True
- self.inp0 = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.outA = self.createOutputPin('A', 'ExecPin')
- self.outB = self.createOutputPin('B', 'ExecPin')
- self.bIsA = self.createOutputPin('IsA', 'BoolPin')
+ self.inp0 = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.outA = self.createOutputPin("A", "ExecPin")
+ self.outB = self.createOutputPin("B", "ExecPin")
+ self.bIsA = self.createOutputPin("IsA", "BoolPin")
self.headerColor = FLOW_CONTROL_COLOR
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addOutputDataType('ExecPin')
- helper.addOutputDataType('BoolPin')
+ helper.addInputDataType("ExecPin")
+ helper.addOutputDataType("ExecPin")
+ helper.addOutputDataType("BoolPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
@@ -49,7 +51,7 @@ def keywords():
@staticmethod
def description():
- return 'Changes flow each time called'
+ return "Changes flow each time called"
def compute(self, *args, **kwargs):
if self.bState:
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/floatRamp.py b/PyFlow/Packages/PyFlowBase/Nodes/floatRamp.py
index 07bf84412..589da3ad8 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/floatRamp.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/floatRamp.py
@@ -23,9 +23,21 @@ class floatRamp(NodeBase):
def __init__(self, name):
super(floatRamp, self).__init__(name)
self.bCacheEnabled = False
- self.input = self.createInputPin('input', 'FloatPin', structure=StructureType.Multi, constraint="0", structConstraint="0")
+ self.input = self.createInputPin(
+ "input",
+ "FloatPin",
+ structure=StructureType.Multi,
+ constraint="0",
+ structConstraint="0",
+ )
self.input.enableOptions(PinOptions.AlwaysPushDirty)
- self.output = self.createOutputPin('result', 'FloatPin', structure=StructureType.Multi, constraint="0", structConstraint="0")
+ self.output = self.createOutputPin(
+ "result",
+ "FloatPin",
+ structure=StructureType.Multi,
+ constraint="0",
+ structConstraint="0",
+ )
self.output.enableOptions(PinOptions.AlwaysPushDirty)
self.ramp = splineRamp()
self._curveTypes = ["linear", "bezier"]
@@ -34,8 +46,8 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('FloatPin')
- helper.addOutputDataType('FloatPin')
+ helper.addInputDataType("FloatPin")
+ helper.addOutputDataType("FloatPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
helper.addInputStruct(StructureType.Multi)
@@ -44,9 +56,9 @@ def pinTypeHints():
helper.addOutputStruct(StructureType.Array)
return helper
- def serialize(self):
+ def serialize(self):
orig = super(floatRamp, self).serialize()
- orig["ramp"] = [[x.getU(),x.getV()] for x in self.ramp.sortedItems()]
+ orig["ramp"] = [[x.getU(), x.getV()] for x in self.ramp.sortedItems()]
orig["curveType"] = self._curveType
return orig
@@ -54,13 +66,13 @@ def postCreate(self, jsonTemplate=None):
super(floatRamp, self).postCreate(jsonTemplate)
if "ramp" in jsonTemplate:
for x in jsonTemplate["ramp"]:
- self.ramp.addItem(x[0],x[1])
+ self.ramp.addItem(x[0], x[1])
if "curveType" in jsonTemplate:
- self._curveType = jsonTemplate["curveType"]
+ self._curveType = jsonTemplate["curveType"]
@staticmethod
def category():
- return 'Common'
+ return "Common"
@staticmethod
def keywords():
@@ -68,15 +80,15 @@ def keywords():
@staticmethod
def description():
- return 'Editable fCurve mapped from 0 to 1, click on empty Space to add point, and right Click to delete point'
+ return "Editable fCurve mapped from 0 to 1, click on empty Space to add point, and right Click to delete point"
def compute(self, *args, **kwargs):
bezier = self._curveTypes[self._curveType] == "bezier"
if not self.input.isArray():
- self.output.setData(self.ramp.evaluateAt(self.input.getData(),bezier))
+ self.output.setData(self.ramp.evaluateAt(self.input.getData(), bezier))
else:
result = []
for i in self.input.getData():
- result.append(self.ramp.evaluateAt(i,bezier))
+ result.append(self.ramp.evaluateAt(i, bezier))
self.output.setData(result)
push(self.output)
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/forEachLoop.py b/PyFlow/Packages/PyFlowBase/Nodes/forEachLoop.py
index 9e402c263..b440b8701 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/forEachLoop.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/forEachLoop.py
@@ -22,23 +22,27 @@
class forEachLoop(NodeBase):
def __init__(self, name):
super(forEachLoop, self).__init__(name)
- self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.array = self.createInputPin('array', 'AnyPin', structure=StructureType.Array, constraint="1")
+ self.inExec = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.array = self.createInputPin(
+ "array", "AnyPin", structure=StructureType.Array, constraint="1"
+ )
self.array.enableOptions(PinOptions.AllowAny)
- self.loopBody = self.createOutputPin('LoopBody', 'ExecPin')
- self.elem = self.createOutputPin('element', 'AnyPin', constraint="1")
+ self.loopBody = self.createOutputPin("LoopBody", "ExecPin")
+ self.elem = self.createOutputPin("element", "AnyPin", constraint="1")
self.elem.enableOptions(PinOptions.AllowAny)
- self.completed = self.createOutputPin('Completed', 'ExecPin')
+ self.completed = self.createOutputPin("Completed", "ExecPin")
self.headerColor = FLOW_CONTROL_COLOR
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('ExecPin')
- helper.addOutputDataType('AnyPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("ExecPin")
+ helper.addOutputDataType("AnyPin")
helper.addInputStruct(StructureType.Single)
helper.addInputStruct(StructureType.Array)
helper.addOutputStruct(StructureType.Single)
@@ -46,15 +50,15 @@ def pinTypeHints():
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
- return ['iter', 'for', 'loop', 'each']
+ return ["iter", "for", "loop", "each"]
@staticmethod
def description():
- return 'For each loop'
+ return "For each loop"
def compute(self, *args, **kwargs):
ls = self.array.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/forLoop.py b/PyFlow/Packages/PyFlowBase/Nodes/forLoop.py
index 21415508f..08b924530 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/forLoop.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/forLoop.py
@@ -22,39 +22,39 @@
class forLoop(NodeBase):
def __init__(self, name):
super(forLoop, self).__init__(name)
- self.inExec = self.createInputPin('inExec', 'ExecPin', None, self.compute)
- self.firstIndex = self.createInputPin('Start', 'IntPin')
- self.lastIndex = self.createInputPin('Stop', 'IntPin')
- self.step = self.createInputPin('Step', 'IntPin')
+ self.inExec = self.createInputPin("inExec", "ExecPin", None, self.compute)
+ self.firstIndex = self.createInputPin("Start", "IntPin")
+ self.lastIndex = self.createInputPin("Stop", "IntPin")
+ self.step = self.createInputPin("Step", "IntPin")
self.step.setData(1)
- self.loopBody = self.createOutputPin('LoopBody', 'ExecPin')
- self.index = self.createOutputPin('Index', 'IntPin')
- self.completed = self.createOutputPin('Completed', 'ExecPin')
+ self.loopBody = self.createOutputPin("LoopBody", "ExecPin")
+ self.index = self.createOutputPin("Index", "IntPin")
+ self.completed = self.createOutputPin("Completed", "ExecPin")
self.headerColor = FLOW_CONTROL_COLOR
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('IntPin')
- helper.addOutputDataType('ExecPin')
- helper.addOutputDataType('IntPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("IntPin")
+ helper.addOutputDataType("ExecPin")
+ helper.addOutputDataType("IntPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
- return ['iter']
+ return ["iter"]
@staticmethod
def description():
- return 'For loop'
+ return "For loop"
def compute(self, *args, **kwargs):
indexFrom = self.firstIndex.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/forLoopBegin.py b/PyFlow/Packages/PyFlowBase/Nodes/forLoopBegin.py
index 28882ce52..4ac7ce197 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/forLoopBegin.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/forLoopBegin.py
@@ -21,57 +21,58 @@
import threading
+
class forLoopBegin(NodeBase):
def __init__(self, name):
super(forLoopBegin, self).__init__(name)
self._working = False
self.currentIndex = 0
self.prevIndex = -1
- self.inExec = self.createInputPin('inExec', 'ExecPin', None, self.compute)
- self.firstIndex = self.createInputPin('Start', 'IntPin')
- self.lastIndex = self.createInputPin('Stop', 'IntPin')
- self.loopEndNode = self.createInputPin('Paired block', 'StringPin')
- self.loopEndNode.setInputWidgetVariant("ObjectPathWIdget")
-
- self.loopBody = self.createOutputPin('LoopBody', 'ExecPin')
- self.index = self.createOutputPin('Index', 'IntPin')
+ self.inExec = self.createInputPin("inExec", "ExecPin", None, self.compute)
+ self.firstIndex = self.createInputPin("Start", "IntPin")
+ self.lastIndex = self.createInputPin("Stop", "IntPin")
+ self.loopEndNode = self.createInputPin("Paired block", "StringPin")
+ self.loopEndNode.setInputWidgetVariant("ObjectPathWidget")
+
+ self.loopBody = self.createOutputPin("LoopBody", "ExecPin")
+ self.index = self.createOutputPin("Index", "IntPin")
self.headerColor = FLOW_CONTROL_ORANGE
self.setExperimental()
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('IntPin')
- helper.addOutputDataType('ExecPin')
- helper.addOutputDataType('IntPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("IntPin")
+ helper.addOutputDataType("ExecPin")
+ helper.addOutputDataType("IntPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
- return ['iter']
+ return ["iter"]
@staticmethod
def description():
- return 'For loop begin block'
+ return "For loop begin block"
def reset(self):
self.currentIndex = 0
self.prevIndex = -1
- #self._working = False
+ # self._working = False
def isDone(self):
indexTo = self.lastIndex.getData()
if self.currentIndex >= indexTo:
self.reset()
- #loopEndNode = PathsRegistry().getEntity(self.loopEndNode.getData())
- #loopEndNode.completed.call()
+ # loopEndNode = PathsRegistry().getEntity(self.loopEndNode.getData())
+ # loopEndNode.completed.call()
self._working = False
return True
return False
@@ -80,7 +81,7 @@ def onNext(self, *args, **kwargs):
while not self.isDone():
if self.currentIndex > self.prevIndex:
self.index.setData(self.currentIndex)
- self.prevIndex = self.currentIndex
+ self.prevIndex = self.currentIndex
self.loopBody.call()
def compute(self, *args, **kwargs):
@@ -99,7 +100,9 @@ def compute(self, *args, **kwargs):
else:
self.setError("{} not found".format(endNodePath))
if not self._working:
- self.thread = threading.Thread(target=self.onNext,args=(self, args, kwargs))
- self.thread.start()
+ self.thread = threading.Thread(
+ target=self.onNext, args=(self, args, kwargs)
+ )
+ self.thread.start()
self._working = True
- #self.onNext(*args, **kwargs)
+ # self.onNext(*args, **kwargs)
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/forLoopWithBreak.py b/PyFlow/Packages/PyFlowBase/Nodes/forLoopWithBreak.py
index 43e1df3be..3e108b3f4 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/forLoopWithBreak.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/forLoopWithBreak.py
@@ -23,26 +23,26 @@ class forLoopWithBreak(NodeBase):
def __init__(self, name):
super(forLoopWithBreak, self).__init__(name)
self.stop = False
- self.inExec = self.createInputPin('inExec', 'ExecPin', None, self.compute)
- self.firstIndex = self.createInputPin('Start', 'IntPin')
- self.lastIndex = self.createInputPin('Stop', 'IntPin')
+ self.inExec = self.createInputPin("inExec", "ExecPin", None, self.compute)
+ self.firstIndex = self.createInputPin("Start", "IntPin")
+ self.lastIndex = self.createInputPin("Stop", "IntPin")
self.lastIndex.setDefaultValue(10)
- self.step = self.createInputPin('Step', 'IntPin')
+ self.step = self.createInputPin("Step", "IntPin")
self.step.setDefaultValue(1)
- self.breakExec = self.createInputPin('Break', 'ExecPin', None, self.interrupt)
+ self.breakExec = self.createInputPin("Break", "ExecPin", None, self.interrupt)
- self.loopBody = self.createOutputPin('LoopBody', 'ExecPin')
- self.index = self.createOutputPin('Index', 'IntPin')
- self.completed = self.createOutputPin('Completed', 'ExecPin')
+ self.loopBody = self.createOutputPin("LoopBody", "ExecPin")
+ self.index = self.createOutputPin("Index", "IntPin")
+ self.completed = self.createOutputPin("Completed", "ExecPin")
self.headerColor = FLOW_CONTROL_COLOR
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('IntPin')
- helper.addOutputDataType('ExecPin')
- helper.addOutputDataType('IntPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("IntPin")
+ helper.addOutputDataType("ExecPin")
+ helper.addOutputDataType("IntPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@@ -52,15 +52,15 @@ def interrupt(self, *args, **kwargs):
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
- return ['iter']
+ return ["iter"]
@staticmethod
def description():
- return 'For loop with ability to break'
+ return "For loop with ability to break"
def compute(self, *args, **kwargs):
indexFrom = self.firstIndex.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/getVar.py b/PyFlow/Packages/PyFlowBase/Nodes/getVar.py
index 7b14d8a2c..dbf0ffa9e 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/getVar.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/getVar.py
@@ -14,7 +14,6 @@
from copy import copy
-import uuid
from PyFlow.Packages.PyFlowBase import PACKAGE_NAME
from PyFlow.Core import NodeBase
@@ -26,12 +25,14 @@
class getVar(NodeBase):
def __init__(self, name, var=None):
super(getVar, self).__init__(name)
- assert(isinstance(var, Variable))
+ assert isinstance(var, Variable)
self._var = var
if var.structure == StructureType.Dict:
- self.out = self.createOutputPin('out', var.value.valueType, structure=StructureType.Dict, constraint="2")
+ self.out = self.createOutputPin(
+ "out", var.value.valueType, structure=StructureType.Dict, constraint="2"
+ )
else:
- self.out = self.createOutputPin('out', var.dataType)
+ self.out = self.createOutputPin("out", var.dataType)
self.out.disableOptions(PinOptions.RenamingEnabled)
self._var.valueChanged.connect(self.onVarValueChanged)
@@ -69,7 +70,7 @@ def recreateOutput(self, dataType):
self.out.kill()
del self.out
self.out = None
- self.out = CreateRawPin('out', self, dataType, PinDirection.Output)
+ self.out = CreateRawPin("out", self, dataType, PinDirection.Output)
self.out.disableOptions(PinOptions.RenamingEnabled)
self.updateStructure()
return self.out
@@ -113,7 +114,7 @@ def onVarValueChanged(self, *args, **kwargs):
def serialize(self):
default = NodeBase.serialize(self)
if self.var is not None:
- default['varUid'] = str(self.var.uid)
+ default["varUid"] = str(self.var.uid)
return default
@staticmethod
@@ -126,7 +127,7 @@ def keywords():
@staticmethod
def description():
- return 'Access variable value'
+ return "Access variable value"
def compute(self, *args, **kwargs):
self.out.setData(copy(self.var.value))
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/graphNodes.py b/PyFlow/Packages/PyFlowBase/Nodes/graphNodes.py
index c51408627..a1fe2226c 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/graphNodes.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/graphNodes.py
@@ -13,22 +13,25 @@
## limitations under the License.
-from blinker import Signal
+import uuid
from PyFlow.Core import NodeBase
from PyFlow.Core.Common import *
+import json
class graphInputs(NodeBase):
"""Represents a group of input pins on compound node
"""
+
def __init__(self, name):
super(graphInputs, self).__init__(name)
self.bCacheEnabled = True
def getUniqPinName(self, name):
- result = name
- graphNodes = self.graph().getNodesList(classNameFilters=['graphInputs', 'graphOutputs'])
+ graphNodes = self.graph().getNodesList(
+ classNameFilters=["graphInputs", "graphOutputs"]
+ )
conflictingPinNames = set()
for node in graphNodes:
for pin in node.pins:
@@ -38,7 +41,7 @@ def getUniqPinName(self, name):
@staticmethod
def category():
- return 'SubGraphs'
+ return "SubGraphs"
@staticmethod
def keywords():
@@ -46,22 +49,27 @@ def keywords():
@staticmethod
def description():
- return ''
+ return ""
def addOutPin(self, name=None, dataType="AnyPin"):
if name is None:
- name = self.getUniqPinName('in')
- p = self.createOutputPin(name, dataType, constraint=name, structConstraint=name, structure=StructureType.Multi)
+ name = self.getUniqPinName("in")
+ p = self.createOutputPin(
+ name,
+ dataType,
+ constraint=name,
+ structConstraint=name,
+ structure=StructureType.Multi,
+ )
p.enableOptions(PinOptions.RenamingEnabled | PinOptions.Dynamic)
if dataType == "AnyPin":
p.enableOptions(PinOptions.AllowAny | PinOptions.DictElementSupported)
return p
-
def compute(self, *args, **kwargs):
for o in self.outputs.values():
for i in o.affected_by:
- if len(i.affected_by)>0:
+ if len(i.affected_by) > 0:
for e in i.affected_by:
o.setData(e.getData())
else:
@@ -74,21 +82,28 @@ def postCreate(self, jsonTemplate=None):
if jsonTemplate is not None:
sortedOutputs = sorted(jsonTemplate["outputs"], key=lambda x: x["pinIndex"])
for outPinJson in sortedOutputs:
- if outPinJson['name'] not in existingPins:
- dynOut = self.addOutPin(outPinJson['name'], outPinJson["dataType"])
- dynOut.uid = uuid.UUID(outPinJson['uuid'])
+ if outPinJson["name"] not in existingPins:
+ dynOut = self.addOutPin(outPinJson["name"], outPinJson["dataType"])
+ dynOut.uid = uuid.UUID(outPinJson["uuid"])
+ try:
+ val = json.loads(outPinJson["value"], cls=dynOut.jsonDecoderClass())
+ dynOut.setData(val)
+ except:
+ dynOut.setData(dynOut.defaultValue())
class graphOutputs(NodeBase):
"""Represents a group of output pins on compound node
"""
+
def __init__(self, name):
super(graphOutputs, self).__init__(name)
self.bCacheEnabled = False
def getUniqPinName(self, name):
- result = name
- graphNodes = self.graph().getNodesList(classNameFilters=['graphInputs', 'graphOutputs'])
+ graphNodes = self.graph().getNodesList(
+ classNameFilters=["graphInputs", "graphOutputs"]
+ )
conflictingPinNames = set()
for node in graphNodes:
for pin in node.pins:
@@ -98,7 +113,7 @@ def getUniqPinName(self, name):
@staticmethod
def category():
- return 'SubGraphs'
+ return "SubGraphs"
@staticmethod
def keywords():
@@ -106,7 +121,7 @@ def keywords():
@staticmethod
def description():
- return ''
+ return ""
def postCreate(self, jsonTemplate=None):
super(graphOutputs, self).postCreate(jsonTemplate=jsonTemplate)
@@ -115,14 +130,20 @@ def postCreate(self, jsonTemplate=None):
if jsonTemplate is not None:
sortedInputs = sorted(jsonTemplate["inputs"], key=lambda x: x["pinIndex"])
for inPinJson in sortedInputs:
- if inPinJson['name'] not in existingPins:
- inDyn = self.addInPin(inPinJson['name'], inPinJson["dataType"])
- inDyn.uid = uuid.UUID(inPinJson['uuid'])
+ if inPinJson["name"] not in existingPins:
+ inDyn = self.addInPin(inPinJson["name"], inPinJson["dataType"])
+ inDyn.uid = uuid.UUID(inPinJson["uuid"])
def addInPin(self, name=None, dataType="AnyPin"):
if name is None:
- name = self.getUniqPinName('out')
- p = self.createInputPin(name, dataType, constraint=name, structConstraint=name, structure=StructureType.Multi)
+ name = self.getUniqPinName("out")
+ p = self.createInputPin(
+ name,
+ dataType,
+ constraint=name,
+ structConstraint=name,
+ structure=StructureType.Multi,
+ )
p.enableOptions(PinOptions.RenamingEnabled | PinOptions.Dynamic)
if dataType == "AnyPin":
p.enableOptions(PinOptions.AllowAny | PinOptions.DictElementSupported)
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/imageDisplay.py b/PyFlow/Packages/PyFlowBase/Nodes/imageDisplay.py
index 1b917232c..49a23cab4 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/imageDisplay.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/imageDisplay.py
@@ -24,26 +24,28 @@ class imageDisplay(NodeBase):
def __init__(self, name):
super(imageDisplay, self).__init__(name)
self.loadImage = Signal(str)
- self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.entity = self.createInputPin('path', 'StringPin')
- self.outExec = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin', None)
- self.failed = self.createOutputPin("failed", 'ExecPin', None)
+ self.inExec = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.entity = self.createInputPin("path", "StringPin")
+ self.outExec = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin", None)
+ self.failed = self.createOutputPin("failed", "ExecPin", None)
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('StringPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("StringPin")
helper.addInputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'UI'
+ return "UI"
@staticmethod
def keywords():
- return ['image']
+ return ["image"]
@staticmethod
def description():
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py b/PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py
index 764e81f26..ccf6f6455 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/loopEnd.py
@@ -23,32 +23,34 @@
class loopEnd(NodeBase):
def __init__(self, name):
super(loopEnd, self).__init__(name)
- self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.loopBeginNode = self.createInputPin('Paired block', 'StringPin')
- self.loopBeginNode.setInputWidgetVariant("ObjectPathWIdget")
- self.completed = self.createOutputPin('Completed', 'ExecPin')
+ self.inExec = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.loopBeginNode = self.createInputPin("Paired block", "StringPin")
+ self.loopBeginNode.setInputWidgetVariant("ObjectPathWidget")
+ self.completed = self.createOutputPin("Completed", "ExecPin")
self.headerColor = FLOW_CONTROL_ORANGE
self.setExperimental()
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('StringPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("StringPin")
helper.addInputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
- return ['iter', 'end']
+ return ["iter", "end"]
@staticmethod
def description():
- return 'For loop end block'
+ return "For loop end block"
def compute(self, *args, **kwargs):
node = PathsRegistry().getEntity(self.loopBeginNode.getData())
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/makeAnyDict.py b/PyFlow/Packages/PyFlowBase/Nodes/makeAnyDict.py
index e18c4224f..197031a8a 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/makeAnyDict.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/makeAnyDict.py
@@ -22,16 +22,32 @@
class makeAnyDict(NodeBase):
def __init__(self, name):
super(makeAnyDict, self).__init__(name)
- self.KeyType = self.createInputPin('KeyType', 'AnyPin', defaultValue=str(""), constraint="1", supportedPinDataTypes=getHashableDataTypes())
+ self.KeyType = self.createInputPin(
+ "KeyType",
+ "AnyPin",
+ defaultValue=str(""),
+ constraint="1",
+ supportedPinDataTypes=getHashableDataTypes(),
+ )
self.KeyType.hidden = True
- self.arrayData = self.createInputPin('data', 'AnyPin', structure=StructureType.Dict)
- self.arrayData.enableOptions(PinOptions.AllowMultipleConnections | PinOptions.AllowAny | PinOptions.DictElementSupported)
- self.arrayData.disableOptions(PinOptions.ChangeTypeOnConnection | PinOptions.SupportsOnlyArrays)
- self.outArray = self.createOutputPin('out', 'AnyPin', structure=StructureType.Dict)
+ self.arrayData = self.createInputPin(
+ "data", "AnyPin", structure=StructureType.Dict
+ )
+ self.arrayData.enableOptions(
+ PinOptions.AllowMultipleConnections
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported
+ )
+ self.arrayData.disableOptions(
+ PinOptions.ChangeTypeOnConnection | PinOptions.SupportsOnlyArrays
+ )
+ self.outArray = self.createOutputPin(
+ "out", "AnyPin", structure=StructureType.Dict
+ )
self.outArray.enableOptions(PinOptions.AllowAny)
self.outArray.disableOptions(PinOptions.ChangeTypeOnConnection)
- self.result = self.createOutputPin('result', 'BoolPin')
+ self.result = self.createOutputPin("result", "BoolPin")
self.arrayData.onPinDisconnected.connect(self.inPinDisconnected)
self.arrayData.onPinConnected.connect(self.inPinConnected)
self.KeyType.typeChanged.connect(self.updateDicts)
@@ -39,9 +55,9 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('AnyPin')
- helper.addOutputDataType('BoolPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("AnyPin")
+ helper.addOutputDataType("BoolPin")
helper.addInputStruct(StructureType.Dict)
helper.addOutputStruct(StructureType.Dict)
helper.addOutputStruct(StructureType.Single)
@@ -49,7 +65,7 @@ def pinTypeHints():
@staticmethod
def category():
- return 'GenericTypes'
+ return "GenericTypes"
@staticmethod
def keywords():
@@ -57,12 +73,12 @@ def keywords():
@staticmethod
def description():
- return 'Creates a list from connected pins'
+ return "Creates a list from connected pins"
- def updateDicts(self,dataType):
- self.arrayData.updateConnectedDicts([],self.KeyType.dataType)
+ def updateDicts(self, dataType):
+ self.arrayData.updateConnectedDicts([], self.KeyType.dataType)
- def inPinConnected(self,inputpin):
+ def inPinConnected(self, inputpin):
inp = inputpin.getDictElementNode([])
if inp and inputpin.owningNode() != inp:
dataType = self.KeyType.dataType
@@ -70,33 +86,33 @@ def inPinConnected(self,inputpin):
dataType = inp.key.dataType
if self.KeyType not in inp.constraints[inp.key.constraint]:
inp.constraints[inp.key.constraint].append(self.KeyType)
- if inp.key not in self.constraints[inp.key.constraint]:
+ if inp.key not in self.constraints[inp.key.constraint]:
self.constraints[inp.key.constraint].append(inp.key)
for i in self.constraints[inp.key.constraint]:
- i.setType(dataType)
+ i.setType(dataType)
-
- def inPinDisconnected(self,inp):
+ def inPinDisconnected(self, inp):
inp = inp.getDictElementNode([])
elements = [i.getDictElementNode([]) for i in self.arrayData.affected_by]
- if inp is not None :
+ if inp is not None:
if self.KeyType in inp.constraints[inp.key.constraint]:
inp.constraints[inp.key.constraint].remove(self.KeyType)
- if inp.key in self.constraints[inp.key.constraint]:
+ if inp.key in self.constraints[inp.key.constraint]:
self.constraints[inp.key.constraint].remove(inp.key)
def compute(self, *args, **kwargs):
outArray = PFDict(self.KeyType.dataType)
- ySortedPins = sorted(self.arrayData.affected_by, key=lambda pin: pin.owningNode().y)
+ ySortedPins = sorted(
+ self.arrayData.affected_by, key=lambda pin: pin.owningNode().y
+ )
for i in ySortedPins:
if isinstance(i.getData(), DictElement):
outArray[i.getData()[0]] = i.getData()[1]
elif isinstance(i.getData(), PFDict):
- for key,value in i.getData().items():
+ for key, value in i.getData().items():
outArray[key] = value
self.outArray.setData(outArray)
self.arrayData.setData(outArray)
self.result.setData(True)
-
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/makeArray.py b/PyFlow/Packages/PyFlowBase/Nodes/makeArray.py
index 803c2c6b9..1f474af36 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/makeArray.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/makeArray.py
@@ -21,23 +21,27 @@
class makeArray(NodeBase):
def __init__(self, name):
super(makeArray, self).__init__(name)
- self.arrayData = self.createInputPin('data', 'AnyPin', structure=StructureType.Array, constraint="1")
+ self.arrayData = self.createInputPin(
+ "data", "AnyPin", structure=StructureType.Array, constraint="1"
+ )
self.arrayData.enableOptions(PinOptions.AllowMultipleConnections)
self.arrayData.disableOptions(PinOptions.SupportsOnlyArrays)
- self.sorted = self.createInputPin('sorted', 'BoolPin')
- self.reversed = self.createInputPin('reversed', 'BoolPin')
- self.outArray = self.createOutputPin('out', 'AnyPin', structure=StructureType.Array, constraint="1")
+ self.sorted = self.createInputPin("sorted", "BoolPin")
+ self.reversed = self.createInputPin("reversed", "BoolPin")
+ self.outArray = self.createOutputPin(
+ "out", "AnyPin", structure=StructureType.Array, constraint="1"
+ )
- self.result = self.createOutputPin('result', 'BoolPin')
+ self.result = self.createOutputPin("result", "BoolPin")
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addInputDataType('BoolPin')
- helper.addOutputDataType('AnyPin')
- helper.addOutputDataType('BoolPin')
+ helper.addInputDataType("AnyPin")
+ helper.addInputDataType("BoolPin")
+ helper.addOutputDataType("AnyPin")
+ helper.addOutputDataType("BoolPin")
helper.addInputStruct(StructureType.Array)
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Array)
@@ -46,7 +50,7 @@ def pinTypeHints():
@staticmethod
def category():
- return 'GenericTypes'
+ return "GenericTypes"
@staticmethod
def keywords():
@@ -54,11 +58,13 @@ def keywords():
@staticmethod
def description():
- return 'Creates a list from connected pins'
+ return "Creates a list from connected pins"
def compute(self, *args, **kwargs):
outArray = []
- ySortedPins = sorted(self.arrayData.affected_by, key=lambda pin: pin.owningNode().y)
+ ySortedPins = sorted(
+ self.arrayData.affected_by, key=lambda pin: pin.owningNode().y
+ )
for i in ySortedPins:
if isinstance(i.getData(), list):
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/makeDict.py b/PyFlow/Packages/PyFlowBase/Nodes/makeDict.py
index 493defa21..b30f07a66 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/makeDict.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/makeDict.py
@@ -22,15 +22,29 @@
class makeDict(NodeBase):
def __init__(self, name):
super(makeDict, self).__init__(name)
- self.KeyType = self.createInputPin('KeyType', 'AnyPin', defaultValue=str(""), constraint="1", supportedPinDataTypes=getHashableDataTypes())
+ self.KeyType = self.createInputPin(
+ "KeyType",
+ "AnyPin",
+ defaultValue=str(""),
+ constraint="1",
+ supportedPinDataTypes=getHashableDataTypes(),
+ )
self.KeyType.hidden = True
- self.arrayData = self.createInputPin('data', 'AnyPin', structure=StructureType.Dict, constraint="2")
- self.arrayData.enableOptions(PinOptions.AllowMultipleConnections | PinOptions.AllowAny | PinOptions.DictElementSupported)
+ self.arrayData = self.createInputPin(
+ "data", "AnyPin", structure=StructureType.Dict, constraint="2"
+ )
+ self.arrayData.enableOptions(
+ PinOptions.AllowMultipleConnections
+ | PinOptions.AllowAny
+ | PinOptions.DictElementSupported
+ )
self.arrayData.disableOptions(PinOptions.SupportsOnlyArrays)
- self.outArray = self.createOutputPin('out', 'AnyPin', structure=StructureType.Dict, constraint="2")
+ self.outArray = self.createOutputPin(
+ "out", "AnyPin", structure=StructureType.Dict, constraint="2"
+ )
self.outArray.enableOptions(PinOptions.AllowAny)
- self.result = self.createOutputPin('result', 'BoolPin')
+ self.result = self.createOutputPin("result", "BoolPin")
self.arrayData.onPinDisconnected.connect(self.inPinDisconnected)
self.arrayData.onPinConnected.connect(self.inPinConnected)
self.KeyType.typeChanged.connect(self.updateDicts)
@@ -38,9 +52,9 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('AnyPin')
- helper.addOutputDataType('BoolPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("AnyPin")
+ helper.addOutputDataType("BoolPin")
helper.addInputStruct(StructureType.Dict)
helper.addOutputStruct(StructureType.Dict)
helper.addOutputStruct(StructureType.Single)
@@ -48,7 +62,7 @@ def pinTypeHints():
@staticmethod
def category():
- return 'GenericTypes'
+ return "GenericTypes"
@staticmethod
def keywords():
@@ -56,7 +70,7 @@ def keywords():
@staticmethod
def description():
- return 'Creates a list from connected pins'
+ return "Creates a list from connected pins"
def postCreate(self, jsonData):
super(makeDict, self).postCreate(jsonData)
@@ -90,7 +104,9 @@ def inPinDisconnected(self, inp):
def compute(self, *args, **kwargs):
outArray = PFDict(self.KeyType.dataType, self.arrayData.dataType)
- ySortedPins = sorted(self.arrayData.affected_by, key=lambda pin: pin.owningNode().y)
+ ySortedPins = sorted(
+ self.arrayData.affected_by, key=lambda pin: pin.owningNode().y
+ )
for i in ySortedPins:
if isinstance(i.getData(), DictElement):
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/makeDictElement.py b/PyFlow/Packages/PyFlowBase/Nodes/makeDictElement.py
index 3ccf7cc1f..475d209ef 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/makeDictElement.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/makeDictElement.py
@@ -23,11 +23,27 @@ class makeDictElement(NodeBase):
def __init__(self, name):
super(makeDictElement, self).__init__(name)
self.bCacheEnabled = False
- self.key = self.createInputPin('key', 'AnyPin', structure=StructureType.Single, constraint="1", supportedPinDataTypes=getHashableDataTypes())
- self.value = self.createInputPin('value', 'AnyPin', structure=StructureType.Multi, constraint="2")
+ self.key = self.createInputPin(
+ "key",
+ "AnyPin",
+ structure=StructureType.Single,
+ constraint="1",
+ supportedPinDataTypes=getHashableDataTypes(),
+ )
+ self.value = self.createInputPin(
+ "value", "AnyPin", structure=StructureType.Multi, constraint="2"
+ )
self.value.enableOptions(PinOptions.AllowAny)
- self.outArray = self.createOutputPin('out', 'AnyPin', defaultValue=DictElement(), structure=StructureType.Single, constraint="2")
- self.outArray.enableOptions(PinOptions.AllowAny | PinOptions.DictElementSupported)
+ self.outArray = self.createOutputPin(
+ "out",
+ "AnyPin",
+ defaultValue=DictElement(),
+ structure=StructureType.Single,
+ constraint="2",
+ )
+ self.outArray.enableOptions(
+ PinOptions.AllowAny | PinOptions.DictElementSupported
+ )
self.outArray.onPinConnected.connect(self.outPinConnected)
self.outArray.onPinDisconnected.connect(self.outPinDisConnected)
self.key.dataBeenSet.connect(self.dataBeenSet)
@@ -36,8 +52,8 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('AnyPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("AnyPin")
helper.addInputStruct(StructureType.Single)
helper.addInputStruct(StructureType.Multi)
helper.addOutputStruct(StructureType.Single)
@@ -45,7 +61,7 @@ def pinTypeHints():
@staticmethod
def category():
- return 'GenericTypes'
+ return "GenericTypes"
@staticmethod
def keywords():
@@ -53,7 +69,7 @@ def keywords():
@staticmethod
def description():
- return 'Creates a Dict Element'
+ return "Creates a Dict Element"
def dataBeenSet(self, pin=None):
try:
@@ -67,7 +83,7 @@ def outPinDisConnected(self, inp):
if dictNode:
if dictNode.KeyType in self.constraints[self.key.constraint]:
self.constraints[self.key.constraint].remove(dictNode.KeyType)
- if self.key in dictNode.constraints[self.key.constraint]:
+ if self.key in dictNode.constraints[self.key.constraint]:
dictNode.constraints[self.key.constraint].remove(self.key)
self.outPinConnected(self.outArray)
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/makeList.py b/PyFlow/Packages/PyFlowBase/Nodes/makeList.py
index 7fafa16cc..ce1112172 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/makeList.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/makeList.py
@@ -21,26 +21,36 @@
class makeList(NodeBase):
def __init__(self, name):
super(makeList, self).__init__(name)
- self.listData = self.createInputPin('data', 'AnyPin', structure=StructureType.Array)
- self.listData.enableOptions(PinOptions.AllowMultipleConnections | PinOptions.DictElementSupported | PinOptions.AllowAny)
- self.listData.disableOptions(PinOptions.ChangeTypeOnConnection | PinOptions.SupportsOnlyArrays)
-
- self.sorted = self.createInputPin('sorted', 'BoolPin')
- self.reversed = self.createInputPin('reversed', 'BoolPin')
- self.outList = self.createOutputPin('out', 'AnyPin', structure=StructureType.Array)
+ self.listData = self.createInputPin(
+ "data", "AnyPin", structure=StructureType.Array
+ )
+ self.listData.enableOptions(
+ PinOptions.AllowMultipleConnections
+ | PinOptions.DictElementSupported
+ | PinOptions.AllowAny
+ )
+ self.listData.disableOptions(
+ PinOptions.ChangeTypeOnConnection | PinOptions.SupportsOnlyArrays
+ )
+
+ self.sorted = self.createInputPin("sorted", "BoolPin")
+ self.reversed = self.createInputPin("reversed", "BoolPin")
+ self.outList = self.createOutputPin(
+ "out", "AnyPin", structure=StructureType.Array
+ )
self.outList.disableOptions(PinOptions.ChangeTypeOnConnection)
self.outList.enableOptions(PinOptions.AllowAny)
- self.result = self.createOutputPin('result', 'BoolPin')
+ self.result = self.createOutputPin("result", "BoolPin")
self.checkForErrors()
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addInputDataType('BoolPin')
- helper.addOutputDataType('AnyPin')
- helper.addOutputDataType('BoolPin')
+ helper.addInputDataType("AnyPin")
+ helper.addInputDataType("BoolPin")
+ helper.addOutputDataType("AnyPin")
+ helper.addOutputDataType("BoolPin")
helper.addInputStruct(StructureType.Array)
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Array)
@@ -49,7 +59,7 @@ def pinTypeHints():
@staticmethod
def category():
- return 'GenericTypes'
+ return "GenericTypes"
@staticmethod
def keywords():
@@ -57,7 +67,7 @@ def keywords():
@staticmethod
def description():
- return 'Creates a list from connected pins'
+ return "Creates a list from connected pins"
def compute(self, *args, **kwargs):
outList = []
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/pythonNode.py b/PyFlow/Packages/PyFlowBase/Nodes/pythonNode.py
index ac24ee649..d5608d506 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/pythonNode.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/pythonNode.py
@@ -24,7 +24,7 @@
class pythonNode(NodeBase):
def __init__(self, name):
super(pythonNode, self).__init__(name)
- self._nodeData = ''
+ self._nodeData = ""
self.bCacheEnabled = False
@property
@@ -40,7 +40,7 @@ def ensureNameUnique(self):
@nodeData.setter
def nodeData(self, codeString):
- if codeString == '':
+ if codeString == "":
return
try:
self._nodeData = codeString
@@ -73,7 +73,7 @@ def nodeCompute(*args, **kwargs):
def serialize(self):
default = super(pythonNode, self).serialize()
- default['nodeData'] = self.nodeData
+ default["nodeData"] = self.nodeData
return default
def postCreate(self, jsonTemplate=None):
@@ -82,36 +82,40 @@ def postCreate(self, jsonTemplate=None):
if jsonTemplate is None:
return
- if 'nodeData' in jsonTemplate:
- self.nodeData = jsonTemplate['nodeData']
+ if "nodeData" in jsonTemplate:
+ self.nodeData = jsonTemplate["nodeData"]
- for inpJson in jsonTemplate['inputs']:
+ for inpJson in jsonTemplate["inputs"]:
pin = self.getPinByName(inpJson["name"])
if not pin:
- pin = self.createInputPin(pinName=inpJson["name"],
- dataType=inpJson["dataType"],
- defaultValue=getPinDefaultValueByType(inpJson["dataType"]),
- foo=self.compute)
+ pin = self.createInputPin(
+ pinName=inpJson["name"],
+ dataType=inpJson["dataType"],
+ defaultValue=getPinDefaultValueByType(inpJson["dataType"]),
+ callback=self.compute,
+ )
pin.deserialize(inpJson)
- for outJson in jsonTemplate['outputs']:
+ for outJson in jsonTemplate["outputs"]:
pin = self.getPinByName(outJson["name"])
if not pin:
- pin = self.createOutputPin(pinName=inpJson["name"],
- dataType=inpJson["dataType"],
- defaultValue=getPinDefaultValueByType(inpJson["dataType"]))
+ pin = self.createOutputPin(
+ pinName=inpJson["name"],
+ dataType=inpJson["dataType"],
+ defaultValue=getPinDefaultValueByType(inpJson["dataType"]),
+ )
pin.deserialize(outJson)
self.autoAffectPins()
@staticmethod
def category():
- return 'Common'
+ return "Common"
@staticmethod
def keywords():
- return ['Code', 'Expression', 'py']
+ return ["Code", "Expression", "py"]
@staticmethod
def description():
- return 'Python script node'
+ return "Python script node"
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/reroute.py b/PyFlow/Packages/PyFlowBase/Nodes/reroute.py
index b0db44d2f..1cc3e1039 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/reroute.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/reroute.py
@@ -21,8 +21,20 @@
class reroute(NodeBase):
def __init__(self, name):
super(reroute, self).__init__(name)
- self.input = self.createInputPin("in", 'AnyPin', structure=StructureType.Multi, constraint="1", structConstraint="1")
- self.output = self.createOutputPin("out", 'AnyPin', structure=StructureType.Multi, constraint="1", structConstraint="1")
+ self.input = self.createInputPin(
+ "in",
+ "AnyPin",
+ structure=StructureType.Multi,
+ constraint="1",
+ structConstraint="1",
+ )
+ self.output = self.createOutputPin(
+ "out",
+ "AnyPin",
+ structure=StructureType.Multi,
+ constraint="1",
+ structConstraint="1",
+ )
self.input.checkForErrors = False
self.output.checkForErrors = False
self.input.enableOptions(PinOptions.AllowAny | PinOptions.DictElementSupported)
@@ -33,15 +45,15 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('AnyPin')
- helper.addOutputDataType('AnyPin')
+ helper.addInputDataType("AnyPin")
+ helper.addOutputDataType("AnyPin")
helper.addInputStruct(StructureType.Multi)
helper.addOutputStruct(StructureType.Multi)
return helper
@staticmethod
def category():
- return 'Common'
+ return "Common"
def compute(self, *args, **kwargs):
self.output.setData(self.input.getData())
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/rerouteExecs.py b/PyFlow/Packages/PyFlowBase/Nodes/rerouteExecs.py
index b993cdafa..c51f7536a 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/rerouteExecs.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/rerouteExecs.py
@@ -21,8 +21,8 @@
class rerouteExecs(NodeBase):
def __init__(self, name):
super(rerouteExecs, self).__init__(name)
- self.input = self.createInputPin("in", 'ExecPin')
- self.output = self.createOutputPin("out", 'ExecPin')
+ self.input = self.createInputPin("in", "ExecPin")
+ self.output = self.createOutputPin("out", "ExecPin")
pinAffects(self.input, self.output)
self.input.call = self.output.call
@@ -33,15 +33,15 @@ def postCreate(self, jsonTemplate=None):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'Common'
+ return "Common"
def compute(self, *args, **kwargs):
pass
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/retriggerableDelay.py b/PyFlow/Packages/PyFlowBase/Nodes/retriggerableDelay.py
index a045c1a26..3ad94d216 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/retriggerableDelay.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/retriggerableDelay.py
@@ -21,10 +21,12 @@
class retriggerableDelay(NodeBase):
def __init__(self, name):
super(retriggerableDelay, self).__init__(name)
- self.inp0 = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.delay = self.createInputPin('Delay(s)', 'FloatPin')
+ self.inp0 = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.delay = self.createInputPin("Delay(s)", "FloatPin")
self.delay.setDefaultValue(0.5)
- self.out0 = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin')
+ self.out0 = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin")
self.process = False
self._total = 0.0
self._currentDelay = 0.0
@@ -32,16 +34,16 @@ def __init__(self, name):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('FloatPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("FloatPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
@@ -49,7 +51,7 @@ def keywords():
@staticmethod
def description():
- return 'Delayed call. With ability to reset.'
+ return "Delayed call. With ability to reset."
def Tick(self, delta):
if self.process:
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/sequence.py b/PyFlow/Packages/PyFlowBase/Nodes/sequence.py
index d533539f2..2c33cd3d2 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/sequence.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/sequence.py
@@ -23,38 +23,38 @@
class sequence(NodeBase):
def __init__(self, name):
super(sequence, self).__init__(name)
- self.inExecPin = self.createInputPin('inExec', 'ExecPin', None, self.compute)
+ self.inExecPin = self.createInputPin("inExec", "ExecPin", None, self.compute)
self.headerColor = FLOW_CONTROL_COLOR
def createOutputPin(self, *args, **kwargs):
pinName = str(len(self.outputs) + 1)
- p = CreateRawPin(pinName, self, 'ExecPin', PinDirection.Output)
+ p = CreateRawPin(pinName, self, "ExecPin", PinDirection.Output)
p.enableOptions(PinOptions.Dynamic)
return p
def serialize(self):
data = super(sequence, self).serialize()
- data['numOutputs'] = len(self.outputs)
+ data["numOutputs"] = len(self.outputs)
return data
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
def postCreate(self, jsonTemplate=None):
super(sequence, self).postCreate(jsonTemplate=jsonTemplate)
# recreate dynamic pins
- if jsonTemplate is not None and 'numOutputs' in jsonTemplate:
- for i in range(jsonTemplate['numOutputs']):
+ if jsonTemplate is not None and "numOutputs" in jsonTemplate:
+ for i in range(jsonTemplate["numOutputs"]):
self.createOutputPin()
@staticmethod
@@ -63,7 +63,7 @@ def keywords():
@staticmethod
def description():
- return 'The Sequence node allows for a single execution pulse to trigger a series of events in order. The node may have any number of outputs, all of which get called as soon as the Sequence node receives an input. They will always get called in order, but without any delay. To a typical user, the outputs will likely appear to have been triggered simultaneously.'
+ return "The Sequence node allows for a single execution pulse to trigger a series of events in order. The node may have any number of outputs, all of which get called as soon as the Sequence node receives an input. They will always get called in order, but without any delay. To a typical user, the outputs will likely appear to have been triggered simultaneously."
def compute(self, *args, **kwargs):
for out in self.outputs.values():
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/setVar.py b/PyFlow/Packages/PyFlowBase/Nodes/setVar.py
index d929a2a51..13966702d 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/setVar.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/setVar.py
@@ -24,15 +24,31 @@
class setVar(NodeBase):
def __init__(self, name, var=None):
super(setVar, self).__init__(name)
- self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.outExec = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin')
+ self.inExec = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.compute
+ )
+ self.outExec = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin")
self._var = var
if var.structure == StructureType.Dict:
- self.out = self.createOutputPin('out', self._var.value.valueType, structure=self._var.structure, constraint="2")
- self.inp = self.createInputPin('inp', self._var.value.valueType, structure=self._var.structure, constraint="2")
+ self.out = self.createOutputPin(
+ "out",
+ self._var.value.valueType,
+ structure=self._var.structure,
+ constraint="2",
+ )
+ self.inp = self.createInputPin(
+ "inp",
+ self._var.value.valueType,
+ structure=self._var.structure,
+ constraint="2",
+ )
else:
- self.out = self.createOutputPin('out', self._var.dataType, structure=self._var.structure)
- self.inp = self.createInputPin('inp', self._var.dataType, structure=self._var.structure)
+ self.out = self.createOutputPin(
+ "out", self._var.dataType, structure=self._var.structure
+ )
+ self.inp = self.createInputPin(
+ "inp", self._var.dataType, structure=self._var.structure
+ )
self.inp.disableOptions(PinOptions.RenamingEnabled)
self.out.disableOptions(PinOptions.RenamingEnabled)
@@ -103,7 +119,7 @@ def recreateInput(self, dataType):
self.inp.kill()
del self.inp
self.inp = None
- self.inp = CreateRawPin('inp', self, dataType, PinDirection.Input)
+ self.inp = CreateRawPin("inp", self, dataType, PinDirection.Input)
self.inp.disableOptions(PinOptions.RenamingEnabled)
return self.inp
@@ -111,7 +127,7 @@ def recreateOutput(self, dataType):
self.out.kill()
del self.out
self.out = None
- self.out = CreateRawPin('out', self, dataType, PinDirection.Output)
+ self.out = CreateRawPin("out", self, dataType, PinDirection.Output)
self.out.disableOptions(PinOptions.RenamingEnabled)
return self.out
@@ -120,7 +136,7 @@ def variableUid(self):
def serialize(self):
default = NodeBase.serialize(self)
- default['varUid'] = str(self.var.uid)
+ default["varUid"] = str(self.var.uid)
return default
@staticmethod
@@ -133,7 +149,7 @@ def keywords():
@staticmethod
def description():
- return 'Set variable value'
+ return "Set variable value"
def compute(self, *args, **kwargs):
newValue = self.inp.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/singletonThreadSampleNode.py b/PyFlow/Packages/PyFlowBase/Nodes/singletonThreadSampleNode.py
new file mode 100644
index 000000000..6b88532b1
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/Nodes/singletonThreadSampleNode.py
@@ -0,0 +1,80 @@
+from PyFlow.Core import NodeBase
+from PyFlow.Core.NodeBase import NodePinsSuggestionsHelper
+from PyFlow.Core.Common import *
+
+import threading
+
+import time
+
+@SingletonDecorator
+class singletonThread():
+ isRunning = False
+ instanceCount = 0
+ def __init__(self):
+ self.Runner = threading.Thread(target=self.run_loop, daemon=True)
+ self.value = 0
+ if not self.isRunning:
+ self.Runner.start()
+ self.isRunning = True
+
+ def run_loop(self):
+ self.isRunning = True
+ while self.isRunning:
+ time.sleep(0.1)
+ print("running")
+ self.value +=1
+
+ def cleanUp(self):
+ print(self.instanceCount)
+ self.instanceCount -= 1
+ if self.instanceCount == 0:
+ self.isRunning = False
+ self.Runner.join()
+ del self
+ print("cleanUp")
+
+class singletonThreadSampleNode(NodeBase):
+ def __init__(self, name):
+ super(singletonThreadSampleNode, self).__init__(name)
+ self.singletonThread = None
+ self.value = self.createOutputPin('value', 'IntPin')
+
+ def postCreate(self, jsonTemplate=None):
+ self.singletonThread = singletonThread()
+ if not self.singletonThread.isRunning:
+ self.singletonThread.Runner = threading.Thread(target=self.singletonThread.run_loop, daemon=True)
+ self.singletonThread.Runner.start()
+ self.bCacheEnabled = False
+ super(singletonThreadSampleNode, self).postCreate(jsonTemplate=jsonTemplate)
+
+ @staticmethod
+ def pinTypeHints():
+ helper = NodePinsSuggestionsHelper()
+ helper.addInputDataType('StringPin')
+ helper.addOutputDataType('AnyPin')
+ helper.addInputStruct(StructureType.Single)
+ helper.addOutputStruct(StructureType.Dict)
+ return helper
+
+ @staticmethod
+ def category():
+ return 'Interactive singletonThreads'
+
+ @staticmethod
+ def keywords():
+ return []
+
+ @staticmethod
+ def description():
+ return "Get singletonThreadSampleNode Infos."
+
+ def kill(self, *args, **kwargs):
+ print("deleting singletonThreadSampleNode")
+ self.singletonThread.cleanUp()
+ super(singletonThreadSampleNode, self).kill(*args, **kwargs)
+
+ def compute(self, *args, **kwargs):
+ if not self.singletonThread.isRunning:
+ self.singletonThread.Runner = threading.Thread(target=self.singletonThread.run_loop, daemon=True)
+ self.singletonThread.Runner.start()
+ self.value.setData(self.singletonThread.value)
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/stickyNote.py b/PyFlow/Packages/PyFlowBase/Nodes/stickyNote.py
index 79e0ab8ed..328f03d7b 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/stickyNote.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/stickyNote.py
@@ -22,7 +22,7 @@ def __init__(self, name):
@staticmethod
def category():
- return 'UI'
+ return "UI"
@staticmethod
def keywords():
@@ -30,4 +30,4 @@ def keywords():
@staticmethod
def description():
- return 'Sticky Note to save info with the graph'
+ return "Sticky Note to save info with the graph"
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/storeArgs.py b/PyFlow/Packages/PyFlowBase/Nodes/storeArgs.py
new file mode 100644
index 000000000..12821f93c
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/Nodes/storeArgs.py
@@ -0,0 +1,102 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import asyncio
+import time
+
+from PyFlow.Core import NodeBase, PinBase
+from PyFlow.Core.Common import *
+from PyFlow.Core.NodeBase import NodePinsSuggestionsHelper
+from PyFlow.Packages.PyFlowBase.Nodes import FLOW_CONTROL_COLOR
+import json
+
+
+class storeArgs(NodeBase):
+ def __init__(self, name):
+ super(storeArgs, self).__init__(name)
+ self.bCacheEnabled = False
+ self.hold_pos = self.createInputPin('0', 'StringPin', "")
+ self.combine_result = self.createOutputPin('combine_result', 'StringPin', "")
+
+ def addInPin(self, name, dataType):
+ p_in = self.createInputPin(name, dataType)
+ p_in.enableOptions(PinOptions.RenamingEnabled | PinOptions.Dynamic | PinOptions.Storable)
+ return p_in
+
+ def addOutPin(self, name, dataType):
+ p_out = self.createOutputPin(name, dataType)
+ return p_out
+
+ def postCreate(self, jsonTemplate=None):
+ super(storeArgs, self).postCreate(jsonTemplate=jsonTemplate)
+ # recreate dynamically created pins
+ existingPins = self.namePinInputsMap
+ if jsonTemplate is not None:
+ sortedInputs = sorted(jsonTemplate["inputs"], key=lambda x: x["pinIndex"])
+ for pinJson in sortedInputs:
+ if pinJson['name'] not in existingPins:
+ pinDyn = self.addInPin(pinJson['name'], pinJson["dataType"])
+ pinDyn.uid = uuid.UUID(pinJson['uuid'])
+ try:
+ val = json.loads(pinJson['value'], cls=pinDyn.jsonDecoderClass())
+ pinDyn.setData(val)
+ except:
+ pinDyn.setData(pinDyn.defaultValue())
+ sortedOutputs = sorted(jsonTemplate["outputs"], key=lambda x: x["pinIndex"])
+ for pinJson in sortedOutputs:
+ if pinJson['name'] not in existingPins:
+ pinDyn = self.addOutPin(pinJson['name'], pinJson["dataType"])
+ pinDyn.uid = uuid.UUID(pinJson['uuid'])
+ try:
+ val = json.loads(pinJson['value'], cls=pinDyn.jsonDecoderClass())
+ pinDyn.setData(val)
+ except:
+ pinDyn.setData(pinDyn.defaultValue())
+
+ @staticmethod
+ def pinTypeHints():
+ helper = NodePinsSuggestionsHelper()
+ return helper
+
+ @staticmethod
+ def category():
+ return 'Cmd'
+
+ @staticmethod
+ def keywords():
+ return []
+
+ @staticmethod
+ def description():
+ return 'store args'
+
+ def compute(self, *args, **kwargs):
+ cmd_line = ""
+ output_map = self.namePinOutputsMap
+ for elem in self.orderedInputs.values():
+ name = elem.name.lstrip("> ")
+ if name in output_map:
+ output_map[name].setData(elem.getData())
+ if 0 == len(name) or name.isdigit():
+ cmd_line += " {0} ".format(elem.getData())
+ continue
+ if 1 == len(name) and name.isalpha():
+ cmd_line += " -{0} {1} ".format(name ,elem.getData())
+ continue
+ if name[:1] == "-":
+ cmd_line += " {0} {1} ".format(name ,elem.getData())
+ continue
+ cmd_line += " --{0} {1} ".format(name ,elem.getData())
+ self.combine_result.setData(cmd_line)
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/stringToArray.py b/PyFlow/Packages/PyFlowBase/Nodes/stringToArray.py
index 06a41a2df..e5fbb0dfe 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/stringToArray.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/stringToArray.py
@@ -22,16 +22,20 @@
class stringToArray(NodeBase):
def __init__(self, name):
super(stringToArray, self).__init__(name)
- self.arrayData = self.createInputPin('data', 'StringPin', structure=StructureType.Single)
- self.outArray = self.createOutputPin('out', 'AnyPin', structure=StructureType.Array)
- self.result = self.createOutputPin('result', 'BoolPin')
+ self.arrayData = self.createInputPin(
+ "data", "StringPin", structure=StructureType.Single
+ )
+ self.outArray = self.createOutputPin(
+ "out", "AnyPin", structure=StructureType.Array
+ )
+ self.result = self.createOutputPin("result", "BoolPin")
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('StringPin')
- helper.addOutputDataType('AnyPin')
- helper.addOutputDataType('BoolPin')
+ helper.addInputDataType("StringPin")
+ helper.addOutputDataType("AnyPin")
+ helper.addOutputDataType("BoolPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Array)
helper.addOutputStruct(StructureType.Single)
@@ -39,7 +43,7 @@ def pinTypeHints():
@staticmethod
def category():
- return 'GenericTypes'
+ return "GenericTypes"
@staticmethod
def keywords():
@@ -47,11 +51,11 @@ def keywords():
@staticmethod
def description():
- return 'Creates a list from ast.literal_eval(data) and then converts to output DataType'
+ return "Creates a list from ast.literal_eval(data) and then converts to output DataType"
def compute(self, *args, **kwargs):
outArray = []
- stringData = "[%s]"%self.arrayData.getData()
+ stringData = "[%s]" % self.arrayData.getData()
if self.outArray.dataType == "AnyPin":
self.outArray.setData(outArray)
self.result.setData(False)
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/subProcess.py b/PyFlow/Packages/PyFlowBase/Nodes/subProcess.py
new file mode 100644
index 000000000..c81cc605d
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/Nodes/subProcess.py
@@ -0,0 +1,182 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import asyncio
+import time
+import uuid
+from PyFlow.Core import NodeBase, PinBase
+from PyFlow.Core.Common import *
+from PyFlow.Core.NodeBase import NodePinsSuggestionsHelper
+from PyFlow.Packages.PyFlowBase.Nodes import FLOW_CONTROL_COLOR
+import json
+
+
+class subProcess(NodeBase):
+ def __init__(self, name):
+ super(subProcess, self).__init__(name)
+ self.bCacheEnabled = False
+ self.inExecPin = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
+ self.cwd = self.createInputPin('cwd', 'StringPin', "")
+ self.cmd = self.createInputPin('cmd', 'StringPin')
+ self.cmd_end = self.createInputPin('cmd_end', 'StringPin')
+ self.cmd_end.enableOptions(PinOptions.AllowMultipleConnections)
+ self.cmd_opt = self.createInputPin('cmd_opt', 'StringPin')
+ self.cmd_opt.enableOptions(PinOptions.AllowMultipleConnections)
+ self.outExecPin = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin')
+ self.error_num = self.createOutputPin('error_num', 'IntPin')
+ self.std_msg = self.createOutputPin('std_msg', 'StringPin')
+ self.error_msg = self.createOutputPin('error_msg', 'StringPin')
+ self.all_msg = self.createOutputPin('all_msg', 'StringPin')
+ self.is_running = self.createOutputPin('is_running', 'BoolPin', False)
+ self.headerColor = FLOW_CONTROL_COLOR
+ self.proc = None
+ self.proc_task:asyncio.Task = None
+ self.proc_task_uuid = uuid.uuid4()
+ self.proc_task_args = None
+ self.proc_task_kwargs =None
+
+ def addInPin(self, name, dataType):
+ p = self.createInputPin(name, dataType)
+ p.enableOptions(PinOptions.RenamingEnabled | PinOptions.Dynamic
+ | PinOptions.AllowMultipleConnections | PinOptions.Storable)
+ return p
+
+ def postCreate(self, jsonTemplate=None):
+ super(subProcess, self).postCreate(jsonTemplate=jsonTemplate)
+ # recreate dynamically created pins
+ existingPins = self.namePinInputsMap
+ if jsonTemplate is not None:
+ sortedInputs = sorted(jsonTemplate["inputs"], key=lambda x: x["pinIndex"])
+ for inPinJson in sortedInputs:
+ if inPinJson['name'] not in existingPins:
+ inDyn = self.addInPin(inPinJson['name'], inPinJson["dataType"])
+ inDyn.uid = uuid.UUID(inPinJson['uuid'])
+ try:
+ val = json.loads(inPinJson['value'], cls=inDyn.jsonDecoderClass())
+ inDyn.setData(val)
+ except:
+ inDyn.setData(inDyn.defaultValue())
+
+ @staticmethod
+ def pinTypeHints():
+ helper = NodePinsSuggestionsHelper()
+ helper.addInputDataType('ExecPin')
+ helper.addInputDataType('StringPin')
+ helper.addInputDataType('StringPin')
+ helper.addInputDataType('StringPin')
+ helper.addInputDataType('StringPin')
+ helper.addOutputDataType('ExecPin')
+ helper.addOutputDataType('IntPin')
+ helper.addOutputDataType('StringPin')
+ helper.addOutputDataType('StringPin')
+ helper.addOutputDataType('StringPin')
+ helper.addInputStruct(StructureType.Single)
+ helper.addOutputStruct(StructureType.Single)
+ return helper
+
+ @staticmethod
+ def category():
+ return 'Cmd'
+
+ @staticmethod
+ def keywords():
+ return []
+
+ @staticmethod
+ def description():
+ return 'call an subprocess'
+
+ def compute(self, *args, **kwargs):
+ cmd_head = str(self.cmd.getData())
+ if len(cmd_head) <= 0:
+ self.error_num.setData(-1)
+ self.std_msg.setData("")
+ self.error_msg.setData("")
+ self.all_msg.setData("")
+ self.outExecPin.call(*args, **kwargs)
+ else:
+ cmd_opt = str(self.cmd_opt.getData())
+ cmd_end = str(self.cmd_end.getData())
+ opt_except_pin = [self.inExecPin, self.cmd, self.cmd_end, self.cmd_opt, self.cwd]
+ cmd_opt_ext = ""
+ for elem in self.orderedInputs.values():
+ if elem in opt_except_pin:
+ continue
+ name = elem.name.lstrip()
+ if 0 == len(name) or name.isdigit():
+ cmd_opt_ext += " {0} ".format(elem.getData())
+ continue
+ if 1 == len(name) and name.isalpha():
+ cmd_opt_ext += " -{0} {1} ".format(name ,elem.getData())
+ continue
+ if name[:1] == "-":
+ cmd_opt_ext += " {0} {1} ".format(name ,elem.getData())
+ continue
+ cmd_opt_ext += " --{0} {1} ".format(name ,elem.getData())
+ cmd = f"{cmd_head} {cmd_opt} {cmd_opt_ext} {cmd_end} "
+ if self.proc_task:
+ self.proc_task.cancel("interrupt")
+ self.proc_task = None
+ self.proc_task_uuid = uuid.uuid4()
+ self.proc_task_args = args
+ self.proc_task_kwargs = kwargs
+ self.is_running.setData(True)
+ self.proc_task = asyncio.get_event_loop().create_task(self._run_cmd(self.proc_task_uuid, cmd, self.cwd.getData()))
+
+ async def _run_cmd(self, _uuid, cmd, cwd):
+ if None != self.proc and None == self.proc.returncode:
+ self.proc.terminate()
+ self.computing.send()
+ self.proc = None
+ uuid = _uuid
+ if len(cwd.strip(" ")) > 0:
+ proc:asyncio.subprocess.Process = await asyncio.create_subprocess_shell(
+ cmd, cwd=cwd, stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE)
+ else:
+ proc:asyncio.subprocess.Process = await asyncio.create_subprocess_shell(
+ cmd, stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE)
+ self.proc = proc
+ stdout, stderr = await proc.communicate()
+ ret_code = proc.returncode
+ if uuid == self.proc_task_uuid:
+ self.proc = None
+ return uuid, ret_code, stdout, stderr
+
+ def Tick(self, delta):
+ super(subProcess, self).Tick(delta)
+ if self.proc_task and self.proc_task.done():
+ proc_task:asyncio.Task = self.proc_task
+ self.proc_task = None
+ if not proc_task.cancelled():
+ try:
+ uuid, error_num, stdout, stderr = proc_task.result()
+ if uuid == self.proc_task_uuid:
+ self.error_num.setData(error_num)
+ self.std_msg.setData(stdout)
+ self.error_msg.setData(stderr)
+ self.all_msg.setData(f"{stdout}\n{stderr}")
+ except Exception as e:
+ print("subProcess proc task raise exception", e)
+ self.error_num.setData(-1)
+ self.std_msg.setData("")
+ self.error_msg.setData(str(e))
+ self.all_msg.setData(str(e))
+ if self.proc:
+ if None == self.proc.returncode:
+ self.proc.terminate()
+ self.proc = None
+ self.computed.send()
+ self.is_running.setData(False)
+ self.outExecPin.call(*self.proc_task_args, **self.proc_task_kwargs)
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/switchOnString.py b/PyFlow/Packages/PyFlowBase/Nodes/switch.py
similarity index 73%
rename from PyFlow/Packages/PyFlowBase/Nodes/switchOnString.py
rename to PyFlow/Packages/PyFlowBase/Nodes/switch.py
index 8c97846c1..8106e243c 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/switchOnString.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/switch.py
@@ -18,21 +18,27 @@
from PyFlow.Core import PinBase
from PyFlow.Core.Common import *
from PyFlow.Packages.PyFlowBase.Nodes import FLOW_CONTROL_COLOR
+import uuid
-
-class switchOnString(NodeBase):
+class switch(NodeBase):
def __init__(self, name):
- super(switchOnString, self).__init__(name)
+ super(switch, self).__init__(name)
self.inExecPin = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.compute)
- self.inString = self.createInputPin('string', 'StringPin')
+ self.inVal = self.createInputPin('in', 'AnyPin', supportedPinDataTypes=['StringPin', 'IntPin', 'BoolPin'])
self.defaultPin = self.createOutputPin('default', 'ExecPin')
self.headerColor = FLOW_CONTROL_COLOR
- def addOutPin(self):
- name = self.getUniqPinName("option")
- return self.addNamedOutPin(name)
-
- def addNamedOutPin(self, name):
+ def postCreate(self, jsonTemplate=None):
+ super(switch, self).postCreate(jsonTemplate=jsonTemplate)
+ existingPins = self.namePinOutputsMap
+ if jsonTemplate is not None:
+ sortedOutputs = sorted(jsonTemplate['outputs'], key=lambda pinDict: pinDict["pinIndex"])
+ for pinJson in sortedOutputs:
+ if pinJson['name'] not in existingPins:
+ inDyn = self.addOutPin(pinJson['name'])
+ inDyn.uid = uuid.UUID(pinJson['uuid'])
+
+ def addOutPin(self, name):
p = self.createOutputPin(name, 'ExecPin')
p.enableOptions(PinOptions.RenamingEnabled | PinOptions.Dynamic)
pinAffects(self.inExecPin, p)
@@ -61,20 +67,9 @@ def description():
return 'Execute output depending on input string'
def compute(self, *args, **kwargs):
- string = self.inString.getData()
+ string = str(self.inVal.getData())
namePinOutputsMap = self.namePinOutputsMap
if string in namePinOutputsMap:
namePinOutputsMap[string].call(*args, **kwargs)
else:
self.defaultPin.call(*args, **kwargs)
-
- def postCreate(self, jsonTemplate=None):
- super(switchOnString, self).postCreate(jsonTemplate=jsonTemplate)
- # recreate dynamically created pins
- existingPins = self.namePinOutputsMap
- if jsonTemplate is not None:
- sortedOutputs = sorted(jsonTemplate["outputs"], key=lambda x: x["pinIndex"])
- for outPinJson in sortedOutputs:
- if outPinJson['name'] not in existingPins:
- dynOut = self.addNamedOutPin(outPinJson['name'])
- dynOut.uid = uuid.UUID(outPinJson['uuid'])
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/tick.py b/PyFlow/Packages/PyFlowBase/Nodes/tick.py
index 8111c05e8..1a93d5e45 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/tick.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/tick.py
@@ -22,24 +22,24 @@
class tick(NodeBase):
def __init__(self, name):
super(tick, self).__init__(name)
- self.enabled = self.createInputPin("enabled", 'BoolPin')
- self.out = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, 'ExecPin')
- self.delta = self.createOutputPin("delta", 'FloatPin')
+ self.enabled = self.createInputPin("enabled", "BoolPin")
+ self.out = self.createOutputPin(DEFAULT_OUT_EXEC_NAME, "ExecPin")
+ self.delta = self.createOutputPin("delta", "FloatPin")
self.headerColor = FLOW_CONTROL_COLOR
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('BoolPin')
- helper.addOutputDataType('FloatPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("BoolPin")
+ helper.addOutputDataType("FloatPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
def Tick(self, delta):
super(tick, self).Tick(delta)
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/timer.py b/PyFlow/Packages/PyFlowBase/Nodes/timer.py
index 08bca2c44..3eae58ceb 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/timer.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/timer.py
@@ -23,10 +23,10 @@
class timer(NodeBase):
def __init__(self, name):
super(timer, self).__init__(name)
- self.out = self.createOutputPin("OUT", 'ExecPin')
- self.beginPin = self.createInputPin("Begin", 'ExecPin', None, self.start)
- self.stopPin = self.createInputPin("Stop", 'ExecPin', None, self.stop)
- self.interval = self.createInputPin("Delta(s)", 'FloatPin')
+ self.out = self.createOutputPin("OUT", "ExecPin")
+ self.beginPin = self.createInputPin("Begin", "ExecPin", None, self.start)
+ self.stopPin = self.createInputPin("Stop", "ExecPin", None, self.stop)
+ self.interval = self.createInputPin("Delta(s)", "FloatPin")
self.interval.setDefaultValue(0.2)
self.accum = 0.0
self.bWorking = False
@@ -46,9 +46,9 @@ def Tick(self, delta):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('FloatPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("FloatPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@@ -63,4 +63,4 @@ def start(self, *args, **kwargs):
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/whileLoop.py b/PyFlow/Packages/PyFlowBase/Nodes/whileLoop.py
index 45d2140c0..53d05b79a 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/whileLoop.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/whileLoop.py
@@ -22,10 +22,12 @@
class whileLoop(NodeBase):
def __init__(self, name):
super(whileLoop, self).__init__(name)
- self.inExec = self.createInputPin(DEFAULT_IN_EXEC_NAME, 'ExecPin', None, self.begin)
- self.bCondition = self.createInputPin('Condition', 'BoolPin')
- self.loopBody = self.createOutputPin('LoopBody', 'ExecPin')
- self.completed = self.createOutputPin('Completed', 'ExecPin')
+ self.inExec = self.createInputPin(
+ DEFAULT_IN_EXEC_NAME, "ExecPin", None, self.begin
+ )
+ self.bCondition = self.createInputPin("Condition", "BoolPin")
+ self.loopBody = self.createOutputPin("LoopBody", "ExecPin")
+ self.completed = self.createOutputPin("Completed", "ExecPin")
self.bProcess = False
self._dirty = False
self.headerColor = FLOW_CONTROL_COLOR
@@ -36,15 +38,15 @@ def begin(self, *args, **kwargs):
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('BoolPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("BoolPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
@@ -70,4 +72,4 @@ def Tick(self, deltaTime):
@staticmethod
def description():
- return 'The WhileLoop node will output a result so long as a specific condition is true. During each iteration of the loop, it checks to see the current status of its input boolean value. As soon as it reads false, the loop breaks.\nAs with While loops in programming languages, extra care must be taken to prevent infinite loops from occurring.'
+ return "The WhileLoop node will output a result so long as a specific condition is true. During each iteration of the loop, it checks to see the current status of its input boolean value. As soon as it reads false, the loop breaks.\nAs with While loops in programming languages, extra care must be taken to prevent infinite loops from occurring."
diff --git a/PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py b/PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py
index 44314c481..d56cd07ee 100644
--- a/PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py
+++ b/PyFlow/Packages/PyFlowBase/Nodes/whileLoopBegin.py
@@ -24,36 +24,36 @@ class whileLoopBegin(NodeBase):
def __init__(self, name):
super(whileLoopBegin, self).__init__(name)
self.lastCondition = False
- self.inExec = self.createInputPin('inExec', 'ExecPin', None, self.compute)
- self.condition = self.createInputPin('Condition', 'BoolPin')
- self.loopEndNode = self.createInputPin('Paired block', 'StringPin')
- self.loopEndNode.setInputWidgetVariant("ObjectPathWIdget")
+ self.inExec = self.createInputPin("inExec", "ExecPin", None, self.compute)
+ self.condition = self.createInputPin("Condition", "BoolPin")
+ self.loopEndNode = self.createInputPin("Paired block", "StringPin")
+ self.loopEndNode.setInputWidgetVariant("ObjectPathWidget")
- self.loopBody = self.createOutputPin('LoopBody', 'ExecPin')
+ self.loopBody = self.createOutputPin("LoopBody", "ExecPin")
self.headerColor = FLOW_CONTROL_ORANGE
self.setExperimental()
@staticmethod
def pinTypeHints():
helper = NodePinsSuggestionsHelper()
- helper.addInputDataType('ExecPin')
- helper.addInputDataType('BoolPin')
- helper.addOutputDataType('ExecPin')
+ helper.addInputDataType("ExecPin")
+ helper.addInputDataType("BoolPin")
+ helper.addOutputDataType("ExecPin")
helper.addInputStruct(StructureType.Single)
helper.addOutputStruct(StructureType.Single)
return helper
@staticmethod
def category():
- return 'FlowControl'
+ return "FlowControl"
@staticmethod
def keywords():
- return ['iter']
+ return ["iter"]
@staticmethod
def description():
- return 'While loop begin block'
+ return "While loop begin block"
def onNext(self, *args, **kwargs):
currentCondition = self.condition.getData()
diff --git a/PyFlow/Packages/PyFlowBase/Pins/AnyPin.py b/PyFlow/Packages/PyFlowBase/Pins/AnyPin.py
index 02509c97d..ddf67c8c4 100644
--- a/PyFlow/Packages/PyFlowBase/Pins/AnyPin.py
+++ b/PyFlow/Packages/PyFlowBase/Pins/AnyPin.py
@@ -15,13 +15,10 @@
from blinker import Signal
import json
-from Qt import QtGui
-from nine import str
from PyFlow.Core import PinBase
from PyFlow.Core.Common import *
from PyFlow import getAllPinClasses
-from PyFlow import CreateRawPin
from PyFlow import findPinClassByType
from PyFlow import getPinDefaultValueByType
@@ -32,20 +29,20 @@ class AnyPin(PinBase):
This Pin Type is an abstraction of Pins, it is a Pin that will act as any other defined Pin.
This type of Pin allow to create abstract Nodes that can operate in more than one dataType.
- By default AnyPin non Initialized will be marked as error, as PyFlow can't know what is inside.
+ By default, AnyPin non Initialized will be marked as error, as PyFlow can't know what is inside.
This Error can be avoided by enabling :py:attr:`PyFlow.Core.Common.PinOptions.AllowAny`. That's how NonTyped Lists are made.
- By default :py:attr:`PyFlow.Core.Common.PinOptions.ChangeTypeOnConnection` is enabled, and that means that it will change
+ By default, :py:attr:`PyFlow.Core.Common.PinOptions.ChangeTypeOnConnection` is enabled, and that means that it will change
its internal dataType to the new dataType provided by connection or user Initialization. If disabled, pin will not allow changes.
Is important to define a bunch of allowedDataTypes on pin creation, this will restrict what pins can be connected and what no,
so even being a AnyPin, it can be defined to allow for example only ["FloatPin","IntPin"] so only those could be connected.
:param self.singleInit: can be set to True, so once initialized, it will never be able to change dataType
- :param self.checkForErrors: can be set To False so it will never try to find errors
+ :param self.checkForErrors: can be set To False, so it will never try to find errors
Signals:
- * **typeChanged** : Fired when dataType has change
+ * **typeChanged** : Fired when dataType has changed
"""
@@ -67,7 +64,9 @@ def __init__(self, name, owningNode, direction, **kwargs):
self.singleInit = False
self.checkForErrors = True
self.enableOptions(PinOptions.ChangeTypeOnConnection)
- self._defaultSupportedDataTypes = self._supportedDataTypes = tuple([pin.__name__ for pin in getAllPinClasses() if pin.IsValuePin()])
+ self._defaultSupportedDataTypes = self._supportedDataTypes = tuple(
+ [pin.__name__ for pin in getAllPinClasses() if pin.IsValuePin()]
+ )
self.canChange = True
self._super = None
self.prevDataType = None
@@ -97,15 +96,15 @@ def IsValuePin():
@staticmethod
def defColor():
- return (200, 200, 200, 255)
+ return 200, 200, 200, 255
@staticmethod
def color():
- return (200, 200, 200, 255)
+ return 200, 200, 200, 255
@staticmethod
def pinDataTypeHint():
- return 'AnyPin', None
+ return "AnyPin", None
@staticmethod
def internalDataStructure():
@@ -139,22 +138,29 @@ def setTypeFromData(self, data):
if type(data) == pType:
if pin.__name__ != self.activeDataType:
if self.optionEnabled(PinOptions.ChangeTypeOnConnection):
- traverseConstrainedPins(self, lambda x: self.updateOnConnectionCallback(x, pin.__name__, True, None))
+ traverseConstrainedPins(
+ self,
+ lambda x: self.updateOnConnectionCallback(
+ x, pin.__name__, True, None
+ ),
+ )
self.owningNode().checkForErrors()
break
- def updateError(self, traversed=[], updateNeis=False):
+ def updateError(self, traversed=None, updateNeis=False):
"""Check if pin dataType is "AnyPin" and if it is, checks if it can change Type on connection, and if it can, marked as error.
- Is a iterative Function that traverses connected and constrained Pins
+ Is an iterative Function that traverses connected and constrained Pins
:param traversed: Current Iterated neighbors, defaults to []
:type traversed: list, optional
:param updateNeis: Try to update Constrained Pins parents error display, it can be slow so use carefully, defaults to False
:type updateNeis: bool, optional
"""
+ if traversed is None:
+ traversed = []
if not self.checkForErrors:
return
- nodePins = set([self])
+ nodePins = {self}
if self.constraint:
nodePins = set(self.owningNode().constraints[self.constraint])
for connectedPin in getConnectedPins(self):
@@ -162,9 +168,17 @@ def updateError(self, traversed=[], updateNeis=False):
nodePins.add(connectedPin)
for neighbor in nodePins:
if neighbor not in traversed:
- if all([neighbor.activeDataType == "AnyPin",
- neighbor.canChangeTypeOnConnection([], neighbor.optionEnabled(PinOptions.ChangeTypeOnConnection), []),
- not neighbor.optionEnabled(PinOptions.AllowAny)]):
+ if all(
+ [
+ neighbor.activeDataType == "AnyPin",
+ neighbor.canChangeTypeOnConnection(
+ [],
+ neighbor.optionEnabled(PinOptions.ChangeTypeOnConnection),
+ [],
+ ),
+ not neighbor.optionEnabled(PinOptions.AllowAny),
+ ]
+ ):
neighbor.setError("AnyPin Not Initialized")
neighbor.super = None
else:
@@ -189,9 +203,10 @@ def serialize(self):
if constrainedType != self.__class__.__name__:
pinClass = findPinClassByType(constrainedType)
# serialize with active type's encoder
- dt['value'] = json.dumps(
- self.currentData(), cls=pinClass.jsonEncoderClass())
- dt['currDataType'] = constrainedType
+ dt["value"] = json.dumps(
+ self.currentData(), cls=pinClass.jsonEncoderClass()
+ )
+ dt["currDataType"] = constrainedType
return dt
def deserialize(self, jsonData):
@@ -206,8 +221,7 @@ def deserialize(self, jsonData):
pinClass = findPinClassByType(self.activeDataType)
try:
- self.setData(json.loads(
- jsonData['value'], cls=pinClass.jsonDecoderClass()))
+ self.setData(json.loads(jsonData["value"], cls=pinClass.jsonDecoderClass()))
except:
self.setData(self.defaultValue())
@@ -223,8 +237,9 @@ def pinConnected(self, other):
"""
super(AnyPin, self).pinConnected(other)
self._lastError2 = self._lastError
- self.updateError([], self.activeDataType ==
- "AnyPin" or self.prevDataType == "AnyPin")
+ self.updateError(
+ [], self.activeDataType == "AnyPin" or self.prevDataType == "AnyPin"
+ )
self.owningNode().checkForErrors()
def aboutToConnect(self, other):
@@ -236,9 +251,16 @@ def aboutToConnect(self, other):
:param other: Pin that will be connected to this Pin.
:type other: :py:class:`PyFlow.Core.PinBase.PinBase`
"""
- if self.canChangeTypeOnConnection([], self.optionEnabled(PinOptions.ChangeTypeOnConnection), []):
+ if self.canChangeTypeOnConnection(
+ [], self.optionEnabled(PinOptions.ChangeTypeOnConnection), []
+ ):
dataType = other.dataType
- traverseConstrainedPins(self, lambda pin: self.updateOnConnectionCallback(pin, dataType, False, other))
+ traverseConstrainedPins(
+ self,
+ lambda pin: self.updateOnConnectionCallback(
+ pin, dataType, False, other
+ ),
+ )
super(AnyPin, self).aboutToConnect(other)
def pinDisconnected(self, other):
@@ -250,7 +272,9 @@ def pinDisconnected(self, other):
:type other: :py:class:`PyFlow.Core.PinBase.PinBase`
"""
super(AnyPin, self).pinDisconnected(other)
- self.updateError([], self.activeDataType == "AnyPin" or self.prevDataType == "AnyPin")
+ self.updateError(
+ [], self.activeDataType == "AnyPin" or self.prevDataType == "AnyPin"
+ )
self._lastError2 = self._lastError
if self.activeDataType == "AnyPin" and self._lastError2 is None:
self.prevDataType = "AnyPin"
@@ -258,7 +282,8 @@ def pinDisconnected(self, other):
self.prevDataType = None
self.owningNode().checkForErrors()
- def updateOnConnectionCallback(self, pin, dataType, init=False, other=None):
+ @staticmethod
+ def updateOnConnectionCallback(pin, dataType, init=False, other=None):
"""Method Called in traverse function :py:func:`PyFlow.Core.Common.traverseConstrainedPins`
This Function is called for all the connected Pins to the initial Pin calling it.
@@ -270,33 +295,53 @@ def updateOnConnectionCallback(self, pin, dataType, init=False, other=None):
:type dataType: string
:param init: If initializing AnyPin can have same strength as other types, if not, "AnyPin" Pin will always be weaker than other dataType, if, defaults to False
:type init: bool, optional
- :param other: other Pin to heredate stuff from him, defaults to None
+ :param other: other Pin to here-date stuff from him, defaults to None
:type other: :py:class:`PyFlow.Core.PinBase.PinBase`, optional
"""
free = pin.checkFree([])
if free:
- if (dataType == "AnyPin" and not init):
+ if dataType == "AnyPin" and not init:
if not other:
return
else:
- if pin.dataType != "AnyPin" and pin.dataType in other.allowedDataTypes([], other._supportedDataTypes) and other.canChangeTypeOnConnection([], other.optionEnabled(PinOptions.ChangeTypeOnConnection), []):
+ if (
+ pin.dataType != "AnyPin"
+ and pin.dataType
+ in other.allowedDataTypes([], other._supportedDataTypes)
+ and other.canChangeTypeOnConnection(
+ [],
+ other.optionEnabled(PinOptions.ChangeTypeOnConnection),
+ [],
+ )
+ ):
dataType = pin.dataType
- if any([dataType in pin.allowedDataTypes([], pin._supportedDataTypes),
+ if any(
+ [
+ dataType in pin.allowedDataTypes([], pin._supportedDataTypes),
dataType == "AnyPin",
- (pin.checkFree([], False) and dataType in pin.allowedDataTypes([], pin._defaultSupportedDataTypes, defaults=True))]):
+ (
+ pin.checkFree([], False)
+ and dataType
+ in pin.allowedDataTypes(
+ [], pin._defaultSupportedDataTypes, defaults=True
+ )
+ ),
+ ]
+ ):
a = pin.setType(dataType)
if a:
if other:
if pin.optionEnabled(PinOptions.ChangeTypeOnConnection):
pin._supportedDataTypes = other.allowedDataTypes(
- [], other._supportedDataTypes)
+ [], other._supportedDataTypes
+ )
if dataType == "AnyPin":
if pin.optionEnabled(PinOptions.ChangeTypeOnConnection):
pin._supportedDataTypes = pin._defaultSupportedDataTypes
pin.supportedDataTypes = lambda: pin._supportedDataTypes
- def checkFree(self, checked=[], selfCheck=True):
+ def checkFree(self, checked=None, selfCheck=True):
"""Recursive Function to find if all connected Pins are of type :py:class:`AnyPin` and canChange On connection,
so basically it checks if a Pin is free to change its dataType to another one
@@ -308,6 +353,8 @@ def checkFree(self, checked=[], selfCheck=True):
:returns: True if Pin can change current dataType
:rtype: bool
"""
+ if checked is None:
+ checked = []
if self.constraint is None or self.dataType == self.__class__.__name__:
return True
else:
@@ -319,10 +366,10 @@ def checkFree(self, checked=[], selfCheck=True):
if c not in checked:
con.append(c)
else:
- free = True
checked.append(self)
canChange = self.canChangeTypeOnConnection(
- [], self.optionEnabled(PinOptions.ChangeTypeOnConnection), [])
+ [], self.optionEnabled(PinOptions.ChangeTypeOnConnection), []
+ )
free = canChange
for port in self.owningNode().constraints[self.constraint] + con:
if port not in checked:
@@ -333,7 +380,9 @@ def checkFree(self, checked=[], selfCheck=True):
free = port.checkFree(checked)
return free
- def allowedDataTypes(self, checked=[], dataTypes=[], selfCheck=True, defaults=False):
+ def allowedDataTypes(
+ self, checked=None, dataTypes=None, selfCheck=True, defaults=False
+ ):
"""Recursive Function to intersect allowedDatatypes of all connected pins.
:param checked: Already visited Pins, defaults to []
@@ -348,7 +397,14 @@ def allowedDataTypes(self, checked=[], dataTypes=[], selfCheck=True, defaults=Fa
:returns: List containing all the intersected dataTypes
:rtype: list
"""
- if not self.optionEnabled(PinOptions.ChangeTypeOnConnection) and self.activeDataType == "AnyPin":
+ if checked is None:
+ checked = []
+ if dataTypes is None:
+ dataTypes = []
+ if (
+ not self.optionEnabled(PinOptions.ChangeTypeOnConnection)
+ and self.activeDataType == "AnyPin"
+ ):
return self._defaultSupportedDataTypes
con = []
neis = []
@@ -365,37 +421,45 @@ def allowedDataTypes(self, checked=[], dataTypes=[], selfCheck=True, defaults=Fa
if port not in checked:
checked.append(port)
if not defaults:
- dataTypes = list(set(dataTypes) & set(
- port._supportedDataTypes))
+ dataTypes = list(set(dataTypes) & set(port._supportedDataTypes))
else:
- dataTypes = list(set(dataTypes) & set(
- port._defaultSupportedDataTypes))
+ dataTypes = list(
+ set(dataTypes) & set(port._defaultSupportedDataTypes)
+ )
dataTypes = port.allowedDataTypes(
- checked, dataTypes, selfCheck=True, defaults=defaults)
+ checked, dataTypes, selfCheck=True, defaults=defaults
+ )
return dataTypes
def initType(self, dataType, initializing=False):
"""Same as :py:func:`AnyPin.aboutToConnect` but instead of using another Pin using a dataType name
- :param dataType: New DataType we want the pin to became
+ :param dataType: New DataType we want the pin to become
:type dataType: string
:param initializing: If initializing AnyPin can have same strength as other types, if not, "AnyPin" Pin will always be weaker than other dataType, if, defaults to False
:type initializing: bool, optional
:returns: True if it can change to the asked dataType
:rtype: bool
"""
- if self.canChangeTypeOnConnection([], self.optionEnabled(PinOptions.ChangeTypeOnConnection), []):
+ if self.canChangeTypeOnConnection(
+ [], self.optionEnabled(PinOptions.ChangeTypeOnConnection), []
+ ):
traverseConstrainedPins(
- self, lambda pin: self.updateOnConnectionCallback(pin, dataType, initializing))
+ self,
+ lambda pin: self.updateOnConnectionCallback(
+ pin, dataType, initializing
+ ),
+ )
self._lastError2 = self._lastError
- self.updateError([], self.activeDataType ==
- "AnyPin" or self.prevDataType == "AnyPin")
+ self.updateError(
+ [], self.activeDataType == "AnyPin" or self.prevDataType == "AnyPin"
+ )
self.owningNode().checkForErrors()
return True
return False
def setType(self, dataType):
- """Here is where :py:class:`AnyPin` heredates all the properties from other defined dataTypes and act like those
+ """Here is where :py:class:`AnyPin` here-dates all the properties from other defined dataTypes and act like those
:param dataType: New DataType
:type dataType: string
diff --git a/PyFlow/Packages/PyFlowBase/Pins/BoolPin.py b/PyFlow/Packages/PyFlowBase/Pins/BoolPin.py
index 7bf4b7eea..c8063a92f 100644
--- a/PyFlow/Packages/PyFlowBase/Pins/BoolPin.py
+++ b/PyFlow/Packages/PyFlowBase/Pins/BoolPin.py
@@ -14,11 +14,11 @@
from PyFlow.Core import PinBase
-from PyFlow.Core.Common import *
class BoolPin(PinBase):
"""doc string for BoolPin"""
+
def __init__(self, name, parent, direction, **kwargs):
super(BoolPin, self).__init__(name, parent, direction, **kwargs)
self.setDefaultValue(False)
@@ -29,15 +29,15 @@ def IsValuePin():
@staticmethod
def supportedDataTypes():
- return ('BoolPin', 'IntPin',)
+ return "BoolPin", "IntPin"
@staticmethod
def pinDataTypeHint():
- return 'BoolPin', False
+ return "BoolPin", False
@staticmethod
def color():
- return (255, 0, 0, 255)
+ return 255, 0, 0, 255
@staticmethod
def internalDataStructure():
diff --git a/PyFlow/Packages/PyFlowBase/Pins/ExecPin.py b/PyFlow/Packages/PyFlowBase/Pins/ExecPin.py
index 119def552..ae19fb419 100644
--- a/PyFlow/Packages/PyFlowBase/Pins/ExecPin.py
+++ b/PyFlow/Packages/PyFlowBase/Pins/ExecPin.py
@@ -43,11 +43,11 @@ def IsValuePin():
@staticmethod
def supportedDataTypes():
- return ('ExecPin',)
+ return ("ExecPin",)
@staticmethod
def pinDataTypeHint():
- return 'ExecPin', None
+ return "ExecPin", None
@staticmethod
def internalDataStructure():
@@ -55,7 +55,7 @@ def internalDataStructure():
@staticmethod
def color():
- return (200, 200, 200, 255)
+ return 200, 200, 200, 255
def setData(self, data):
pass
diff --git a/PyFlow/Packages/PyFlowBase/Pins/FloatPin.py b/PyFlow/Packages/PyFlowBase/Pins/FloatPin.py
index 5f4e5b5cd..854a7aecd 100644
--- a/PyFlow/Packages/PyFlowBase/Pins/FloatPin.py
+++ b/PyFlow/Packages/PyFlowBase/Pins/FloatPin.py
@@ -29,16 +29,16 @@ def IsValuePin():
@staticmethod
def pinDataTypeHint():
- '''data type index and default value'''
- return 'FloatPin', 0.0
+ """data type index and default value"""
+ return "FloatPin", 0.0
@staticmethod
def color():
- return (96, 169, 23, 255)
+ return 96, 169, 23, 255
@staticmethod
def supportedDataTypes():
- return ('FloatPin', 'IntPin',)
+ return "FloatPin", "IntPin"
@staticmethod
def internalDataStructure():
diff --git a/PyFlow/Packages/PyFlowBase/Pins/IntPin.py b/PyFlow/Packages/PyFlowBase/Pins/IntPin.py
index b72a84f62..f28f9663a 100644
--- a/PyFlow/Packages/PyFlowBase/Pins/IntPin.py
+++ b/PyFlow/Packages/PyFlowBase/Pins/IntPin.py
@@ -14,11 +14,11 @@
from PyFlow.Core import PinBase
-from PyFlow.Core.Common import *
class IntPin(PinBase):
"""doc string for IntPin"""
+
def __init__(self, name, parent, direction, **kwargs):
super(IntPin, self).__init__(name, parent, direction, **kwargs)
self.setDefaultValue(0)
@@ -29,7 +29,7 @@ def IsValuePin():
@staticmethod
def pinDataTypeHint():
- return 'IntPin', 0
+ return "IntPin", 0
@staticmethod
def color():
@@ -37,7 +37,7 @@ def color():
@staticmethod
def supportedDataTypes():
- return ('IntPin', 'FloatPin',)
+ return ("IntPin", "FloatPin")
@staticmethod
def internalDataStructure():
diff --git a/PyFlow/Packages/PyFlowBase/Pins/StringPin.py b/PyFlow/Packages/PyFlowBase/Pins/StringPin.py
index 2b0dc2198..5068498ad 100644
--- a/PyFlow/Packages/PyFlowBase/Pins/StringPin.py
+++ b/PyFlow/Packages/PyFlowBase/Pins/StringPin.py
@@ -15,11 +15,11 @@
from PyFlow.Core import PinBase
from PyFlow.Core.Common import *
-from nine import str
class StringPin(PinBase):
"""doc string for StringPin"""
+
def __init__(self, name, parent, direction, **kwargs):
super(StringPin, self).__init__(name, parent, direction, **kwargs)
self.setDefaultValue("")
@@ -30,23 +30,25 @@ def IsValuePin():
def getInputWidgetVariant(self):
if self.annotationDescriptionDict is not None:
- if PinSpecifires.VALUE_LIST in self.annotationDescriptionDict:
+ if PinSpecifiers.VALUE_LIST in self.annotationDescriptionDict:
return "EnumWidget"
- if PinSpecifires.INPUT_WIDGET_VARIANT in self.annotationDescriptionDict:
- return self.annotationDescriptionDict[PinSpecifires.INPUT_WIDGET_VARIANT]
+ if PinSpecifiers.INPUT_WIDGET_VARIANT in self.annotationDescriptionDict:
+ return self.annotationDescriptionDict[
+ PinSpecifiers.INPUT_WIDGET_VARIANT
+ ]
return self._inputWidgetVariant
@staticmethod
def supportedDataTypes():
- return ('StringPin',)
+ return ("StringPin",)
@staticmethod
def color():
- return (255, 8, 127, 255)
+ return 255, 8, 127, 255
@staticmethod
def pinDataTypeHint():
- return 'StringPin', ''
+ return "StringPin", ""
@staticmethod
def internalDataStructure():
diff --git a/PyFlow/Packages/PyFlowBase/PrefsWidgets/General.py b/PyFlow/Packages/PyFlowBase/PrefsWidgets/General.py
index 34efbf29f..92d282890 100644
--- a/PyFlow/Packages/PyFlowBase/PrefsWidgets/General.py
+++ b/PyFlow/Packages/PyFlowBase/PrefsWidgets/General.py
@@ -15,8 +15,8 @@
import os
-from Qt import QtCore
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy.QtWidgets import *
from PyFlow.UI.EditorHistory import EditorHistory
from PyFlow.UI.Widgets.PropertiesFramework import CollapsibleFormWidget
@@ -25,6 +25,7 @@
class GeneralPreferences(CategoryWidgetBase):
"""docstring for GeneralPreferences."""
+
def __init__(self, parent=None):
super(GeneralPreferences, self).__init__(parent)
self.layout = QVBoxLayout(self)
@@ -32,12 +33,14 @@ def __init__(self, parent=None):
self.layout.setSpacing(2)
commonCategory = CollapsibleFormWidget(headName="Common")
- defaultTempFolder = os.path.join(os.path.expanduser('~'), "PyFlowTemp")
+ defaultTempFolder = os.path.join(os.path.expanduser("~"), "PyFlowTemp")
defaultTempFolder = os.path.normpath(defaultTempFolder)
self.tempFilesDir = QLineEdit(defaultTempFolder)
commonCategory.addWidget("TempFilesDir", self.tempFilesDir)
self.additionalPackagePaths = QLineEdit("")
- commonCategory.addWidget("Additional package locations", self.additionalPackagePaths)
+ commonCategory.addWidget(
+ "Additional package locations", self.additionalPackagePaths
+ )
self.layout.addWidget(commonCategory)
self.lePythonEditor = QLineEdit("sublime_text.exe @FILE")
@@ -48,6 +51,7 @@ def __init__(self, parent=None):
def setHistoryCapacity():
EditorHistory().capacity = self.historyDepth.value()
+
self.historyDepth.editingFinished.connect(setHistoryCapacity)
commonCategory.addWidget("History depth", self.historyDepth)
@@ -59,7 +63,7 @@ def setHistoryCapacity():
def initDefaults(self, settings):
settings.setValue("EditorCmd", "sublime_text.exe @FILE")
- settings.setValue("TempFilesDir", os.path.expanduser('~/PyFlowTemp'))
+ settings.setValue("TempFilesDir", os.path.expanduser("~/PyFlowTemp"))
settings.setValue("HistoryDepth", 50)
settings.setValue("RedirectOutput", True)
@@ -68,7 +72,9 @@ def serialize(self, settings):
settings.setValue("TempFilesDir", self.tempFilesDir.text())
settings.setValue("ExtraPackageDirs", self.additionalPackagePaths.text())
settings.setValue("HistoryDepth", self.historyDepth.value())
- settings.setValue("RedirectOutput", self.redirectOutput.checkState() == QtCore.Qt.Checked)
+ settings.setValue(
+ "RedirectOutput", self.redirectOutput.checkState() == QtCore.Qt.Checked
+ )
def onShow(self, settings):
self.lePythonEditor.setText(settings.value("EditorCmd"))
diff --git a/PyFlow/Packages/PyFlowBase/PrefsWidgets/InputPrefs.py b/PyFlow/Packages/PyFlowBase/PrefsWidgets/InputPrefs.py
index 624f3ff47..05c2781a6 100644
--- a/PyFlow/Packages/PyFlowBase/PrefsWidgets/InputPrefs.py
+++ b/PyFlow/Packages/PyFlowBase/PrefsWidgets/InputPrefs.py
@@ -16,11 +16,14 @@
import json
from collections import defaultdict
-from Qt.QtWidgets import *
+from qtpy.QtWidgets import *
from PyFlow.ConfigManager import ConfigManager
-from PyFlow.Input import InputAction, InputManager
-from PyFlow.UI.Widgets.PropertiesFramework import CollapsibleFormWidget, PropertiesWidget
+from PyFlow.Input import InputManager
+from PyFlow.UI.Widgets.PropertiesFramework import (
+ CollapsibleFormWidget,
+ PropertiesWidget,
+)
from PyFlow.UI.Widgets.PreferencesWindow import CategoryWidgetBase
from PyFlow.UI.Widgets.InputActionWidget import InputActionWidget
from PyFlow.UI.Canvas.UICommon import clearLayout
@@ -28,6 +31,7 @@
class InputPreferences(CategoryWidgetBase):
"""docstring for InputPreferences."""
+
def __init__(self, parent=None):
super(InputPreferences, self).__init__(parent)
self.content = QWidget()
@@ -57,7 +61,11 @@ def onShow(self, settings):
for inputActionVariant in variants:
actionWidget = InputActionWidget(inputActionRef=inputActionVariant)
actionWidget.setAction(inputActionVariant)
- category.addWidget(label=inputActionVariant.getName(), widget=actionWidget, maxLabelWidth=150)
+ category.addWidget(
+ label=inputActionVariant.getName(),
+ widget=actionWidget,
+ maxLabelWidth=150,
+ )
properties.addWidget(category)
category.setCollapsed(True)
self.layout.addWidget(properties)
diff --git a/PyFlow/Packages/PyFlowBase/PrefsWidgets/ThemePrefs.py b/PyFlow/Packages/PyFlowBase/PrefsWidgets/ThemePrefs.py
index 7119c1cdb..eaee9bbbd 100644
--- a/PyFlow/Packages/PyFlowBase/PrefsWidgets/ThemePrefs.py
+++ b/PyFlow/Packages/PyFlowBase/PrefsWidgets/ThemePrefs.py
@@ -16,12 +16,14 @@
import os
import inspect
import json
-from collections import defaultdict
-from Qt.QtWidgets import *
-from Qt import QtGui
+from qtpy.QtWidgets import *
+from qtpy import QtGui
-from PyFlow.UI.Widgets.PropertiesFramework import CollapsibleFormWidget, PropertiesWidget
+from PyFlow.UI.Widgets.PropertiesFramework import (
+ CollapsibleFormWidget,
+ PropertiesWidget,
+)
from PyFlow.UI.Canvas.UICommon import clearLayout
from PyFlow.UI.Widgets.QtSliders import pyf_ColorSlider, pyf_Slider
from PyFlow.UI.Utils.stylesheet import editableStyleSheet, ConnectionTypes
@@ -34,6 +36,7 @@
class ThemePreferences(CategoryWidgetBase):
"""docstring for ThemePreferences."""
+
def __init__(self, parent=None):
super(ThemePreferences, self).__init__(parent)
self.content = QWidget()
@@ -59,32 +62,80 @@ def onShow(self, settings):
options = inspect.getmembers(editableStyleSheet())
for name, obj in options:
if isinstance(obj, QtGui.QColor):
- inp = pyf_ColorSlider(type="int", alpha=len(list(obj.toTuple())) == 4, startColor=list(obj.toTuple()))
- inp.valueChanged.connect(lambda color, name=name, update=True: editableStyleSheet().setColor(name, color, update))
- if name in ["TextColor", "MainColor", "TextSelectedColor", "ButtonsColor"]:
+ inp = pyf_ColorSlider(
+ type="int",
+ alpha=len(list(obj.toTuple())) == 4,
+ startColor=list(obj.toTuple()),
+ )
+ inp.valueChanged.connect(
+ lambda color, name=name, update=True: editableStyleSheet().setColor(
+ name, color, update
+ )
+ )
+ if name in [
+ "TextColor",
+ "MainColor",
+ "TextSelectedColor",
+ "ButtonsColor",
+ ]:
general.addWidget(name, inp)
- elif name in ["InputFieldColor", "BgColor", "BgColorDarker", "BgColorBright", "BorderColor", "LoggerBgColor"]:
+ elif name in [
+ "InputFieldColor",
+ "BgColor",
+ "BgColorDarker",
+ "BgColorBright",
+ "BorderColor",
+ "LoggerBgColor",
+ ]:
bg.addWidget(name, inp)
- elif name in ["CanvasBgColor", "CanvastextColor", "CanvasGridColor", "CanvasGridColorDarker"]:
+ elif name in [
+ "CanvasBgColor",
+ "CanvastextColor",
+ "CanvasGridColor",
+ "CanvasGridColorDarker",
+ ]:
canvas.addWidget(name, inp)
elif isinstance(obj, list):
- if name in ["GridSizeFine", "GridSizeHuge", "ConnectionRoundness", "ConnectionOffset"]:
+ if name in [
+ "GridSizeFine",
+ "GridSizeHuge",
+ "ConnectionRoundness",
+ "ConnectionOffset",
+ ]:
inp = pyf_Slider(self)
inp.setValue(obj[0])
inp.setMinimum(0)
inp.setMaximum(1000.0)
- inp.valueChanged.connect(lambda color, name=name, update=False: editableStyleSheet().setColor(name, color, update))
- elif name in ["DrawNumbers", "SetAppStyleSheet","DrawGrid"]:
+ inp.valueChanged.connect(
+ lambda color, name=name, update=False: editableStyleSheet().setColor(
+ name, color, update
+ )
+ )
+ elif name in ["DrawNumbers", "SetAppStyleSheet", "DrawGrid"]:
inp = QCheckBox()
inp.setChecked(obj[0])
- inp.stateChanged.connect(lambda color, name=name, update=True: editableStyleSheet().setColor(name, color, update))
+ inp.stateChanged.connect(
+ lambda color, name=name, update=True: editableStyleSheet().setColor(
+ name, color, update
+ )
+ )
elif name == "ConnectionMode":
inp = QComboBox()
for i in ConnectionTypes:
inp.addItem(i.name)
inp.setCurrentIndex(obj[0])
- inp.currentIndexChanged.connect(lambda value, name=name, update=False: editableStyleSheet().setColor(name, value, update))
- elif name in ["LOD_Number", "NodeSwitch", "ConnectionSwitch", "PinSwitch", "CanvasSwitch"]:
+ inp.currentIndexChanged.connect(
+ lambda value, name=name, update=False: editableStyleSheet().setColor(
+ name, value, update
+ )
+ )
+ elif name in [
+ "LOD_Number",
+ "NodeSwitch",
+ "ConnectionSwitch",
+ "PinSwitch",
+ "CanvasSwitch",
+ ]:
inp = pyf_Slider(self, type="int")
inp.setValue(obj[0])
if name != "LOD_Number":
@@ -94,13 +145,26 @@ def onShow(self, settings):
else:
lodMax = inp
inp.setMinimum(0)
- inp.valueChanged.connect(lambda color, name=name, update=False: editableStyleSheet().setColor(name, color, update))
+ inp.valueChanged.connect(
+ lambda color, name=name, update=False: editableStyleSheet().setColor(
+ name, color, update
+ )
+ )
- if name in ["ConnectionMode", "ConnectionRoundness","ConnectionOffset"]:
+ if name in [
+ "ConnectionMode",
+ "ConnectionRoundness",
+ "ConnectionOffset",
+ ]:
connections.addWidget(name, inp)
elif name == "SetAppStyleSheet":
general.insertWidget(0, name, inp)
- elif name in ["NodeSwitch", "ConnectionSwitch", "PinSwitch", "CanvasSwitch"]:
+ elif name in [
+ "NodeSwitch",
+ "ConnectionSwitch",
+ "PinSwitch",
+ "CanvasSwitch",
+ ]:
lods.addWidget(name, inp)
elif name == "LOD_Number":
lods.insertWidget(0, name, inp)
@@ -118,10 +182,16 @@ def onShow(self, settings):
else:
if isinstance(settings, str):
if settings in editableStyleSheet().presets:
- self.selector.setCurrentIndex(list(editableStyleSheet().presets.keys()).index(settings))
- elif settings and settings.value('Theme_Name'):
- if settings.value('Theme_Name') in editableStyleSheet().presets:
- self.selector.setCurrentIndex(list(editableStyleSheet().presets.keys()).index(settings.value('Theme_Name')))
+ self.selector.setCurrentIndex(
+ list(editableStyleSheet().presets.keys()).index(settings)
+ )
+ elif settings and settings.value("Theme_Name"):
+ if settings.value("Theme_Name") in editableStyleSheet().presets:
+ self.selector.setCurrentIndex(
+ list(editableStyleSheet().presets.keys()).index(
+ settings.value("Theme_Name")
+ )
+ )
self.currTheme = self.selector.currentIndex()
self.layout.addWidget(self.selector)
@@ -154,18 +224,20 @@ def onShow(self, settings):
lay.addWidget(pbDeleteTheme)
self.layout.addLayout(lay)
- def setPreset(self, index):
+ def setPreset(self):
data = editableStyleSheet().presets[self.selector.currentText()]
editableStyleSheet().loadFromData(data)
self.currTheme = self.selector.currentIndex()
self.onShow(self.selector.currentText())
def deleteTheme(self):
- if os.path.exists(os.path.join(THEMES_PATH, self.selector.currentText() + ".json")):
+ if os.path.exists(
+ os.path.join(THEMES_PATH, self.selector.currentText() + ".json")
+ ):
os.remove(os.path.join(THEMES_PATH, self.selector.currentText() + ".json"))
self.selector.removeItem(self.selector.currentIndex())
self.onShow(self.selector.currentText())
- self.setPreset(0)
+ self.setPreset()
def saveTheme(self):
self.saveThemeAs(self.selector.currentText())
@@ -173,11 +245,13 @@ def saveTheme(self):
def saveThemeAs(self, fileName=None):
okPressed = True
if not fileName:
- fileName, okPressed = QInputDialog.getText(self, "Get text", "Your name:", QLineEdit.Normal, "")
- if okPressed and fileName != '':
+ fileName, okPressed = QInputDialog.getText(
+ self, "Get text", "Your name:", QLineEdit.Normal, ""
+ )
+ if okPressed and fileName != "":
data = editableStyleSheet().serialize()
with open(os.path.join(THEMES_PATH, fileName + ".json"), "w") as f:
- json.dump(data, f, separators=(',', ':'))
+ json.dump(data, f, separators=(",", ":"))
self.onShow(fileName)
def serialize(self, settings):
diff --git a/PyFlow/Packages/PyFlowBase/Tools/AlignBottomTool.py b/PyFlow/Packages/PyFlowBase/Tools/AlignBottomTool.py
index b5c340cef..6a0a7b7f9 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/AlignBottomTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/AlignBottomTool.py
@@ -13,17 +13,16 @@
## limitations under the License.
-from nine import str
from PyFlow.UI.Tool.Tool import ShelfTool
from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.Core.Common import Direction
-from Qt import QtGui
-from Qt.QtWidgets import QFileDialog
+from qtpy import QtGui
class AlignBottomTool(ShelfTool):
"""docstring for AlignBottomTool."""
+
def __init__(self):
super(AlignBottomTool, self).__init__()
@@ -37,7 +36,7 @@ def getIcon():
@staticmethod
def name():
- return str("AlignBottomTool")
+ return "AlignBottomTool"
def do(self):
self.pyFlowInstance.getCanvas().alignSelectedNodes(Direction.Down)
diff --git a/PyFlow/Packages/PyFlowBase/Tools/AlignLeftTool.py b/PyFlow/Packages/PyFlowBase/Tools/AlignLeftTool.py
index 1c177a69e..b5a3d7993 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/AlignLeftTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/AlignLeftTool.py
@@ -13,17 +13,16 @@
## limitations under the License.
-from nine import str
from PyFlow.UI.Tool.Tool import ShelfTool
from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.Core.Common import Direction
-from Qt import QtGui
-from Qt.QtWidgets import QFileDialog
+from qtpy import QtGui
class AlignLeftTool(ShelfTool):
"""docstring for AlignLeftTool."""
+
def __init__(self):
super(AlignLeftTool, self).__init__()
@@ -37,7 +36,7 @@ def getIcon():
@staticmethod
def name():
- return str("AlignLeftTool")
+ return "AlignLeftTool"
def do(self):
self.pyFlowInstance.getCanvas().alignSelectedNodes(Direction.Left)
diff --git a/PyFlow/Packages/PyFlowBase/Tools/AlignRightTool.py b/PyFlow/Packages/PyFlowBase/Tools/AlignRightTool.py
index b0ada9480..1ac031756 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/AlignRightTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/AlignRightTool.py
@@ -13,17 +13,16 @@
## limitations under the License.
-from nine import str
from PyFlow.UI.Tool.Tool import ShelfTool
from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.Core.Common import Direction
-from Qt import QtGui
-from Qt.QtWidgets import QFileDialog
+from qtpy import QtGui
class AlignRightTool(ShelfTool):
"""docstring for AlignRightTool."""
+
def __init__(self):
super(AlignRightTool, self).__init__()
@@ -37,7 +36,7 @@ def getIcon():
@staticmethod
def name():
- return str("AlignRightTool")
+ return "AlignRightTool"
def do(self):
self.pyFlowInstance.getCanvas().alignSelectedNodes(Direction.Right)
diff --git a/PyFlow/Packages/PyFlowBase/Tools/AlignTopTool.py b/PyFlow/Packages/PyFlowBase/Tools/AlignTopTool.py
index 3c0b24a07..e6ed57fbd 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/AlignTopTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/AlignTopTool.py
@@ -13,17 +13,16 @@
## limitations under the License.
-from nine import str
from PyFlow.UI.Tool.Tool import ShelfTool
from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.Core.Common import Direction
-from Qt import QtGui
-from Qt.QtWidgets import QFileDialog
+from qtpy import QtGui
class AlignTopTool(ShelfTool):
"""docstring for AlignTopTool."""
+
def __init__(self):
super(AlignTopTool, self).__init__()
@@ -37,7 +36,7 @@ def getIcon():
@staticmethod
def name():
- return str("AlignTopTool")
+ return "AlignTopTool"
def do(self):
self.pyFlowInstance.getCanvas().alignSelectedNodes(Direction.Up)
diff --git a/PyFlow/Packages/PyFlowBase/Tools/CompileTool.py b/PyFlow/Packages/PyFlowBase/Tools/CompileTool.py
index c26b0c1c0..527baae41 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/CompileTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/CompileTool.py
@@ -13,19 +13,19 @@
## limitations under the License.
-from nine import str
from PyFlow.UI.Tool.Tool import ShelfTool
from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
-from PyFlow.UI.ContextMenuDataBuilder import ContextMenuDataBuilder
-from Qt import QtGui
-from Qt.QtWidgets import *
+from qtpy import QtGui
+from qtpy.QtWidgets import *
class CompileTool(ShelfTool):
"""docstring for CompileTool."""
+
def __init__(self):
super(CompileTool, self).__init__()
+ self.format = None
def onSetFormat(self, fmt):
self.format = fmt
@@ -40,7 +40,7 @@ def getIcon():
@staticmethod
def name():
- return str("CompileTool")
+ return "CompileTool"
def do(self):
for node in self.pyFlowInstance.graphManager.get().getAllNodes():
diff --git a/PyFlow/Packages/PyFlowBase/Tools/HistoryTool.py b/PyFlow/Packages/PyFlowBase/Tools/HistoryTool.py
index f6784052a..b86d77011 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/HistoryTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/HistoryTool.py
@@ -13,12 +13,10 @@
## limitations under the License.
-from nine import str
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import *
-from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.UI.Tool.Tool import DockTool
from PyFlow.UI.EditorHistory import EditorHistory
@@ -124,4 +122,4 @@ def toolTip():
@staticmethod
def name():
- return str("History")
+ return "History"
diff --git a/PyFlow/Packages/PyFlowBase/Tools/LoggerTool.py b/PyFlow/Packages/PyFlowBase/Tools/LoggerTool.py
index 7d8c046ab..3b27cd199 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/LoggerTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/LoggerTool.py
@@ -13,19 +13,15 @@
## limitations under the License.
-from nine import str
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QAction, QTextBrowser
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import QAction, QTextBrowser
from PyFlow.UI.Tool.Tool import DockTool
-from PyFlow.UI.Views.NodeBox import NodesBox
from PyFlow.UI.Utils.stylesheet import editableStyleSheet
-from PyFlow.Core.GraphManager import GraphManagerSingleton
from PyFlow.Core.Common import SingletonDecorator
from PyFlow.ConfigManager import ConfigManager
import sys
import logging
-import json
import os
import subprocess
@@ -34,6 +30,7 @@
logger = logging.getLogger(None)
+
def addLoggingLevel(levelName, levelNum, methodName=None):
"""
Comprehensively adds a new logging level to the `logging` module and the
@@ -63,11 +60,11 @@ def addLoggingLevel(levelName, levelNum, methodName=None):
methodName = levelName.lower()
if hasattr(logging, levelName):
- raise AttributeError('{} already defined in logging module'.format(levelName))
+ raise AttributeError("{} already defined in logging module".format(levelName))
if hasattr(logging, methodName):
- raise AttributeError('{} already defined in logging module'.format(methodName))
+ raise AttributeError("{} already defined in logging module".format(methodName))
if hasattr(logging.getLoggerClass(), methodName):
- raise AttributeError('{} already defined in logger class'.format(methodName))
+ raise AttributeError("{} already defined in logger class".format(methodName))
# This method was inspired by the answers to Stack Overflow post
# http://stackoverflow.com/q/2183233/2988730, especially
@@ -84,7 +81,9 @@ def logToRoot(message, *args, **kwargs):
setattr(logging.getLoggerClass(), methodName, logForLevel)
setattr(logging, methodName, logToRoot)
-addLoggingLevel('CONSOLEOUTPUT', logging.ERROR + 5)
+
+addLoggingLevel("CONSOLEOUTPUT", logging.ERROR + 5)
+
@SingletonDecorator
class SignalHandler(QtCore.QObject):
@@ -102,13 +101,14 @@ def __init__(self, parent):
sys.stdout = self
def write(self, msg):
- if (not self.signalsBlocked()):
- if msg != '\n':
+ if not self.signalsBlocked():
+ if msg != "\n":
self.text = msg
- logger.info(str(msg))
+ logger.info(msg)
- def flush(self):
- print('flusing from handler')
+ @staticmethod
+ def flush():
+ print("flushing from handler")
class QtHandler(logging.Handler):
@@ -119,25 +119,27 @@ def __init__(self, parent):
def emit(self, record):
if record:
msj = self.format(record)
- if 'flusing from handler' in msj:
+ if "flushing from handler" in msj:
self.messageHolder.flushSig.emit()
- elif 'bytes Downloaded' in msj:
- nb = int(float(msj.split('(')[-1][:-2]))
+ elif "bytes Downloaded" in msj:
+ nb = int(float(msj.split("(")[-1][:-2]))
self.messageHolder.progressSig.emit(nb)
- self.messageHolder.messageWritten.emit('%s\n' % msj)
+ self.messageHolder.messageWritten.emit("%s\n" % msj)
else:
- if record.levelname in ['ERROR', 'CRITICAL']:
- self.messageHolder.errorWritten.emit('%s\n' % msj)
- elif record.levelname == 'WARNING':
- self.messageHolder.warningWritten.emit('%s\n' % msj)
+ if record.levelname in ["ERROR", "CRITICAL"]:
+ self.messageHolder.errorWritten.emit("%s\n" % msj)
+ elif record.levelname == "WARNING":
+ self.messageHolder.warningWritten.emit("%s\n" % msj)
else:
- self.messageHolder.messageWritten.emit('%s\n' % msj)
+ self.messageHolder.messageWritten.emit("%s\n" % msj)
class LoggerTool(DockTool):
"""docstring for NodeBox tool."""
- formater = logging.Formatter("[%(levelname)s %(asctime)s]: %(message)s", "%H:%M:%S")
+ formatter = logging.Formatter(
+ "[%(levelname)s %(asctime)s]: %(message)s", "%H:%M:%S"
+ )
def __init__(self):
super(LoggerTool, self).__init__()
@@ -145,13 +147,16 @@ def __init__(self):
self.logView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.logView.setOpenLinks(False)
self.logView.setReadOnly(True)
- self.logView.setStyleSheet("background-color: %s; Font: 10pt 'Consolas'" %
- "rgba%s" % str(editableStyleSheet().LoggerBgColor.getRgb()))
+ self.logView.setStyleSheet(
+ "background-color: %s; Font: 10pt 'Consolas'"
+ % "rgba%s"
+ % str(editableStyleSheet().LoggerBgColor.getRgb())
+ )
self.clearAction = QAction("Clear", None)
self.clearAction.triggered.connect(self.clearView)
self.logView.addAction(self.clearAction)
self.logView.anchorClicked.connect(self.anchorClickedMethod)
- self.logView.setTextColor(QtGui.QColor('white'))
+ self.logView.setTextColor(QtGui.QColor("white"))
self.setWidget(self.logView)
#####################################################
# Sys Output Redirection
@@ -165,15 +170,19 @@ def __init__(self):
logger.setLevel(logging.DEBUG)
sys.excepthook = LoggerTool.exceptHook
if self.handler and REDIRECT:
- self.handler.setFormatter(LoggerTool.formater)
+ self.handler.setFormatter(LoggerTool.formatter)
logger.addHandler(self.handler)
self.handler.messageHolder.messageWritten.connect(
- lambda value: self.logPython(value, 0))
+ lambda value: self.logPython(value, 0)
+ )
self.handler.messageHolder.warningWritten.connect(
- lambda value: self.logPython(value, 1))
+ lambda value: self.logPython(value, 1)
+ )
self.handler.messageHolder.errorWritten.connect(
- lambda value: self.logPython(value, 2))
+ lambda value: self.logPython(value, 2)
+ )
self.handler.messageHolder.flushSig.connect(self.flushPython)
+
#####################################################
# Logger
#####################################################
@@ -205,39 +214,47 @@ def onDestroy(self):
pass
def logPython(self, text, mode=0):
- colorchart = {
- 0: 'white',
- 1: 'yellow',
- 2: 'red'
- }
- for l in text.split('\n'):
+ colorchart = {0: "white", 1: "yellow", 2: "red"}
+ for l in text.split("\n"):
if len(l) > 0:
splitted = l.split(",")
if len(splitted) >= 3:
- if "File" in splitted[0] and "line" in splitted[1] and "in" in splitted[2]:
+ if (
+ "File" in splitted[0]
+ and "line" in splitted[1]
+ and "in" in splitted[2]
+ ):
file = splitted[0].split('"')[1]
line = splitted[1].split("line ")[1]
if os.path.exists(file):
file = file.replace("\\", "//")
- errorLink = """%s""" % (
- str(file + "::%s" % line), l)
+ errorLink = (
+ """%s"""
+ % (file + "::%s" % line, l)
+ )
self.logView.append(errorLink)
else:
self.logView.append(
- '%s' % (colorchart[mode], l))
+ '%s' % (colorchart[mode], l)
+ )
else:
self.logView.append(
- '%s' % (colorchart[mode], l))
+ '%s' % (colorchart[mode], l)
+ )
def flushPython(self):
- self.logView.moveCursor(QtWidgets.QTextCursor.End,
- QtWidgets.QTextCursor.MoveAnchor)
- self.logView.moveCursor(QtWidgets.QTextCursor.Up,
- QtWidgets.QTextCursor.MoveAnchor)
self.logView.moveCursor(
- QtWidgets.QTextCursor.StartOfLine, QtWidgets.QTextCursor.MoveAnchor)
- self.logView.moveCursor(QtWidgets.QTextCursor.End,
- QtWidgets.QTextCursor.KeepAnchor)
+ QtGui.QTextCursor.End, QtGui.QTextCursor.MoveAnchor
+ )
+ self.logView.moveCursor(
+ QtGui.QTextCursor.Up, QtGui.QTextCursor.MoveAnchor
+ )
+ self.logView.moveCursor(
+ QtGui.QTextCursor.StartOfLine, QtGui.QTextCursor.MoveAnchor
+ )
+ self.logView.moveCursor(
+ QtGui.QTextCursor.End, QtGui.QTextCursor.KeepAnchor
+ )
self.logView.textCursor().removeSelectedText()
def loglevelChanged(self, int):
@@ -251,15 +268,18 @@ def anchorClickedMethod(self, url):
subprocess.Popen(editCmd)
else:
man = self.pyFlowInstance.graphManager
- node = man.get().findNode(str(url.url()))
+ node = man.get().findNode(url.url())
if node:
self.pyFlowInstance.getCanvas().clearSelection()
node.getWrapper().setSelected(True)
self.pyFlowInstance.getCanvas().frameSelectedNodes()
def update(self):
- self.logView.setStyleSheet("background-color: %s; Font: 10pt 'Consolas'" %
- "rgba%s" % str(editableStyleSheet().LoggerBgColor.getRgb()))
+ self.logView.setStyleSheet(
+ "background-color: %s; Font: 10pt 'Consolas'"
+ % "rgba%s"
+ % str(editableStyleSheet().LoggerBgColor.getRgb())
+ )
super(LoggerTool, self).update()
def onShow(self):
@@ -282,4 +302,4 @@ def toolTip():
@staticmethod
def name():
- return str("Logger")
+ return "Logger"
diff --git a/PyFlow/Packages/PyFlowBase/Tools/NodeBoxTool.py b/PyFlow/Packages/PyFlowBase/Tools/NodeBoxTool.py
index afb4c4ed2..1c53f0560 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/NodeBoxTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/NodeBoxTool.py
@@ -13,9 +13,7 @@
## limitations under the License.
-from nine import str
-from Qt import QtCore
-from Qt import QtGui
+from qtpy import QtCore
from PyFlow.UI.Tool.Tool import DockTool
from PyFlow.UI.Views.NodeBox import NodesBox
@@ -23,13 +21,17 @@
class NodeBoxTool(DockTool):
"""docstring for NodeBox tool."""
+
def __init__(self):
super(NodeBoxTool, self).__init__()
+ self.content = None
def onShow(self):
super(NodeBoxTool, self).onShow()
self.setMinimumSize(QtCore.QSize(200, 50))
- self.content = NodesBox(self, self.pyFlowInstance.getCanvas(), False, False, bUseDragAndDrop=True)
+ self.content = NodesBox(
+ self, self.pyFlowInstance.getCanvas(), False, False, bUseDragAndDrop=True
+ )
self.content.setObjectName("NodeBoxToolContent")
self.setWidget(self.content)
@@ -50,4 +52,4 @@ def toolTip():
@staticmethod
def name():
- return str("NodeBox")
+ return "NodeBox"
diff --git a/PyFlow/Packages/PyFlowBase/Tools/PackageBuilder.py b/PyFlow/Packages/PyFlowBase/Tools/PackageBuilder.py
new file mode 100644
index 000000000..c88b44b90
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/Tools/PackageBuilder.py
@@ -0,0 +1,51 @@
+## Copyright 2023 David Lario
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+from PyFlow.UI.Tool.Tool import FormTool
+from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
+from PyFlow.UI.Forms.PackageBuilder import PackageBuilder as PB
+from qtpy import QtGui
+from uuid import uuid4
+
+class PackageBuilder(FormTool):
+ """docstring for AlignBottomTool."""
+ def __init__(self):
+ super(PackageBuilder, self).__init__()
+ self.guid = uuid4()
+
+ @staticmethod
+ def toolTip():
+ return "Package Builder"
+
+ def guid(self):
+ return self.guid
+ @staticmethod
+ def getIcon():
+ return QtGui.QIcon(RESOURCES_DIR + "options_icon.png")
+
+ @staticmethod
+ def getSmallIconPath():
+ return RESOURCES_DIR + "new_file_icon.png"
+
+ @staticmethod
+ def getLargeIconPath():
+ return RESOURCES_DIR + "new_file_icon.png"
+
+ @staticmethod
+ def name():
+ return str("PackageBuilder")
+
+ def do(self):
+ self.pyFlowInstance.newFileFromUi(PB.PackageBuilder())
diff --git a/PyFlow/Packages/PyFlowBase/Tools/PropertiesTool.py b/PyFlow/Packages/PyFlowBase/Tools/PropertiesTool.py
index 006522146..bf14e6c64 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/PropertiesTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/PropertiesTool.py
@@ -13,18 +13,15 @@
## limitations under the License.
-from nine import str
-from Qt import QtCore
-from Qt import QtGui
-from Qt import QtWidgets
+from qtpy import QtWidgets
-from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.UI.Tool.Tool import DockTool
from PyFlow.UI.Widgets.PropertiesFramework import PropertiesWidget
class PropertiesTool(DockTool):
"""docstring for Properties tool."""
+
def __init__(self):
super(PropertiesTool, self).__init__()
self.scrollArea = QtWidgets.QScrollArea(self)
@@ -33,9 +30,13 @@ def __init__(self):
self.propertiesWidget = PropertiesWidget()
self.scrollArea.setWidget(self.propertiesWidget)
- self.propertiesWidget.searchBoxLayout.removeWidget(self.propertiesWidget.lockCheckBox)
+ self.propertiesWidget.searchBoxLayout.removeWidget(
+ self.propertiesWidget.lockCheckBox
+ )
self.addButton(self.propertiesWidget.lockCheckBox)
- self.propertiesWidget.searchBoxLayout.removeWidget(self.propertiesWidget.tearOffCopy)
+ self.propertiesWidget.searchBoxLayout.removeWidget(
+ self.propertiesWidget.tearOffCopy
+ )
self.addButton(self.propertiesWidget.tearOffCopy)
# self.addButton(self.propertiesWidget.settingsButton)
@@ -68,4 +69,4 @@ def toolTip():
@staticmethod
def name():
- return str("Properties")
+ return "Properties"
diff --git a/PyFlow/Packages/PyFlowBase/Tools/ScreenshotTool.py b/PyFlow/Packages/PyFlowBase/Tools/ScreenshotTool.py
index 21db63f58..b75e04560 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/ScreenshotTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/ScreenshotTool.py
@@ -13,17 +13,17 @@
## limitations under the License.
-from nine import str
from PyFlow.UI.Tool.Tool import ShelfTool
from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.UI.ContextMenuDataBuilder import ContextMenuDataBuilder
-from Qt import QtGui
-from Qt.QtWidgets import QFileDialog
+from qtpy import QtGui
+from qtpy.QtWidgets import QFileDialog
class ScreenshotTool(ShelfTool):
"""docstring for ScreenshotTool."""
+
def __init__(self):
super(ScreenshotTool, self).__init__()
self.format = "PNG"
@@ -59,12 +59,12 @@ def getIcon():
@staticmethod
def name():
- return str("ScreenshotTool")
+ return "ScreenshotTool"
def do(self):
name_filter = "Image (*.{0})".format(self.format.lower())
fName = QFileDialog.getSaveFileName(filter=name_filter)
- if not fName[0] == '':
+ if not fName[0] == "":
print("save screen to {0}".format(fName[0]))
img = self.pyFlowInstance.getCanvas().grab()
img.save(fName[0], format=self.format, quality=100)
diff --git a/PyFlow/Packages/PyFlowBase/Tools/SearchResultsTool.py b/PyFlow/Packages/PyFlowBase/Tools/SearchResultsTool.py
index 1dea3f006..a8db915f9 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/SearchResultsTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/SearchResultsTool.py
@@ -13,18 +13,16 @@
## limitations under the License.
-from nine import str
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy.QtWidgets import *
-from PyFlow.Packages.PyFlowBase.Tools import RESOURCES_DIR
from PyFlow.UI.Tool.Tool import DockTool
from PyFlow.UI.Widgets.PropertiesFramework import *
class SearchResultsTool(DockTool):
"""docstring for NodeBox tool."""
+
def __init__(self):
super(SearchResultsTool, self).__init__()
self.layout().setSpacing(0)
@@ -43,7 +41,7 @@ def __init__(self):
self.content.setLockCheckBoxVisible(False)
self.content.setTearOffCopyVisible(False)
- self.content.setObjectName("SearchResultstent")
+ self.content.setObjectName("SearchResults")
self.scrollArea.setWidget(self.content)
self.setWindowTitle(self.uniqueName())
self.setWidget(self.scrollArea)
@@ -55,7 +53,11 @@ def onShowNodesResults(self, uiNodesList):
for node in uiNodesList:
locationString = ">".join(node.location())
btn = QPushButton(locationString)
- btn.clicked.connect(lambda checked=False, n=node: self.pyFlowInstance.getCanvas().frameItems([n]))
+ btn.clicked.connect(
+ lambda checked=False, n=node: self.pyFlowInstance.getCanvas().frameItems(
+ [n]
+ )
+ )
category.addWidget(node.getName(), btn)
self.content.addWidget(category)
@@ -65,7 +67,9 @@ def defaultDockArea():
def onShow(self):
super(SearchResultsTool, self).onShow()
- self.pyFlowInstance.getCanvas().requestShowSearchResults.connect(self.onShowNodesResults)
+ self.pyFlowInstance.getCanvas().requestShowSearchResults.connect(
+ self.onShowNodesResults
+ )
@staticmethod
def toolTip():
@@ -77,4 +81,4 @@ def isSingleton():
@staticmethod
def name():
- return str("Search results")
+ return "Search results"
diff --git a/PyFlow/Packages/PyFlowBase/Tools/VariablesTool.py b/PyFlow/Packages/PyFlowBase/Tools/VariablesTool.py
index c812c28c6..5f6fe7b53 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/VariablesTool.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/VariablesTool.py
@@ -13,12 +13,9 @@
## limitations under the License.
-from nine import str
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QUndoView
-from Qt.QtWidgets import QWidget
-from Qt.QtWidgets import QVBoxLayout
+from qtpy import QtCore
+from qtpy.QtWidgets import QWidget
+from qtpy.QtWidgets import QVBoxLayout
from PyFlow.UI.Tool.Tool import DockTool
from PyFlow.UI.Views.VariablesWidget import VariablesWidget
@@ -26,6 +23,7 @@
class VariablesTool(DockTool):
"""docstring for Variables tool."""
+
def __init__(self):
super(VariablesTool, self).__init__()
self.setMinimumSize(QtCore.QSize(200, 50))
@@ -60,4 +58,4 @@ def toolTip():
@staticmethod
def name():
- return str("Variables")
+ return "Variables"
diff --git a/PyFlow/Packages/PyFlowBase/Tools/__init__.py b/PyFlow/Packages/PyFlowBase/Tools/__init__.py
index a2aa69dae..9b178781b 100644
--- a/PyFlow/Packages/PyFlowBase/Tools/__init__.py
+++ b/PyFlow/Packages/PyFlowBase/Tools/__init__.py
@@ -14,4 +14,5 @@
import os
+
RESOURCES_DIR = os.path.dirname(os.path.realpath(__file__)) + "/res/"
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIAnyPin.py b/PyFlow/Packages/PyFlowBase/UI/UIAnyPin.py
index 93083fa78..0d4b16943 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIAnyPin.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIAnyPin.py
@@ -16,7 +16,7 @@
from PyFlow import findPinClassByType
from PyFlow.UI.Widgets.SelectPinDialog import SelectPinDialog
from PyFlow.UI.Canvas.UIPinBase import UIPinBase
-from Qt import QtGui
+from qtpy import QtGui
class UIAnyPin(UIPinBase):
@@ -26,7 +26,7 @@ def __init__(self, owningNode, raw_pin):
:param owningNode: Owning node
:type owningNode: :class:`PyFlow.UI.Canvas.NodeBase`
:param raw_pin: PinBase reference
- :type raw_pin: :class:`PyFlow.Packages.PyFlowBase.Pins.AnyPin`
+ :type raw_pin: :class:`PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin`
"""
super(UIAnyPin, self).__init__(owningNode, raw_pin)
self._defaultColor = self._pinColor
@@ -43,7 +43,9 @@ def dataTypeBeenSet(self, *args, **kwargs):
self.prevDataType = None
self.setDefault(self._rawPin.defColor())
- def checkFree(self, checked=[], selfCheck=True):
+ def checkFree(self, checked=None, selfCheck=True):
+ if checked is None:
+ checked = []
return self._rawPin.checkFree(checked, selfCheck)
def disconnect(self, other):
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIColorRamp.py b/PyFlow/Packages/PyFlowBase/UI/UIColorRamp.py
index a3f95e22a..aa9fe8967 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIColorRamp.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIColorRamp.py
@@ -14,9 +14,9 @@
import weakref
-from Qt import QtCore
-from Qt import QtGui
-from Qt import QtWidgets
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy import QtWidgets
from PyFlow.UI.Canvas.Painters import NodePainter
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
from PyFlow.UI.Widgets.QtSliders import pyf_RampColor, pyf_ColorSlider
@@ -33,7 +33,9 @@ def changeCurveType(self, index):
self._rawNode._curveType = index
for ramp in self.ramps:
if ramp() is not None:
- ramp().setBezier(self._rawNode._curveTypes[self._rawNode._curveType] == "bezier")
+ ramp().setBezier(
+ self._rawNode._curveTypes[self._rawNode._curveType] == "bezier"
+ )
ramp().updateFromRaw()
for selector in self.selectors:
if selector() is not None:
@@ -64,7 +66,11 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
if not self._rawNode.input.isArray():
inputVal.setMinimum(0.0)
inputVal.setMaximum(1.0)
- ramp = pyf_RampColor(self._rawNode.ramp, bezier=self._rawNode._curveTypes[self._rawNode._curveType] == "bezier", parent=inputsCategory)
+ ramp = pyf_RampColor(
+ self._rawNode.ramp,
+ bezier=self._rawNode._curveTypes[self._rawNode._curveType] == "bezier",
+ parent=inputsCategory,
+ )
ramp.tickClicked.connect(self.rampChanged)
ramp.tickAdded.connect(self.rampChanged)
ramp.tickRemoved.connect(self.rampChanged)
@@ -86,4 +92,6 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
selector.activated.connect(self.changeCurveType)
inputsCategory.insertWidget(preIndex, "CurveType", selector, group=inGroup)
inputsCategory.insertWidget(preIndex + 1, "Ramp", ramp, group=inGroup)
- inputsCategory.insertWidget(preIndex + 1, "Selected Color", colorChanger, group=inGroup)
+ inputsCategory.insertWidget(
+ preIndex + 1, "Selected Color", colorChanger, group=inGroup
+ )
diff --git a/PyFlow/Packages/PyFlowBase/UI/UICombineArgsNode.py b/PyFlow/Packages/PyFlowBase/UI/UICombineArgsNode.py
new file mode 100644
index 000000000..1aa3bf4c7
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/UI/UICombineArgsNode.py
@@ -0,0 +1,43 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+from PyFlow.UI.Canvas.UICommon import DEFAULT_IN_EXEC_NAME
+from PyFlow.UI import RESOURCES_DIR
+from PyFlow.UI.Canvas.UINodeBase import UINodeBase
+from PyFlow.UI.Canvas.UICommon import NodeActionButtonInfo
+from PyFlow.UI.Utils.stylesheet import Colors
+from qtpy import QtCore
+from qtpy.QtWidgets import QInputDialog
+
+
+class UICombineArgs(UINodeBase):
+ pinCreated = QtCore.Signal(object)
+
+ def __init__(self, raw_node):
+ super(UICombineArgs, self).__init__(raw_node)
+ actionAddOut = self._menu.addAction("Add arg")
+ actionAddOut.setToolTip("Add arg")
+ actionAddOut.setData(NodeActionButtonInfo(RESOURCES_DIR + "/pin.svg"))
+ actionAddOut.triggered.connect(self.onAddInPin)
+
+ def onAddInPin(self):
+ name, confirmed = QInputDialog.getText(
+ None, "Rename", "Enter new pin name")
+ if confirmed and name != self.name and name != "":
+ name = self._rawNode.getUniqPinName(name)
+ rawPin = self._rawNode.addInPin(name, "StringPin")
+ uiPin = self._createUIPinWrapper(rawPin)
+ self.pinCreated.emit(uiPin)
+ self.updateNodeShape()
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py b/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py
index 45c6778a3..fe455bf61 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UICommentNode.py
@@ -15,20 +15,20 @@
from types import MethodType
-from Qt.QtWidgets import QGraphicsTextItem
-from Qt.QtWidgets import QGraphicsItem
-from Qt.QtWidgets import QGraphicsWidget
-from Qt.QtWidgets import QInputDialog
-from Qt.QtWidgets import QGraphicsItemGroup
-from Qt.QtWidgets import QGraphicsProxyWidget
-from Qt.QtWidgets import QStyle
-from Qt.QtWidgets import QLabel
-from Qt.QtWidgets import QLineEdit
-from Qt.QtWidgets import QTextBrowser
-from Qt.QtWidgets import QPushButton
-from Qt.QtWidgets import QMenu
-from Qt import QtGui
-from Qt import QtCore
+from qtpy.QtWidgets import QGraphicsTextItem
+from qtpy.QtWidgets import QGraphicsItem
+from qtpy.QtWidgets import QGraphicsWidget
+from qtpy.QtWidgets import QInputDialog
+from qtpy.QtWidgets import QGraphicsItemGroup
+from qtpy.QtWidgets import QGraphicsProxyWidget
+from qtpy.QtWidgets import QStyle
+from qtpy.QtWidgets import QLabel
+from qtpy.QtWidgets import QLineEdit
+from qtpy.QtWidgets import QTextBrowser
+from qtpy.QtWidgets import QPushButton
+from qtpy.QtWidgets import QMenu
+from qtpy import QtGui
+from qtpy import QtCore
from PyFlow.UI.Canvas.UICommon import *
from PyFlow.UI import RESOURCES_DIR
@@ -78,7 +78,7 @@ def kill(self, *args, **kwargs):
# Do not forget to remove collapsed nodes!
if self.collapsed:
for node in self.owningNodes:
- assert(node is not None)
+ assert node is not None
node.kill()
super(UICommentNode, self).kill(*args, **kwargs)
@@ -88,7 +88,9 @@ def hideOwningNodes(self):
# hide fully contained connections
for pin in node.UIPins.values():
for connection in pin.uiConnectionList:
- if self.sceneBoundingRect().contains(connection.sceneBoundingRect()):
+ if self.sceneBoundingRect().contains(
+ connection.sceneBoundingRect()
+ ):
connection.hide()
def onVisibilityChanged(self, bVisible):
@@ -103,7 +105,9 @@ def onVisibilityChanged(self, bVisible):
connection.setVisible(True)
else:
# Hide connection only if fully contained
- if self.sceneBoundingRect().contains(connection.sceneBoundingRect()):
+ if self.sceneBoundingRect().contains(
+ connection.sceneBoundingRect()
+ ):
connection.setVisible(False)
self.canvasRef().update()
@@ -182,10 +186,18 @@ def getCollidedConnections(self, bFully=False):
def intersectsOrContainsEndpointNodes(self, connection):
srcOwningNode = connection.source().owningNode()
dstOwningNode = connection.destination().owningNode()
- intersectsSrcNode = self.sceneBoundingRect().intersects(srcOwningNode.sceneBoundingRect())
- containsSrcNode = self.sceneBoundingRect().contains(srcOwningNode.sceneBoundingRect())
- intersectsDstNode = self.sceneBoundingRect().intersects(dstOwningNode.sceneBoundingRect())
- containsDstNode = self.sceneBoundingRect().contains(dstOwningNode.sceneBoundingRect())
+ intersectsSrcNode = self.sceneBoundingRect().intersects(
+ srcOwningNode.sceneBoundingRect()
+ )
+ containsSrcNode = self.sceneBoundingRect().contains(
+ srcOwningNode.sceneBoundingRect()
+ )
+ intersectsDstNode = self.sceneBoundingRect().intersects(
+ dstOwningNode.sceneBoundingRect()
+ )
+ containsDstNode = self.sceneBoundingRect().contains(
+ dstOwningNode.sceneBoundingRect()
+ )
return intersectsSrcNode, containsSrcNode, intersectsDstNode, containsDstNode
def contextMenuEvent(self, event):
@@ -202,7 +214,10 @@ def aboutToCollapse(self, futureCollapseState):
# save overrides information
for connection in self.partiallyIntersectedConnections:
- self.partiallyIntersectedConnectionsEndpointOverrides[connection] = (connection.sourcePositionOverride, connection.destinationPositionOverride)
+ self.partiallyIntersectedConnectionsEndpointOverrides[connection] = (
+ connection.sourcePositionOverride,
+ connection.destinationPositionOverride,
+ )
for node in self.owningNodes:
if node.owningCommentNode is self:
@@ -230,7 +245,10 @@ def aboutToCollapse(self, futureCollapseState):
if pin.direction == PinDirection.Input:
connection.destinationPositionOverride = None
self.update()
- for connection, overrides in self.partiallyIntersectedConnectionsEndpointOverrides.items():
+ for (
+ connection,
+ overrides,
+ ) in self.partiallyIntersectedConnectionsEndpointOverrides.items():
connection.updateEndpointsPositions()
self.partiallyIntersectedConnections.clear()
@@ -261,7 +279,9 @@ def updateColor(self, color):
def createPropertiesWidget(self, propertiesWidget):
super(UICommentNode, self).createPropertiesWidget(propertiesWidget)
appearanceCategory = CollapsibleFormWidget(headName="Appearance")
- pb = pyf_ColorSlider(type="int", alpha=True, startColor=list(self.color.getRgbF()))
+ pb = pyf_ColorSlider(
+ type="int", alpha=True, startColor=list(self.color.getRgbF())
+ )
pb.valueChanged.connect(self.updateColor)
appearanceCategory.addWidget("Color", pb)
propertiesWidget.insertWidget(appearanceCategory, 1)
diff --git a/PyFlow/Packages/PyFlowBase/UI/UICompoundNode.py b/PyFlow/Packages/PyFlowBase/UI/UICompoundNode.py
index d91e9ddd2..466ca5637 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UICompoundNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UICompoundNode.py
@@ -17,12 +17,12 @@
import logging
import os
-from Qt.QtWidgets import QFileDialog
-from Qt.QtWidgets import QInputDialog
-from Qt.QtWidgets import QMessageBox
+from qtpy.QtWidgets import QFileDialog
+from qtpy.QtWidgets import QInputDialog
+from qtpy.QtWidgets import QMessageBox
from PyFlow import GET_PACKAGE_PATH, GET_PACKAGES
-from PyFlow.UI.Canvas.UICommon import validateGraphDataPackages
+from PyFlow.Core.Common import validateGraphDataPackages
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
from PyFlow.UI.Canvas.UINodeBase import getUINodeInstance
from PyFlow.UI.Utils.stylesheet import Colors
@@ -59,22 +59,32 @@ def rebuild(self):
def onExport(self, root=None):
try:
- savePath, selectedFilter = QFileDialog.getSaveFileName(filter="Subgraph data (*.compound)", dir=root)
+ savePath, selectedFilter = QFileDialog.getSaveFileName(
+ filter="Subgraph data (*.compound)", dir=root
+ )
except:
- savePath, selectedFilter = QFileDialog.getSaveFileName(filter="Subgraph data (*.compound)")
+ savePath, selectedFilter = QFileDialog.getSaveFileName(
+ filter="Subgraph data (*.compound)"
+ )
if savePath != "":
- with open(savePath, 'w') as f:
+ with open(savePath, "w") as f:
json.dump(self._rawNode.rawGraph.serialize(), f, indent=4)
logger.info("{0} data successfully exported!".format(self.getName()))
def onExportToPackage(self):
# check if category is not empty
- if self._rawNode._rawGraph.category == '':
- QMessageBox.information(None, "Warning", "Category is not set! Please step into compound and type category name.")
+ if self._rawNode._rawGraph.category == "":
+ QMessageBox.information(
+ None,
+ "Warning",
+ "Category is not set! Please step into compound and type category name.",
+ )
return
packageNames = list(GET_PACKAGES().keys())
- selectedPackageName, accepted = QInputDialog.getItem(None, "Select", "Select package", packageNames, editable=False)
+ selectedPackageName, accepted = QInputDialog.getItem(
+ None, "Select", "Select package", packageNames, editable=False
+ )
if accepted:
packagePath = GET_PACKAGE_PATH(selectedPackageName)
compoundsDir = os.path.join(packagePath, "Compounds")
@@ -88,9 +98,11 @@ def onExportToPackage(self):
nodeBox.refresh()
def onImport(self):
- openPath, selectedFilter = QFileDialog.getOpenFileName(filter="Subgraph data (*.compound)")
+ openPath, selectedFilter = QFileDialog.getOpenFileName(
+ filter="Subgraph data (*.compound)"
+ )
if openPath != "":
- with open(openPath, 'r') as f:
+ with open(openPath, "r") as f:
data = json.load(f)
self.assignData(data)
@@ -137,4 +149,8 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
wrapper = node.getWrapper()
if wrapper is not None:
if wrapper.bExposeInputsToCompound:
- wrapper.createInputWidgets(inputsCategory, inGroup="{} inputs".format(node.name), pins=False)
+ wrapper.createInputWidgets(
+ inputsCategory,
+ inGroup="{} inputs".format(node.name),
+ pins=False,
+ )
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIConstantNode.py b/PyFlow/Packages/PyFlowBase/UI/UIConstantNode.py
index f3fca5234..31222307c 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIConstantNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIConstantNode.py
@@ -13,9 +13,9 @@
## limitations under the License.
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QComboBox, QCheckBox
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import QComboBox, QCheckBox
from PyFlow.UI.Utils.stylesheet import Colors
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
@@ -32,7 +32,8 @@ def __init__(self, raw_node):
self.headColorOverride = Colors.Gray
self.color = Colors.DarkGray
self.headColor = self.headColorOverride = QtGui.QColor(
- *findPinClassByType("AnyPin").color())
+ *findPinClassByType("AnyPin").color()
+ )
if self.headColor.lightnessF() > 0.75:
self.labelTextColor = QtCore.Qt.black
else:
@@ -45,8 +46,7 @@ def kill(self, *args, **kwargs):
newOuts = []
for i in self.UIoutputs.values():
for connection in i.connections:
- newOuts.append([connection.destination(),
- connection.drawDestination])
+ newOuts.append([connection.destination(), connection.drawDestination])
if inp.connections:
source = inp.connections[0].source()
for out in newOuts:
@@ -70,7 +70,8 @@ def changeOnConection(self, other):
def changeType(self, dataType):
self.headColor = self.headColorOverride = QtGui.QColor(
- *findPinClassByType(dataType).color())
+ *findPinClassByType(dataType).color()
+ )
if self.headColor.lightnessF() > 0.75:
self.labelTextColor = QtCore.Qt.black
else:
@@ -86,8 +87,7 @@ def updateType(self, valToUpdate, inputsCategory, group):
def selectStructure(self, valToUpdate, inputsCategory, group):
if valToUpdate is not None:
del valToUpdate
- super(UIConstantNode, self).createInputWidgets(
- inputsCategory, group)
+ super(UIConstantNode, self).createInputWidgets(inputsCategory, group)
def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
inputVal = None
@@ -101,8 +101,7 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
for i in self._rawNode.pinTypes:
selector.addItem(i)
if self.input.dataType in self._rawNode.pinTypes:
- selector.setCurrentIndex(
- self._rawNode.pinTypes.index(self.input.dataType))
+ selector.setCurrentIndex(self._rawNode.pinTypes.index(self.input.dataType))
structSelector = QComboBox()
for i in [i.name for i in list(StructureType)]:
@@ -112,11 +111,14 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
structSelector.setCurrentIndex(self.input._rawPin._currStructure)
selector.activated.connect(self._rawNode.updateType)
selector.activated.connect(
- lambda: self.updateType(inputVal, inputsCategory, inGroup))
+ lambda: self.updateType(inputVal, inputsCategory, inGroup)
+ )
structSelector.activated.connect(self._rawNode.selectStructure)
structSelector.activated.connect(
- lambda: self.selectStructure(inputVal, inputsCategory, inGroup))
+ lambda: self.selectStructure(inputVal, inputsCategory, inGroup)
+ )
+ inputsCategory.insertWidget(preIndex, "DataType", selector, group=inGroup)
inputsCategory.insertWidget(
- preIndex, "DataType", selector, group=inGroup)
- inputsCategory.insertWidget(preIndex + 1, "Structure", structSelector, group=inGroup)
+ preIndex + 1, "Structure", structSelector, group=inGroup
+ )
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIConvertToNode.py b/PyFlow/Packages/PyFlowBase/UI/UIConvertToNode.py
index c8a771109..a38aea12e 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIConvertToNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIConvertToNode.py
@@ -13,9 +13,9 @@
## limitations under the License.
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QComboBox, QCheckBox
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import QComboBox, QCheckBox
from PyFlow.UI.Utils.stylesheet import Colors
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
@@ -28,7 +28,8 @@ def __init__(self, raw_node):
self.headColorOverride = Colors.Gray
self.color = Colors.DarkGray
self.headColor = self.headColorOverride = QtGui.QColor(
- *findPinClassByType("AnyPin").color())
+ *findPinClassByType("AnyPin").color()
+ )
if self.headColor.lightnessF() > 0.75:
self.labelTextColor = QtCore.Qt.black
else:
@@ -49,7 +50,8 @@ def changeOnConection(self, other):
def changeType(self, dataType):
self.headColor = self.headColorOverride = QtGui.QColor(
- *findPinClassByType(dataType).color())
+ *findPinClassByType(dataType).color()
+ )
if self.headColor.lightnessF() > 0.75:
self.labelTextColor = QtCore.Qt.black
else:
@@ -65,8 +67,7 @@ def createInputWidgets(self, inputsCategory, group=None, pins=True):
for i in self._rawNode.pinTypes:
selector.addItem(i)
if self.output.dataType in self._rawNode.pinTypes:
- selector.setCurrentIndex(
- self._rawNode.pinTypes.index(self.output.dataType))
+ selector.setCurrentIndex(self._rawNode.pinTypes.index(self.output.dataType))
selector.activated.connect(self._rawNode.updateType)
inputsCategory.insertWidget(preIndex, "DataType", selector, group=group)
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIExecPin.py b/PyFlow/Packages/PyFlowBase/UI/UIExecPin.py
index 2827881eb..a5c5aecd0 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIExecPin.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIExecPin.py
@@ -13,13 +13,8 @@
## limitations under the License.
-from Qt import (
- QtGui,
- QtCore
-)
+from qtpy import QtGui,QtCore
-from PyFlow.Core import PinBase
-from PyFlow.Core.Common import *
from PyFlow.UI.Canvas.UIPinBase import UIPinBase
from PyFlow.UI.Canvas.Painters import PinPainter
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIFloatRamp.py b/PyFlow/Packages/PyFlowBase/UI/UIFloatRamp.py
index 470c7730a..45d9a8a8c 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIFloatRamp.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIFloatRamp.py
@@ -14,9 +14,9 @@
import weakref
-from Qt import QtCore
-from Qt import QtGui
-from Qt import QtWidgets
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy import QtWidgets
from PyFlow.UI.Canvas.Painters import NodePainter
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
from PyFlow.UI.Widgets.QtSliders import pyf_RampSpline
@@ -32,7 +32,9 @@ def changeCurveType(self, index):
self._rawNode._curveType = index
for ramp in self.ramps:
if ramp() is not None:
- ramp().setBezier(self._rawNode._curveTypes[self._rawNode._curveType] == "bezier")
+ ramp().setBezier(
+ self._rawNode._curveTypes[self._rawNode._curveType] == "bezier"
+ )
ramp().updateFromRaw()
for selector in self.selectors:
if selector() is not None:
@@ -51,7 +53,10 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
if not self._rawNode.input.isArray():
inputVal.setMinimum(0.0)
inputVal.setMaximum(1.0)
- ramp = pyf_RampSpline(self._rawNode.ramp, bezier=self._rawNode._curveTypes[self._rawNode._curveType] == "bezier")
+ ramp = pyf_RampSpline(
+ self._rawNode.ramp,
+ bezier=self._rawNode._curveTypes[self._rawNode._curveType] == "bezier",
+ )
ramp.tickClicked.connect(self.rampChanged)
ramp.tickAdded.connect(self.rampChanged)
ramp.tickRemoved.connect(self.rampChanged)
@@ -65,5 +70,5 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
selector.addItem(i)
selector.setCurrentIndex(self._rawNode._curveType)
selector.activated.connect(self.changeCurveType)
- inputsCategory.insertWidget(preIndex, "CurveType", selector,group=inGroup)
- inputsCategory.insertWidget(preIndex+1, "Ramp", ramp,group=inGroup)
+ inputsCategory.insertWidget(preIndex, "CurveType", selector, group=inGroup)
+ inputsCategory.insertWidget(preIndex + 1, "Ramp", ramp, group=inGroup)
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py b/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py
index e2669c71d..ddf8a83e4 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIForLoopBeginNode.py
@@ -13,9 +13,9 @@
## limitations under the License.
import uuid
-from Qt import QtGui
-from Qt import QtCore
-from Qt.QtWidgets import *
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import *
from PyFlow.Core.Common import *
from PyFlow.Core.NodeBase import NodeBase
from PyFlow.UI.Utils.stylesheet import Colors
@@ -38,17 +38,19 @@ def postCreate(self, jsonTemplate=None):
def eventDropOnCanvas(self):
# TODO: try to simplify this with Canvas.spawnNode
nodeTemplate = NodeBase.jsonTemplate()
- nodeTemplate['package'] = "PyFlowBase"
- nodeTemplate['lib'] = ""
- nodeTemplate['type'] = "loopEnd"
- nodeTemplate['name'] = self.canvasRef().graphManager.getUniqNodeName("loopEnd")
- nodeTemplate['x'] = self.scenePos().x() + self.geometry().width() + 30
- nodeTemplate['y'] = self.scenePos().y()
- nodeTemplate['uuid'] = str(uuid.uuid4())
+ nodeTemplate["package"] = "PyFlowBase"
+ nodeTemplate["lib"] = ""
+ nodeTemplate["type"] = "loopEnd"
+ nodeTemplate["name"] = self.canvasRef().graphManager.getUniqNodeName("loopEnd")
+ nodeTemplate["x"] = self.scenePos().x() + self.geometry().width() + 30
+ nodeTemplate["y"] = self.scenePos().y()
+ nodeTemplate["uuid"] = str(uuid.uuid4())
endNode = self.canvasRef()._createNode(nodeTemplate)
self.getPinSG("Paired block").setData(str(endNode.path()))
endNode.getPinSG("Paired block").setData(self.path())
- self.canvasRef().connectPins(self.getPinSG("LoopBody"), endNode.getPinSG(DEFAULT_IN_EXEC_NAME))
+ self.canvasRef().connectPins(
+ self.getPinSG("LoopBody"), endNode.getPinSG(DEFAULT_IN_EXEC_NAME)
+ )
def paint(self, painter, option, widget):
self.computeHull()
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIGetVarNode.py b/PyFlow/Packages/PyFlowBase/UI/UIGetVarNode.py
index 810ef73c6..cf4530ca4 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIGetVarNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIGetVarNode.py
@@ -62,7 +62,7 @@ def postCreate(self, jsonTemplate=None):
def serialize(self):
template = UINodeBase.serialize(self)
- template['meta']['var'] = self.var.serialize()
+ template["meta"]["var"] = self.var.serialize()
return template
def onVarSelected(self, varName):
@@ -82,9 +82,13 @@ def onVarSelected(self, varName):
for i in linkedTo:
if i.isAny():
i.setDefault()
- self.canvasRef().connectPinsInternal(self._rawNode.out.getWrapper()(), i.getWrapper()())
+ self.canvasRef().connectPinsInternal(
+ self._rawNode.out.getWrapper()(), i.getWrapper()()
+ )
self.updateHeaderText()
- self.canvasRef().pyFlowInstance.onRequestFillProperties(self.createPropertiesWidget)
+ self.canvasRef().pyFlowInstance.onRequestFillProperties(
+ self.createPropertiesWidget
+ )
self._rawNode.checkForErrors()
self.update()
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIGraphNodes.py b/PyFlow/Packages/PyFlowBase/UI/UIGraphNodes.py
index 3b9a2f1d8..2f92f0fb6 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIGraphNodes.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIGraphNodes.py
@@ -13,7 +13,7 @@
## limitations under the License.
-from Qt import QtCore
+from qtpy import QtCore
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
from PyFlow.UI.Widgets.SelectPinDialog import SelectPinDialog
@@ -39,7 +39,8 @@ def setName(self, name):
oldName = self.getName()
super(UIGraphInputs, self).setName(name)
owningCompoundNode = self.canvasRef().graphManager.findNode(
- self._rawNode.graph().name)
+ self._rawNode.graph().name
+ )
if owningCompoundNode:
uiCompoundNode = owningCompoundNode.getWrapper()
if oldName in uiCompoundNode.groups["input"]:
@@ -48,7 +49,9 @@ def setName(self, name):
if oldName in owningCompoundNode.groups["input"]:
for inp in owningCompoundNode.groups["input"][oldName]:
inp.grop = name
- owningCompoundNode.groups["input"][name] = owningCompoundNode.groups["input"].pop(oldName)
+ owningCompoundNode.groups["input"][name] = owningCompoundNode.groups[
+ "input"
+ ].pop(oldName)
def createPinDialog(self):
self.d = SelectPinDialog()
@@ -76,6 +79,7 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
if self.graph() == GraphManagerSingleton().get().findRootGraph():
self.createOutputWidgets(inputsCategory, inGroup)
+
class UIGraphOutputs(UINodeBase):
pinCreated = QtCore.Signal(object)
@@ -93,7 +97,8 @@ def setName(self, name):
oldName = self.getName()
super(UIGraphOutputs, self).setName(name)
owningCompoundNode = self.canvasRef().graphManager.findNode(
- self._rawNode.graph().name)
+ self._rawNode.graph().name
+ )
if owningCompoundNode:
uiCompoundNode = owningCompoundNode.getWrapper()
if oldName in uiCompoundNode.groups["output"]:
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIImageDisplayNode.py b/PyFlow/Packages/PyFlowBase/UI/UIImageDisplayNode.py
index 0fad41205..c84f66ce1 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIImageDisplayNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIImageDisplayNode.py
@@ -13,10 +13,10 @@
## limitations under the License.
-from Qt import QtGui
+from qtpy import QtGui
from PyFlow.UI import RESOURCES_DIR
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
-from Qt.QtWidgets import QLabel
+from qtpy.QtWidgets import QLabel
class UIImageDisplayNode(UINodeBase):
@@ -38,6 +38,5 @@ def paint(self, painter, option, widget):
super(UIImageDisplayNode, self).paint(painter, option, widget)
def updateSize(self):
- scaledPixmap = self.pixmap.scaledToWidth(
- self.customLayout.geometry().width())
+ scaledPixmap = self.pixmap.scaledToWidth(self.customLayout.geometry().width())
self.Imagelabel.setPixmap(scaledPixmap)
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIMakeDictNode.py b/PyFlow/Packages/PyFlowBase/UI/UIMakeDictNode.py
index 059324b4c..9cb1159dc 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIMakeDictNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIMakeDictNode.py
@@ -13,7 +13,7 @@
## limitations under the License.
-from Qt.QtWidgets import QComboBox
+from qtpy.QtWidgets import QComboBox
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
@@ -28,7 +28,8 @@ def postCreate(self, jsonTemplate=None):
def changeType(self, dataType):
self.input._rawPin.initType(
- self.input._rawPin._defaultSupportedDataTypes[dataType], True)
+ self.input._rawPin._defaultSupportedDataTypes[dataType], True
+ )
def selectStructure(self, name):
self.canvasRef().tryFillPropertiesView(self)
@@ -40,8 +41,11 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
for i in self.input._rawPin._defaultSupportedDataTypes:
selector.addItem(i)
- selector.setCurrentIndex(self.input._rawPin._defaultSupportedDataTypes.index(
- self.input._rawPin.dataType))
+ selector.setCurrentIndex(
+ self.input._rawPin._defaultSupportedDataTypes.index(
+ self.input._rawPin.dataType
+ )
+ )
selector.activated.connect(self.changeType)
inputsCategory.insertWidget(0, "DataType", selector, group=inGroup)
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIPythonNode.py b/PyFlow/Packages/PyFlowBase/UI/UIPythonNode.py
index 2f00820c6..1d5afd0ab 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIPythonNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIPythonNode.py
@@ -19,10 +19,10 @@
import uuid
import logging
-from Qt.QtWidgets import QAction
-from Qt.QtWidgets import QFileDialog
-from Qt.QtWidgets import QInputDialog
-from Qt import QtCore
+from qtpy.QtWidgets import QAction
+from qtpy.QtWidgets import QFileDialog
+from qtpy.QtWidgets import QInputDialog
+from qtpy import QtCore
from PyFlow import GET_PACKAGES
from PyFlow import GET_PACKAGE_PATH
@@ -38,10 +38,10 @@
from PyFlow.Core.Common import *
def prepareNode(node):
- node.createInputPin(pinName="inExec", dataType="ExecPin", foo=node.processNode)
+ node.createInputPin(pinName="inExec", dataType="ExecPin", callback=node.processNode)
node.createOutputPin(pinName="outExec", dataType="ExecPin")
- node.createInputPin(pinName="a", dataType="IntPin", defaultValue=0, foo=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group="")
- node.createInputPin(pinName="b", dataType="IntPin", defaultValue=0, foo=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group="")
+ node.createInputPin(pinName="a", dataType="IntPin", defaultValue=0, callback=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group="")
+ node.createInputPin(pinName="b", dataType="IntPin", defaultValue=0, callback=None, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group="")
node.createOutputPin(pinName="c", dataType="IntPin", defaultValue=0, structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group="")
@@ -63,7 +63,7 @@ def __init__(self, raw_node):
self.actionEdit = self._menu.addAction("Edit")
self.actionEdit.triggered.connect(self.onEdit)
- self._filePath = ''
+ self._filePath = ""
self.fileHandle = None
self.currentEditorProcess = None
@@ -76,7 +76,9 @@ def __init__(self, raw_node):
def onExportToPackage(self):
packageNames = list(GET_PACKAGES().keys())
- selectedPackageName, accepted = QInputDialog.getItem(None, "Select", "Select package", packageNames, editable=False)
+ selectedPackageName, accepted = QInputDialog.getItem(
+ None, "Select", "Select package", packageNames, editable=False
+ )
if accepted:
packagePath = GET_PACKAGE_PATH(selectedPackageName)
pyNodesDir = os.path.join(packagePath, "PyNodes")
@@ -91,18 +93,24 @@ def onExportToPackage(self):
def onExport(self, root=None):
try:
- savePath, selectedFilter = QFileDialog.getSaveFileName(filter="Python node data (*.pynode)", dir=root)
+ savePath, selectedFilter = QFileDialog.getSaveFileName(
+ filter="Python node data (*.pynode)", dir=root
+ )
except:
- savePath, selectedFilter = QFileDialog.getSaveFileName(filter="Python node data (*.pynode)")
+ savePath, selectedFilter = QFileDialog.getSaveFileName(
+ filter="Python node data (*.pynode)"
+ )
if savePath != "":
- with open(savePath, 'w') as f:
+ with open(savePath, "w") as f:
f.write(self.nodeData)
logger.info("{0} data successfully exported!".format(self.getName()))
def onImport(self):
- openPath, selectedFilter = QFileDialog.getOpenFileName(filter="Python node data (*.pynode)")
+ openPath, selectedFilter = QFileDialog.getOpenFileName(
+ filter="Python node data (*.pynode)"
+ )
if openPath != "":
- with open(openPath, 'r') as f:
+ with open(openPath, "r") as f:
dataString = f.read()
self.tryApplyNodeData(dataString)
EditorHistory().saveState("Import python node data", modify=True)
@@ -144,7 +152,7 @@ def onFileChanged(self, path):
return
if not os.path.exists(path):
- self._filePath = ''
+ self._filePath = ""
if self.fileHandle is not None:
self.fileHandle.close()
self.fileHandle = None
@@ -152,7 +160,7 @@ def onFileChanged(self, path):
else:
# open file handle if needed
if self.fileHandle is None:
- self.fileHandle = open(path, 'r')
+ self.fileHandle = open(path, "r")
# read code string
self.fileHandle.seek(0)
@@ -195,7 +203,7 @@ def onEdit(self):
self._filePath = os.path.join(tempFilesDir, "{}.py".format(uidStr))
if not os.path.exists(self._filePath):
- f = open(self._filePath, 'w')
+ f = open(self._filePath, "w")
if self.nodeData == "":
f.write(INITIAL_CODE)
else:
@@ -218,4 +226,4 @@ def onEdit(self):
result = UIPythonNode.watcher.fileChanged.connect(self.onFileChanged)
self.currentEditorProcess = subprocess.Popen(editCmd, shell=True)
- self.fileHandle = open(self._filePath, 'r')
+ self.fileHandle = open(self._filePath, "r")
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIRerouteNodeSmall.py b/PyFlow/Packages/PyFlowBase/UI/UIRerouteNodeSmall.py
index a8749add8..9b692cf65 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIRerouteNodeSmall.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIRerouteNodeSmall.py
@@ -13,8 +13,8 @@
## limitations under the License.
-from Qt import QtCore
-from Qt.QtWidgets import QSizePolicy
+from qtpy import QtCore
+from qtpy.QtWidgets import QSizePolicy
from PyFlow.UI.Utils.stylesheet import Colors
from PyFlow.UI.Canvas.Painters import NodePainter
@@ -90,8 +90,7 @@ def kill(self, *args, **kwargs):
newOuts = []
for i in self.UIoutputs.values():
for connection in i.connections:
- newOuts.append([connection.destination(),
- connection.drawDestination])
+ newOuts.append([connection.destination(), connection.drawDestination])
if inp.connections:
source = inp.connections[0].source()
for out in newOuts:
@@ -117,6 +116,6 @@ def postCreate(self, jsonTemplate=None):
self.updateNodeShape()
def paint(self, painter, option, widget):
- #painter.setPen(QtGui.QPen(QtCore.Qt.green, 0.75))
+ # painter.setPen(QtGui.QPen(QtCore.Qt.green, 0.75))
# painter.drawRect(self.boundingRect())
NodePainter.asRerouteNode(self, painter, option, widget)
diff --git a/PyFlow/Packages/PyFlowBase/UI/UISetVarNode.py b/PyFlow/Packages/PyFlowBase/UI/UISetVarNode.py
index 2f19f92a7..59ad2facb 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UISetVarNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UISetVarNode.py
@@ -13,7 +13,6 @@
## limitations under the License.
-
from PyFlow.UI.Utils.stylesheet import Colors
from PyFlow.Core.Common import *
from PyFlow.UI import RESOURCES_DIR
@@ -51,7 +50,7 @@ def onVariableWasChanged(self):
def serialize(self):
template = UINodeBase.serialize(self)
- template['meta']['var'] = self.var.serialize()
+ template["meta"]["var"] = self.var.serialize()
return template
def onVarSelected(self, varName):
@@ -70,11 +69,13 @@ def onVarSelected(self, varName):
self._rawNode.updateStructure()
for i in outLinkedTo:
self.canvasRef().connectPinsInternal(
- self._rawNode.out.getWrapper()(), i.getWrapper()())
+ self._rawNode.out.getWrapper()(), i.getWrapper()()
+ )
for o in inLinkedTo:
self.canvasRef().connectPinsInternal(
- o.getWrapper()(), self._rawNode.inp.getWrapper()())
+ o.getWrapper()(), self._rawNode.inp.getWrapper()()
+ )
self.updateHeaderText()
@@ -105,4 +106,4 @@ def updateHeaderText(self, name=None):
@staticmethod
def category():
- return 'Variables'
+ return "Variables"
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIStickyNote.py b/PyFlow/Packages/PyFlowBase/UI/UIStickyNote.py
index 66a585b91..24e5f9a1d 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIStickyNote.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIStickyNote.py
@@ -13,16 +13,14 @@
## limitations under the License.
-from nine import *
-
-from Qt import QtCore
-from Qt import QtGui
+from qtpy import QtCore
+from qtpy import QtGui
from PyFlow.UI.Canvas.UICommon import *
from PyFlow.UI.Canvas.Painters import NodePainter
from PyFlow.UI.Canvas.UINodeBase import UINodeBase, InputTextField
from PyFlow.UI.Widgets.QtSliders import pyf_ColorSlider
from PyFlow.UI.Widgets.PropertiesFramework import CollapsibleFormWidget
-from Qt.QtWidgets import (QGraphicsWidget, QGraphicsItem)
+from qtpy.QtWidgets import QGraphicsWidget, QGraphicsItem
class UIStickyNote(UINodeBase):
@@ -33,10 +31,10 @@ def __init__(self, raw_node):
self.labelTextColor = QtGui.QColor(0, 0, 0, 255)
self.resizable = True
self.roundness = 1
- self.textInput = InputTextField(
- "Text Goes Here", self, singleLine=False)
- self.textInput.setPos(QtCore.QPointF(
- 5, self.nodeNameWidget.boundingRect().height()))
+ self.textInput = InputTextField("Text Goes Here", self, singleLine=False)
+ self.textInput.setPos(
+ QtCore.QPointF(5, self.nodeNameWidget.boundingRect().height())
+ )
self.textInput.document().contentsChanged.connect(self.updateSize)
self.textInput.editingFinished.connect(self.editingFinished)
self.textInput.startEditing.connect(self.startEditing)
@@ -56,15 +54,14 @@ def serializationHook(self):
def postCreate(self, jsonTemplate=None):
super(UIStickyNote, self).postCreate(jsonTemplate=jsonTemplate)
if "color" in jsonTemplate["wrapper"]:
- self.color = QtGui.QColor.fromRgba(
- jsonTemplate["wrapper"]["color"])
+ self.color = QtGui.QColor.fromRgba(jsonTemplate["wrapper"]["color"])
if "textColor" in jsonTemplate["wrapper"]:
self.labelTextColor = QtGui.QColor.fromRgba(
- jsonTemplate["wrapper"]["textColor"])
+ jsonTemplate["wrapper"]["textColor"]
+ )
if "currentText" in jsonTemplate["wrapper"]:
self.NonFormatedText = jsonTemplate["wrapper"]["currentText"]
- self.textInput.setHtml(
- self.NonFormatedText.replace("\\n", "
"))
+ self.textInput.setHtml(self.NonFormatedText.replace("\\n", "
"))
def mouseDoubleClickEvent(self, event):
self.textInput.setFlag(QGraphicsWidget.ItemIsFocusable, True)
@@ -94,29 +91,27 @@ def mouseMoveEvent(self, event):
self.updateSize()
def startEditing(self):
- if IS_PYTHON2:
- self.textInput.setPlainText(
- self.NonFormatedText.decode('unicode-escape'))
- else:
- self.textInput.setPlainText(
- bytes(self.NonFormatedText, "utf-8").decode('unicode-escape'))
+ self.textInput.setPlainText(
+ bytes(self.NonFormatedText, "utf-8").decode("unicode-escape")
+ )
def editingFinished(self, succes):
if succes:
- if IS_PYTHON2:
- self.NonFormatedText = self.textInput.toPlainText().encode('unicode-escape')
- self.textInput.setHtml(
- self.NonFormatedText.replace("\\n", "
"))
- else:
- self.NonFormatedText = self.textInput.toPlainText().encode('unicode-escape')
- self.NonFormatedText = self.NonFormatedText.replace(
- b"\\n", b"
").decode('unicode-escape')
- self.textInput.setHtml(self.NonFormatedText)
+ self.NonFormatedText = self.textInput.toPlainText().encode(
+ "unicode-escape"
+ )
+ self.NonFormatedText = self.NonFormatedText.replace(
+ b"\\n", b"
"
+ ).decode("unicode-escape")
+ self.textInput.setHtml(self.NonFormatedText)
def updateSize(self):
self.textInput.setTextWidth(self.boundingRect().width() - 10)
- newHeight = self.textInput.boundingRect().height(
- ) + self.nodeNameWidget.boundingRect().height() + 5
+ newHeight = (
+ self.textInput.boundingRect().height()
+ + self.nodeNameWidget.boundingRect().height()
+ + 5
+ )
if self._rect.height() < newHeight:
self._rect.setHeight(newHeight)
try:
@@ -141,12 +136,14 @@ def updateTextColor(self, color):
def createPropertiesWidget(self, propertiesWidget):
super(UIStickyNote, self).createPropertiesWidget(propertiesWidget)
appearanceCategory = CollapsibleFormWidget(headName="Appearance")
- pb = pyf_ColorSlider(type="int", alpha=True,
- startColor=list(self.color.getRgbF()))
+ pb = pyf_ColorSlider(
+ type="int", alpha=True, startColor=list(self.color.getRgbF())
+ )
pb.valueChanged.connect(self.updateColor)
appearanceCategory.addWidget("Color", pb)
- pb = pyf_ColorSlider(type="int", alpha=True,
- startColor=list(self.labelTextColor.getRgbF()))
+ pb = pyf_ColorSlider(
+ type="int", alpha=True, startColor=list(self.labelTextColor.getRgbF())
+ )
pb.valueChanged.connect(self.updateTextColor)
appearanceCategory.addWidget("TextColor", pb)
- propertiesWidget.insertWidget(appearanceCategory,1)
+ propertiesWidget.insertWidget(appearanceCategory, 1)
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIStoreArgsNode.py b/PyFlow/Packages/PyFlowBase/UI/UIStoreArgsNode.py
new file mode 100644
index 000000000..e5c49fcf6
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/UI/UIStoreArgsNode.py
@@ -0,0 +1,47 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+from PyFlow.UI.Canvas.UICommon import DEFAULT_IN_EXEC_NAME
+from PyFlow.UI import RESOURCES_DIR
+from PyFlow.UI.Canvas.UINodeBase import UINodeBase
+from PyFlow.UI.Canvas.UICommon import NodeActionButtonInfo
+from PyFlow.UI.Utils.stylesheet import Colors
+from qtpy import QtCore
+from qtpy.QtWidgets import QInputDialog
+
+
+class UIStoreArgs(UINodeBase):
+ pinCreated = QtCore.Signal(object)
+
+ def __init__(self, raw_node):
+ super(UIStoreArgs, self).__init__(raw_node)
+ actionAddOut = self._menu.addAction("Add arg")
+ actionAddOut.setToolTip("Add arg")
+ actionAddOut.setData(NodeActionButtonInfo(RESOURCES_DIR + "/pin.svg"))
+ actionAddOut.triggered.connect(self.onAddPin)
+
+ def onAddPin(self):
+ name, confirmed = QInputDialog.getText(
+ None, "Rename", "Enter new pin name")
+ if confirmed and name != self.name and name != "":
+ name = self._rawNode.getUniqPinName(name)
+ rawPin = self._rawNode.addInPin(">" + name, "StringPin")
+ uiPin = self._createUIPinWrapper(rawPin)
+ self.pinCreated.emit(uiPin)
+
+ rawPin = self._rawNode.addOutPin(name, "StringPin")
+ uiPin = self._createUIPinWrapper(rawPin)
+ self.pinCreated.emit(uiPin)
+ self.updateNodeShape()
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/UI/UISubProcessNode.py b/PyFlow/Packages/PyFlowBase/UI/UISubProcessNode.py
new file mode 100644
index 000000000..80e2f1b83
--- /dev/null
+++ b/PyFlow/Packages/PyFlowBase/UI/UISubProcessNode.py
@@ -0,0 +1,69 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+from PyFlow.UI.Canvas.UICommon import DEFAULT_IN_EXEC_NAME
+from PyFlow.UI import RESOURCES_DIR
+from PyFlow.UI.Canvas.UINodeBase import UINodeBase
+from PyFlow.UI.Canvas.UICommon import NodeActionButtonInfo
+from PyFlow.UI.Utils.stylesheet import Colors
+from qtpy import QtCore
+from qtpy.QtWidgets import QLabel
+from qtpy.QtWidgets import QInputDialog
+
+
+class UISubProcess(UINodeBase):
+ pinCreated = QtCore.Signal(object)
+
+ def __init__(self, raw_node):
+ super(UISubProcess, self).__init__(raw_node)
+ actionAddOut = self._menu.addAction("Add option")
+ actionAddOut.setToolTip("Add command option")
+ actionAddOut.setData(NodeActionButtonInfo(RESOURCES_DIR + "/pin.svg"))
+ actionAddOut.triggered.connect(self.onAddInPin)
+ self.computeFlagLabel = QLabel(" idle ")
+ self._rawNode.computing.connect(self.onComputing)
+ self._rawNode.computed.connect(self.onComputed)
+ self.addWidget(self.computeFlagLabel)
+
+ def postCreate(self, jsonTemplate=None):
+ super(UISubProcess, self).postCreate(jsonTemplate=jsonTemplate)
+ inExecPin = self.getPinSG(DEFAULT_IN_EXEC_NAME)
+ inExecPin.bLabelHidden = True
+
+ def onAddInPin(self):
+ name, confirmed = QInputDialog.getText(
+ None, "Rename", "Enter new pin name")
+ if confirmed and name != self.name and name != "":
+ name = self._rawNode.getUniqPinName(name)
+ rawPin = self._rawNode.addInPin(name, "StringPin")
+ uiPin = self._createUIPinWrapper(rawPin)
+ self.pinCreated.emit(uiPin)
+ self.updateNodeShape()
+
+ def postCreate(self, jsonTemplate):
+ UINodeBase.postCreate(self, jsonTemplate)
+ for uiPin in self.UIPins.values():
+ pass
+
+
+ def onComputing(self,*args, **kwargs):
+ self.computeFlagLabel.setText(" working ")
+ # self.setComputing(*args, **kwargs)
+
+ def onComputed(self,*args, **kwargs):
+ self.computeFlagLabel.setText(" idle ")
+ # self.setClean(*args, **kwargs)
+
+
\ No newline at end of file
diff --git a/PyFlow/Packages/PyFlowBase/UI/UISwitchOnStringNode.py b/PyFlow/Packages/PyFlowBase/UI/UISwitchNode.py
similarity index 84%
rename from PyFlow/Packages/PyFlowBase/UI/UISwitchOnStringNode.py
rename to PyFlow/Packages/PyFlowBase/UI/UISwitchNode.py
index a3c035553..3b661815c 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UISwitchOnStringNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UISwitchNode.py
@@ -19,20 +19,21 @@
from PyFlow.UI.Canvas.UICommon import NodeActionButtonInfo
-class UISwitchOnString(UINodeBase):
+class UISwitch(UINodeBase):
def __init__(self, raw_node):
- super(UISwitchOnString, self).__init__(raw_node)
+ super(UISwitch, self).__init__(raw_node)
actionAddOut = self._menu.addAction("Add out pin")
actionAddOut.setToolTip("Adds an option")
actionAddOut.setData(NodeActionButtonInfo(RESOURCES_DIR + "/pin.svg"))
actionAddOut.triggered.connect(self.onAddOutPin)
def postCreate(self, jsonTemplate=None):
- super(UISwitchOnString, self).postCreate(jsonTemplate=jsonTemplate)
+ super(UISwitch, self).postCreate(jsonTemplate=jsonTemplate)
inExecPin = self.getPinSG(DEFAULT_IN_EXEC_NAME)
inExecPin.bLabelHidden = True
def onAddOutPin(self):
- rawPin = self._rawNode.addOutPin()
+ name = self._rawNode.getUniqPinName("0")
+ rawPin = self._rawNode.addOutPin(name)
uiPin = self._createUIPinWrapper(rawPin)
return uiPin
diff --git a/PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py b/PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py
index 3e825bac6..7ce11183f 100644
--- a/PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py
+++ b/PyFlow/Packages/PyFlowBase/UI/UIWhileLoopBeginNode.py
@@ -13,9 +13,9 @@
## limitations under the License.
import uuid
-from Qt import QtGui
-from Qt import QtCore
-from Qt.QtWidgets import *
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import *
from PyFlow.UI.Utils.stylesheet import Colors
from PyFlow.UI.Canvas.Painters import NodePainter
from PyFlow.UI.Canvas.UINodeBase import UINodeBase
@@ -38,17 +38,19 @@ def postCreate(self, jsonTemplate=None):
def eventDropOnCanvas(self):
# TODO: try to simplify this with Canvas.spawnNode
nodeTemplate = NodeBase.jsonTemplate()
- nodeTemplate['package'] = "PyFlowBase"
- nodeTemplate['lib'] = ""
- nodeTemplate['type'] = "loopEnd"
- nodeTemplate['name'] = self.canvasRef().graphManager.getUniqNodeName("loopEnd")
- nodeTemplate['x'] = self.scenePos().x() + self.geometry().width() + 30
- nodeTemplate['y'] = self.scenePos().y()
- nodeTemplate['uuid'] = str(uuid.uuid4())
+ nodeTemplate["package"] = "PyFlowBase"
+ nodeTemplate["lib"] = ""
+ nodeTemplate["type"] = "loopEnd"
+ nodeTemplate["name"] = self.canvasRef().graphManager.getUniqNodeName("loopEnd")
+ nodeTemplate["x"] = self.scenePos().x() + self.geometry().width() + 30
+ nodeTemplate["y"] = self.scenePos().y()
+ nodeTemplate["uuid"] = str(uuid.uuid4())
endNode = self.canvasRef()._createNode(nodeTemplate)
self.getPinSG("Paired block").setData(str(endNode.path()))
endNode.getPinSG("Paired block").setData(self.path())
- self.canvasRef().connectPins(self.getPinSG("LoopBody"), endNode.getPinSG(DEFAULT_IN_EXEC_NAME))
+ self.canvasRef().connectPins(
+ self.getPinSG("LoopBody"), endNode.getPinSG(DEFAULT_IN_EXEC_NAME)
+ )
def paint(self, painter, option, widget):
self.computeHull()
diff --git a/PyFlow/Packages/PyFlowBase/__init__.py b/PyFlow/Packages/PyFlowBase/__init__.py
index 517f340a3..36e6b3654 100644
--- a/PyFlow/Packages/PyFlowBase/__init__.py
+++ b/PyFlow/Packages/PyFlowBase/__init__.py
@@ -1,6 +1,6 @@
"""Base package
"""
-PACKAGE_NAME = 'PyFlowBase'
+PACKAGE_NAME = "PyFlowBase"
from collections import OrderedDict
from PyFlow.UI.UIInterfaces import IPackage
@@ -23,6 +23,8 @@
from PyFlow.Packages.PyFlowBase.FunctionLibraries.MathAbstractLib import MathAbstractLib
from PyFlow.Packages.PyFlowBase.FunctionLibraries.RandomLib import RandomLib
from PyFlow.Packages.PyFlowBase.FunctionLibraries.PathLib import PathLib
+from PyFlow.Packages.PyFlowBase.FunctionLibraries.StringLib import StringLib
+from PyFlow.Packages.PyFlowBase.FunctionLibraries.IOLib import IOLib
# Class based nodes
from PyFlow.Packages.PyFlowBase.Nodes.branch import branch
@@ -41,7 +43,10 @@
from PyFlow.Packages.PyFlowBase.Nodes.forLoopWithBreak import forLoopWithBreak
from PyFlow.Packages.PyFlowBase.Nodes.retriggerableDelay import retriggerableDelay
from PyFlow.Packages.PyFlowBase.Nodes.sequence import sequence
-from PyFlow.Packages.PyFlowBase.Nodes.switchOnString import switchOnString
+from PyFlow.Packages.PyFlowBase.Nodes.switch import switch
+from PyFlow.Packages.PyFlowBase.Nodes.subProcess import subProcess
+from PyFlow.Packages.PyFlowBase.Nodes.combineArgs import combineArgs
+from PyFlow.Packages.PyFlowBase.Nodes.storeArgs import storeArgs
from PyFlow.Packages.PyFlowBase.Nodes.timer import timer
from PyFlow.Packages.PyFlowBase.Nodes.whileLoop import whileLoop
from PyFlow.Packages.PyFlowBase.Nodes.getVar import getVar
@@ -58,7 +63,7 @@
from PyFlow.Packages.PyFlowBase.Nodes.colorRamp import colorRamp
from PyFlow.Packages.PyFlowBase.Nodes.stringToArray import stringToArray
from PyFlow.Packages.PyFlowBase.Nodes.cliexit import cliexit
-
+from PyFlow.Packages.PyFlowBase.Nodes.singletonThreadSampleNode import singletonThreadSampleNode
from PyFlow.Packages.PyFlowBase.Nodes.consoleOutput import consoleOutput
from PyFlow.Packages.PyFlowBase.Nodes.address import address
@@ -86,7 +91,9 @@
from PyFlow.Packages.PyFlowBase.Tools.CompileTool import CompileTool
from PyFlow.Packages.PyFlowBase.Tools.LoggerTool import LoggerTool
-from PyFlow.Packages.PyFlowBase.Exporters.PythonScriptExporter import PythonScriptExporter
+from PyFlow.Packages.PyFlowBase.Exporters.PythonScriptExporter import (
+ PythonScriptExporter,
+)
# Factories
from PyFlow.Packages.PyFlowBase.Factories.UIPinFactory import createUIPin
@@ -98,6 +105,10 @@
from PyFlow.Packages.PyFlowBase.PrefsWidgets.InputPrefs import InputPreferences
from PyFlow.Packages.PyFlowBase.PrefsWidgets.ThemePrefs import ThemePreferences
+# [Forms]
+from PyFlow.Packages.PyFlowBase.Tools.PackageBuilder import PackageBuilder
+from PyFlow.UI.Forms.TextEditor import TextEditor
+
_FOO_LIBS = {
ArrayLib.__name__: ArrayLib(PACKAGE_NAME),
@@ -109,6 +120,8 @@
MathAbstractLib.__name__: MathAbstractLib(PACKAGE_NAME),
RandomLib.__name__: RandomLib(PACKAGE_NAME),
PathLib.__name__: PathLib(PACKAGE_NAME),
+ StringLib.__name__: StringLib(PACKAGE_NAME),
+ IOLib.__name__: IOLib(PACKAGE_NAME),
}
@@ -126,7 +139,10 @@
forLoopWithBreak.__name__: forLoopWithBreak,
retriggerableDelay.__name__: retriggerableDelay,
sequence.__name__: sequence,
- switchOnString.__name__: switchOnString,
+ switch.__name__: switch,
+ subProcess.__name__: subProcess,
+ combineArgs.__name__: combineArgs,
+ storeArgs.__name__: storeArgs,
timer.__name__: timer,
whileLoop.__name__: whileLoop,
whileLoopBegin.__name__: whileLoopBegin,
@@ -156,7 +172,8 @@
colorRamp.__name__: colorRamp,
stringToArray.__name__: stringToArray,
imageDisplay.__name__: imageDisplay,
- cliexit.__name__: cliexit
+ cliexit.__name__: cliexit,
+ singletonThreadSampleNode.__name__: singletonThreadSampleNode,
}
_PINS = {
@@ -176,12 +193,17 @@
_TOOLS[AlignRightTool.__name__] = AlignRightTool
_TOOLS[AlignTopTool.__name__] = AlignTopTool
_TOOLS[AlignBottomTool.__name__] = AlignBottomTool
-_TOOLS[HistoryTool.__name__] = HistoryTool
+
_TOOLS[PropertiesTool.__name__] = PropertiesTool
_TOOLS[VariablesTool.__name__] = VariablesTool
-_TOOLS[NodeBoxTool.__name__] = NodeBoxTool
-_TOOLS[SearchResultsTool.__name__] = SearchResultsTool
+
+_TOOLS[PackageBuilder.__name__] = PackageBuilder
+#_TOOLS[TextEditor.__name__] = TextEditor
+
_TOOLS[LoggerTool.__name__] = LoggerTool
+_TOOLS[HistoryTool.__name__] = HistoryTool
+_TOOLS[SearchResultsTool.__name__] = SearchResultsTool
+_TOOLS[NodeBoxTool.__name__] = NodeBoxTool
_EXPORTERS = OrderedDict()
_EXPORTERS[PythonScriptExporter.__name__] = PythonScriptExporter
@@ -196,6 +218,7 @@
class PyFlowBase(IPackage):
"""Base pyflow package
"""
+
def __init__(self):
super(PyFlowBase, self).__init__()
diff --git a/PyFlow/Packages/__init__.py b/PyFlow/Packages/__init__.py
index 016627cb9..c28ffa4d4 100644
--- a/PyFlow/Packages/__init__.py
+++ b/PyFlow/Packages/__init__.py
@@ -2,4 +2,4 @@
"""
# this line adds extension-packages not installed inside the PyFlow directory
-__path__ = __import__('pkgutil').extend_path(__path__, __name__)
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
diff --git a/PyFlow/Scripts/__init__.py b/PyFlow/Scripts/__init__.py
index 3bee086ea..73d455029 100644
--- a/PyFlow/Scripts/__init__.py
+++ b/PyFlow/Scripts/__init__.py
@@ -14,15 +14,13 @@
import argparse
-import sys
import os
import json
import threading
-import time
from PyFlow.App import PyFlow
from PyFlow import graphUiParser
-from Qt.QtWidgets import QApplication
+from qtpy.QtWidgets import QApplication
from PyFlow import INITIALIZE
from PyFlow.Core.Common import *
from PyFlow.Core.version import currentVersion
@@ -32,17 +30,12 @@
def getGraphArguments(data, parser):
"""Adds arguments to parser using graph input nodes
- :param data: Serialized graph
- :type data: json
+ :param data: Parsed json graph
+ :type data: dict
:param parser: ArgumentParser class instance
:type parser: ArgumentParser
"""
- typeMapping = {
- "BoolPin": bool,
- "StringPin": str,
- "IntPin": int,
- "FloatPin": float
- }
+ typeMapping = {"BoolPin": bool, "StringPin": str, "IntPin": int, "FloatPin": float}
graphNode = None
for node in data["nodes"]:
@@ -53,12 +46,16 @@ def getGraphArguments(data, parser):
for outPin in graphNode["outputs"]:
pinType = outPin["dataType"]
if pinType != "ExecPin":
- parser.add_argument("--{0}".format(outPin["name"]), type=typeMapping[pinType])
+ parser.add_argument(
+ "--{0}".format(outPin["name"]), type=typeMapping[pinType]
+ )
def main():
parser = argparse.ArgumentParser(description="PyFlow CLI")
- parser.add_argument("-m", "--mode", type=str, default="edit", choices=["edit", "run", "runui"])
+ parser.add_argument(
+ "-m", "--mode", type=str, default="edit", choices=["edit", "run", "runui"]
+ )
parser.add_argument("-f", "--filePath", type=str, default="untitled.pygraph")
parser.add_argument("--version", action="version", version=str(currentVersion()))
parsedArguments, unknown = parser.parse_known_args(sys.argv[1:])
@@ -76,7 +73,7 @@ def main():
app.setActiveWindow(instance)
instance.show()
if os.path.exists(filePath):
- with open(filePath, 'r') as f:
+ with open(filePath, "r") as f:
data = json.load(f)
instance.loadFromData(data)
instance.currentFileName = filePath
@@ -87,11 +84,10 @@ def main():
print(e)
if parsedArguments.mode == "run":
- data = None
if not os.path.exists(filePath):
print("No such file. {}".format(filePath))
return
- with open(filePath, 'r') as f:
+ with open(filePath, "r") as f:
data = json.load(f)
getGraphArguments(data, parser)
parsedArguments = parser.parse_args()
diff --git a/PyFlow/Tests/Test_Arrays.py b/PyFlow/Tests/Test_Arrays.py
index a24f0df18..7ba56c589 100644
--- a/PyFlow/Tests/Test_Arrays.py
+++ b/PyFlow/Tests/Test_Arrays.py
@@ -19,33 +19,34 @@
class TestArrays(unittest.TestCase):
-
def setUp(self):
- print('\t[BEGIN TEST]', self._testMethodName)
+ print("\t[BEGIN TEST]", self._testMethodName)
def tearDown(self):
- print('--------------------------------\n')
+ print("--------------------------------\n")
def test_makeList_Node(self):
packages = GET_PACKAGES()
man = GraphManager()
- nodes = packages['PyFlowBase'].GetNodeClasses()
- foos = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"].getFunctions()
- classNodes = packages['PyFlowBase'].GetNodeClasses()
+ nodes = packages["PyFlowBase"].GetNodeClasses()
+ foos = (
+ packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"].getFunctions()
+ )
+ classNodes = packages["PyFlowBase"].GetNodeClasses()
- makeListNode = nodes['makeArray']("mkList")
+ makeListNode = nodes["makeArray"]("mkList")
man.activeGraph().addNode(makeListNode)
- makeIntNode = NodeBase.initializeFromFunction(foos['makeInt'])
- makeIntNode2 = NodeBase.initializeFromFunction(foos['makeInt'])
+ makeIntNode = NodeBase.initializeFromFunction(foos["makeInt"])
+ makeIntNode2 = NodeBase.initializeFromFunction(foos["makeInt"])
man.activeGraph().addNode(makeIntNode)
man.activeGraph().addNode(makeIntNode2)
printNode = classNodes["consoleOutput"]("printer")
man.activeGraph().addNode(printNode)
- connected = connectPins(makeIntNode[str('out')], makeListNode[str('data')])
- connected = connectPins(makeIntNode2[str('out')], makeListNode[str('data')])
+ connected = connectPins(makeIntNode[str("out")], makeListNode[str("data")])
+ connected = connectPins(makeIntNode2[str("out")], makeListNode[str("data")])
self.assertEqual(connected, True)
- connected = connectPins(makeListNode[str('out')], printNode[str("entity")])
+ connected = connectPins(makeListNode[str("out")], printNode[str("entity")])
printNode[DEFAULT_IN_EXEC_NAME].call()
- result = makeListNode[str('out')].getData()
+ result = makeListNode[str("out")].getData()
self.assertEqual(Counter(result), Counter([0, 0]))
diff --git a/PyFlow/Tests/Test_BasePackage.py b/PyFlow/Tests/Test_BasePackage.py
index 78de45503..265ceacbd 100644
--- a/PyFlow/Tests/Test_BasePackage.py
+++ b/PyFlow/Tests/Test_BasePackage.py
@@ -18,39 +18,42 @@
class TestBasePackage(unittest.TestCase):
-
def setUp(self):
- print('\t[BEGIN TEST]', self._testMethodName)
+ print("\t[BEGIN TEST]", self._testMethodName)
def tearDown(self):
- print('--------------------------------\n')
+ print("--------------------------------\n")
def test_branch_node(self):
packages = GET_PACKAGES()
man = GraphManager()
- foos = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"].getFunctions()
- nodes = packages['PyFlowBase'].GetNodeClasses()
+ foos = (
+ packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"].getFunctions()
+ )
+ nodes = packages["PyFlowBase"].GetNodeClasses()
printNode1 = nodes["consoleOutput"]("print")
man.activeGraph().addNode(printNode1)
- printNode1.setData(str('entity'), "first")
+ printNode1.setData(str("entity"), "first")
branchNode = nodes["branch"]("branchNODE")
self.assertIsNotNone(branchNode, "branch node is not created")
man.activeGraph().addNode(branchNode)
- branchNode.setData('Condition', True)
+ branchNode.setData("Condition", True)
- connected = connectPins(printNode1[str(DEFAULT_OUT_EXEC_NAME)], branchNode[str("In")])
+ connected = connectPins(
+ printNode1[str(DEFAULT_OUT_EXEC_NAME)], branchNode[str("In")]
+ )
self.assertEqual(connected, True, "failed to connect")
printNodeTrue = nodes["consoleOutput"]("print")
man.activeGraph().addNode(printNodeTrue)
- printNodeTrue.setData('entity', "True executed")
+ printNodeTrue.setData("entity", "True executed")
printNodeFalse = nodes["consoleOutput"]("print")
man.activeGraph().addNode(printNodeFalse)
- printNodeFalse.setData('entity', "False executed")
+ printNodeFalse.setData("entity", "False executed")
- connectPins(branchNode[str('True')], printNodeTrue[DEFAULT_IN_EXEC_NAME])
- connectPins(branchNode[str('False')], printNodeFalse[DEFAULT_IN_EXEC_NAME])
+ connectPins(branchNode[str("True")], printNodeTrue[DEFAULT_IN_EXEC_NAME])
+ connectPins(branchNode[str("False")], printNodeFalse[DEFAULT_IN_EXEC_NAME])
printNode1.call(DEFAULT_IN_EXEC_NAME, message="TEST MESSAGE")
diff --git a/PyFlow/Tests/Test_General.py b/PyFlow/Tests/Test_General.py
index a076c1aef..39d119f5a 100644
--- a/PyFlow/Tests/Test_General.py
+++ b/PyFlow/Tests/Test_General.py
@@ -21,19 +21,18 @@
class TestGeneral(unittest.TestCase):
-
def setUp(self):
- print('\t[BEGIN TEST]', self._testMethodName)
+ print("\t[BEGIN TEST]", self._testMethodName)
def tearDown(self):
- print('--------------------------------\n')
+ print("--------------------------------\n")
def test_connect_pins_by_indexes(self):
man = GraphManager()
packages = GET_PACKAGES()
- mathLib = packages['PyFlowBase'].GetFunctionLibraries()["MathAbstractLib"]
- defaultLib = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"]
- classNodes = packages['PyFlowBase'].GetNodeClasses()
+ mathLib = packages["PyFlowBase"].GetFunctionLibraries()["MathAbstractLib"]
+ defaultLib = packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"]
+ classNodes = packages["PyFlowBase"].GetNodeClasses()
foos = mathLib.getFunctions()
defaultLibFoos = defaultLib.getFunctions()
@@ -45,7 +44,7 @@ def test_connect_pins_by_indexes(self):
man.activeGraph().addNode(addNode2)
man.activeGraph().addNode(printNode)
- makeIntNode.setData('i', 5)
+ makeIntNode.setData("i", 5)
connection = connectPinsByIndexes(makeIntNode, 1, addNode2, 1)
self.assertEqual(connection, True, "FAILED TO ADD EDGE")
@@ -54,26 +53,31 @@ def test_connect_pins_by_indexes(self):
self.assertEqual(connection, True, "FAILED TO ADD EDGE")
printNode[DEFAULT_IN_EXEC_NAME].call()
- self.assertEqual(addNode2.getData('out'), 5, "NODES EVALUATION IS INCORRECT")
+ self.assertEqual(addNode2.getData("out"), 5, "NODES EVALUATION IS INCORRECT")
def test_graph_location(self):
packages = GET_PACKAGES()
man = GraphManager()
- subgraphNodeClass = packages['PyFlowBase'].GetNodeClasses()['compound']
- subgraphNodeInstance = subgraphNodeClass(str('compound'))
+ subgraphNodeClass = packages["PyFlowBase"].GetNodeClasses()["compound"]
+ subgraphNodeInstance = subgraphNodeClass(str("compound"))
man.activeGraph().addNode(subgraphNodeInstance)
# step inside compound
man.selectGraphByName(subgraphNodeInstance.name)
- self.assertEqual(Counter(man.location()), Counter([man.findRootGraph().name, subgraphNodeInstance.name]))
- self.assertEqual(Counter(subgraphNodeInstance.rawGraph.location()), Counter(man.location()))
+ self.assertEqual(
+ Counter(man.location()),
+ Counter([man.findRootGraph().name, subgraphNodeInstance.name]),
+ )
+ self.assertEqual(
+ Counter(subgraphNodeInstance.rawGraph.location()), Counter(man.location())
+ )
def test_add_int_no_exec(self):
man = GraphManager()
packages = GET_PACKAGES()
- mathLib = packages['PyFlowBase'].GetFunctionLibraries()["MathAbstractLib"]
- defaultLib = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"]
- classNodes = packages['PyFlowBase'].GetNodeClasses()
+ mathLib = packages["PyFlowBase"].GetFunctionLibraries()["MathAbstractLib"]
+ defaultLib = packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"]
+ classNodes = packages["PyFlowBase"].GetNodeClasses()
foos = mathLib.getFunctions()
defaultLibFoos = defaultLib.getFunctions()
@@ -85,22 +89,22 @@ def test_add_int_no_exec(self):
man.activeGraph().addNode(addNode2)
man.activeGraph().addNode(printNode)
- makeIntNode.setData('i', 5)
+ makeIntNode.setData("i", 5)
- connection = connectPins(makeIntNode[str('out')], addNode2[str('a')])
+ connection = connectPins(makeIntNode[str("out")], addNode2[str("a")])
self.assertEqual(connection, True, "FAILED TO ADD EDGE")
- connection = connectPins(addNode2[str('out')], printNode[str("entity")])
+ connection = connectPins(addNode2[str("out")], printNode[str("entity")])
self.assertEqual(connection, True, "FAILED TO ADD EDGE")
printNode[DEFAULT_IN_EXEC_NAME].call()
- self.assertEqual(addNode2.getData('out'), 5, "NODES EVALUATION IS INCORRECT")
+ self.assertEqual(addNode2.getData("out"), 5, "NODES EVALUATION IS INCORRECT")
def test_foo_node_ref_set_data(self):
packages = GET_PACKAGES()
- randomLib = packages['PyFlowBase'].GetFunctionLibraries()["RandomLib"]
- defaultLib = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"]
- classNodes = packages['PyFlowBase'].GetNodeClasses()
+ randomLib = packages["PyFlowBase"].GetFunctionLibraries()["RandomLib"]
+ defaultLib = packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"]
+ classNodes = packages["PyFlowBase"].GetNodeClasses()
randomLibFoos = randomLib.getFunctions()
defaultLibFoos = defaultLib.getFunctions()
@@ -115,9 +119,9 @@ def test_foo_node_ref_set_data(self):
self.assertIsNotNone(randintNode)
self.assertIsNotNone(printNode)
- pPrintInputValuePin = printNode[str('entity')]
+ pPrintInputValuePin = printNode[str("entity")]
- edge2Created = connectPins(randintNode[str('Result')], pPrintInputValuePin)
+ edge2Created = connectPins(randintNode[str("Result")], pPrintInputValuePin)
self.assertEqual(edge2Created, True, "FAILED TO CONNECT INT AND ANY")
values = set()
@@ -129,7 +133,7 @@ def test_foo_node_ref_set_data(self):
def test_reconnect_value(self):
packages = GET_PACKAGES()
- defaultLib = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"]
+ defaultLib = packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"]
foos = defaultLib.getFunctions()
n1 = NodeBase.initializeFromFunction(foos["makeBool"])
@@ -141,8 +145,8 @@ def test_reconnect_value(self):
man.activeGraph().addNode(n2)
man.activeGraph().addNode(n3)
- n1Out = n1.getPinSG(str('out'), PinSelectionGroup.Outputs)
- n3b = n3.getPinSG(str('b'), PinSelectionGroup.Inputs)
+ n1Out = n1.getPinSG(str("out"), PinSelectionGroup.Outputs)
+ n3b = n3.getPinSG(str("b"), PinSelectionGroup.Inputs)
# connect n1.out and n3.b
c1 = connectPins(n1Out, n3b)
# check connection was created
@@ -152,7 +156,7 @@ def test_reconnect_value(self):
# check n3.b affected by n1.out
self.assertEqual(n1Out in n3b.affected_by, True)
- n2Out = n2.getPinSG(str('out'), PinSelectionGroup.Outputs)
+ n2Out = n2.getPinSG(str("out"), PinSelectionGroup.Outputs)
# connect n2.out to n3.b
# n3.b is connected with n1.out
# we expect n3.b breaks all connections before connecting with n2.out
@@ -168,7 +172,7 @@ def test_reconnect_value(self):
def test_are_pins_connected(self):
packages = GET_PACKAGES()
- intlib = packages['PyFlowBase'].GetFunctionLibraries()["MathAbstractLib"]
+ intlib = packages["PyFlowBase"].GetFunctionLibraries()["MathAbstractLib"]
foos = intlib.getFunctions()
addNode1 = NodeBase.initializeFromFunction(foos["add"])
@@ -179,8 +183,8 @@ def test_are_pins_connected(self):
man.activeGraph().addNode(addNode1)
man.activeGraph().addNode(addNode2)
- pinOut = addNode1.getPinSG(str('out'), PinSelectionGroup.Outputs)
- pinInp = addNode2.getPinSG(str('a'), PinSelectionGroup.Inputs)
+ pinOut = addNode1.getPinSG(str("out"), PinSelectionGroup.Outputs)
+ pinInp = addNode2.getPinSG(str("a"), PinSelectionGroup.Inputs)
bConnected = connectPins(pinOut, pinInp)
self.assertEqual(bConnected, True, "FAILED TO ADD EDGE")
self.assertEqual(arePinsConnected(pinOut, pinInp), True)
@@ -206,12 +210,12 @@ def test_variable_scope(self):
# create two subgraphs and variables inside
packages = GET_PACKAGES()
- subgraphNodeClass = packages['PyFlowBase'].GetNodeClasses()['compound']
- varGetterClass = packages['PyFlowBase'].GetNodeClasses()['getVar']
- varSetterClass = packages['PyFlowBase'].GetNodeClasses()['setVar']
+ subgraphNodeClass = packages["PyFlowBase"].GetNodeClasses()["compound"]
+ varGetterClass = packages["PyFlowBase"].GetNodeClasses()["getVar"]
+ varSetterClass = packages["PyFlowBase"].GetNodeClasses()["setVar"]
- subgraphNodeInstance1 = subgraphNodeClass('subgraph1')
- subgraphNodeInstance2 = subgraphNodeClass('subgraph2')
+ subgraphNodeInstance1 = subgraphNodeClass("subgraph1")
+ subgraphNodeInstance2 = subgraphNodeClass("subgraph2")
self.assertEqual(man.activeGraph().addNode(subgraphNodeInstance1), True)
self.assertEqual(man.activeGraph().addNode(subgraphNodeInstance2), True)
@@ -226,8 +230,16 @@ def test_variable_scope(self):
man.selectRootGraph()
# Check variables scope visibility
- self.assertEqual(man.activeGraph().addNode(v1Getter), False, "Variable access error! Variables in child graphs should not be visible to parent ones!")
- self.assertEqual(man.activeGraph().addNode(v1Setter), False, "Variable access error! Variables in child graphs should not be visible to parent ones!")
+ self.assertEqual(
+ man.activeGraph().addNode(v1Getter),
+ False,
+ "Variable access error! Variables in child graphs should not be visible to parent ones!",
+ )
+ self.assertEqual(
+ man.activeGraph().addNode(v1Setter),
+ False,
+ "Variable access error! Variables in child graphs should not be visible to parent ones!",
+ )
# goto subgraph2 and create variable there
man.selectGraphByName(subgraphNodeInstance2.name)
@@ -247,7 +259,9 @@ def test_variable_scope(self):
# two variables. One from subgraph1 + one from root
self.assertEqual(len(vars), 2, "failed to gather variables")
varsValues = [i.value for i in vars]
- self.assertEqual(Counter(varsValues), Counter([0, 1]), "variables are incorrect")
+ self.assertEqual(
+ Counter(varsValues), Counter([0, 1]), "variables are incorrect"
+ )
man.selectRootGraph()
# goto subgraph2 and ask variables there
@@ -256,7 +270,9 @@ def test_variable_scope(self):
# two variables. One from subgraph2 + one from root
self.assertEqual(len(vars), 2, "failed to gather variables")
varsValues = [i.value for i in vars]
- self.assertEqual(Counter(varsValues), Counter([0, 2]), "variables are incorrect")
+ self.assertEqual(
+ Counter(varsValues), Counter([0, 2]), "variables are incorrect"
+ )
man.selectRootGraph()
def test_get_bool_var(self):
@@ -266,24 +282,26 @@ def test_get_bool_var(self):
man = GraphManager()
# create bool variable
- v1 = man.activeGraph().createVariable(str('BoolPin'))
+ v1 = man.activeGraph().createVariable(str("BoolPin"))
v1.value = False
# create variable getter node
- varGetterClass = packages["PyFlowBase"].GetNodeClasses()['getVar']
+ varGetterClass = packages["PyFlowBase"].GetNodeClasses()["getVar"]
# since variable is bool, bool pin will be created
- varGetterInstance = varGetterClass(str('v1Getter'), v1)
+ varGetterInstance = varGetterClass(str("v1Getter"), v1)
man.activeGraph().addNode(varGetterInstance)
# create print node
- defaultLib = packages["PyFlowBase"].GetFunctionLibraries()['DefaultLib']
+ defaultLib = packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"]
printerInstance = classNodes["consoleOutput"]("print")
man.activeGraph().addNode(printerInstance)
# connect to print node input
- varOutPin = varGetterInstance.getPinSG(str('out'), PinSelectionGroup.Outputs)
- printInPin = printerInstance.getPinSG(str('entity'), PinSelectionGroup.Inputs)
- printInExecPin = printerInstance.getPinSG(str('inExec'), PinSelectionGroup.Inputs)
+ varOutPin = varGetterInstance.getPinSG(str("out"), PinSelectionGroup.Outputs)
+ printInPin = printerInstance.getPinSG(str("entity"), PinSelectionGroup.Inputs)
+ printInExecPin = printerInstance.getPinSG(
+ str("inExec"), PinSelectionGroup.Inputs
+ )
connected = connectPins(varOutPin, printInPin)
self.assertEqual(connected, True, "var getter is not connected")
@@ -309,24 +327,30 @@ def test_kill_variable(self):
v1.value = False
# create variable getter node
- varGetterClass = packages["PyFlowBase"].GetNodeClasses()['getVar']
- varGetterInstance = varGetterClass(str('v1Getter'), v1)
+ varGetterClass = packages["PyFlowBase"].GetNodeClasses()["getVar"]
+ varGetterInstance = varGetterClass(str("v1Getter"), v1)
man.activeGraph().addNode(varGetterInstance)
# create print node
- defaultLib = packages["PyFlowBase"].GetFunctionLibraries()['DefaultLib']
- printerInstance = packages["PyFlowBase"].GetNodeClasses()['consoleOutput']("print")
+ defaultLib = packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"]
+ printerInstance = packages["PyFlowBase"].GetNodeClasses()["consoleOutput"](
+ "print"
+ )
man.activeGraph().addNode(printerInstance)
# connect to print node input
- varOutPin = varGetterInstance.getPinSG(str('out'), PinSelectionGroup.Outputs)
- printInPin = printerInstance.getPinSG(str('entity'), PinSelectionGroup.Inputs)
- printInExecPin = printerInstance.getPinSG(str('inExec'), PinSelectionGroup.Inputs)
+ varOutPin = varGetterInstance.getPinSG(str("out"), PinSelectionGroup.Outputs)
+ printInPin = printerInstance.getPinSG(str("entity"), PinSelectionGroup.Inputs)
+ printInExecPin = printerInstance.getPinSG(
+ str("inExec"), PinSelectionGroup.Inputs
+ )
connected = connectPins(varOutPin, printInPin)
self.assertEqual(connected, True, "var getter is not connected")
man.activeGraph().killVariable(v1)
- self.assertEqual(v1 not in man.activeGraph().getVars(), True, "variable not killed")
+ self.assertEqual(
+ v1 not in man.activeGraph().getVars(), True, "variable not killed"
+ )
def test_set_bool_var(self):
packages = GET_PACKAGES()
@@ -334,20 +358,22 @@ def test_set_bool_var(self):
man = GraphManager()
# create bool type variable
- v1 = man.activeGraph().createVariable(str('BoolPin'))
+ v1 = man.activeGraph().createVariable(str("BoolPin"))
# this will accept only bools
v1.value = False
# create variable setter node
- varSetterClass = packages["PyFlowBase"].GetNodeClasses()['setVar']
- varSetterInstance = varSetterClass(str('v1Setter'), v1)
+ varSetterClass = packages["PyFlowBase"].GetNodeClasses()["setVar"]
+ varSetterInstance = varSetterClass(str("v1Setter"), v1)
setterAdded = man.activeGraph().addNode(varSetterInstance)
self.assertEqual(setterAdded, True)
# set new value to setter node input pin
- inExecPin = varSetterInstance.getPinSG(DEFAULT_IN_EXEC_NAME, PinSelectionGroup.Inputs)
- inPin = varSetterInstance.getPinSG(str('inp'), PinSelectionGroup.Inputs)
- outPin = varSetterInstance.getPinSG(str('out'), PinSelectionGroup.Outputs)
+ inExecPin = varSetterInstance.getPinSG(
+ DEFAULT_IN_EXEC_NAME, PinSelectionGroup.Inputs
+ )
+ inPin = varSetterInstance.getPinSG(str("inp"), PinSelectionGroup.Inputs)
+ outPin = varSetterInstance.getPinSG(str("out"), PinSelectionGroup.Outputs)
self.assertIsNotNone(inExecPin)
self.assertIsNotNone(inPin)
self.assertIsNotNone(outPin)
@@ -366,8 +392,8 @@ def test_subgraph_execs(self):
man = GraphManager()
# create empty compound
- subgraphNodeClass = packages['PyFlowBase'].GetNodeClasses()['compound']
- subgraphNodeInstance = subgraphNodeClass(str('compound'))
+ subgraphNodeClass = packages["PyFlowBase"].GetNodeClasses()["compound"]
+ subgraphNodeInstance = subgraphNodeClass(str("compound"))
man.activeGraph().addNode(subgraphNodeInstance)
# step inside compound
@@ -384,36 +410,52 @@ def test_subgraph_execs(self):
# this should expose input pin on compound node
outPin = inputs1.addOutPin()
man.Tick(0.02)
- self.assertEqual(len(subgraphNodeInstance.namePinInputsMap), 1, "failed to expose input pin")
- self.assertEqual(list(subgraphNodeInstance.inputs.values())[0].name, outPin.name)
- outPin.setName('inAnyExec')
+ self.assertEqual(
+ len(subgraphNodeInstance.namePinInputsMap), 1, "failed to expose input pin"
+ )
+ self.assertEqual(
+ list(subgraphNodeInstance.inputs.values())[0].name, outPin.name
+ )
+ outPin.setName("inAnyExec")
# create input pin on graphOutputs node
# this should expose output pin on compound node
inPin = outputs1.addInPin()
man.Tick(0.02)
- self.assertEqual(len(subgraphNodeInstance.namePinOutputsMap), 1, "failed to expose input pin")
- self.assertEqual(list(subgraphNodeInstance.outputs.values())[0].name, inPin.name)
- inPin.setName('outAnyExec')
-
- subgraphInAnyExec = subgraphNodeInstance.getPinSG(str('inAnyExec'), PinSelectionGroup.Inputs)
- self.assertIsNotNone(subgraphInAnyExec, "failed to find compound input exec pin")
- subgraphOutAnyExec = subgraphNodeInstance.getPinSG(str('outAnyExec'), PinSelectionGroup.Outputs)
+ self.assertEqual(
+ len(subgraphNodeInstance.namePinOutputsMap), 1, "failed to expose input pin"
+ )
+ self.assertEqual(
+ list(subgraphNodeInstance.outputs.values())[0].name, inPin.name
+ )
+ inPin.setName("outAnyExec")
+
+ subgraphInAnyExec = subgraphNodeInstance.getPinSG(
+ str("inAnyExec"), PinSelectionGroup.Inputs
+ )
+ self.assertIsNotNone(
+ subgraphInAnyExec, "failed to find compound input exec pin"
+ )
+ subgraphOutAnyExec = subgraphNodeInstance.getPinSG(
+ str("outAnyExec"), PinSelectionGroup.Outputs
+ )
self.assertIsNotNone(subgraphOutAnyExec, "failed to find compound out exec pin")
# add print node inside
- foos = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"].getFunctions()
+ foos = (
+ packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"].getFunctions()
+ )
- printNode1 = packages["PyFlowBase"].GetNodeClasses()['consoleOutput']("print")
+ printNode1 = packages["PyFlowBase"].GetNodeClasses()["consoleOutput"]("print")
man.activeGraph().addNode(printNode1)
printNode1.setData("entity", "hello from compound")
def test_graph_serialization(self):
man = GraphManager()
packages = GET_PACKAGES()
- lib = packages['PyFlowBase'].GetFunctionLibraries()["MathAbstractLib"]
- defaultLib = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"]
- classNodes = packages['PyFlowBase'].GetNodeClasses()
+ lib = packages["PyFlowBase"].GetFunctionLibraries()["MathAbstractLib"]
+ defaultLib = packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"]
+ classNodes = packages["PyFlowBase"].GetNodeClasses()
foos = lib.getFunctions()
defFoos = defaultLib.getFunctions()
@@ -425,7 +467,7 @@ def test_graph_serialization(self):
man.activeGraph().addNode(addNode2)
man.activeGraph().addNode(printNode)
- makeInt.setData('i', 5)
+ makeInt.setData("i", 5)
connected = connectPinsByIndexes(makeInt, 1, addNode2, 1)
self.assertEqual(connected, True)
@@ -444,17 +486,17 @@ def test_graph_serialization(self):
# load
man.deserialize(dataJson)
- restoredAddNode2 = man.activeGraph().findNode(str('makeInt'))
- printNode = man.activeGraph().findNode(str('printer'))
+ restoredAddNode2 = man.activeGraph().findNode(str("makeInt"))
+ printNode = man.activeGraph().findNode(str("printer"))
printNode[DEFAULT_IN_EXEC_NAME].call()
- self.assertEqual(restoredAddNode2.getData('out'), 5, "Incorrect calc")
+ self.assertEqual(restoredAddNode2.getData("out"), 5, "Incorrect calc")
def test_graph_depth(self):
man = GraphManager()
packages = GET_PACKAGES()
- subgraphNodeClass = packages['PyFlowBase'].GetNodeClasses()['compound']
- subgraphNodeInstance = subgraphNodeClass(str(str('compound')))
+ subgraphNodeClass = packages["PyFlowBase"].GetNodeClasses()["compound"]
+ subgraphNodeInstance = subgraphNodeClass(str(str("compound")))
man.activeGraph().addNode(subgraphNodeInstance)
self.assertEqual(man.activeGraph().depth(), 1)
@@ -466,8 +508,8 @@ def test_manager_serialization(self):
man = GraphManager()
packages = GET_PACKAGES()
- subgraphNodeClass = packages['PyFlowBase'].GetNodeClasses()['compound']
- subgraphNodeInstance = subgraphNodeClass(str('compound'))
+ subgraphNodeClass = packages["PyFlowBase"].GetNodeClasses()["compound"]
+ subgraphNodeInstance = subgraphNodeClass(str("compound"))
man.activeGraph().addNode(subgraphNodeInstance)
man.selectGraph(subgraphNodeInstance)
@@ -478,25 +520,41 @@ def test_manager_serialization(self):
# this should expose input pin on compound node
outPin = inputs1.addOutPin()
man.Tick(0.02)
- self.assertEqual(len(subgraphNodeInstance.namePinInputsMap), 1, "failed to expose input pin")
- self.assertEqual(list(subgraphNodeInstance.inputs.values())[0].name, outPin.name)
+ self.assertEqual(
+ len(subgraphNodeInstance.namePinInputsMap), 1, "failed to expose input pin"
+ )
+ self.assertEqual(
+ list(subgraphNodeInstance.inputs.values())[0].name, outPin.name
+ )
self.assertEqual(outPin.optionEnabled(PinOptions.Dynamic), True)
# change inner pin name and check it is reflected outside
outPin.setName("first")
- self.assertEqual(list(subgraphNodeInstance.inputs.values())[0].name, outPin.name, "name is not synchronized")
+ self.assertEqual(
+ list(subgraphNodeInstance.inputs.values())[0].name,
+ outPin.name,
+ "name is not synchronized",
+ )
# create input pin on graphOutputs node
# this should expose output pin on compound node
inPin = outputs1.addInPin()
man.Tick(0.02)
- self.assertEqual(len(subgraphNodeInstance.namePinOutputsMap), 1, "failed to expose input pin")
- self.assertEqual(list(subgraphNodeInstance.outputs.values())[0].name, inPin.name)
+ self.assertEqual(
+ len(subgraphNodeInstance.namePinOutputsMap), 1, "failed to expose input pin"
+ )
+ self.assertEqual(
+ list(subgraphNodeInstance.outputs.values())[0].name, inPin.name
+ )
self.assertEqual(inPin.optionEnabled(PinOptions.RenamingEnabled), True)
# change inner pin name and check it is reflected outside
inPin.setName("second")
- self.assertEqual(list(subgraphNodeInstance.outputs.values())[0].name, inPin.name, "name is not synchronized")
+ self.assertEqual(
+ list(subgraphNodeInstance.outputs.values())[0].name,
+ inPin.name,
+ "name is not synchronized",
+ )
depthsBefore = [g.depth() for g in man.getAllGraphs()]
@@ -507,19 +565,31 @@ def test_manager_serialization(self):
man.deserialize(saved)
self.assertIsNotNone(man.activeGraph())
nameAfter = man.getAllNodes(classNameFilters="compound")[0].name
- self.assertEqual(nameBefore, nameAfter, "names are incorrect {0} - {1}".format(nameBefore, nameAfter))
+ self.assertEqual(
+ nameBefore,
+ nameAfter,
+ "names are incorrect {0} - {1}".format(nameBefore, nameAfter),
+ )
depthsAfter = [g.depth() for g in man.getAllGraphs()]
- self.assertEqual(Counter(depthsBefore), Counter(depthsAfter), "failed to restore graphs depths")
+ self.assertEqual(
+ Counter(depthsBefore),
+ Counter(depthsAfter),
+ "failed to restore graphs depths",
+ )
def test_any_pin_speed(self):
packages = GET_PACKAGES()
man = GraphManager()
- classNodes = packages['PyFlowBase'].GetNodeClasses()
- defaultLib = packages['PyFlowBase'].GetFunctionLibraries()["DefaultLib"].getFunctions()
- arrayLib = packages['PyFlowBase'].GetFunctionLibraries()["ArrayLib"].getFunctions()
-
- makeArrayNode = classNodes['makeArray']("makeArray")
+ classNodes = packages["PyFlowBase"].GetNodeClasses()
+ defaultLib = (
+ packages["PyFlowBase"].GetFunctionLibraries()["DefaultLib"].getFunctions()
+ )
+ arrayLib = (
+ packages["PyFlowBase"].GetFunctionLibraries()["ArrayLib"].getFunctions()
+ )
+
+ makeArrayNode = classNodes["makeArray"]("makeArray")
arraySlice = NodeBase.initializeFromFunction(arrayLib["arraySlice"])
arrayLen = NodeBase.initializeFromFunction(arrayLib["arrayElementCount"])
makeInt0 = NodeBase.initializeFromFunction(defaultLib["makeInt"])
@@ -565,5 +635,5 @@ def test_any_pin_speed(self):
print("DELTA:", time.process_time() - start)
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/PyFlow/Tests/TestsBase.py b/PyFlow/Tests/TestsBase.py
index a7356378c..a23ca4f1e 100644
--- a/PyFlow/Tests/TestsBase.py
+++ b/PyFlow/Tests/TestsBase.py
@@ -14,19 +14,10 @@
import unittest
-from nine import str
from PyFlow.Core.Common import *
-from PyFlow import(
- INITIALIZE,
- GET_PACKAGES
-)
-
-from PyFlow.Core import(
- GraphBase,
- PinBase,
- NodeBase,
- GraphManager
-)
+from PyFlow import INITIALIZE, GET_PACKAGES
+
+from PyFlow.Core import GraphBase, PinBase, NodeBase, GraphManager
INITIALIZE()
diff --git a/PyFlow/UI/Canvas/AutoPanController.py b/PyFlow/UI/Canvas/AutoPanController.py
index 736e0110d..0f13174f2 100644
--- a/PyFlow/UI/Canvas/AutoPanController.py
+++ b/PyFlow/UI/Canvas/AutoPanController.py
@@ -13,7 +13,7 @@
## limitations under the License.
-from Qt import QtGui
+from qtpy import QtGui
from PyFlow.Core.Common import *
@@ -42,8 +42,7 @@ def Tick(self, rect, pos):
if pos.y() > rect.height():
self.autoPanDelta = QtGui.QVector2D(0.0, self.amount)
self.beenOutside = True
- self.amount = clamp(
- abs(rect.height() - pos.y()) * 0.1, 0.0, 25.0)
+ self.amount = clamp(abs(rect.height() - pos.y()) * 0.1, 0.0, 25.0)
if self.beenOutside and rect.contains(pos):
self.reset()
diff --git a/PyFlow/UI/Canvas/CanvasBase.py b/PyFlow/UI/Canvas/CanvasBase.py
index 324c80a85..db0388410 100644
--- a/PyFlow/UI/Canvas/CanvasBase.py
+++ b/PyFlow/UI/Canvas/CanvasBase.py
@@ -1,6 +1,6 @@
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import *
from PyFlow.UI.Canvas.UICommon import *
from PyFlow.UI.Utils.stylesheet import editableStyleSheet
@@ -36,7 +36,9 @@ def __init__(self):
self.mousePos = QtCore.QPointF(0, 0)
self._lastMousePos = QtCore.QPointF(0, 0)
- self.centerOn(QtCore.QPointF(self.sceneRect().width() / 2, self.sceneRect().height() / 2))
+ self.centerOn(
+ QtCore.QPointF(self.sceneRect().width() / 2, self.sceneRect().height() / 2)
+ )
def createScene(self):
scene = QGraphicsScene(self)
@@ -90,11 +92,7 @@ def manipulationMode(self, value):
self.viewport().setCursor(QtCore.Qt.ArrowCursor)
def wheelEvent(self, event):
- (xfo, invRes) = self.transform().inverted()
- topLeft = xfo.map(self.rect().topLeft())
- bottomRight = xfo.map(self.rect().bottomRight())
- center = (topLeft + bottomRight) * 0.5
- zoomFactor = 1.0 + event.delta() * self._mouseWheelZoomRate
+ zoomFactor = 1.0 + event.angleDelta().y() * self._mouseWheelZoomRate
self.zoom(zoomFactor)
@@ -102,7 +100,7 @@ def zoom(self, scale_factor):
self.factor = self.transform().m22()
futureScale = self.factor * scale_factor
if futureScale <= self._minimum_scale:
- scale_factor = (self._minimum_scale) / self.factor
+ scale_factor = self._minimum_scale / self.factor
if futureScale >= self._maximum_scale:
scale_factor = (self._maximum_scale - 0.1) / self.factor
self.scale(scale_factor, scale_factor)
@@ -120,7 +118,6 @@ def frameRect(self, rect):
# zoom to fit content
ws = windowRect.size()
rect += QtCore.QMargins(40, 40, 40, 40)
- rs = rect.size()
widthRef = ws.width()
heightRef = ws.height()
sx = widthRef / rect.width()
@@ -158,25 +155,31 @@ def currentViewScale(self):
return self.transform().m22()
def getLodValueFromScale(self, numLods=5, scale=1.0):
- lod = lerp(numLods, 1, GetRangePct(self.viewMinimumScale(), self.viewMaximumScale(), scale))
+ lod = lerp(
+ numLods,
+ 1,
+ GetRangePct(self.viewMinimumScale(), self.viewMaximumScale(), scale),
+ )
return int(round(lod))
def getLodValueFromCurrentScale(self, numLods=5):
return self.getLodValueFromScale(numLods, self.currentViewScale())
def getCanvasLodValueFromCurrentScale(self):
- return self.getLodValueFromScale(editableStyleSheet().LOD_Number[0], self.currentViewScale())
+ return self.getLodValueFromScale(
+ editableStyleSheet().LOD_Number[0], self.currentViewScale()
+ )
def drawBackground(self, painter, rect):
super(CanvasBase, self).drawBackground(painter, rect)
lod = self.getCanvasLodValueFromCurrentScale()
self.boundingRect = rect
- polygon = self.mapToScene(self.viewport().rect())
-
painter.fillRect(rect, QtGui.QBrush(editableStyleSheet().CanvasBgColor))
- left = int(rect.left()) - (int(rect.left()) % editableStyleSheet().GridSizeFine[0])
+ left = int(rect.left()) - (
+ int(rect.left()) % editableStyleSheet().GridSizeFine[0]
+ )
top = int(rect.top()) - (int(rect.top()) % editableStyleSheet().GridSizeFine[0])
if editableStyleSheet().DrawGrid[0] >= 1:
@@ -200,8 +203,12 @@ def drawBackground(self, painter, rect):
painter.drawLines(gridLines)
# Draw thick grid
- left = int(rect.left()) - (int(rect.left()) % editableStyleSheet().GridSizeHuge[0])
- top = int(rect.top()) - (int(rect.top()) % editableStyleSheet().GridSizeHuge[0])
+ left = int(rect.left()) - (
+ int(rect.left()) % editableStyleSheet().GridSizeHuge[0]
+ )
+ top = int(rect.top()) - (
+ int(rect.top()) % editableStyleSheet().GridSizeHuge[0]
+ )
# Draw vertical thick lines
gridLines = []
@@ -234,7 +241,11 @@ def drawBackground(self, painter, rect):
y += editableStyleSheet().GridSizeHuge[0]
inty = int(y)
if y > top + 30:
- painter.setPen(QtGui.QPen(editableStyleSheet().CanvasGridColorDarker.lighter(300)))
+ painter.setPen(
+ QtGui.QPen(
+ editableStyleSheet().CanvasGridColorDarker.lighter(300)
+ )
+ )
painter.drawText(rect.left(), y - 1.0, str(inty))
x = float(left)
@@ -242,5 +253,11 @@ def drawBackground(self, painter, rect):
x += editableStyleSheet().GridSizeHuge[0]
intx = int(x)
if x > left + 30:
- painter.setPen(QtGui.QPen(editableStyleSheet().CanvasGridColorDarker.lighter(300)))
- painter.drawText(x, rect.top() + painter.font().pointSize(), str(intx))
\ No newline at end of file
+ painter.setPen(
+ QtGui.QPen(
+ editableStyleSheet().CanvasGridColorDarker.lighter(300)
+ )
+ )
+ painter.drawText(
+ x, rect.top() + painter.font().pointSize(), str(intx)
+ )
diff --git a/PyFlow/UI/Canvas/IConvexHullBackDrop.py b/PyFlow/UI/Canvas/IConvexHullBackDrop.py
index 96858829c..f920494ed 100644
--- a/PyFlow/UI/Canvas/IConvexHullBackDrop.py
+++ b/PyFlow/UI/Canvas/IConvexHullBackDrop.py
@@ -13,13 +13,14 @@
## limitations under the License.
-from Qt import QtGui
-from Qt import QtCore
+from qtpy import QtGui
+from qtpy import QtCore
from PyFlow.UI.Canvas.loopBackDrop import backDrop
from PyFlow.Core.PathsRegistry import PathsRegistry
from PyFlow.UI.Utils.ConvexHull import convex_hull
from PyFlow.UI.Canvas.Painters import ConnectionPainter
+
class IConvexHullBackDrop(object):
"""Convex hull backdrop routines. Used by for loop and while loop nodes"""
@@ -45,7 +46,10 @@ def computeHull(self):
p = [self.getTopMostOwningCollapsedComment()]
else:
p = [self]
- if loopEndNode.__class__.__name__ == "loopEnd" and loopEndNode.getWrapper() is not None:
+ if (
+ loopEndNode.__class__.__name__ == "loopEnd"
+ and loopEndNode.getWrapper() is not None
+ ):
uiLoopEnd = loopEndNode.getWrapper()
if loopEndNode.isUnderActiveGraph():
if uiLoopEnd.isUnderCollapsedComment():
@@ -81,4 +85,4 @@ def computeHull(self):
path = []
for i in self.convex_hull:
path.append(QtCore.QPointF(i[0], i[1]))
- self.poly,none = ConnectionPainter.roundCornersPath(path,6,True)
+ self.poly, none = ConnectionPainter.roundCornersPath(path, 6, True)
diff --git a/PyFlow/UI/Canvas/NodeActionButton.py b/PyFlow/UI/Canvas/NodeActionButton.py
index 9136f5b84..9f1963b77 100644
--- a/PyFlow/UI/Canvas/NodeActionButton.py
+++ b/PyFlow/UI/Canvas/NodeActionButton.py
@@ -13,17 +13,17 @@
## limitations under the License.
-from Qt import QtCore, QtGui
-from Qt import QtSvg
-from Qt.QtWidgets import QGraphicsWidget
-from Qt.QtWidgets import QSizePolicy
-
+from qtpy import QtCore, QtGui
+from qtpy.QtWidgets import QGraphicsWidget
+from qtpy.QtWidgets import QSizePolicy
+from qtpy.QtSvgWidgets import QGraphicsSvgItem
class NodeActionButtonBase(QGraphicsWidget):
"""Base class for all node's actions buttons.
By default it calls action `triggered` signal. Have default svg 10x10 icon.
"""
+
def __init__(self, svgFilePath, action, uiNode):
super(NodeActionButtonBase, self).__init__(uiNode)
self.setAcceptHoverEvents(True)
@@ -31,7 +31,7 @@ def __init__(self, svgFilePath, action, uiNode):
self.setGraphicsItem(self)
self.setSizePolicy(QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum))
self.action = action
- self.svgIcon = QtSvg.QGraphicsSvgItem(svgFilePath, self)
+ self.svgIcon = QGraphicsSvgItem(svgFilePath, self)
self.setToolTip(self.action.toolTip())
self.hovered = False
uiNode._actionButtons.add(self)
diff --git a/PyFlow/UI/Canvas/Painters.py b/PyFlow/UI/Canvas/Painters.py
index 0021b09cb..c16332c80 100644
--- a/PyFlow/UI/Canvas/Painters.py
+++ b/PyFlow/UI/Canvas/Painters.py
@@ -13,11 +13,9 @@
# limitations under the License.
from math import atan2, degrees
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QStyle
-
-from PyFlow import getPinFromData
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import QStyle
from PyFlow.UI.Canvas.UICommon import *
from PyFlow.Core.Common import *
@@ -31,7 +29,6 @@
# Determines how to paint the node
class NodePainter(object):
-
@staticmethod
def drawDeprecated(node, painter, option, widget):
if node.isDeprecated():
@@ -41,8 +38,9 @@ def drawDeprecated(node, painter, option, widget):
textRect = node.boundingRect()
textRect.setTop(textRect.height() - 10)
painter.fillRect(textRect, Colors.Red.darker(130))
- painter.drawText(textRect, QtCore.Qt.AlignCenter |
- QtCore.Qt.AlignVCenter, "Deprecated")
+ painter.drawText(
+ textRect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, "Deprecated"
+ )
@staticmethod
def drawExperimental(node, painter, option, widget):
@@ -53,8 +51,9 @@ def drawExperimental(node, painter, option, widget):
textRect = node.boundingRect()
textRect.setTop(textRect.height() - 10)
painter.fillRect(textRect, Colors.Yellow.darker(180))
- painter.drawText(textRect, QtCore.Qt.AlignCenter |
- QtCore.Qt.AlignVCenter, "Experimental")
+ painter.drawText(
+ textRect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, "Experimental"
+ )
@staticmethod
def drawResizeHandles(node, painter, option, widget):
@@ -121,8 +120,9 @@ def asCommentNode(node, painter, option, widget):
painter.setPen(pen)
painter.setBrush(QtGui.QColor(0, 0, 0, 0))
painter.drawRoundedRect(frame, node.roundness, node.roundness)
- painter.drawLine(frame.left() + 5, node.labelHeight,
- frame.right() - 5, node.labelHeight)
+ painter.drawLine(
+ frame.left() + 5, node.labelHeight, frame.right() - 5, node.labelHeight
+ )
NodePainter.drawResizeHandles(node, painter, option, widget)
@staticmethod
@@ -134,22 +134,18 @@ def drawGroups(node, painter, option, widget):
if grp.hovered and grp.expanded:
if grp.numPins() > 0:
grpPos = grp.geometry().bottomLeft()
- lastPinPos = grp._pins[grp.numPins(
- ) - 1].geometry().bottomLeft()
+ lastPinPos = grp._pins[grp.numPins() - 1].geometry().bottomLeft()
painter.drawLine(grpPos, grpPos + inputsOffset)
- painter.drawLine(grpPos + inputsOffset,
- lastPinPos + inputsOffset)
+ painter.drawLine(grpPos + inputsOffset, lastPinPos + inputsOffset)
painter.drawLine(lastPinPos + inputsOffset, lastPinPos)
for grp in node.groups["output"].values():
if grp.hovered and grp.expanded:
if grp.numPins() > 0:
grpPos = grp.geometry().bottomRight()
- lastPinPos = grp._pins[grp.numPins(
- ) - 1].geometry().bottomRight()
+ lastPinPos = grp._pins[grp.numPins() - 1].geometry().bottomRight()
painter.drawLine(grpPos, grpPos + outputsOffset)
- painter.drawLine(grpPos + outputsOffset,
- lastPinPos + outputsOffset)
+ painter.drawLine(grpPos + outputsOffset, lastPinPos + outputsOffset)
painter.drawLine(lastPinPos + outputsOffset, lastPinPos)
@staticmethod
@@ -207,7 +203,11 @@ def default(node, painter, option, widget):
else:
painter.fillRect(lr, headColor)
- if not node.isCallable() and node._rawNode.bCacheEnabled or node._rawNode.__class__.__name__=="graphOutputs":
+ if (
+ not node.isCallable()
+ and node._rawNode.bCacheEnabled
+ or node._rawNode.__class__.__name__ == "graphOutputs"
+ ):
NodePainter.drawState(node, painter, pen, lod, SWITCH_LOD, r)
if not node.isValid():
@@ -238,14 +238,14 @@ def default(node, painter, option, widget):
@staticmethod
def drawState(node, painter, pen, lod, SWITCH_LOD, r):
- prevWidth = pen.width()
+ prevWidth = pen.width()
paint = False
if node.computing:
pen.setColor(Colors.Yellow)
pen.setStyle(node.optPenSelectedType)
pen.setWidth(prevWidth * 2)
paint = True
- else:
+ else:
if node._rawNode.isDirty():
pen.setColor(Colors.Orange)
pen.setStyle(node.optPenSelectedType)
@@ -255,7 +255,7 @@ def drawState(node, painter, pen, lod, SWITCH_LOD, r):
pen.setColor(Colors.Green)
pen.setStyle(node.optPenSelectedType)
pen.setWidth(prevWidth * 2)
- paint = True
+ paint = True
if paint and node.isValid():
painter.setPen(pen)
@@ -265,10 +265,10 @@ def drawState(node, painter, pen, lod, SWITCH_LOD, r):
rect.setWidth(rect.width() - pen.width() / 2)
rect.setHeight(rect.height() - pen.width() / 2)
rect.setX(pen.width() / 2)
- rect.setY(rect.y() + pen.width() / 2)
+ rect.setY(rect.y() + pen.width() / 2)
painter.drawRoundedRect(rect, node.roundness, node.roundness)
else:
- painter.drawRect(r)
+ painter.drawRect(r)
pen.setWidth(prevWidth)
@staticmethod
@@ -290,14 +290,14 @@ def asVariableGetter(node, painter, option, widget):
pen.setColor(editableStyleSheet().MainColor)
pen.setStyle(node.optPenSelectedType)
painter.setPen(pen)
- painter.drawRoundedRect(node.boundingRect(),
- node.roundness, node.roundness)
+ painter.drawRoundedRect(node.boundingRect(), node.roundness, node.roundness)
painter.setFont(node.nodeNameFont)
painter.setPen(QtGui.QPen(QtCore.Qt.white, 0.5))
textRect = node.boundingRect()
textRect.setWidth(textRect.width() - 10)
- painter.drawText(textRect, QtCore.Qt.AlignCenter |
- QtCore.Qt.AlignVCenter, node.var.name)
+ painter.drawText(
+ textRect, QtCore.Qt.AlignCenter | QtCore.Qt.AlignVCenter, node.var.name
+ )
@staticmethod
def asRerouteNode(node, painter, option, widget):
@@ -322,8 +322,11 @@ def asRerouteNode(node, painter, option, widget):
pen.setStyle(node.optPenSelectedType)
pen.setWidth(width * 1.5)
painter.setPen(pen)
- painter.drawEllipse(node.boundingRect().center(
- ), node.drawRect.width() / 2, node.drawRect.width() / 2)
+ painter.drawEllipse(
+ node.boundingRect().center(),
+ node.drawRect.width() / 2,
+ node.drawRect.width() / 2,
+ )
# Determines how to paint a pin
@@ -340,14 +343,13 @@ def asValuePin(pin, painter, option, widget):
frame = QtCore.QRectF(QtCore.QPointF(0, 0), pin.geometry().size())
- w = frame.width() / 2
- h = frame.height() / 2
halfPinSize = pin.pinSize / 2
if lod < SWITCH_LOD and not pin.bLabelHidden:
painter.setFont(pin._font)
- textWidth = QtGui.QFontMetrics(
- painter.font()).horizontalAdvance(pin.displayName())
+ textWidth = QtGui.QFontMetrics(painter.font()).horizontalAdvance(
+ pin.displayName()
+ )
textHeight = QtGui.QFontMetrics(painter.font()).height()
x = 1 + pin.pinSize + halfPinSize
if pin.direction == PinDirection.Output:
@@ -359,7 +361,8 @@ def asValuePin(pin, painter, option, widget):
pinCenter = pin.pinCenter()
radialGrad = QtGui.QRadialGradient(
- pinCenter.x(), pinCenter.y() - 0.3, halfPinSize * 0.8)
+ pinCenter.x(), pinCenter.y() - 0.3, halfPinSize * 0.8
+ )
if not pin._rawPin.hasConnections():
radialGrad.setColorAt(0, pin.color().darker(280))
radialGrad.setColorAt(0.5, pin.color().darker(280))
@@ -375,8 +378,12 @@ def asValuePin(pin, painter, option, widget):
painter.setBrush(QtGui.QColor(128, 128, 128, 30))
painter.drawRoundedRect(frame, 3, 3)
painter.setBrush(radialGrad)
- painter.drawEllipse(pinCenter.x() - halfPinSize,
- pinCenter.y() - halfPinSize, pin.pinSize, pin.pinSize)
+ painter.drawEllipse(
+ pinCenter.x() - halfPinSize,
+ pinCenter.y() - halfPinSize,
+ pin.pinSize,
+ pin.pinSize,
+ )
@staticmethod
def asExecPin(pin, painter, option, widget):
@@ -390,8 +397,9 @@ def asExecPin(pin, painter, option, widget):
painter.setPen(PinPainter._execPen)
if lod < SWITCH_LOD and not pin.bLabelHidden:
- textWidth = QtGui.QFontMetrics(
- painter.font()).horizontalAdvance(pin.displayName())
+ textWidth = QtGui.QFontMetrics(painter.font()).horizontalAdvance(
+ pin.displayName()
+ )
textHeight = QtGui.QFontMetrics(painter.font()).height()
x = 1 + pin.pinSize + halfPinSize
if pin.direction == PinDirection.Output:
@@ -406,16 +414,20 @@ def asExecPin(pin, painter, option, widget):
else:
painter.setBrush(QtCore.Qt.NoBrush)
pinCenter = pin.pinCenter()
- xOffset = pinCenter.x() - \
- pin.pinSize if pin.direction == PinDirection.Input else pinCenter.x() - \
- pin.pinSize * 0.8
- arrow = QtGui.QPolygonF([QtCore.QPointF(2, 0.0),
- QtCore.QPointF(2 + pin.pinSize / 2.0, 0.0),
- QtCore.QPointF(2 + pin.pinSize,
- pin.pinSize / 2.0),
- QtCore.QPointF(
- 2 + pin.pinSize / 2.0, pin.pinSize),
- QtCore.QPointF(2, pin.pinSize)]).translated(xOffset, 1)
+ xOffset = (
+ pinCenter.x() - pin.pinSize
+ if pin.direction == PinDirection.Input
+ else pinCenter.x() - pin.pinSize * 0.8
+ )
+ arrow = QtGui.QPolygonF(
+ [
+ QtCore.QPointF(2, 0.0),
+ QtCore.QPointF(2 + pin.pinSize / 2.0, 0.0),
+ QtCore.QPointF(2 + pin.pinSize, pin.pinSize / 2.0),
+ QtCore.QPointF(2 + pin.pinSize / 2.0, pin.pinSize),
+ QtCore.QPointF(2, pin.pinSize),
+ ]
+ ).translated(xOffset, 1)
painter.drawPolygon(arrow)
if pin.hovered:
painter.setPen(QtCore.Qt.NoPen)
@@ -428,14 +440,21 @@ def asGroupPin(pin, painter, option, widget):
painter.setBrush(QtGui.QBrush(Colors.AbsoluteBlack))
# painter.setBrush(QtCore.Qt.NoBrush)
if not pin.expanded:
- arrow = QtGui.QPolygonF([QtCore.QPointF(0.0, 0.0),
- QtCore.QPointF(
- pin.pinSize, pin.pinSize / 2.0),
- QtCore.QPointF(0, pin.pinSize)])
+ arrow = QtGui.QPolygonF(
+ [
+ QtCore.QPointF(0.0, 0.0),
+ QtCore.QPointF(pin.pinSize, pin.pinSize / 2.0),
+ QtCore.QPointF(0, pin.pinSize),
+ ]
+ )
else:
- arrow = QtGui.QPolygonF([QtCore.QPointF(pin.pinSize / 2, pin.pinSize),
- QtCore.QPointF(0, 0),
- QtCore.QPointF(pin.pinSize, 0)])
+ arrow = QtGui.QPolygonF(
+ [
+ QtCore.QPointF(pin.pinSize / 2, pin.pinSize),
+ QtCore.QPointF(0, 0),
+ QtCore.QPointF(pin.pinSize, 0),
+ ]
+ )
painter.drawPolygon(arrow)
@staticmethod
@@ -460,8 +479,9 @@ def asArrayPin(pin, painter, option, widget):
frame = QtCore.QRectF(QtCore.QPointF(0, 0), pin.geometry().size())
halfPinSize = pin.pinSize / 2
painter.setFont(pin._font)
- textWidth = QtGui.QFontMetrics(
- painter.font()).horizontalAdvance(pin.displayName())
+ textWidth = QtGui.QFontMetrics(painter.font()).horizontalAdvance(
+ pin.displayName()
+ )
textHeight = QtGui.QFontMetrics(painter.font()).height()
x = 1 + pin.pinSize + halfPinSize
if pin.direction == PinDirection.Output:
@@ -500,8 +520,9 @@ def asDictPin(pin, painter, option, widget):
frame = QtCore.QRectF(QtCore.QPointF(0, 0), pin.geometry().size())
halfPinSize = pin.pinSize / 2
painter.setFont(pin._font)
- textWidth = QtGui.QFontMetrics(
- painter.font()).horizontalAdvance(pin.displayName())
+ textWidth = QtGui.QFontMetrics(painter.font()).horizontalAdvance(
+ pin.displayName()
+ )
textHeight = QtGui.QFontMetrics(painter.font()).height()
x = 1 + pin.pinSize + halfPinSize
if pin.direction == PinDirection.Output:
@@ -515,9 +536,9 @@ def asDictPin(pin, painter, option, widget):
painter.setBrush(QtGui.QColor(128, 128, 128, 30))
painter.drawRoundedRect(frame, 3, 3)
+
# Determines how to paint a connection:
class ConnectionPainter(object):
-
@staticmethod
def linearPath(path, closed=False):
mPath = QtGui.QPainterPath()
@@ -546,8 +567,8 @@ def chanferPath(path, offset, closed=False):
maxLen = max(0, min(fDist.length() / 2, (bDist.length()) / 2))
- dot = fDist.x() * bDist.x() + fDist.y() * bDist.y() # dot product
- det = fDist.x() * bDist.y() - fDist.y() * bDist.x() # determinant
+ dot = fDist.x() * bDist.x() + fDist.y() * bDist.y() # dot product
+ det = fDist.x() * bDist.y() - fDist.y() * bDist.x() # determinant
angle = degrees(atan2(det, dot)) # atan2(y, x) or atan2(sin, cos)
if abs(angle) > 45:
@@ -621,31 +642,39 @@ def roundCornersPath(path, roundness, closed=False, highlightedSegment=-1):
prevPoint = QtGui.QVector2D(point.x(), point.y())
fDist = nextPoint - currPoint
bDist = currPoint - prevPoint
- xRoundnes = min(min(roundness, fDist.length() / 2.0), bDist.length() / 2.0)
+ xRoundnes = min(
+ min(roundness, fDist.length() / 2.0), bDist.length() / 2.0
+ )
n = currPoint + fDist.normalized() * xRoundnes
p = currPoint - bDist.normalized() * xRoundnes
highlightedSegmentPath.lineTo(QtCore.QPointF(p.x(), p.y()))
- highlightedSegmentPath.quadTo(QtCore.QPointF(currPoint.x(), currPoint.y()), QtCore.QPointF(n.x(), n.y()))
+ highlightedSegmentPath.quadTo(
+ QtCore.QPointF(currPoint.x(), currPoint.y()),
+ QtCore.QPointF(n.x(), n.y()),
+ )
return mPath, highlightedSegmentPath
@staticmethod
- def BasicCircuit(p1, p2,
- offset=20,
- roundness=5,
- sameSide=0,
- lod=0,
- complexLine=False,
- vOffset=0,
- hOffsetL=0,
- vOffsetSShape=0,
- hOffsetR=0,
- hOffsetRSShape=0,
- hOffsetLSShape=0,
- snapVToFirst=False,
- snapVToSecond=False,
- highlightedSegment=-1):
+ def BasicCircuit(
+ p1,
+ p2,
+ offset=20,
+ roundness=5,
+ sameSide=0,
+ lod=0,
+ complexLine=False,
+ vOffset=0,
+ hOffsetL=0,
+ vOffsetSShape=0,
+ hOffsetR=0,
+ hOffsetRSShape=0,
+ hOffsetLSShape=0,
+ snapVToFirst=False,
+ snapVToSecond=False,
+ highlightedSegment=-1,
+ ):
SWITCH_LOD = editableStyleSheet().ConnectionSwitch[0]
offset1 = offset
offset2 = -offset
@@ -693,7 +722,9 @@ def BasicCircuit(p1, p2,
if lod >= SWITCH_LOD:
mPath = ConnectionPainter.linearPath(path)
else:
- mPath, section = ConnectionPainter.roundCornersPath(path, roundness, highlightedSegment=highlightedSegment)
+ mPath, section = ConnectionPainter.roundCornersPath(
+ path, roundness, highlightedSegment=highlightedSegment
+ )
return mPath, path, section
@staticmethod
diff --git a/PyFlow/UI/Canvas/SelectionRect.py b/PyFlow/UI/Canvas/SelectionRect.py
index d68ec77dd..a83862e13 100644
--- a/PyFlow/UI/Canvas/SelectionRect.py
+++ b/PyFlow/UI/Canvas/SelectionRect.py
@@ -13,7 +13,7 @@
## limitations under the License.
-from Qt import QtGui, QtWidgets, QtCore
+from qtpy import QtGui, QtWidgets, QtCore
class SelectionRect(QtWidgets.QGraphicsWidget):
@@ -51,8 +51,7 @@ def setDragPoint(self, dragPoint, modifiers):
topLeft.setY(dragPoint.y())
bottomRight.setY(self.__mouseDownPos.y())
self.setPos(topLeft)
- self.resize(bottomRight.x() - topLeft.x(),
- bottomRight.y() - topLeft.y())
+ self.resize(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())
def paint(self, painter, option, widget):
rect = self.windowFrameRect()
diff --git a/PyFlow/UI/Canvas/UICommon.py b/PyFlow/UI/Canvas/UICommon.py
index 5a793d190..6fc325132 100644
--- a/PyFlow/UI/Canvas/UICommon.py
+++ b/PyFlow/UI/Canvas/UICommon.py
@@ -28,48 +28,6 @@ def rst2html(rst):
return ""
-def fetchPackageNames(graphJson):
- """Parses serialized graph and returns all package names it uses
-
- :param graphJson: Serialized graph
- :type graphJson: dict
- :rtyoe: list(str)
- """
- packages = set()
-
- def worker(graphData):
- for node in graphData["nodes"]:
- packages.add(node["package"])
-
- for inpJson in node["inputs"]:
- packages.add(inpJson['package'])
-
- for outJson in node["inputs"]:
- packages.add(outJson['package'])
-
- if "graphData" in node:
- worker(node["graphData"])
- worker(graphJson)
- return packages
-
-
-def validateGraphDataPackages(graphData, missedPackages=set()):
- """Checks if packages used in serialized data accessible
-
- Missed packages will be added to output set
-
- :param graphData: Serialized graph
- :type graphData: dict
- :param missedPackages: Package names that missed
- :type missedPackages: str
- :rtype: bool
- """
- existingPackages = GET_PACKAGES().keys()
- graphPackages = fetchPackageNames(graphData)
- for pkg in graphPackages:
- if pkg not in existingPackages:
- missedPackages.add(pkg)
- return len(missedPackages) == 0
class VisibilityPolicy(IntEnum):
@@ -99,19 +57,18 @@ class Spacings:
def lineRectIntersection(l, r):
- it_left, impactLeft = l.intersect(
- QtCore.QLineF(r.topLeft(), r.bottomLeft()))
+ it_left, impactLeft = l.intersect(QtCore.QLineF(r.topLeft(), r.bottomLeft()))
if it_left == QtCore.QLineF.BoundedIntersection:
return impactLeft
it_top, impactTop = l.intersect(QtCore.QLineF(r.topLeft(), r.topRight()))
if it_top == QtCore.QLineF.BoundedIntersection:
return impactTop
it_bottom, impactBottom = l.intersect(
- QtCore.QLineF(r.bottomLeft(), r.bottomRight()))
+ QtCore.QLineF(r.bottomLeft(), r.bottomRight())
+ )
if it_bottom == QtCore.QLineF.BoundedIntersection:
return impactBottom
- it_right, impactRight = l.intersect(
- QtCore.QLineF(r.topRight(), r.bottomRight()))
+ it_right, impactRight = l.intersect(QtCore.QLineF(r.topRight(), r.bottomRight()))
if it_right == QtCore.QLineF.BoundedIntersection:
return impactRight
@@ -169,6 +126,10 @@ def SVG_ICON_KEY(self):
def PURE_NODE_HEAD_COLOR(self):
return Colors.NodeNameRectGreen
+ @property
+ def COMPUTING_NODE_HEAD_COLOR(self):
+ return Colors.Red
+
@property
def CALLABLE_NODE_HEAD_COLOR(self):
return Colors.NodeNameRectBlue
diff --git a/PyFlow/UI/Canvas/UIConnection.py b/PyFlow/UI/Canvas/UIConnection.py
index 64aec3dfd..75059676f 100644
--- a/PyFlow/UI/Canvas/UIConnection.py
+++ b/PyFlow/UI/Canvas/UIConnection.py
@@ -16,12 +16,12 @@
import weakref
from uuid import UUID, uuid4
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QGraphicsPathItem
-from Qt.QtWidgets import QGraphicsEllipseItem
-from Qt.QtWidgets import QMenu
-from Qt.QtWidgets import QStyle
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import QGraphicsPathItem
+from qtpy.QtWidgets import QGraphicsEllipseItem
+from qtpy.QtWidgets import QMenu
+from qtpy.QtWidgets import QStyle
from PyFlow.UI.Utils.stylesheet import editableStyleSheet, Colors, ConnectionTypes
from PyFlow.UI.Canvas.UICommon import NodeDefaults
@@ -29,11 +29,11 @@
from PyFlow.Core.Common import *
-
# UIConnection between pins
class UIConnection(QGraphicsPathItem):
"""UIConnection is a cubic spline curve. It represents connection between two pins.
"""
+
def __init__(self, source, destination, canvas):
QGraphicsPathItem.__init__(self)
self.setAcceptedMouseButtons(QtCore.Qt.LeftButton)
@@ -69,7 +69,13 @@ def __init__(self, source, destination, canvas):
if source.isExec():
self.thickness = 2
- self.pen = QtGui.QPen(self.color, self.thickness, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
+ self.pen = QtGui.QPen(
+ self.color,
+ self.thickness,
+ QtCore.Qt.SolidLine,
+ QtCore.Qt.RoundCap,
+ QtCore.Qt.RoundJoin,
+ )
points = self.getEndPoints()
self.updateCurve(points[0], points[1])
@@ -107,7 +113,8 @@ def __init__(self, source, destination, canvas):
self.bubble.setPos(point)
self.bubble.hide()
- self.source()._rawPin.onExecute.connect(self.performEvaluationFeedback)
+
+ self.source().OnPinExecute.connect(self.performEvaluationFeedback)
self.shouldAnimate = False
self.timeline = QtCore.QTimeLine(2000)
self.timeline.setFrameRange(0, 100)
@@ -122,9 +129,13 @@ def performEvaluationFeedback(self, *args, **kwargs):
self.timeline.start()
def timelineFrameChanged(self, frameNum):
- percentage = currentProcessorTime() - self.source()._rawPin.getLastExecutionTime()
+ percentage = (
+ currentProcessorTime() - self.source()._rawPin.getLastExecutionTime()
+ )
self.shouldAnimate = percentage < 0.5
- point = self.mPath.pointAtPercent(float(frameNum) / float(self.timeline.endFrame()))
+ point = self.mPath.pointAtPercent(
+ float(frameNum) / float(self.timeline.endFrame())
+ )
self.bubble.setPos(point)
if not self.shouldAnimate:
self.timeline.stop()
@@ -138,15 +149,25 @@ def isUnderCollapsedComment(self):
dstNode = self.destination().owningNode()
srcComment = srcNode.owningCommentNode
dstComment = dstNode.owningCommentNode
- if srcComment is not None and dstComment is not None and srcComment == dstComment and srcComment.collapsed:
+ if (
+ srcComment is not None
+ and dstComment is not None
+ and srcComment == dstComment
+ and srcComment.collapsed
+ ):
return True
return False
def isUnderActiveGraph(self):
- return self.canvasRef().graphManager.activeGraph() == self.source()._rawPin.owningNode().graph()
+ return (
+ self.canvasRef().graphManager.activeGraph()
+ == self.source()._rawPin.owningNode().graph()
+ )
def __repr__(self):
- return "{0} -> {1}".format(self.source().getFullName(), self.destination().getFullName())
+ return "{0} -> {1}".format(
+ self.source().getFullName(), self.destination().getFullName()
+ )
def setColor(self, color):
self.pen.setColor(color)
@@ -164,11 +185,15 @@ def updateEndpointsPositions(self):
if srcComment.collapsed:
rightSideEndpointGetter = srcComment.getRightSideEdgesPoint
if srcNodeUnderCollapsedComment:
- rightSideEndpointGetter = topMostCollapsedComment.getRightSideEdgesPoint
+ rightSideEndpointGetter = (
+ topMostCollapsedComment.getRightSideEdgesPoint
+ )
self.sourcePositionOverride = rightSideEndpointGetter
else:
if srcNodeUnderCollapsedComment:
- self.sourcePositionOverride = topMostCollapsedComment.getRightSideEdgesPoint
+ self.sourcePositionOverride = (
+ topMostCollapsedComment.getRightSideEdgesPoint
+ )
else:
self.sourcePositionOverride = None
else:
@@ -183,11 +208,15 @@ def updateEndpointsPositions(self):
if dstComment.collapsed:
rightSideEndpointGetter = dstComment.getLeftSideEdgesPoint
if dstNodeUnderCollapsedComment:
- rightSideEndpointGetter = topMostCollapsedComment.getLeftSideEdgesPoint
+ rightSideEndpointGetter = (
+ topMostCollapsedComment.getLeftSideEdgesPoint
+ )
self.destinationPositionOverride = rightSideEndpointGetter
else:
if dstNodeUnderCollapsedComment:
- self.destinationPositionOverride = topMostCollapsedComment.getLeftSideEdgesPoint
+ self.destinationPositionOverride = (
+ topMostCollapsedComment.getLeftSideEdgesPoint
+ )
else:
self.destinationPositionOverride = None
else:
@@ -220,57 +249,64 @@ def uid(self):
@uid.setter
def uid(self, value):
if self._uid in self.canvasRef().connections:
- self.canvasRef().connections[value] = self.canvasRef().connections.pop(self._uid)
+ self.canvasRef().connections[value] = self.canvasRef().connections.pop(
+ self._uid
+ )
self._uid = value
-
+
+ # TODO: Check why this serialization sometimes fails
def applyJsonData(self, data):
- hOffsetL = data['hOffsetL']
+ hOffsetL = data["hOffsetL"]
if hOffsetL is not None:
self.hOffsetL = float(hOffsetL)
- hOffsetR = data['hOffsetR']
+ hOffsetR = data["hOffsetR"]
if hOffsetR is not None:
self.hOffsetR = float(hOffsetR)
- hOffsetLSShape = data['hOffsetLSShape']
+ hOffsetLSShape = data["hOffsetLSShape"]
if hOffsetLSShape is not None:
self.hOffsetLSShape = float(hOffsetLSShape)
- hOffsetRSShape = data['hOffsetRSShape']
+ hOffsetRSShape = data["hOffsetRSShape"]
if hOffsetRSShape is not None:
self.hOffsetRSShape = float(hOffsetRSShape)
- vOffset = data['vOffset']
+ vOffset = data["vOffset"]
if vOffset is not None:
self.vOffset = float(vOffset)
- vOffsetSShape = data['vOffsetSShape']
+ vOffsetSShape = data["vOffsetSShape"]
if vOffsetSShape is not None:
self.vOffsetSShape = float(vOffsetSShape)
- snapVToFirst = data['snapVToFirst']
+ snapVToFirst = data["snapVToFirst"]
if snapVToFirst is not None:
self.snapVToFirst = bool(snapVToFirst)
- snapVToSecond = data['snapVToSecond']
+ snapVToSecond = data["snapVToSecond"]
if snapVToSecond is not None:
self.snapVToSecond = bool(snapVToSecond)
self.getEndPoints()
def serialize(self):
- script = {'sourceUUID': str(self.source().uid),
- 'destinationUUID': str(self.destination().uid),
- 'sourceName': self.source()._rawPin.getFullName(),
- 'destinationName': self.destination()._rawPin.getFullName(),
- 'uuid': str(self.uid),
- 'hOffsetL': str(self.hOffsetL),
- 'hOffsetR': str(self.hOffsetR),
- 'hOffsetLSShape': str(self.hOffsetLSShape),
- 'hOffsetRSShape': str(self.hOffsetRSShape),
- 'vOffset': str(self.vOffset),
- 'vOffsetSShape': str(self.vOffsetSShape),
- 'snapVToFirst': int(self.snapVToFirst),
- 'snapVToSecond': int(self.snapVToSecond),
- }
+ script = {
+ "sourceUUID": str(self.source().uid),
+ "destinationUUID": str(self.destination().uid),
+ "sourceName": self.source()._rawPin.getFullName(),
+ "destinationName": self.destination()._rawPin.getFullName(),
+ "uuid": str(self.uid),
+ "hOffsetL": str(self.hOffsetL),
+ "hOffsetR": str(self.hOffsetR),
+ "hOffsetLSShape": str(self.hOffsetLSShape),
+ "hOffsetRSShape": str(self.hOffsetRSShape),
+ "vOffset": str(self.vOffset),
+ "vOffsetSShape": str(self.vOffsetSShape),
+ "snapVToFirst": int(self.snapVToFirst),
+ "snapVToSecond": int(self.snapVToSecond),
+ }
return script
def __str__(self):
- return '{0} >>> {1}'.format(self.source()._rawPin.getFullName(), self.destination()._rawPin.getFullName())
+ return "{0} >>> {1}".format(
+ self.source()._rawPin.getFullName(),
+ self.destination()._rawPin.getFullName(),
+ )
def drawThick(self):
self.pen.setWidthF(self.thickness + (self.thickness / 1.5))
@@ -332,15 +368,24 @@ def getEndPoints(self):
if self.destinationPositionOverride is not None:
p2 = self.destinationPositionOverride()
- if editableStyleSheet().ConnectionMode[0] in [ConnectionTypes.Circuit, ConnectionTypes.ComplexCircuit]:
+ if editableStyleSheet().ConnectionMode[0] in [
+ ConnectionTypes.Circuit,
+ ConnectionTypes.ComplexCircuit,
+ ]:
self.sameSide = 0
p1n, p2n = p1, p2
xDistance = p2.x() - p1.x()
- if self.destination().owningNode()._rawNode.__class__.__name__ in ["reroute", "rerouteExecs"]:
+ if self.destination().owningNode()._rawNode.__class__.__name__ in [
+ "reroute",
+ "rerouteExecs",
+ ]:
if xDistance < 0:
p2n, p1n = p1, p2
self.sameSide = 1
- if self.source().owningNode()._rawNode.__class__.__name__ in ["reroute", "rerouteExecs"]:
+ if self.source().owningNode()._rawNode.__class__.__name__ in [
+ "reroute",
+ "rerouteExecs",
+ ]:
if xDistance < 0:
p1n, p2n = p1, p2
self.sameSide = -1
@@ -437,7 +482,7 @@ def mouseMoveEvent(self, event):
self.pressedSegment = 0
else:
self.snapVToFirst = False
- if p1.y() + self.vOffset > p2.y() - 3 and p1.y() + self.vOffset < p2.y() + 3:
+ if p2.y() - 3 < p1.y() + self.vOffset < p2.y() + 3:
self.snapVToSecond = True
self.pressedSegment = 2
else:
@@ -484,11 +529,17 @@ def updateCurve(self, p1, p2):
self.mPath.moveTo(p1)
if xDistance < 0:
- self.mPath.cubicTo(QtCore.QPoint(p1.x() + xDistance / -multiply, p1.y()),
- QtCore.QPoint(p2.x() - xDistance / -multiply, p2.y()), p2)
+ self.mPath.cubicTo(
+ QtCore.QPoint(p1.x() + xDistance / -multiply, p1.y()),
+ QtCore.QPoint(p2.x() - xDistance / -multiply, p2.y()),
+ p2,
+ )
else:
- self.mPath.cubicTo(QtCore.QPoint(p1.x() + xDistance / multiply,
- p1.y()), QtCore.QPoint(p2.x() - xDistance / 2, p2.y()), p2)
+ self.mPath.cubicTo(
+ QtCore.QPoint(p1.x() + xDistance / multiply, p1.y()),
+ QtCore.QPoint(p2.x() - xDistance / 2, p2.y()),
+ p2,
+ )
self.setPath(self.mPath)
@@ -514,10 +565,35 @@ def paint(self, painter, option, widget):
self.sShape = xDistance < 0
sectionPath = None
if editableStyleSheet().ConnectionMode[0] == ConnectionTypes.Circuit:
- seg = self.hoverSegment if self.hoverSegment != -1 and self.linPath and self.pressedSegment == -1 else self.pressedSegment
- self.mPath, self.linPath, sectionPath = ConnectionPainter.BasicCircuit(p1, p2, offset, roundness, self.sameSide, lod, False, self.vOffset, self.hOffsetL, self.vOffsetSShape, self.hOffsetR, self.hOffsetRSShape, self.hOffsetLSShape, self.snapVToFirst, self.snapVToSecond, seg)
+ seg = (
+ self.hoverSegment
+ if self.hoverSegment != -1
+ and self.linPath
+ and self.pressedSegment == -1
+ else self.pressedSegment
+ )
+ self.mPath, self.linPath, sectionPath = ConnectionPainter.BasicCircuit(
+ p1,
+ p2,
+ offset,
+ roundness,
+ self.sameSide,
+ lod,
+ False,
+ self.vOffset,
+ self.hOffsetL,
+ self.vOffsetSShape,
+ self.hOffsetR,
+ self.hOffsetRSShape,
+ self.hOffsetLSShape,
+ self.snapVToFirst,
+ self.snapVToSecond,
+ seg,
+ )
elif editableStyleSheet().ConnectionMode[0] == ConnectionTypes.ComplexCircuit:
- self.mPath, self.linPath, sectionPath = ConnectionPainter.BasicCircuit(p1, p2, offset, roundness, self.sameSide, lod, True)
+ self.mPath, self.linPath, sectionPath = ConnectionPainter.BasicCircuit(
+ p1, p2, offset, roundness, self.sameSide, lod, True
+ )
elif editableStyleSheet().ConnectionMode[0] == ConnectionTypes.Cubic:
self.mPath = ConnectionPainter.Cubic(p1, p2, 150, lod)
self.linPath = None
diff --git a/PyFlow/UI/Canvas/UINodeBase.py b/PyFlow/UI/Canvas/UINodeBase.py
index 076d0b052..c76204c97 100644
--- a/PyFlow/UI/Canvas/UINodeBase.py
+++ b/PyFlow/UI/Canvas/UINodeBase.py
@@ -13,19 +13,17 @@
## limitations under the License.
-from nine import str
import logging
-from Qt import QtCore
-from Qt import QtGui
-from Qt import QtSvg
-from Qt.QtWidgets import *
+import uuid
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy import QtSvg
+from qtpy.QtWidgets import *
+from qtpy.QtSvgWidgets import QGraphicsSvgItem
+
from PyFlow.ConfigManager import ConfigManager
from PyFlow.Core.Common import *
-from PyFlow.UI.Canvas.UIPinBase import (
- UIPinBase,
- getUIPinInstance,
- PinGroup
-)
+from PyFlow.UI.Canvas.UIPinBase import UIPinBase, getUIPinInstance, PinGroup
from PyFlow.UI.EditorHistory import EditorHistory
from PyFlow.UI.Canvas.UICommon import *
from PyFlow.UI.Widgets.InputWidgets import createInputWidget
@@ -43,6 +41,7 @@
class CollapseNodeActionButton(NodeActionButtonBase):
"""docstring for CollapseNodeActionButton."""
+
def __init__(self, svgFilePath, action, uiNode):
super(CollapseNodeActionButton, self).__init__(svgFilePath, action, uiNode)
self.svgIcon.setElementId("Collapse")
@@ -55,10 +54,13 @@ def mousePressEvent(self, event):
self.svgIcon.setElementId("Collapse")
-class NodeNameValidator(QtGui.QRegExpValidator):
+class NodeNameValidator(QtGui.QRegularExpressionValidator):
"""docstring for NodeNameValidator."""
+
def __init__(self, parent=None):
- super(NodeNameValidator, self).__init__(QtCore.QRegExp('^[a-zA-Z][a-zA-Z0-9_]*$'), parent)
+ super(NodeNameValidator, self).__init__(
+ QtCore.QRegularExpression("^[a-zA-Z][a-zA-Z0-9_]*$"), parent
+ )
class InputTextField(QGraphicsTextItem):
@@ -68,7 +70,9 @@ class InputTextField(QGraphicsTextItem):
def __init__(self, text, node, parent=None, singleLine=False, validator=None):
super(InputTextField, self).__init__(text, parent)
self.node = node
- self.setFlags(QGraphicsWidget.ItemSendsGeometryChanges | QGraphicsWidget.ItemIsSelectable)
+ self.setFlags(
+ QGraphicsWidget.ItemSendsGeometryChanges | QGraphicsWidget.ItemIsSelectable
+ )
self.singleLine = singleLine
self.setObjectName("Nothing")
self.origMoveEvent = self.mouseMoveEvent
@@ -134,7 +138,7 @@ def mouseDoubleClickEvent(self, event):
self.setFocus()
def focusInEvent(self, event):
- self.node.canvasRef().disableSortcuts()
+ self.node.canvasRef().disableShortcuts()
self.setTextInteractionFlags(QtCore.Qt.TextEditorInteraction)
self.setObjectName("MouseLocked")
self.textBeforeEditing = self.toPlainText()
@@ -142,7 +146,7 @@ def focusInEvent(self, event):
super(InputTextField, self).focusInEvent(event)
def focusOutEvent(self, event):
- self.node.canvasRef().enableSortcuts()
+ self.node.canvasRef().enableShortcuts()
cursor = self.textCursor()
cursor.clearSelection()
self.setTextCursor(cursor)
@@ -170,10 +174,18 @@ def __init__(self, parent=None):
self.setAcceptHoverEvents(True)
self.setFlag(QGraphicsWidget.ItemSendsGeometryChanges)
self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)
- self.labelItem = InputTextField(self.parentItem().getName(), parent, self, singleLine=True, validator=NodeNameValidator())
+ self.labelItem = InputTextField(
+ self.parentItem().getName(),
+ parent,
+ self,
+ singleLine=True,
+ validator=NodeNameValidator(),
+ )
self.labelItem.setDefaultTextColor(self.parentItem()._labelTextColor)
self.labelItem.setAcceptHoverEvents(True)
- self.labelItem.document().contentsChanged.connect(self.parentItem().updateNodeShape)
+ self.labelItem.document().contentsChanged.connect(
+ self.parentItem().updateNodeShape
+ )
self.labelItem.editingFinished.connect(self.parentItem().finalizeRename)
self.labelItem.hoverMoveEvent = self.hoverMoveEvent
@@ -239,11 +251,14 @@ class UINodeBase(QGraphicsWidget, IPropertiesViewSupport, IUINode):
"""
Default node description
"""
+
# Event called when node name changes
displayNameChanged = QtCore.Signal(str)
drawlabel = None
- def __init__(self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverride=None):
+ def __init__(
+ self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverride=None
+ ):
super(UINodeBase, self).__init__()
self.setFlag(QGraphicsWidget.ItemIsMovable)
self.setFlag(QGraphicsWidget.ItemIsFocusable)
@@ -261,7 +276,7 @@ def __init__(self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverri
self._rawNode.setWrapper(self)
self._rawNode.killed.connect(self.kill)
self._rawNode.tick.connect(self.Tick)
- self._rawNode.errorOccured.connect(self.onNodeErrorOccurred)
+ self._rawNode.errorOccurred.connect(self.onNodeErrorOccurred)
self._rawNode.errorCleared.connect(self.onNodeErrorCleared)
self._rawNode.setDirty.connect(self.setDirty)
self._rawNode.computing.connect(self.setComputing)
@@ -298,10 +313,12 @@ def __init__(self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverri
# GUI Layout
self.drawLayoutsDebug = False
self.nodeLayout = QGraphicsLinearLayout(QtCore.Qt.Vertical)
- self.nodeLayout.setContentsMargins(NodeDefaults().CONTENT_MARGINS,
- NodeDefaults().CONTENT_MARGINS,
- NodeDefaults().CONTENT_MARGINS,
- NodeDefaults().CONTENT_MARGINS)
+ self.nodeLayout.setContentsMargins(
+ NodeDefaults().CONTENT_MARGINS,
+ NodeDefaults().CONTENT_MARGINS,
+ NodeDefaults().CONTENT_MARGINS,
+ NodeDefaults().CONTENT_MARGINS,
+ )
self.nodeLayout.setSpacing(NodeDefaults().LAYOUTS_SPACING)
self.headerLayout = QGraphicsLinearLayout(QtCore.Qt.Horizontal)
@@ -320,9 +337,13 @@ def __init__(self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverri
self.exposedActionButtonsLayout = QGraphicsLinearLayout(QtCore.Qt.Horizontal)
self.exposedActionButtonsLayout.setContentsMargins(0, 0, 0, 0)
self.exposedActionButtonsLayout.setSpacing(2)
- self.exposedActionButtonsLayout.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
+ self.exposedActionButtonsLayout.setSizePolicy(
+ QSizePolicy.Maximum, QSizePolicy.Maximum
+ )
self.headerLayout.addItem(self.exposedActionButtonsLayout)
- self.headerLayout.setAlignment(self.exposedActionButtonsLayout, QtCore.Qt.AlignRight)
+ self.headerLayout.setAlignment(
+ self.exposedActionButtonsLayout, QtCore.Qt.AlignRight
+ )
self.customLayout = QGraphicsLinearLayout(QtCore.Qt.Vertical)
self.customLayout.setContentsMargins(0, 0, 0, 0)
@@ -357,7 +378,7 @@ def __init__(self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverri
self.setLayout(self.nodeLayout)
- self.svgIcon = QtSvg.QGraphicsSvgItem(self)
+ self.svgIcon = QGraphicsSvgItem(self)
self.svgIcon.setPos(-6, -6)
self._image = None
@@ -372,8 +393,16 @@ def __init__(self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverri
self.bResize = False
self.resizeDirection = (0, 0)
self.resizeStripsSize = 2
- self.resizeStrips = [0, 0, 0, 0, # Left, Top, Right, Bottom
- 0, 0, 0, 0] # BottomRight, BottomLeft, TopLeft, TopRight
+ self.resizeStrips = [
+ 0,
+ 0,
+ 0,
+ 0, # Left, Top, Right, Bottom
+ 0,
+ 0,
+ 0,
+ 0,
+ ] # BottomRight, BottomLeft, TopLeft, TopRight
self.roundness = NodeDefaults().CORNERS_ROUND_FACTOR
# Hiding/Moving By Group/collapse/By Pin
@@ -402,11 +431,17 @@ def __init__(self, raw_node, w=80, color=Colors.NodeBackgrounds, headColorOverri
self.actionToggleCollapse = self._menu.addAction("ToggleCollapse")
self.actionToggleCollapse.setToolTip("Toggles node's body collapsed or not")
self.actionToggleCollapse.triggered.connect(self.toggleCollapsed)
- self.actionToggleCollapse.setData(NodeActionButtonInfo(":/nodeCollapse.svg", CollapseNodeActionButton))
+ self.actionToggleCollapse.setData(
+ NodeActionButtonInfo(":/nodeCollapse.svg", CollapseNodeActionButton)
+ )
self.actionRefresh = self._menu.addAction("Refresh")
self.actionRefresh.triggered.connect(self.onRefresh)
- self.actionToggleExposeWidgetsToCompound = self._menu.addAction("Expose properties")
- self.actionToggleExposeWidgetsToCompound.triggered.connect(self.onToggleExposeProperties)
+ self.actionToggleExposeWidgetsToCompound = self._menu.addAction(
+ "Expose properties"
+ )
+ self.actionToggleExposeWidgetsToCompound.triggered.connect(
+ self.onToggleExposeProperties
+ )
self.actionCopyPath = self._menu.addAction("Copy path")
self.actionCopyPath.triggered.connect(self.onCopyPathToClipboard)
self._rawNode.computed.connect(self.onComputed)
@@ -426,7 +461,10 @@ def hoverEnterEvent(self, event):
if not self.isValid():
self.setToolTip(self.getLastErrorMessage())
else:
- self.setToolTip("%s\nComputingTime: %s"%(rst2html(self.description()),self._rawNode._computingTime))
+ self.setToolTip(
+ "%s\nComputingTime: %s"
+ % (rst2html(self.description()), self._rawNode._computingTime)
+ )
def eventDropOnCanvas(self):
pass
@@ -435,14 +473,21 @@ def setSingleLineName(self, bSingleLine=True):
self.nodeNameWidget.labelItem.singleLine = bSingleLine
def setNameValidationEnabled(self, bEnabled=True):
- self.nodeNameWidget.labelItem.validator = None if not bEnabled else NodeNameValidator()
+ self.nodeNameWidget.labelItem.validator = (
+ None if not bEnabled else NodeNameValidator()
+ )
def isNameValidationEnabled(self):
return self.nodeNameWidget.labelItem.validator is not None
def onToggleExposeProperties(self):
self.setExposePropertiesToCompound(not self.bExposeInputsToCompound)
- EditorHistory().saveState("{} exposing widgets".format("Start" if self.bExposeInputsToCompound else "Stop"), modify=True)
+ EditorHistory().saveState(
+ "{} exposing widgets".format(
+ "Start" if self.bExposeInputsToCompound else "Stop"
+ ),
+ modify=True,
+ )
def setExposePropertiesToCompound(self, bExpose):
self.bExposeInputsToCompound = bExpose
@@ -654,7 +699,7 @@ def setData(self, pinName, data):
p.setData(data)
def getPinSG(self, name, pinsGroup=PinSelectionGroup.BothSides):
- pin = self._rawNode.getPinSG(str(name), pinsGroup)
+ pin = self._rawNode.getPinSG(name, pinsGroup)
if pin is not None:
if pin.getWrapper() is not None:
return pin.getWrapper()()
@@ -686,7 +731,10 @@ def onNodeErrorOccurred(self, *args, **kwargs):
errorString = args[0]
error = {"Node": self._rawNode.name, "Error": errorString}
if ConfigManager().shouldRedirectOutput():
- errorLink = """%s""" % (self._rawNode.name, str(error))
+ errorLink = (
+ """%s"""
+ % (self._rawNode.name, error)
+ )
logging.error(errorLink)
else:
logging.error(errorString)
@@ -695,11 +743,17 @@ def onNodeErrorOccurred(self, *args, **kwargs):
def onNodeErrorCleared(self, *args, **kwargs):
# restore node ui to clean
- self.setToolTip("%s\nComputingTime: %s"%(rst2html(self.description()),self._rawNode._computingTime))
+ self.setToolTip(
+ "%s\nComputingTime: %s"
+ % (rst2html(self.description()), self._rawNode._computingTime)
+ )
self.update()
def onComputed(self, *args, **kwargs):
- self.setToolTip("%s\nComputingTime: %s"%(rst2html(self.description()),self._rawNode._computingTime))
+ self.setToolTip(
+ "%s\nComputingTime: %s"
+ % (rst2html(self.description()), self._rawNode._computingTime)
+ )
self.update()
def toggleCollapsed(self):
@@ -723,16 +777,16 @@ def serializationHook(self):
# to gather ui specific info
template = {}
if self.resizable:
- template['resize'] = {'w': self._rect.right(), 'h': self._rect.bottom()}
- template['collapsed'] = self.collapsed
- template['headerHtml'] = self.nodeNameWidget.getHtml()
- template['exposeInputsToCompound'] = self.bExposeInputsToCompound
+ template["resize"] = {"w": self._rect.right(), "h": self._rect.bottom()}
+ template["collapsed"] = self.collapsed
+ template["headerHtml"] = self.nodeNameWidget.getHtml()
+ template["exposeInputsToCompound"] = self.bExposeInputsToCompound
if len(self.groups) > 0:
- template['groups'] = {'input': {}, 'output': {}}
- for name, grp in self.groups['input'].items():
- template['groups']['input'][name] = grp.expanded
- for name, grp in self.groups['output'].items():
- template['groups']['output'][name] = grp.expanded
+ template["groups"] = {"input": {}, "output": {}}
+ for name, grp in self.groups["input"].items():
+ template["groups"]["input"][name] = grp.expanded
+ for name, grp in self.groups["output"].items():
+ template["groups"]["output"][name] = grp.expanded
return template
def buttonsWidth(self):
@@ -741,7 +795,9 @@ def buttonsWidth(self):
headerWidth = 0
numActions = len(self._actionButtons)
headerWidth += numActions * 10
- headerWidth += self.headerLayout.spacing() * 2 + NodeDefaults().CONTENT_MARGINS * 2
+ headerWidth += (
+ self.headerLayout.spacing() * 2 + NodeDefaults().CONTENT_MARGINS * 2
+ )
return headerWidth
except:
return 0
@@ -766,20 +822,30 @@ def getNodeHeight(self):
iPinsHeight = 0
for pin in ipins:
if pin.isVisible():
- iPinsHeight += pin.sizeHint(None, None).height() + NodeDefaults().LAYOUTS_SPACING
+ iPinsHeight += (
+ pin.sizeHint(None, None).height()
+ + NodeDefaults().LAYOUTS_SPACING
+ )
oPinsHeight = 0
for pin in opins:
if pin.isVisible():
- oPinsHeight += pin.sizeHint(None, None).height() + NodeDefaults().LAYOUTS_SPACING
+ oPinsHeight += (
+ pin.sizeHint(None, None).height()
+ + NodeDefaults().LAYOUTS_SPACING
+ )
h += max(iPinsHeight, oPinsHeight)
igrhHeight = 0
ogrhHeight = 0
for grp in self.groups["input"].values():
- igrhHeight += grp.sizeHint(None, None).height() + NodeDefaults().LAYOUTS_SPACING
+ igrhHeight += (
+ grp.sizeHint(None, None).height() + NodeDefaults().LAYOUTS_SPACING
+ )
for grp in self.groups["output"].values():
- ogrhHeight += grp.sizeHint(None, None).height() + NodeDefaults().LAYOUTS_SPACING
+ ogrhHeight += (
+ grp.sizeHint(None, None).height() + NodeDefaults().LAYOUTS_SPACING
+ )
h += max(igrhHeight, ogrhHeight)
except Exception as e:
@@ -851,9 +917,15 @@ def updateNodeShape(self):
self.canvasRef().update()
self.nodeNameWidget.updateGeometry()
self.nodeNameWidget.update()
- self.pinsLayout.setPreferredWidth(self.getNodeWidth() - self.nodeLayout.spacing())
- self.headerLayout.setPreferredWidth(self.getNodeWidth() - self.nodeLayout.spacing())
- self.customLayout.setPreferredWidth(self.getNodeWidth() - self.nodeLayout.spacing())
+ self.pinsLayout.setPreferredWidth(
+ self.getNodeWidth() - self.nodeLayout.spacing()
+ )
+ self.headerLayout.setPreferredWidth(
+ self.getNodeWidth() - self.nodeLayout.spacing()
+ )
+ self.customLayout.setPreferredWidth(
+ self.getNodeWidth() - self.nodeLayout.spacing()
+ )
def onVisibilityChanged(self, bVisible):
pass
@@ -875,7 +947,7 @@ def getImageDrawRect(self):
return r
def onChangeColor(self, label=False):
- res = QColorDialog.getColor(self.color, None, 'Node color setup')
+ res = QColorDialog.getColor(self.color, None, "Node color setup")
if res.isValid():
res.setAlpha(80)
self.color = res
@@ -883,13 +955,16 @@ def onChangeColor(self, label=False):
self.update()
def updateNodeHeaderColor(self):
- if self.headColorOverride is None:
- if self.isCallable():
- self.headColor = NodeDefaults().CALLABLE_NODE_HEAD_COLOR
- else:
- self.headColor = NodeDefaults().PURE_NODE_HEAD_COLOR
+ if self.computing:
+ self.headColor = NodeDefaults().COMPUTING_NODE_HEAD_COLOR
else:
- self.headColor = self.headColorOverride
+ if self.headColorOverride is None:
+ if self.isCallable():
+ self.headColor = NodeDefaults().CALLABLE_NODE_HEAD_COLOR
+ else:
+ self.headColor = NodeDefaults().PURE_NODE_HEAD_COLOR
+ else:
+ self.headColor = self.headColorOverride
def postCreate(self, jsonTemplate=None):
self.updateNodeHeaderColor()
@@ -903,7 +978,7 @@ def postCreate(self, jsonTemplate=None):
if self._rawNode.graph is None:
print(self._rawNode.getName())
- assert(self._rawNode.graph() is not None), "NODE GRAPH IS NONE"
+ assert self._rawNode.graph() is not None, "NODE GRAPH IS NONE"
if self.canvasRef is not None:
if self.canvasRef().graphManager.activeGraph() != self._rawNode.graph():
self.hide()
@@ -916,16 +991,22 @@ def postCreate(self, jsonTemplate=None):
headerHtml = self.name
if jsonTemplate is not None and jsonTemplate["wrapper"] is not None:
if "exposeInputsToCompound" in jsonTemplate["wrapper"]:
- self.setExposePropertiesToCompound(jsonTemplate["wrapper"]["exposeInputsToCompound"])
+ self.setExposePropertiesToCompound(
+ jsonTemplate["wrapper"]["exposeInputsToCompound"]
+ )
if "collapsed" in jsonTemplate["wrapper"]:
self.collapsed = jsonTemplate["wrapper"]["collapsed"]
if "headerHtml" in jsonTemplate["wrapper"]:
headerHtml = jsonTemplate["wrapper"]["headerHtml"]
if "groups" in jsonTemplate["wrapper"]:
try:
- for groupName, expanded in jsonTemplate["wrapper"]["groups"]["input"].items():
+ for groupName, expanded in jsonTemplate["wrapper"]["groups"][
+ "input"
+ ].items():
self.groups["input"][groupName].setExpanded(expanded)
- for groupName, expanded in jsonTemplate["wrapper"]["groups"]["output"].items():
+ for groupName, expanded in jsonTemplate["wrapper"]["groups"][
+ "output"
+ ].items():
self.groups["output"][groupName].setExpanded(expanded)
except Exception as e:
pass
@@ -934,9 +1015,12 @@ def postCreate(self, jsonTemplate=None):
if self.isDeprecated():
description = self.deprecationMessage()
if description:
- self.setToolTip("%s\nComputingTime: %s"%(rst2html(self.description()),self._rawNode._computingTime))
+ self.setToolTip(
+ "%s\nComputingTime: %s"
+ % (rst2html(self.description()), self._rawNode._computingTime)
+ )
else:
- self.setToolTip("\nComputingTime: %s"%self._rawNode._computingTime)
+ self.setToolTip("\nComputingTime: %s" % self._rawNode._computingTime)
if self.resizable:
w = self.getNodeWidth()
h = self.getNodeHeight()
@@ -969,7 +1053,7 @@ def createActionButtons(self):
def addWidget(self, widget):
if not self.hasCustomLayout:
- self.nodeLayout.insertItem(1, self.customLayout)
+ self.nodeLayout.insertItem(2, self.customLayout)
self.hasCustomLayout = True
ProxyWidget = QGraphicsProxyWidget()
ProxyWidget.setWidget(widget)
@@ -1047,7 +1131,9 @@ def updateOwningCommentNode(self):
collidingItems = self.collidingItems(QtCore.Qt.ContainsItemShape)
collidingNodes = set()
for item in collidingItems:
- if item.sceneBoundingRect().contains(self.sceneBoundingRect()) and isinstance(item, UINodeBase):
+ if item.sceneBoundingRect().contains(
+ self.sceneBoundingRect()
+ ) and isinstance(item, UINodeBase):
if item.isCommentNode:
collidingNodes.add(item)
owningCommentNode = None
@@ -1066,7 +1152,10 @@ def updateOwningCommentNode(self):
owningCommentNode = smallest
self.owningCommentNode = owningCommentNode
if self.owningCommentNode is not None:
- if owningCommentNode._rawNode.graph() == self.canvasRef().graphManager.activeGraph():
+ if (
+ owningCommentNode._rawNode.graph()
+ == self.canvasRef().graphManager.activeGraph()
+ ):
self.owningCommentNode.owningNodes.add(self)
def getCollidedNodes(self, bFullyCollided=True, classNameFilters=set()):
@@ -1080,7 +1169,10 @@ def getCollidedNodes(self, bFullyCollided=True, classNameFilters=set()):
if classNameFilters:
if node.__class__.__name__ not in classNameFilters:
continue
- if node._rawNode.graph() != self.canvasRef().graphManager.activeGraph():
+ if (
+ node._rawNode.graph()
+ != self.canvasRef().graphManager.activeGraph()
+ ):
continue
collidingNodes.add(node)
else:
@@ -1088,23 +1180,29 @@ def getCollidedNodes(self, bFullyCollided=True, classNameFilters=set()):
if classNameFilters:
if node.__class__.__name__ not in classNameFilters:
continue
- if node._rawNode.graph() != self.canvasRef().graphManager.activeGraph():
+ if (
+ node._rawNode.graph()
+ != self.canvasRef().graphManager.activeGraph()
+ ):
continue
collidingNodes.add(node)
return collidingNodes
- def setDirty(self,*args, **kwargs):
+ def setDirty(self, *args, **kwargs):
self.computing = False
self.dirty = True
+ self.updateNodeHeaderColor()
self.update()
- def setComputing(self,*args, **kwargs):
+ def setComputing(self, *args, **kwargs):
self.computing = True
+ self.updateNodeHeaderColor()
self.update()
- def setClean(self,*args, **kwargs):
+ def setClean(self, *args, **kwargs):
self.computing = False
self.dirty = False
+ self.updateNodeHeaderColor()
self.update()
def paint(self, painter, option, widget):
@@ -1125,28 +1223,28 @@ def paint(self, painter, option, widget):
def shouldResize(self, cursorPos):
result = {"resize": False, "direction": self.resizeDirection}
- if self.resizeStrips[0] == 1: # left
+ if self.resizeStrips[0] == 1: # left
result["resize"] = True
result["direction"] = (-1, 0)
- if self.resizeStrips[1] == 1: # top
+ if self.resizeStrips[1] == 1: # top
result["resize"] = True
result["direction"] = (0, -1)
- if self.resizeStrips[2] == 1: # right
+ if self.resizeStrips[2] == 1: # right
result["resize"] = True
result["direction"] = (1, 0)
- if self.resizeStrips[3] == 1: # bottom
+ if self.resizeStrips[3] == 1: # bottom
result["resize"] = True
result["direction"] = (0, 1)
- if self.resizeStrips[4] == 1: # bottom right
+ if self.resizeStrips[4] == 1: # bottom right
result["resize"] = True
result["direction"] = (1, 1)
- if self.resizeStrips[5] == 1: # bottom left
+ if self.resizeStrips[5] == 1: # bottom left
result["resize"] = True
result["direction"] = (-1, 1)
- if self.resizeStrips[6] == 1: # top left
+ if self.resizeStrips[6] == 1: # top left
result["resize"] = True
result["direction"] = (-1, -1)
- if self.resizeStrips[7] == 1: # top right
+ if self.resizeStrips[7] == 1: # top right
result["resize"] = True
result["direction"] = (1, -1)
return result
@@ -1174,7 +1272,7 @@ def mouseMoveEvent(self, event):
# resize
if self.bResize:
delta = event.scenePos() - self.mousePressPos
- if self.resizeDirection == (-1, 0): # left
+ if self.resizeDirection == (-1, 0): # left
posdelta = self.mapToScene(event.pos()) - self.origPos
posdelta2 = self.mapToScene(event.pos()) - self.initPos
newWidth = -posdelta2.x() + self.initialRectWidth
@@ -1183,7 +1281,7 @@ def mouseMoveEvent(self, event):
self.origPos = self.pos()
self._rect.setWidth(newWidth)
self.updateNodeShape()
- elif self.resizeDirection == (0, -1): # top
+ elif self.resizeDirection == (0, -1): # top
posdelta = self.mapToScene(event.pos()) - self.origPos
posdelta2 = self.mapToScene(event.pos()) - self.initPos
minHeight = -posdelta2.y() + self.initialRectHeight
@@ -1198,12 +1296,12 @@ def mouseMoveEvent(self, event):
self._rect.setWidth(newWidth)
self.w = newWidth
self.updateNodeShape()
- elif self.resizeDirection == (0, 1): # bottom
+ elif self.resizeDirection == (0, 1): # bottom
newHeight = delta.y() + self.initialRectHeight
if newHeight > self.minHeight:
self._rect.setHeight(newHeight)
self.updateNodeShape()
- elif self.resizeDirection == (1, 1): # bottom right
+ elif self.resizeDirection == (1, 1): # bottom right
newWidth = delta.x() + self.initialRectWidth
newHeight = delta.y() + self.initialRectHeight
if newWidth > self.minWidth:
@@ -1213,7 +1311,7 @@ def mouseMoveEvent(self, event):
if newHeight > self.minHeight:
self._rect.setHeight(newHeight)
self.updateNodeShape()
- elif self.resizeDirection == (-1, 1): # bottom left
+ elif self.resizeDirection == (-1, 1): # bottom left
newHeight = delta.y() + self.initialRectHeight
if newHeight > self.minHeight:
self._rect.setHeight(newHeight)
@@ -1225,7 +1323,7 @@ def mouseMoveEvent(self, event):
self.origPos = self.pos()
self._rect.setWidth(newWidth)
self.updateNodeShape()
- elif self.resizeDirection == (-1, -1): # top left
+ elif self.resizeDirection == (-1, -1): # top left
posdelta = self.mapToScene(event.pos()) - self.origPos
posdelta2 = self.mapToScene(event.pos()) - self.initPos
minHeight = -posdelta2.y() + self.initialRectHeight
@@ -1277,8 +1375,18 @@ def hoverMoveEvent(self, event):
leftStrip = QtCore.QRectF(0, rf, self.resizeStripsSize, height - rf * 2)
topStrip = QtCore.QRectF(rf, 0, width - rf * 2, self.resizeStripsSize)
- rightStrip = QtCore.QRectF(width - self.resizeStripsSize, rf, self.resizeStripsSize, height - rf * 2)
- bottomStrip = QtCore.QRectF(rf, height - self.resizeStripsSize, width - rf * 2, self.resizeStripsSize)
+ rightStrip = QtCore.QRectF(
+ width - self.resizeStripsSize,
+ rf,
+ self.resizeStripsSize,
+ height - rf * 2,
+ )
+ bottomStrip = QtCore.QRectF(
+ rf,
+ height - self.resizeStripsSize,
+ width - rf * 2,
+ self.resizeStripsSize,
+ )
bottomRightStrip = QtCore.QRectF(width - rf, height - rf, rf, rf)
bottomLeftStrip = QtCore.QRectF(0, height - rf, rf, rf)
@@ -1303,12 +1411,12 @@ def contextMenuEvent(self, event):
def clone(self):
templ = self.serialize()
- templ['name'] = self.name
- templ['uuid'] = str(uuid.uuid4())
- for inp in templ['inputs']:
- inp['uuid'] = str(uuid.uuid4())
- for out in templ['outputs']:
- out['uuid'] = str(uuid.uuid4())
+ templ["name"] = self.name
+ templ["uuid"] = str(uuid.uuid4())
+ for inp in templ["inputs"]:
+ inp["uuid"] = str(uuid.uuid4())
+ for out in templ["outputs"]:
+ out["uuid"] = str(uuid.uuid4())
new_node = self.canvasRef().createNode(templ)
return new_node
@@ -1319,7 +1427,7 @@ def createPropertiesWidget(self, propertiesWidget):
le_name.setReadOnly(True)
baseCategory.addWidget("Name", le_name)
- leUid = QLineEdit(str(self._rawNode.graph().name))
+ leUid = QLineEdit(self._rawNode.graph().name)
leUid.setReadOnly(True)
baseCategory.addWidget("Owning graph", leUid)
@@ -1353,10 +1461,16 @@ def createInputWidgets(self, inputsCategory, inGroup=None, pins=True):
if inp.isArray() or inp.isDict() or inp._rawPin.hidden:
continue
dataSetter = inp.call if inp.isExec() else inp.setData
- w = createInputWidget(inp.dataType, dataSetter, inp.defaultValue(), inp.getInputWidgetVariant(), pinAnnotations=inp._rawPin.annotationDescriptionDict)
+ w = createInputWidget(
+ inp.dataType,
+ dataSetter,
+ inp.defaultValue(),
+ inp.getInputWidgetVariant(),
+ pinAnnotations=inp._rawPin.annotationDescriptionDict,
+ )
if w:
w.setToolTip(inp.description)
- #inp._rawPin.dataBeenSet.connect(w.setWidgetValueNoSignals)
+ # inp._rawPin.dataBeenSet.connect(w.setWidgetValueNoSignals)
w.blockWidgetSignals(True)
data = inp.currentData()
if isinstance(inp.currentData(), DictElement):
@@ -1378,7 +1492,13 @@ def createOutputWidgets(self, inputsCategory, headName="Outputs"):
if inp.isArray() or inp.isDict() or inp._rawPin.hidden:
continue
dataSetter = inp.call if inp.isExec() else inp.setData
- w = createInputWidget(inp.dataType, dataSetter, inp.defaultValue(), inp.getInputWidgetVariant(), pinAnnotations=inp._rawPin.annotationDescriptionDict)
+ w = createInputWidget(
+ inp.dataType,
+ dataSetter,
+ inp.defaultValue(),
+ inp.getInputWidgetVariant(),
+ pinAnnotations=inp._rawPin.annotationDescriptionDict,
+ )
if w:
w.setToolTip(inp.description)
inp._rawPin.dataBeenSet.connect(w.setWidgetValueNoSignals)
@@ -1436,7 +1556,7 @@ def kill(self, *args, **kwargs):
scene = self.scene()
if scene is not None:
self.scene().removeItem(self)
- del(self)
+ del self
def shoutDown(self):
pass
@@ -1483,7 +1603,9 @@ def _createUIPinWrapper(self, rawPin, index=-1, group=None, linkedPin=None):
insertionIndex = -1
if grpItem is not None:
self.groups["input"][rawPin.group] = grpItem
- insertionIndex = findItemIndex(self.inputsLayout, grpItem) + grpItem.numPins() + 1
+ insertionIndex = (
+ findItemIndex(self.inputsLayout, grpItem) + grpItem.numPins() + 1
+ )
self.inputsLayout.setAlignment(grpItem, QtCore.Qt.AlignLeft)
grpItem.addPin(p)
self.inputsLayout.insertItem(insertionIndex, p)
@@ -1494,7 +1616,9 @@ def _createUIPinWrapper(self, rawPin, index=-1, group=None, linkedPin=None):
insertionIndex = -1
if grpItem is not None:
self.groups["output"][rawPin.group] = grpItem
- insertionIndex = findItemIndex(self.outputsLayout, grpItem) + grpItem.numPins() + 1
+ insertionIndex = (
+ findItemIndex(self.outputsLayout, grpItem) + grpItem.numPins() + 1
+ )
self.outputsLayout.setAlignment(grpItem, QtCore.Qt.AlignRight)
grpItem.addPin(p)
self.outputsLayout.insertItem(insertionIndex, p)
@@ -1526,6 +1650,7 @@ def recreate(node):
newNode.uid = uid
return newNode
+
def REGISTER_UI_NODE_FACTORY(packageName, factory):
if packageName not in UI_NODES_FACTORIES:
UI_NODES_FACTORIES[packageName] = factory
diff --git a/PyFlow/UI/Canvas/UIPinBase.py b/PyFlow/UI/Canvas/UIPinBase.py
index e70825891..cb4b106ec 100644
--- a/PyFlow/UI/Canvas/UIPinBase.py
+++ b/PyFlow/UI/Canvas/UIPinBase.py
@@ -13,19 +13,14 @@
## limitations under the License.
-import weakref
-
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QApplication
-from Qt.QtWidgets import QGraphicsWidget
-from Qt.QtWidgets import QMenu
-from Qt.QtWidgets import QInputDialog
-from Qt.QtWidgets import QSizePolicy
-from Qt.QtWidgets import QPlainTextEdit
-
-from PyFlow.Core.Common import *
-from PyFlow.UI.Utils.stylesheet import Colors, editableStyleSheet
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import QApplication
+from qtpy.QtWidgets import QGraphicsWidget
+from qtpy.QtWidgets import QMenu
+from qtpy.QtWidgets import QInputDialog
+from qtpy.QtWidgets import QSizePolicy
+
from PyFlow.UI.Canvas.Painters import PinPainter
from PyFlow.UI.Canvas.WatchPinValueItem import WatchItem
from PyFlow.UI.Canvas.UICommon import *
@@ -48,6 +43,7 @@ class UIPinBase(QGraphicsWidget):
displayNameChanged = QtCore.Signal(str)
OnPinChanged = QtCore.Signal(object)
OnPinDeleted = QtCore.Signal(object)
+ OnPinExecute = QtCore.Signal(object)
def __init__(self, owningNode, raw_pin):
"""UI wrapper for :class:`PyFlow.Core.PinBase`
@@ -55,13 +51,13 @@ def __init__(self, owningNode, raw_pin):
:param owningNode: Owning node
:type owningNode: :class:`PyFlow.UI.Canvas.NodeBase`
:param raw_pin: PinBase reference
- :type raw_pin: :class:`PyFlow.Core.PinBase`
+ :type raw_pin: :class:`PyFlow.Core.PinBase.PinBase`
"""
super(UIPinBase, self).__init__()
self.setGraphicsItem(self)
self.setFlag(QGraphicsWidget.ItemSendsGeometryChanges)
- self.setCacheMode(self.DeviceCoordinateCache)
+ #self.setCacheMode(self.DeviceCoordinateCache)
self.setAcceptHoverEvents(True)
self.setZValue(1)
self.setParentItem(owningNode)
@@ -71,24 +67,24 @@ def __init__(self, owningNode, raw_pin):
self.watchWidget = None
if self._rawPin is not None:
self._rawPin.serializationHook.connect(self.serializationHook)
- self._rawPin.containerTypeChanged.connect(
- self.onContainerTypeChanged)
+ self._rawPin.containerTypeChanged.connect(self.onContainerTypeChanged)
self._displayName = self._rawPin.name
self._rawPin.setWrapper(self)
self._rawPin.killed.connect(self.kill)
self._rawPin.nameChanged.connect(self.setDisplayName)
-
+ self._rawPin.onExecute.connect(self.on_rawPin_executed)
# Context menu for pin
self.menu = QMenu()
self.menu.addAction("Rename").triggered.connect(self.onRename)
self.menu.addAction("Remove").triggered.connect(self._rawPin.kill)
- self.actionDisconnect = self.menu.addAction('Disconnect all')
+ self.actionDisconnect = self.menu.addAction("Disconnect all")
self.actionDisconnect.triggered.connect(self._rawPin.disconnectAll)
self.actionResetValue = self.menu.addAction("Reset value")
self.actionResetValue.triggered.connect(self.resetToDefault)
if self._rawPin._structure == StructureType.Multi:
self.menu.addAction("changeStructure").triggered.connect(
- self.selectStructure)
+ self.selectStructure
+ )
self.actionWatchValue = self.menu.addAction("Watch")
self.actionWatchValue.triggered.connect(self.toggleWatchValue)
self._rawPin.dataBeenSet.connect(self.updateWatchWidgetValue)
@@ -106,7 +102,12 @@ def __init__(self, owningNode, raw_pin):
self._labelColor = QtCore.Qt.white
self._execPen = QtGui.QPen(Colors.White, 0.5, QtCore.Qt.SolidLine)
self._dirty_pen = QtGui.QPen(
- Colors.DirtyPen, 0.5, QtCore.Qt.DashLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin)
+ Colors.DirtyPen,
+ 0.5,
+ QtCore.Qt.DashLine,
+ QtCore.Qt.RoundCap,
+ QtCore.Qt.RoundJoin,
+ )
self.uiConnectionList = []
@@ -119,6 +120,9 @@ def __init__(self, owningNode, raw_pin):
@property
def wrapperJsonData(self):
return self._rawPin.wrapperJsonData
+
+ def on_rawPin_executed(self, *args, **kwargs):
+ self.OnPinExecute.emit(self)
def onCopyPathToClipboard(self):
QApplication.clipboard().clear()
@@ -141,7 +145,11 @@ def path(self):
def updateWatchWidget(self):
if self.watchWidget is not None:
- scenePos = self.sceneBoundingRect().bottomLeft() if self.direction == PinDirection.Input else self.sceneBoundingRect().bottomRight()
+ scenePos = (
+ self.sceneBoundingRect().bottomLeft()
+ if self.direction == PinDirection.Input
+ else self.sceneBoundingRect().bottomRight()
+ )
self.watchWidget.setPos(scenePos)
self.watchWidget.setVisible(self.owningNode().isVisible())
@@ -176,20 +184,25 @@ def pinCenter(self):
frame = QtCore.QRectF(QtCore.QPointF(0, 0), self.geometry().size())
halfPinSize = self.pinSize / 2
pinX = self.pinSize
- pinY = (frame.height() / 2)
+ pinY = frame.height() / 2
if not self.bLabelHidden:
if self.direction == PinDirection.Output:
pinX = frame.width() - self.pinSize + halfPinSize
result = QtCore.QPointF(pinX, pinY)
if self.owningNode().collapsed:
labelHeight = self.owningNode().labelHeight
- #labelHeight += self.owningNode().nodeLayout.spacing()
+ # labelHeight += self.owningNode().nodeLayout.spacing()
if self.direction == PinDirection.Input:
result = self.mapFromItem(
- self.owningNode(), QtCore.QPointF(0, labelHeight))
+ self.owningNode(), QtCore.QPointF(0, labelHeight)
+ )
if self.direction == PinDirection.Output:
- result = self.mapFromItem(self.owningNode(), QtCore.QPointF(
- self.owningNode().sizeHint(None, None).width(), labelHeight))
+ result = self.mapFromItem(
+ self.owningNode(),
+ QtCore.QPointF(
+ self.owningNode().sizeHint(None, None).width(), labelHeight
+ ),
+ )
return result
def onContainerTypeChanged(self, *args, **kwargs):
@@ -238,14 +251,12 @@ def setMenuItemEnabled(self, actionName, bEnabled):
action.setVisible(bEnabled)
def syncRenamable(self):
- renamingEnabled = self._rawPin.optionEnabled(
- PinOptions.RenamingEnabled)
+ renamingEnabled = self._rawPin.optionEnabled(PinOptions.RenamingEnabled)
# self._label()._isEditable = renamingEnabled
self.setMenuItemEnabled("Rename", renamingEnabled)
def onRename(self):
- name, confirmed = QInputDialog.getText(
- None, "Rename", "Enter new pin name")
+ name, confirmed = QInputDialog.getText(None, "Rename", "Enter new pin name")
if confirmed and name != self.name and name != "":
uniqueName = self._rawPin.owningNode().getUniqPinName(name)
self.setName(uniqueName)
@@ -255,7 +266,8 @@ def onRename(self):
def syncDynamic(self):
self.setMenuItemEnabled(
- "Remove", self._rawPin.optionEnabled(PinOptions.Dynamic))
+ "Remove", self._rawPin.optionEnabled(PinOptions.Dynamic)
+ )
@property
def structureType(self):
@@ -378,8 +390,8 @@ def assignRawPin(self, rawPin):
def serializationHook(self, *args, **kwargs):
data = {}
- data['bLabelHidden'] = self.bLabelHidden
- data['displayName'] = self.displayName()
+ data["bLabelHidden"] = self.bLabelHidden
+ data["displayName"] = self.displayName()
wiresData = {}
for wire in self.uiConnectionList:
wiresData[wire.destination().pinIndex] = wire.serialize()
@@ -407,7 +419,9 @@ def sizeHint(self, which, constraint):
height = QtGui.QFontMetrics(self._font).height()
width = self.pinSize * 2
if not self.bLabelHidden:
- width += QtGui.QFontMetrics(self._font).horizontalAdvance(self.displayName())
+ width += QtGui.QFontMetrics(self._font).horizontalAdvance(
+ self.displayName()
+ )
return QtCore.QSizeF(width, height)
def shape(self):
@@ -468,7 +482,8 @@ def pinDisconnected(self, other):
def selectStructure(self):
item, ok = QInputDialog.getItem(
- None, "", "", ([i.name for i in list(StructureType)]), 0, False)
+ None, "", "", ([i.name for i in list(StructureType)]), 0, False
+ )
if ok and item:
self._rawPin.changeStructure(StructureType[item], True)
@@ -523,16 +538,18 @@ def setExpanded(self, expanded):
if expanded:
wire.destinationPositionOverride = None
else:
- wire.destinationPositionOverride = lambda: self.scenePos() + QtCore.QPointF(0,
- self.geometry().height())
+ wire.destinationPositionOverride = lambda: self.scenePos() + QtCore.QPointF(
+ 0, self.geometry().height()
+ )
if pin.direction == PinDirection.Output:
for wire in pin.uiConnectionList:
if expanded:
wire.sourcePositionOverride = None
else:
- wire.sourcePositionOverride = lambda: self.scenePos(
- ) + QtCore.QPointF(self.geometry().width(), self.geometry().height())
+ wire.sourcePositionOverride = lambda: self.scenePos() + QtCore.QPointF(
+ self.geometry().width(), self.geometry().height()
+ )
self.update()
self.owningNode().update()
@@ -559,11 +576,13 @@ def name(self):
@name.setter
def name(self, newName):
if self._direction == PinDirection.Input:
- self.owningNode().groups["input"][newName] = self.owningNode(
- ).groups["input"].pop(self._name)
+ self.owningNode().groups["input"][newName] = (
+ self.owningNode().groups["input"].pop(self._name)
+ )
else:
- self.owningNode().groups["output"][newName] = self.owningNode(
- ).groups["output"].pop(self._name)
+ self.owningNode().groups["output"][newName] = (
+ self.owningNode().groups["output"].pop(self._name)
+ )
self._name = newName
def hoverEnterEvent(self, event):
@@ -582,13 +601,14 @@ def hoverLeaveEvent(self, event):
def sizeHint(self, which, constraint):
height = QtGui.QFontMetrics(self._font).height()
- width = QtGui.QFontMetrics(self._font).horizontalAdvance(self.name) + self.pinSize
+ width = (
+ QtGui.QFontMetrics(self._font).horizontalAdvance(self.name) + self.pinSize
+ )
return QtCore.QSizeF(width, height)
def paint(self, painter, option, widget):
frame = QtCore.QRectF(QtCore.QPointF(0, 0), self.geometry().size())
frame = frame.translated(self.pinSize * 1.1, 0)
- groupBGColor = self.owningNode().color.lighter(150)
bgRect = QtCore.QRectF(frame)
bgRect.setX(0)
painter.setFont(self._font)
@@ -596,8 +616,9 @@ def paint(self, painter, option, widget):
painter.drawText(frame, self.name)
painter.setPen(QtGui.QPen(self.labelColor, 0.1))
- square = QtCore.QRectF(QtCore.QPointF(0, 0), QtCore.QSizeF(
- self.pinSize / 1.1, self.pinSize / 1.1))
+ square = QtCore.QRectF(
+ QtCore.QPointF(0, 0), QtCore.QSizeF(self.pinSize / 1.1, self.pinSize / 1.1)
+ )
square2 = square.translated(0, (self.pinSize / 1.1) / 3)
painter.drawRect(square2)
@@ -621,7 +642,6 @@ def REGISTER_UI_PIN_FACTORY(packageName, factory):
def getUIPinInstance(owningNode, raw_instance):
packageName = raw_instance.packageName
- instance = None
if packageName in UI_PINS_FACTORIES:
return UI_PINS_FACTORIES[packageName](owningNode, raw_instance)
else:
diff --git a/PyFlow/UI/Canvas/UIVariable.py b/PyFlow/UI/Canvas/UIVariable.py
index e6da212a4..46a89901c 100644
--- a/PyFlow/UI/Canvas/UIVariable.py
+++ b/PyFlow/UI/Canvas/UIVariable.py
@@ -15,24 +15,24 @@
import json
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QWidget
-from Qt.QtWidgets import QLineEdit
-from Qt.QtWidgets import QComboBox
-from Qt.QtWidgets import QHBoxLayout
-from Qt.QtWidgets import QLabel
-from Qt.QtWidgets import QSpacerItem
-from Qt.QtWidgets import QSizePolicy
-from Qt.QtWidgets import QPushButton
-from Qt.QtWidgets import QInputDialog
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import QWidget
+from qtpy.QtWidgets import QLineEdit
+from qtpy.QtWidgets import QComboBox
+from qtpy.QtWidgets import QHBoxLayout
+from qtpy.QtWidgets import QLabel
+from qtpy.QtWidgets import QSpacerItem
+from qtpy.QtWidgets import QSizePolicy
+from qtpy.QtWidgets import QPushButton
+from qtpy.QtWidgets import QInputDialog
from PyFlow import getHashableDataTypes
from PyFlow.Core.Common import *
from PyFlow.UI.EditorHistory import EditorHistory
from PyFlow.UI.UIInterfaces import IPropertiesViewSupport
from PyFlow.UI.Widgets.InputWidgets import createInputWidget
-from PyFlow.UI.Widgets.PropertiesFramework import PropertiesWidget, CollapsibleFormWidget
+from PyFlow.UI.Widgets.PropertiesFramework import CollapsibleFormWidget
from PyFlow import getPinDefaultValueByType
from PyFlow.UI.Widgets.EnumComboBox import EnumComboBox
from PyFlow import findPinClassByType
@@ -52,7 +52,9 @@ def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
- keyColor = QtGui.QColor.fromRgb(*findPinClassByType(self.parent().dataType).color())
+ keyColor = QtGui.QColor.fromRgb(
+ *findPinClassByType(self.parent().dataType).color()
+ )
structure = self.parent()._rawVariable.structure
if structure == StructureType.Single:
@@ -84,8 +86,12 @@ def paintEvent(self, event):
if structure == StructureType.Dict:
dictObject = self.parent()._rawVariable.value
- keyColor = QtGui.QColor.fromRgb(*findPinClassByType(dictObject.keyType).color())
- valueColor = QtGui.QColor.fromRgb(*findPinClassByType(dictObject.valueType).color())
+ keyColor = QtGui.QColor.fromRgb(
+ *findPinClassByType(dictObject.keyType).color()
+ )
+ valueColor = QtGui.QColor.fromRgb(
+ *findPinClassByType(dictObject.valueType).color()
+ )
gridSize = 3
size = self.height()
@@ -114,7 +120,9 @@ def __init__(self, rawVariable, variablesWidget, parent=None):
self.horizontalLayout.setSpacing(1)
self.horizontalLayout.setContentsMargins(1, 1, 1, 1)
self.horizontalLayout.setObjectName("horizontalLayout")
- self.widget = TypeWidget(findPinClassByType(self._rawVariable.dataType).color(), self)
+ self.widget = TypeWidget(
+ findPinClassByType(self._rawVariable.dataType).color(), self
+ )
self.widget.setObjectName("widget")
self.horizontalLayout.addWidget(self.widget)
self.labelName = QLabel(self)
@@ -142,7 +150,9 @@ def __init__(self, rawVariable, variablesWidget, parent=None):
def onStructureChanged(self, name):
self._rawVariable.structure = StructureType[name]
- self.variablesWidget.pyFlowInstance.onRequestFillProperties(self.createPropertiesWidget)
+ self.variablesWidget.pyFlowInstance.onRequestFillProperties(
+ self.createPropertiesWidget
+ )
EditorHistory().saveState("Change variable struct", modify=True)
self.widget.update()
@@ -172,13 +182,24 @@ def createPropertiesWidget(self, propertiesWidget):
if self._rawVariable.structure == StructureType.Dict:
dictObject = self._rawVariable.value
keyTypeSelector = EnumComboBox(values=getHashableDataTypes())
- valueTypeSelector = EnumComboBox([pin.__name__ for pin in getAllPinClasses() if pin.IsValuePin() if pin.__name__ != "AnyPin"])
+ valueTypeSelector = EnumComboBox(
+ [
+ pin.__name__
+ for pin in getAllPinClasses()
+ if pin.IsValuePin()
+ if pin.__name__ != "AnyPin"
+ ]
+ )
keyTypeSelector.setEditable(False)
valueTypeSelector.setEditable(False)
- keyTypeSelector.setCurrentIndex(keyTypeSelector.findText(dictObject.keyType))
- valueTypeSelector.setCurrentIndex(valueTypeSelector.findText(dictObject.valueType))
+ keyTypeSelector.setCurrentIndex(
+ keyTypeSelector.findText(dictObject.keyType)
+ )
+ valueTypeSelector.setCurrentIndex(
+ valueTypeSelector.findText(dictObject.valueType)
+ )
keyTypeSelector.changeCallback.connect(self.onDictKeyTypeChanged)
valueTypeSelector.changeCallback.connect(self.onDictValueTypeChanged)
@@ -186,7 +207,14 @@ def createPropertiesWidget(self, propertiesWidget):
baseCategory.addWidget("Key type", keyTypeSelector)
baseCategory.addWidget("Value type", valueTypeSelector)
else:
- cbTypes = EnumComboBox([pin.__name__ for pin in getAllPinClasses() if pin.IsValuePin() if pin.__name__ != "AnyPin"])
+ cbTypes = EnumComboBox(
+ [
+ pin.__name__
+ for pin in getAllPinClasses()
+ if pin.IsValuePin()
+ if pin.__name__ != "AnyPin"
+ ]
+ )
cbTypes.setCurrentIndex(cbTypes.findText(self.dataType))
cbTypes.changeCallback.connect(self.setDataType)
cbTypes.setEditable(False)
@@ -194,9 +222,16 @@ def createPropertiesWidget(self, propertiesWidget):
propertiesWidget.addWidget(baseCategory)
# structure type
- cbStructure = EnumComboBox([i.name for i in (StructureType.Single, StructureType.Array, StructureType.Dict)])
+ cbStructure = EnumComboBox(
+ [
+ i.name
+ for i in (StructureType.Single, StructureType.Array, StructureType.Dict)
+ ]
+ )
cbStructure.setEditable(False)
- cbStructure.setCurrentIndex(cbStructure.findText(self._rawVariable.structure.name))
+ cbStructure.setCurrentIndex(
+ cbStructure.findText(self._rawVariable.structure.name)
+ )
cbStructure.changeCallback.connect(self.onStructureChanged)
propertiesWidget.addWidget(baseCategory)
baseCategory.addWidget("Structure", cbStructure)
@@ -206,9 +241,15 @@ def createPropertiesWidget(self, propertiesWidget):
# current value
if self._rawVariable.structure == StructureType.Single:
if not type(self._rawVariable.value) in {list, set, dict, tuple}:
+
def valSetter(x):
self._rawVariable.value = x
- w = createInputWidget(self._rawVariable.dataType, valSetter, getPinDefaultValueByType(self._rawVariable.dataType))
+
+ w = createInputWidget(
+ self._rawVariable.dataType,
+ valSetter,
+ getPinDefaultValueByType(self._rawVariable.dataType),
+ )
if w:
w.setWidgetValue(self._rawVariable.value)
w.setObjectName(self._rawVariable.name)
@@ -216,37 +257,47 @@ def valSetter(x):
# access level
cb = QComboBox()
- cb.addItem('public', 0)
- cb.addItem('private', 1)
- cb.addItem('protected', 2)
+ cb.addItem("public", 0)
+ cb.addItem("private", 1)
+ cb.addItem("protected", 2)
def accessLevelChanged(x):
self._rawVariable.accessLevel = AccessLevel[x]
EditorHistory().saveState("Change variable access level", modify=True)
+
cb.currentTextChanged.connect(accessLevelChanged)
cb.setCurrentIndex(self._rawVariable.accessLevel)
- valueCategory.addWidget('Access level', cb)
+ valueCategory.addWidget("Access level", cb)
propertiesWidget.addWidget(valueCategory)
def onFindRefsClicked(self):
from PyFlow.App import PyFlow
+
refs = [n.getWrapper() for n in self._rawVariable.findRefs()]
app = self.variablesWidget.pyFlowInstance
if "Search results" not in [t.name() for t in app.getRegisteredTools()]:
app.invokeDockToolByName("PyFlowBase", "Search results")
- self.variablesWidget.pyFlowInstance.getCanvas().requestShowSearchResults.emit(refs)
+ self.variablesWidget.pyFlowInstance.getCanvas().requestShowSearchResults.emit(
+ refs
+ )
def onKillClicked(self):
# check refs and ask user what to do
refs = self._rawVariable.findRefs()
if len(refs) > 0:
- item, accepted = QInputDialog.getItem(None, 'Decide!', 'What to do with getters and setters in canvas?', ['kill', 'leave'], editable=False)
+ item, accepted = QInputDialog.getItem(
+ None,
+ "Decide!",
+ "What to do with getters and setters in canvas?",
+ ["kill", "leave"],
+ editable=False,
+ )
if accepted:
self.variablesWidget.killVar(self)
- if item == 'kill':
+ if item == "kill":
for i in refs:
i.kill()
- elif item == 'leave':
+ elif item == "leave":
for i in refs:
i.var = None
else:
@@ -256,7 +307,7 @@ def onKillClicked(self):
def dataType(self):
return self._rawVariable.dataType
- @ dataType.setter
+ @dataType.setter
def dataType(self, value):
self._rawVariable.dataType = value
self.widget.color = findPinClassByType(self._rawVariable.dataType).color()
@@ -289,12 +340,12 @@ def uid(self, value):
@staticmethod
def jsonTemplate():
template = {
- 'name': None,
- 'uuid': None,
- 'value': None,
- 'type': None,
- 'package': None,
- 'accessLevel': None
+ "name": None,
+ "uuid": None,
+ "value": None,
+ "type": None,
+ "package": None,
+ "accessLevel": None,
}
return template
@@ -302,35 +353,40 @@ def serialize(self):
pinClass = findPinClassByType(self._rawVariable.dataType)
template = UIVariable.jsonTemplate()
- template['name'] = self._rawVariable.name
- template['uuid'] = str(self._rawVariable.uid)
+ template["name"] = self._rawVariable.name
+ template["uuid"] = str(self._rawVariable.uid)
if self._rawVariable.dataType == "AnyPin":
# don't save any variables
# value will be calculated for this type of variables
- template['value'] = None
+ template["value"] = None
else:
- template['value'] = json.dumps(self._rawVariable.value, cls=pinClass.jsonEncoderClass())
+ template["value"] = json.dumps(
+ self._rawVariable.value, cls=pinClass.jsonEncoderClass()
+ )
- template['type'] = self._rawVariable.dataType
- template['package'] = self._rawVariable.packageName
- template['accessLevel'] = self._rawVariable.accessLevel.value
+ template["type"] = self._rawVariable.dataType
+ template["package"] = self._rawVariable.packageName
+ template["accessLevel"] = self._rawVariable.accessLevel.value
return template
@staticmethod
def deserialize(data, graph):
- pinClass = findPinClassByType(data['dataType'])
+ pinClass = findPinClassByType(data["dataType"])
- varUid = uuid.UUID(data['uuid'])
+ varUid = uuid.UUID(data["uuid"])
var = graph.getApp().variablesWidget.createVariable(
- dataType=data['dataType'], accessLevel=AccessLevel(data['accessLevel']), uid=varUid)
- var.setName(data['name'])
- var.setDataType(data['dataType'])
-
- if data['dataType'] == 'AnyPin':
- var.value = getPinDefaultValueByType('AnyPin')
+ dataType=data["dataType"],
+ accessLevel=AccessLevel(data["accessLevel"]),
+ uid=varUid,
+ )
+ var.setName(data["name"])
+ var.setDataType(data["dataType"])
+
+ if data["dataType"] == "AnyPin":
+ var.value = getPinDefaultValueByType("AnyPin")
else:
- var.value = json.loads(data['value'], cls=pinClass.jsonDecoderClass())
+ var.value = json.loads(data["value"], cls=pinClass.jsonDecoderClass())
return var
diff --git a/PyFlow/UI/Canvas/WatchPinValueItem.py b/PyFlow/UI/Canvas/WatchPinValueItem.py
index e390cd261..913befaf8 100644
--- a/PyFlow/UI/Canvas/WatchPinValueItem.py
+++ b/PyFlow/UI/Canvas/WatchPinValueItem.py
@@ -1,14 +1,13 @@
-from Qt import QtCore
-from Qt import QtGui
+from qtpy import QtCore
-from Qt.QtWidgets import *
+from qtpy.QtWidgets import *
from PyFlow.UI.Utils.stylesheet import editableStyleSheet
-from PyFlow.Core.Common import *
class WatchItem(QGraphicsTextItem):
"""docstring for WatchItem."""
+
def __init__(self, text=""):
super(WatchItem, self).__init__(text)
diff --git a/PyFlow/UI/Canvas/loopBackDrop.py b/PyFlow/UI/Canvas/loopBackDrop.py
index d76024676..1fde8119a 100644
--- a/PyFlow/UI/Canvas/loopBackDrop.py
+++ b/PyFlow/UI/Canvas/loopBackDrop.py
@@ -12,9 +12,9 @@
## See the License for the specific language governing permissions and
## limitations under the License.
-from Qt import QtGui
-from Qt import QtCore
-from Qt.QtWidgets import QGraphicsWidget
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import QGraphicsWidget
class backDrop(QGraphicsWidget):
@@ -22,7 +22,10 @@ def __init__(self, parent):
super(backDrop, self).__init__()
self.parent = parent
self.rect = QtCore.QRectF()
- self.parent._rawNode.killed.connect(self.parentNodeKilled)
+ try:
+ self.parent._rawNode.killed.connect(self.parentNodeKilled)
+ except:
+ pass
def parentNodeKilled(self, *args):
scene = self.scene()
@@ -32,7 +35,10 @@ def parentNodeKilled(self, *args):
def boundingRect(self):
try:
- return QtCore.QRectF(QtCore.QPointF(self.parent.left - 5, self.parent.top + 5), QtCore.QPointF(self.parent.right + 5, self.parent.down - 5))
+ return QtCore.QRectF(
+ QtCore.QPointF(self.parent.left - 5, self.parent.top + 5),
+ QtCore.QPointF(self.parent.right + 5, self.parent.down - 5),
+ )
except:
return QtCore.QRectF(0, 0, 0, 0)
diff --git a/PyFlow/UI/CompileUiQt.py b/PyFlow/UI/CompileUiQt.py
index 67e373b6d..3f632ad17 100644
--- a/PyFlow/UI/CompileUiQt.py
+++ b/PyFlow/UI/CompileUiQt.py
@@ -18,29 +18,29 @@
import subprocess
-CURRENT_DIR = os.path.dirname(__file__).replace('\\', '/') + '/'
-INTERPRETER_PATH = 'python.exe'
+CURRENT_DIR = os.path.dirname(__file__).replace("\\", "/") + "/"
+INTERPRETER_PATH = "python.exe"
def ui_to_py(ui_file):
if not os.path.isfile(ui_file):
- msg = 'no such file'
+ msg = "no such file"
print(msg)
return msg
- py_file_name = os.path.splitext(ui_file)[0] + '.py'
- with open(py_file_name, 'w') as py_file:
+ py_file_name = os.path.splitext(ui_file)[0] + ".py"
+ with open(py_file_name, "w") as py_file:
try:
pyside2uic.compileUi(ui_file, py_file)
- print('{0} converted to {1}.'.format(ui_file.upper(), py_file_name.upper()))
+ print("{0} converted to {1}.".format(ui_file.upper(), py_file_name.upper()))
except Exception as e:
- print('Error: compilation error.', e)
+ print("Error: compilation error.", e)
bakFileName = py_file_name.replace(".py", "_backup.py")
# convert to cross compatible code
- subprocess.call([INTERPRETER_PATH, '-m', 'Qt', '--convert', py_file_name])
+ subprocess.call([INTERPRETER_PATH, "-m", "Qt", "--convert", py_file_name])
- if(os.path.isfile(bakFileName)):
+ if os.path.isfile(bakFileName):
os.remove(bakFileName)
print("REMOVING", bakFileName)
@@ -51,11 +51,11 @@ def compile():
continue
for f in files:
if "." in f:
- ext = f.split('.')[1]
- if ext == 'ui':
+ ext = f.split(".")[1]
+ if ext == "ui":
uiFile = os.path.join(d, f)
ui_to_py(uiFile)
-if __name__ == '__main__':
+if __name__ == "__main__":
compile()
diff --git a/PyFlow/UI/ContextMenuDataBuilder.py b/PyFlow/UI/ContextMenuDataBuilder.py
index 80faf3794..6b6de850b 100644
--- a/PyFlow/UI/ContextMenuDataBuilder.py
+++ b/PyFlow/UI/ContextMenuDataBuilder.py
@@ -18,6 +18,7 @@
class ContextMenuDataBuilder(object):
"""docstring for ContextMenuDataBuilder."""
+
def __init__(self):
super(ContextMenuDataBuilder, self).__init__()
self._storage = OrderedDict()
@@ -30,10 +31,10 @@ def addEntry(self, name, title, callback=None, icon=None, parentEntry=None):
if name not in self._menu:
menu = OrderedDict()
- menu['name'] = name
- menu['title'] = title
- menu['icon'] = icon
- menu['callback'] = callback
+ menu["name"] = name
+ menu["title"] = title
+ menu["icon"] = icon
+ menu["callback"] = callback
self._storage[name] = menu
if parentEntry is not None and parentEntry in self._storage:
diff --git a/PyFlow/UI/ContextMenuGenerator.py b/PyFlow/UI/ContextMenuGenerator.py
index 6e2427c4b..b8cbe0e86 100644
--- a/PyFlow/UI/ContextMenuGenerator.py
+++ b/PyFlow/UI/ContextMenuGenerator.py
@@ -13,12 +13,12 @@
## limitations under the License.
-from Qt.QtWidgets import QMenu
-from Qt.QtWidgets import QAction
+from qtpy.QtWidgets import QMenu
class ContextMenuGenerator(object):
"""docstring for ContextMenuGenerator."""
+
def __init__(self, menuDataBuilder):
super(ContextMenuGenerator, self).__init__()
self.builder = menuDataBuilder
@@ -27,7 +27,7 @@ def __createMenuEntry(self, parentMenu, menuEntryData):
if "separator" in menuEntryData:
parentMenu.addSeparator()
return
- icon = menuEntryData['icon']
+ icon = menuEntryData["icon"]
if "sub_menu" in menuEntryData:
subMenuData = menuEntryData["sub_menu"]
subMenu = parentMenu.addMenu(menuEntryData["title"])
@@ -35,10 +35,10 @@ def __createMenuEntry(self, parentMenu, menuEntryData):
subMenu.setIcon(icon)
self.__createMenuEntry(subMenu, subMenuData)
else:
- action = parentMenu.addAction(menuEntryData['title'])
+ action = parentMenu.addAction(menuEntryData["title"])
if icon is not None:
action.setIcon(icon)
- action.triggered.connect(menuEntryData['callback'])
+ action.triggered.connect(menuEntryData["callback"])
def generate(self):
menuData = self.builder.get()
diff --git a/PyFlow/UI/EditorHistory.py b/PyFlow/UI/EditorHistory.py
index a3290fe17..0768002ed 100644
--- a/PyFlow/UI/EditorHistory.py
+++ b/PyFlow/UI/EditorHistory.py
@@ -13,9 +13,7 @@
## limitations under the License.
-import uuid
from blinker import Signal
-from collections import OrderedDict
from PyFlow.Core.Common import *
from PyFlow.Core.Common import SingletonDecorator
@@ -25,6 +23,7 @@
class _EditorState(object):
"""docstring for _EditorState."""
+
def __init__(self, text, modify):
super(_EditorState, self).__init__()
self.text = text
@@ -42,6 +41,7 @@ def __repr__(self):
class EditorHistory(object):
"""docstring for EditorHistory."""
+
def __init__(self, app):
self.statePushed = Signal(object)
@@ -51,7 +51,9 @@ def __init__(self, app):
self.app = app
self.stack = list()
try:
- self._capacity = int(ConfigManager().getPrefsValue("PREFS", "General/HistoryDepth"))
+ self._capacity = int(
+ ConfigManager().getPrefsValue("PREFS", "General/HistoryDepth")
+ )
except:
self._capacity = 10
@@ -98,7 +100,6 @@ def currentIndex(self):
def push(self, edState):
if self.currentIndex < self.count() - 1:
- nextState = None
while True:
index = self.count() - 1
nextState = self.stack[index]
diff --git a/PyFlow/UI/EncodeResources.py b/PyFlow/UI/EncodeResources.py
index 11b242bc0..c3df82a11 100644
--- a/PyFlow/UI/EncodeResources.py
+++ b/PyFlow/UI/EncodeResources.py
@@ -14,49 +14,56 @@
import os
-import sys
import subprocess
-from Qt import __binding__
+from qtpy import __binding__
binding = __import__(__binding__)
path = os.path.dirname(binding.__file__)
if __binding__ == "PySide2":
- app = 'pyside2-rcc.exe'
+ app = "pyside2-rcc.exe"
elif __binding__ == "PySide":
- app = 'pyside-rcc.exe'
+ app = "pyside-rcc.exe"
elif __binding__ == "PyQt4":
- app = 'pyrcc4.exe'
+ app = "pyrcc4.exe"
elif __binding__ == "PyQt5":
- app = 'pyrcc5.exe'
+ app = "pyrcc5.exe"
def main():
- print('Encoding : Resources')
+ print("Encoding : Resources")
filepath = os.path.abspath("./resources")
- resourceFile = 'Resources.qrc'
+ resourceFile = "Resources.qrc"
- with open(resourceFile, 'w') as outf:
- outf.write('\n \n')
+ with open(resourceFile, "w") as outf:
+ outf.write("\n \n")
for root, dirs, files in os.walk("resources"):
for file in files:
- if '.qrc' not in file:
+ if ".qrc" not in file:
dirname = os.path.relpath(os.path.join(root, file))
print(dirname)
- write = ' %s\n' % (
- file, dirname)
+ write = ' %s\n' % (file, dirname)
outf.write(write)
outf.write(" \n")
outf.close()
- args = [os.path.join(path, app), "-compress", "2", "-threshold", "3", '-o',
- os.path.join(os.path.dirname(filepath), r'resources.py'), resourceFile]
+ args = [
+ os.path.join(path, app),
+ "-compress",
+ "2",
+ "-threshold",
+ "3",
+ "-o",
+ os.path.join(os.path.dirname(filepath), r"resources.py"),
+ resourceFile,
+ ]
p = subprocess.Popen(
- args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+ )
out, err = p.communicate()
print(out)
print(err)
# import resources
- print('Compiled : Resources')
+ print("Compiled : Resources")
if __name__ == "__main__":
diff --git a/PyFlow/UI/Forms/Database/frmDatabaseView.ui b/PyFlow/UI/Forms/Database/frmDatabaseView.ui
new file mode 100644
index 000000000..1170cc177
--- /dev/null
+++ b/PyFlow/UI/Forms/Database/frmDatabaseView.ui
@@ -0,0 +1,386 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 1164
+ 659
+
+
+
+ Form
+
+
+ -
+
+
+ 0
+
+
+
+ Bases
+
+
+
-
+
+
+ -
+
+
+ 0
+
+
-
+
+
-
+
+
+ Engine
+
+
+
+ -
+
+
+ -
+
+
+ Abstract View
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Table Args
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Meta Data
+
+
+
+ -
+
+
+ Default Fields
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Timestamp
+
+
+
+ -
+
+
+ Add Fields
+
+
+
+ -
+
+
+ -
+
+
+ Name
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 59
+ 20
+
+
+
+
+
+
+
+
+ Sessions
+
+
+ -
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
+ Database Type
+
+
+
+ -
+
+
+ Login
+
+
+
+ -
+
+
+ Password
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Schema
+
+
+
+ -
+
+
+ -
+
+
+ Local Bind Part
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ SSH Tunnel Forwarder
+
+
+
+ -
+
+
-
+
+
+ IP Address
+
+
+
+ -
+
+
+ -
+
+
+ Login
+
+
+
+ -
+
+
+ -
+
+
+ Password
+
+
+
+ -
+
+
+ -
+
+
+ Remote Bind Address
+
+
+
+ -
+
+
+ -
+
+
+ Port
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+ Tables
+
+
+ -
+
+
-
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ New Table
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ 0
+
+
-
+
+
+ New Field
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PyFlow/UI/Forms/PackageBuilder/NodeandFunctionDesigner.ui b/PyFlow/UI/Forms/PackageBuilder/NodeandFunctionDesigner.ui
new file mode 100644
index 000000000..27fac0b8c
--- /dev/null
+++ b/PyFlow/UI/Forms/PackageBuilder/NodeandFunctionDesigner.ui
@@ -0,0 +1,263 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 536
+ 421
+
+
+
+ Dialog
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/PyFlow/UI/Forms/PackageBuilder/PackageBuilder.py b/PyFlow/UI/Forms/PackageBuilder/PackageBuilder.py
new file mode 100644
index 000000000..256406f30
--- /dev/null
+++ b/PyFlow/UI/Forms/PackageBuilder/PackageBuilder.py
@@ -0,0 +1,2628 @@
+## Copyright 2023 David Lario
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import os
+import shutil
+#import subprocess
+import uuid
+import inspect
+import importlib
+
+from qtpy.QtCore import QCoreApplication
+from qtpy.QtGui import QIcon, QPixmap
+from sqlalchemy.sql.coercions import cls
+
+from PyFlow import Packages
+
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import *
+
+from qtpy.uic import loadUiType, loadUi
+from qtpy import QtUiTools
+from blinker import Signal
+
+path = os.path.dirname(os.path.abspath(__file__))
+packageRoot = Packages.__path__[0]
+
+uiFile = os.path.join(path, 'PackageBuilder.ui')
+#WindowTemplate, TemplateBaseClass = loadUiType(uiFile)
+RESOURCES_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "UI/resources/")
+
+from qtpy.QtCore import QItemSelectionModel #Because Pyside hijacked from pyqt
+from qtpy.QtCore import QSortFilterProxyModel, QRegularExpression, QModelIndex
+from qtpy.QtGui import QStandardItemModel, QStandardItem
+
+class TableComboModel(QComboBox):
+ #Should combine with to customWidgets
+ #currentIndexChanged2 = pyqtSignal([dict])
+
+ def __init__(self, parent, *args, **kwargs):
+ super(TableComboModel, self).__init__(parent)
+ #super(ComboModel, self).currentIndexChanged[dict].connect(self.currentIndexChangedDictionary)
+
+ self.setModel(kwargs['dataModel'])
+ self.setModelColumn(1)
+ self.column = kwargs['column']
+ self.row = kwargs['row']
+ self.id = kwargs['id']
+
+ self.dataout = {}
+ self.dataout['id'] = kwargs['id']
+ self.dataout['column'] = kwargs['column']
+ self.dataout['row'] = kwargs['row']
+ self.dataout['dataModel'] = kwargs['dataModel']
+ self.newItemName = ""
+
+ #for key in kwargs:
+ # print("another keyword arg: %s: %s" % (key, kwargs[key]))
+
+ #self.currentIndexChanged.connect(self.currentIndexChangedDictionary)
+ self.currentIndexChanged.connect(self.on_currentIndexChanged)
+ self.currentIndexChanged.connect(self.currentIndexChangedDictionary)
+ self.editTextChanged.connect(self.on_newInformation)
+ self.setEditable(True)
+
+ def column(self):
+ return self.x
+
+ def row(self):
+ return self.y
+
+ def rowCount(self, parent=QtCore.QModelIndex()):
+ if parent.isValid():
+ return 0
+ return len(self.dbIds)
+
+ def columnCount(self, parent=QtCore.QModelIndex()):
+ if parent.isValid():
+ return 0
+ return 2
+
+ def setValue(self, index, column=0):
+ for row in range(self.model().rowCount()):
+ if str(self.model().index(row, column).data()) == str(index):
+ #print(self.model().index(row, 0).data(), index)
+ self.setCurrentIndex(row)
+ break
+
+ def data(self, index, role=QtCore.Qt.DisplayRole):
+ #print('ComboModel data')
+ if not index.isValid() or ( role != QtGui.Qt.DisplayRole and role != QtGui.Qt.EditRole ):
+ print('ComboModel return invalid QVariant')
+ return QtCore.QVariant()
+ if index.column() == 0:
+ return QtCore.QVariant(self.dbIds[index.row()])
+ if index.column() == 1:
+ return QtCore.QVariant(self.dbValues[index.row()])
+ print('ComboModel return invalid QVariant')
+ return QtCore.QVariant()
+
+ def currentIndexChangedDictionary(self, index):
+ #print("Combo Index changed2:", index)
+ #self.dataout['index'] = index
+ self.dataout['value'] = self.dataout['dataModel'].index(index, 0).data()
+ #self.currentIndexChanged2.emit(self.dataout)
+
+ def on_newInformation(self, newName):
+ if 1==2:
+ if newName != "":
+ self.newItemName = newName
+ else:
+ #Create a new model that has old and new data
+ cmbTableModel = QStandardItemModel(0, 1, self)
+ cmbTableModel.setItem(0, 1, QStandardItem(self.newItemName))
+
+ for row in range(self.dataout['dataModel'].rowCount()):
+ cmbTableModel.setItem(row+1, 1, QStandardItem(self.dataout['dataModel'].index(row,1).data()))
+
+ self.setModel(cmbTableModel)
+
+ def on_currentIndexChanged(self, index):
+ pass
+ #print("Combo Index changed:", index) #, self.sender().x(), self.y)
+ #self.currentIndexChanged2.emit(self.dataout)
+
+class PackageBuilder(QMdiSubWindow):
+ def __init__(self, parent=None):
+ super(PackageBuilder, self).__init__(parent)
+
+ self.ui = QtUiTools.QUiLoader().load(uiFile)
+ self.PackageName = "PackageBuilder"
+ self.parent = parent
+
+ self.defaultfolderlist = ["Tools", "FunctionLibraries", "Nodes", "Pins", "PrefsWidgets", "UI"]
+ packageRoot = Packages.__path__[0]
+ self.ui.txtPackageFolderLocation.setText(packageRoot)
+
+ rowcount = 0
+ self.tooldict = {}
+
+ self.functiondict = {}
+ self.pindict = {}
+
+ self.pindefs = {}
+ self.pindefs["Inputs"] = {}
+ self.pindefs["Outputs"] = {}
+
+ self.codeDict = {}
+ self.codeList = []
+ self.pindata = {}
+ self.selectedPinName = ""
+ self.selectedPinDir = ""
+ self.workingFile = ""
+
+ packagelistmodel = QStandardItemModel(0, 1)
+ for directories in os.listdir(packageRoot):
+ if directories[1] != "_":
+ packagelistmodel.setItem(rowcount, 0, QStandardItem(directories))
+ rowcount += 1
+
+ self.packagelistModelproxy = QtCore.QSortFilterProxyModel(self)
+ #self.packagelistModelproxy.setSourceModel(packagelistmodel)
+
+ self.ui.lstPackages.setModel(self.packagelistModelproxy)
+ self.ui.lstPackages.setModel(packagelistmodel)
+ self.ui.lstPackages.setModelColumn(0)
+ self.ui.lstPackages.clicked.connect(self.onSelectPackage)
+
+ self.ui.cmdOpenPackageFolder.clicked.connect(self.onOpenPackageFolder)
+ self.ui.txtPackageFilter.textChanged.connect(self.onChangeFilterValue)
+ self.ui.tvPackageItems.header().hide()
+
+ #self.ui.tblFInputPins.selectionModel().selectionChanged.connect(self.on_tblFInputPins_Changed)
+ self.ui.tblFInputPins.clicked.connect(self.on_tblFInputPins_clicked)
+
+ #self.ui.tblFOutputPins.selectionModel().selectionChanged.connect(self.on_tblFOutputPins_Changed)
+ self.ui.tblFOutputPins.clicked.connect(self.on_tblFOutputPins_clicked)
+ self.ui.tvPackageItems.itemClicked.connect(self.on_tvPackageItems_clicked)
+
+
+ self.ui.cmdCreatePackage.clicked.connect(self.on_cmdCreatePackage_clicked)
+
+ self.ui.cmdCommandAddSmallIcon.clicked.connect(self.on_cmdCommandAddSmallIcon_clicked)
+ self.ui.cmdCommandAddMediumIcon.clicked.connect(self.on_cmdCommandAddMediumIcon_clicked)
+ self.ui.cmdCommandAddLargeIcon.clicked.connect(self.on_cmdCommandAddLargeIcon_clicked)
+
+ self.onPinScan()
+
+ @staticmethod
+ def supportedSoftwares():
+ """Under what software to work
+ """
+ return ["any"]
+
+ def getMenuOrder(self):
+ menuOrder = ["Tools", "Windows", "Help"]
+ return menuOrder
+
+ def getMenuLayout(self):
+ menuDict = {}
+ separatorCount = 0
+ packageList = []
+ menuDict["Packages"] = packageList
+
+ fileMenuList = []
+ menuDict["File"] = fileMenuList
+
+ packageToolList = []
+ packageToolList.append({"Action": "Add Action", "Package": "PackageBuilder", "PackageGroup": "ProgramBase","Instance": self, "Command": "PackageBuilder"})
+ menuDict["Tools"] = packageToolList
+
+ windowMenuList = []
+ menuDict["Windows"] = windowMenuList
+
+ helpMenuList = []
+ helpMenuList.append({"Action": "Add Action", "Package": "ProgramBase", "PackageGroup": "ProgramBase", "Instance": self, "Command": "About"})
+ helpMenuList.append({"Action": "Add Action", "Package": "ProgramBase", "PackageGroup": "ProgramBase", "Instance": self, "Command": "HomePage"})
+ menuDict["Help"] = helpMenuList
+
+ return menuDict
+
+ def getRibbonOrder(self):
+ ribbonOrder = ["Tools"]
+ return ribbonOrder
+
+ def getRibbonLayout(self):
+ ribbonBarDict = {}
+ ribbonItems = []
+ ribbonItems.append({"Bar": "ProgramBase", "Section": "Tools", "Widget": "Small Button", "SmallIcon": RESOURCES_DIR + "new_file_icon.png", "LargeIcon": RESOURCES_DIR + "new_file_icon.png", "Action": "Add Action", "Package": "ProgramBase", "PackageGroup": "ProgramBase","Instance": self, "Command": "ToolBar"})
+ ribbonBarDict["Tools"] = ribbonItems
+ return ribbonBarDict
+
+ def getToolBarLayout(self):
+
+ toolBarDict = {}
+ pyFlowToolBar = []
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action", "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "NewFile"})
+ toolBarDict["File"] = pyFlowToolBar
+
+ return toolBarDict
+
+ def onChangeFilterValue(self, text):
+ #self.packagelistModelproxy.setFilterKeyColumn(text)
+
+ search = QtCore.QRegularExpression(str(text), QRegularExpression.CaseInsensitiveOption)
+ #self.packagelistModelproxy.setFilterRegExp(search)
+
+ def onSelectPackage(self, index):
+ QCoreApplication.processEvents()
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(index.row(), 0).data()
+ #selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex(), 0, None).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ self.ui.tvPackageItems.clear()
+ self.ui.txtPackageName.setText(selectedpackage)
+
+ CommandList = {}
+ FunctionList = {}
+ PrefsWidgetsList = {}
+
+ for directories in os.listdir(packagepath):
+ if directories[1] != "_":
+ parent = QTreeWidgetItem(self.ui.tvPackageItems)
+ parent.setText(0, directories)
+ #parent.setFlags(parent.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable)
+ filepath = os.path.join(packagepath, directories)
+ for file in os.listdir(filepath):
+ if file[1] != "_" and file[-3:]==".py":
+ child = QTreeWidgetItem(parent)
+ #child.clicked.connect(self.on_tvPackageItems_clicked)
+ #child.setFlags(child.flags() | QtCore.Qt.ItemIsUserCheckable)
+ child.setText(0, file[:-3])
+ #child.setCheckState(0, QtCore.Qt.Unchecked)
+ filefullpath = os.path.join(filepath, file)
+
+ try:
+ f = open(filefullpath, "r")
+ for lineitem in f:
+ if directories[1] == "FunctionLibrary":
+ if len(lineitem) > 10:
+ if lineitem[:5] == "class":
+ classnamestart = lineitem.find(" ")
+ classnameend = lineitem.find("(")
+ #child2 = QTreeWidgetItem(child)
+ #child2.setFlags(child2.flags() | QtCore.Qt.ItemIsUserCheckable)
+ #child2.setText(0, lineitem[classnamestart+1:classnameend])
+ #child2.setCheckState(0, QtCore.Qt.Unchecked)
+ if lineitem.find("def ") != -1:
+ if lineitem[8] != "_":
+ classnamestart = 7
+ classnameend = lineitem.find("(")
+ child2 = QTreeWidgetItem(child)
+ #child2.setFlags(child2.flags() | QtCore.Qt.ItemIsUserCheckable)
+ classname = lineitem[classnamestart+1:classnameend]
+ if file.find(lineitem[classnamestart+1:classnameend]) == -1:
+ functionname = lineitem[classnamestart+1:classnameend]
+ child2.setText(0, functionname)
+ #child2.setCheckState(0, QtCore.Qt.Unchecked)
+ except:
+ pass
+
+ def packageScan(self):
+ print(f"Class name: {cls.__name__}")
+ print("Methods:")
+ methods = inspect.getmembers(cls, predicate=inspect.isfunction)
+ for _, method in methods:
+ print(method)
+
+ def analyze_package(package_name):
+ try:
+ package = importlib.import_module(package_name)
+ except ImportError:
+ print(f"Error: Package '{package_name}' not found.")
+ return
+
+ classes = inspect.getmembers(package, predicate=inspect.isclass)
+ functions = inspect.getmembers(package, predicate=inspect.isfunction)
+
+ print(f"Package name: {package_name}\n")
+ '''print("Classes:")
+ for _, cls in classes:
+ print_class_info(cls)
+
+ print("Functions:")
+ for _, function in functions:
+ print_function_info(function)'''
+
+ def userFriendlyCurrentFile(self):
+ return "Friendly Name" #self.strippedName(self.curFile)
+
+ def onPinScan(self):
+ packageRoot = Packages.__path__[0]
+ self.pinDict = {}
+ for root, dirs, files in os.walk(packageRoot, topdown=False):
+ for name in files:
+ directories = os.path.join(root, name)
+ if "Pin.py" in name:
+ PinName = name.replace(".py", "")
+ self.pinDict[PinName] = directories
+
+ @QtCore.Slot(QTreeWidgetItem, int)
+ def on_tvPackageItems_clicked(self, it, col):
+ QCoreApplication.processEvents()
+ parents = []
+ current_item = it
+ current_parent = current_item.parent()
+
+ # Walk up the tree and collect all parent items of this item
+ while not current_parent is None:
+ parents.insert(0, current_parent.text(col))
+ current_item = current_parent
+ current_parent = current_item.parent()
+
+ filefullpath = Packages.__path__[0] + "\\"
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ selecteditem = it.text(col) + ".py"
+ filefullpath = os.path.join(filefullpath, selectedpackage)
+
+ for items in parents:
+ filefullpath = os.path.join(filefullpath, items)
+
+ filefullpath = os.path.join(filefullpath, selecteditem)
+
+ if filefullpath.find("\\Tools") != -1:
+
+ #todo: check if package level was selected
+ deft = it.text(col)
+ self.workingFile = filefullpath
+ self.initializeform
+ self.initializePinData
+ filename = filefullpath.split("\\")[-1]
+
+ self.ui.twPackage.setCurrentIndex(1)
+ implementdata = ""
+ definitiondata = ""
+ codedata = ""
+
+ if os.path.exists(filefullpath):
+ self.ui.txtCommandFileName.setText(filename)
+ self.ui.txtCommandName.setText(deft)
+ self.loadToolData(filefullpath)
+
+
+ if filefullpath.find("\\FunctionLibraries") != -1:
+ self.loadAllFunctions()
+ #todo: check if package level was selected
+ deft = it.text(col)
+ self.workingFile = filefullpath
+ self.initializeform
+ self.initializePinData
+ filename = filefullpath.split("\\")[-1]
+ self.ui.txtFunctionFileName.setText(filename)
+ self.ui.txtFunctionName.setText(deft)
+
+ self.loadAllFunctions()
+ implementdata = ""
+ definitiondata = ""
+ codedata = ""
+ self.ui.twPackage.setCurrentIndex(2)
+
+ try:
+ for idata in self.functiondict[deft]["Implement"]:
+ implementdata += idata
+ self.ui.txtFImplement.setText(implementdata)
+
+ for ddata in self.functiondict[deft]["Definition"]:
+ definitiondata += ddata
+ self.ui.txtFDef.setText(definitiondata)
+
+ code = ""
+ for codeline in self.functiondict[deft]["Code"]:
+ code += codeline
+ self.ui.txtCode.setText(code)
+
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.setText("")
+
+ if "CATEGORY" in self.functiondict[deft]["MetaData"]:
+ self.ui.txtMetaCategory.setText(self.functiondict[deft]["MetaData"]["CATEGORY"])
+ if "KEYWORDS" in self.functiondict[deft]["MetaData"]:
+ self.ui.txtMetaKeywords.setText(self.functiondict[deft]["MetaData"]["KEYWORDS"])
+ if "CacheEnabled" in self.functiondict[deft]["MetaData"]:
+ self.ui.chkMetaCacheEnabled.setChecked(self.functiondict[deft]["MetaData"]["CacheEnabled"])
+
+ self.pindefs = self.functiondict[deft]["PinDefs"]
+
+ if deft in self.functiondict:
+ self.loadPinTable(deft)
+
+ except:
+ pass
+
+ if filefullpath.find("\\Nodes") != -1:
+ deft = it.text(col)
+ self.ui.txtNodeFileName.setText(deft)
+ self.selectedNodeDataName = deft.replace(".py", "")
+ #filefullpath = os.path.join(filefullpath, deft)
+ self.loadNodeProperties(filefullpath)
+ self.parseNodePins()
+ self.ui.twPackage.setCurrentIndex(3)
+
+ if filefullpath.find("\\Pins") != -1:
+ self.loadPinProperties(filefullpath)
+ print(self.pindict)
+
+
+ if filefullpath.find("\\SQL") != -1:
+ deft = it.text(col)
+ self.loadTableProperties(filefullpath)
+ self.ui.twPackage.setCurrentIndex(8)
+
+
+ def onOpenPackageFolder(self):
+
+ #print(Packages.__path__[0] + "\\")
+ os.startfile(Packages.__path__[0])
+ #subprocess.Popen(r'explorer' + Packages.__path__[0] + "\\")
+
+
+ def loadFunctionProperties(self, filefullpath, functionname):
+
+ previousitem = ""
+ implementdata = ""
+ readingimplementdata = -1
+ readingdefdata = 0
+ defdata = ""
+ codedata = []
+ eof = 0
+ defname = ""
+ code = ""
+ codedescription = ""
+ NestDict = {}
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ if lineitem.find("class") != -1:
+ self.intro = code
+ precode = code
+ code += lineitem
+ codedata.append(lineitem)
+ #print(lineitem)
+ if lineitem.find("super") != -1:
+ code = ""
+ codedata = []
+
+ if lineitem.find("@staticmethod") != -1 or index == filesize-1:
+ readingdefdata = 0
+ if defname == functionname:
+ if precode.find("@staticmethod") != -1:
+ NestDict = {}
+ implement2 = implementdata
+ NestDict["Implement"] = implement2.replace("@staticmethod", "")
+ NestDict["Definition"] = defdata
+ NestDict["CodeDescription"] = codedescription
+ NestDict["Code"] = codedata[:-1]
+ self.functiondict[functionname] = NestDict
+
+ self.parseFunctionFile(functionname)
+ break
+ else:
+ implementdata = ""
+
+ if lineitem.find("def ") != -1 and lineitem.find(" " + functionname) != -1:
+ defnamestart = 7
+ defnameend = lineitem.find("(")
+ defname = lineitem[defnamestart + 1:defnameend]
+ readingdefdata = 1
+
+ if readingdefdata == 1:
+ if lineitem.find("def ") != -1:
+ lineitem = lineitem[defnameend+1:]
+ readingimplementdata = 0
+ defdata += lineitem.strip()
+ if defdata[-1] == ":":
+ readingdefdata = 0
+ codedata = []
+
+ if (lineitem.find("@IMPLEMENT_NODE") != -1) or readingimplementdata == 1:
+ implementdata += lineitem.strip()
+ readingimplementdata = 1
+
+ '''if "\'\'\'" in lineitem or "\"\"\"" in lineitem and readingdefdata == 0:
+ codedescription += lineitem[8:]
+ else:
+ codedata.append(lineitem[8:])'''
+ except:
+ pass
+
+
+ def loadToolData(self, filefullpath):
+ codenotes = 0
+ importlist = []
+ importstart = 0
+ classstart = 0
+ super = 0
+ staticmethod = []
+ definition = []
+ codedata = []
+
+ filefullpath2 = Packages.__path__[0] + "\\"
+ filename = self.ui.txtCommandFileName.text()
+ defname = filename[:-3]
+
+ filefullpath2 = os.path.join(filefullpath2, self.ui.txtPackageName.text())
+ filefullpath2 = os.path.join(filefullpath2, "Tools")
+
+ self.initToolDict(defname)
+
+ self.tooldict[defname]["Filename"] = defname
+
+ self.tooldict[defname]["Description"] = ""
+ self.tooldict[defname]["Category"] = ""
+ self.tooldict[defname]["Keywords"] = ""
+
+ self.tooldict[defname]["KeyboardShortCut"] = ""
+ self.tooldict[defname]["ResourceFolder"] = ""
+
+ self.ui.cmdCommandAddSmallIcon.setIcon(QIcon())
+ self.ui.txtCommandSmallIcon.setText("")
+ self.ui.cmdCommandAddMediumIcon.setIcon(QIcon())
+ self.ui.cmdCommandAddMediumIcon.setText("")
+ self.ui.cmdCommandAddLargeIcon.setIcon(QIcon())
+ self.ui.txtCommandLargeIcon.setText("")
+
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ codedata.append(lineitem)
+
+ if lineitem.find("import") != -1:
+ importlist.append(index)
+
+ if lineitem.find("class") != -1:
+ classstart = index
+
+ if lineitem.find("def") != -1:
+ definition.append(index)
+
+ defCount = len(definition)
+ for count, defitem in enumerate(definition):
+ line = codedata[defitem]
+ if count == defCount-1:
+ endCodeBlock = len(codedata)
+ else:
+ endCodeBlock = definition[count+1]-1
+
+ if codedata[defitem - 1].find("@staticmethod") != -1:
+ staticmethod = True
+ else:
+ staticmethod = False
+
+ if codedata[defitem].find("__init__") != -1:
+ if codedata[defitem].find("super") != -1:
+ pass
+
+ if codedata[defitem].find("toolTip") != -1:
+ for row in range(defitem,endCodeBlock):
+ line2 = codedata[row]
+ if codedata[row].find("return") != -1:
+ tooltip = codedata[row][15:]
+ self.ui.txtCommandToolTip.setText(tooltip[1:-2])
+ self.tooldict[defname]["ToolTip"] = ""
+
+ if codedata[defitem].find("getIcon") != -1:
+ for row in range(defitem, endCodeBlock):
+ a = codedata[row]
+ if codedata[row].find("return") != -1:
+ getIcon = codedata[row][15:]
+ #Todo: Load Icon Image to Button
+ parts = getIcon.split("(")
+ icon_path = parts[1][:-1]
+ parts = icon_path.split('"')
+ filename = parts[1]
+ RESOURCES_DIR = os.path.join(filefullpath2, "res")
+ self.ui.cmdCommandAddSmallIcon.setIcon(QtGui.QIcon(os.path.join(RESOURCES_DIR, filename)))
+ self.ui.txtCommandSmallIcon.setText(filename)
+ self.tooldict[defname]["SmallIcon"] = getIcon
+
+ if codedata[defitem].find("getSmallIcon") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ getSmallIcon = codedata[row][15:]
+ #self.ui.txtCommandgetIcon.setText(getSmallIcon)
+ # self.ui.cmdCommandCommandAddSmallIcon.setIcon(QtGui.QIcon(getIcon))
+ self.tooldict[defname]["SmallIcon"] = getSmallIcon
+
+ if codedata[defitem].find("getMediumIcon") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ getMediumIcon = codedata[row][15:]
+ #self.ui.txtCommandgetIcon.setText(getMediumIcon)
+ # self.ui.cmdAddCommandMediumIcon.setIcon(QtGui.QIcon(getIcon))
+ self.tooldict[defname]["MediumIcon"] = getMediumIcon
+
+
+ if codedata[defitem].find("getIcon") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ getLargeIcon = codedata[row][15:]
+ #self.ui.txtCommandgetIcon.setText(getLargeIcon)
+ # self.ui.cmdCommandAddLargeIcon.setIcon(QtGui.QIcon(getIcon))
+ self.tooldict[defname]["LargeIcon"] = getLargeIcon
+
+ if codedata[defitem].find("keywords") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ keywords = codedata[row][15:]
+ self.ui.txtCommandgetIcon.setText(keywords)
+ self.tooldict[defname]["keywords"] = keywords
+
+ if codedata[defitem].find("keyboardshortcut") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ keywords = codedata[row][15:]
+ self.ui.txtKeyBoardShortcut.setText(keywords)
+ self.tooldict[defname]["keyboardshortcut"] = keywords
+
+ if codedata[defitem].find("name") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ name = codedata[row][15:]
+ parts = name.split('"')
+ self.ui.txtCommandName.setText(parts[1])
+ #Todo: Remove String Data
+ self.tooldict[defname]["Name"] = name
+
+ if codedata[defitem].find("do") != -1:
+ code = ""
+ for codeline in codedata[defitem:endCodeBlock]:
+ code += codeline
+ self.ui.txtCommandCode.setText(code)
+ self.tooldict[defname]["PythonCode"] = ""
+ @QtCore.Slot()
+ def on_cmdCommandAddSmallIcon_clicked(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, "Open File", "", "All Files (*.*)")
+ self.ui.cmdCommandAddSmallIcon.setIcon(QtGui.QIcon(file_path))
+ self.ui.txtCommandSmallIcon.setText(os.path.basename(file_path))
+
+ @QtCore.Slot()
+ def on_cmdCommandAddMediumIcon_clicked(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, "Open File", "", "All Files (*.*)")
+ self.ui.cmdCommandAddMediumIcon.setIcon(QtGui.QIcon(file_path))
+ self.ui.txtCommandMediumIcon.setText(os.path.basename(file_path))
+
+ @QtCore.Slot()
+ def on_cmdCommandAddLargeIcon_clicked(self):
+ file_path, _ = QFileDialog.getOpenFileName(self, "Open File", "", "All Files (*.*)")
+ self.ui.cmdCommandAddLargeIcon.setIcon(QtGui.QIcon(file_path))
+ self.ui.txtCommandLargeIcon.setText(os.path.basename(file_path))
+
+ def parseFunctionFile(self, defname):
+ #https://nedbatchelder.com/text/python-parsers.html
+ #Maybe use Code Parser
+
+ ''' Function Dictionary Structure
+ ["Name"] - Function Name
+ ["Order"] - Order in Appearance
+ ["Meta"] - Meta Data
+ ["Inputs"] - Pin Input List
+ ["Variable"] - Input Pin Name
+ ["DataType"] - Input Pin Data Type
+ ["DefaultValue"] - Input Pin Default Value
+ ["PinSpecifiers"] - Input Pin Options
+ ["Outputs"]
+ ["Variable"] - Output Pin Name
+ ["DataType"] - Output Pin Data Type
+ ["DefaultValue"] - Output Pin Default Value
+ ["PinSpecifiers"] - Output Pin Options
+ '''
+
+ implementdata = self.functiondict[defname]["Implement"]
+
+ '''IMPLEMENT_NODE(func=None, returns=(DataType, DefaultValue, PinSpecficiers), meta={NodeMeta.CATEGORY: 'Default', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Pure):'''
+ istyle = "kwarg"
+ ikeywords = ["func", "returns", "meta"]
+
+ '''PinSpecifiers = {PinSpecifiers.List: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported, PinSpecifiers.Value: "1"}'''
+ parseparameters = {"start":"{", "end": "}", "delimination": ","}
+ valuetype = {"\"": "Value", "|": "List"}
+
+ '''meta = {NodeMeta.CATEGORY: 'Utils', NodeMeta.KEYWORDS: ['id'], NodeMeta.CACHE_ENABLED: False}'''
+ parseparameters = {"start": "{", "end": "}", "delimination": ","}
+ valuetype = {"\"": "Value", "[]": "List"}
+
+ defdata = self.functiondict[defname]["Definition"]
+ style = "csv"
+ pos = ["DataType", "DefaultValue", "PinSpecifiers"]
+
+ codedata = self.functiondict[defname]["Code"]
+
+ implementdata2 = []
+ defdata2 = []
+
+ code = ""
+ for line, linedata in enumerate(codedata):
+ if "staticmethod" not in linedata:
+ if line == 0:
+ output = linedata.replace("\"", "").replace("\'","")[8:]
+ self.ui.txtCodeDescription.setText(output)
+ else:
+ code += linedata[8:]
+
+ self.functiondict[defname]["Code"] = codedata
+
+ #Extracting Information from Implement
+ itemdict = {}
+ itemdict["FunctionName"] = defname
+
+ idata = {}
+ pos = 1
+
+ while pos <= len(implementdata)-1:
+ if implementdata[pos] == "=":
+ pos2 = pos
+ while implementdata[pos2] != "(" and implementdata[pos2] != ",":
+ pos2 -= 1
+ variable = implementdata[pos2 + 1:pos].strip()
+ pos3 = pos + 1
+ while implementdata[pos3] != "=" and pos3 != len(implementdata) - 1:
+ pos3 += 1
+ if pos3 != len(implementdata) - 1:
+ pos4 = pos3
+ while implementdata[pos4] != ",":
+ pos4 -= 1
+ settings = implementdata[pos + 1:pos4].strip()
+ else:
+ settings = implementdata[pos + 1:len(implementdata) - 1].strip()
+ pos4 = len(implementdata) - 1
+ idata[variable] = settings.strip()
+ pos = pos4
+ pos += 1
+
+ bracketstart = implementdata.find("returns") + len("returns") + 2
+ bracketend = bracketstart
+ bracketcount = 1
+ while bracketend < len(implementdata) and bracketcount != 0:
+ if implementdata[bracketend:bracketend + 1] == "(":
+ bracketcount += 1
+ if implementdata[bracketend:bracketend + 1] == ")":
+ bracketcount -= 1
+ bracketend += 1
+ bracketstuff = implementdata[bracketstart:bracketend-1]
+ #implementdata = implementdata.replace(bracketstuff,"bracketstuff")
+
+ curlbracketstart = implementdata.find("meta") + len("meta") + 2
+ curlbracketend = curlbracketstart
+ bracketcount = 1
+ metadata = {}
+
+ if curlbracketstart != -1:
+ while curlbracketend < len(implementdata) and bracketcount != 0:
+ if implementdata[curlbracketend:curlbracketend + 1] == "{":
+ bracketcount += 1
+ if implementdata[curlbracketend:curlbracketend + 1] == "}":
+ bracketcount -= 1
+ curlbracketend += 1
+
+ metalist = implementdata[curlbracketstart:curlbracketend-1]
+ for y in metalist.split(","):
+ itemdata = y.strip().split(":")
+ if itemdata[0] == "NodeMeta.CATEGORY":
+ metadata["CATEGORY"] = itemdata[1].strip()
+ #self.ui.txtMetaCategory.setText(itemdata[1])
+ if itemdata[0] == "NodeMeta.KEYWORDS":
+ metadata["KEYWORDS"] = itemdata[1].strip()
+ #self.ui.txtMetaKeywords.setText(itemdata[1])
+ if itemdata[0] == "NodeMeta.CACHE_ENABLED":
+ metadata["CACHE_ENABLED"] = itemdata[1].strip()
+ #self.ui.chkMetaCacheEnabled.setChecked(itemdata[1])
+
+
+ self.functiondict[defname]["MetaData"] = metadata
+ implementdata2.append(implementdata[:curlbracketstart-6])
+ implementdata2.append(implementdata[curlbracketstart-6:curlbracketend - 1] + "})")
+
+ #Definition Item
+ defs = {}
+ pos = 1
+ while pos <= len(defdata)-1:
+ if defdata[pos] == "=":
+ pos2 = pos
+ while pos2 != -1 and defdata[pos2] != ",":
+ pos2 -= 1
+ variable = defdata[pos2 + 1:pos].strip()
+ pos3 = pos + 1
+ while defdata[pos3] != "=" and pos3 != len(defdata) - 1:
+ pos3 += 1
+ if pos3 != len(defdata) - 1:
+ pos4 = pos3
+ while defdata[pos4] != ",":
+ pos4 -= 1
+ settings = defdata[pos + 1:pos4].strip()
+ else:
+ settings = defdata[pos + 1:len(defdata) - 1].strip()
+ pos4 = len(defdata) - 1
+ defs[variable] = settings.strip()
+ pos = pos4
+ pos += 1
+
+ #Output Pin
+
+ outputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ rowcount = 0
+ pinOutCounter = 0
+ pindefs = {}
+ pindefs["Inputs"] = {}
+ pindefs["Outputs"] = {}
+ pindata = {}
+ data = idata["returns"]
+
+ if data != "None":
+ pinOutCounter += 1
+ curlbracketstart = data.find("{") + 1
+ curlbracketend = curlbracketstart
+ bracketcount = 1
+ if curlbracketstart != 0:
+ while curlbracketend < len(data) and bracketcount != 0:
+ if data[curlbracketend:curlbracketend + 1] == "{":
+ bracketcount += 1
+ if data[curlbracketend:curlbracketend + 1] == "}":
+ bracketcount -= 1
+ curlbracketend += 1
+
+ curlystuff3 = data[curlbracketstart:curlbracketend - 1]
+ #data = data.replace(curlystuff3, "curlystuff")
+ pindata = {}
+ for y in curlystuff3.split(","):
+ itemdata = y.strip().split(":")
+ if itemdata[0] == "PinSpecifiers.SUPPORTED_DATA_TYPES":
+ pindata["SUPPORTED_DATA_TYPES"] = itemdata[1][1:].strip()
+ if itemdata[0] == "PinSpecifiers.CONSTRAINT":
+ pindata["CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.STRUCT_CONSTRAINT":
+ pindata["STRUCT_CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.ENABLED_OPTIONS":
+ pindata["ENABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DISABLED_OPTIONS":
+ pindata["DISABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.INPUT_WIDGET_VARIANT":
+ pindata["INPUT_WIDGET_VARIANT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DESCRIPTION":
+ pindata["DESCRIPTION"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_LIST":
+ pindata["VALUE_LIST"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_RANGE":
+ pindata["VALUE_RANGE"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DRAGGER_STEPS":
+ pindata["DRAGGER_STEPS"] = itemdata[1].strip()
+
+ listdata = data.split(",")
+ pindata["Name"] = "out"
+ pindata["Direction"] = "Out"
+ pindata["Order"] = pinOutCounter
+ pindata["DataType"] = listdata[0][1:].strip().replace("\"", "").replace("\'", "")
+ pindata["DefaultValue"] = listdata[1].strip().replace("))","")
+ pindefs["Outputs"]["out"] = pindata
+
+ #InputPin
+
+ rowcount2 = 0
+
+ pindata = {}
+ curlystuff3 = None
+ PinCounter = 0
+
+ for variable, data in defs.items():
+ #print(variable, data)
+ PinCounter += 1
+ if data.find("REF") == -1:
+ curlbracketstart = data.find("{") + 1
+ curlbracketend = curlbracketstart
+ bracketcount = 1
+ if curlbracketstart != 0:
+ while curlbracketend < len(data) and bracketcount != 0:
+ if data[curlbracketend:curlbracketend + 1] == "{":
+ bracketcount += 1
+ if data[curlbracketend:curlbracketend + 1] == "}":
+ bracketcount -= 1
+ curlbracketend += 1
+
+ curlystuff3 = data[curlbracketstart:curlbracketend - 1]
+
+ if curlystuff3 != None: data = data.replace(curlystuff3, "curlystuff")
+ listdata = data.split(",")
+ pindata = {}
+ pindata["Name"] = variable
+ pindata["Direction"] = "In"
+ pindata["Order"] = PinCounter
+ pindata["DataType"] = listdata[0][1:].strip().replace("\"", "").replace("\'", "")
+ if len(listdata) >= 2:
+ pindata["DefaultValue"] = listdata[1].strip()
+
+ if curlystuff3 is not None:
+ for y in curlystuff3.split(","):
+ itemdata = y.strip().split(":")
+ if itemdata[0] == "PinSpecifiers.SUPPORTED_DATA_TYPES":
+ pindata["SUPPORTED_DATA_TYPES"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.CONSTRAINT":
+ pindata["CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.STRUCT_CONSTRAINT":
+ pindata["STRUCT_CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.ENABLED_OPTIONS":
+ pindata["ENABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DISABLED_OPTIONS":
+ pindata["DISABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.INPUT_WIDGET_VARIANT":
+ pindata["INPUT_WIDGET_VARIANT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DESCRIPTION":
+ pindata["DESCRIPTION"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_LIST":
+ pindata["VALUE_LIST"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_RANGE":
+ pindata["VALUE_RANGE"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DRAGGER_STEPS":
+ pindata["DRAGGER_STEPS"] = itemdata[1].strip()
+
+ pindefs["Inputs"][variable] = pindata
+
+ rowcount2 += 1
+ else:
+ pinOutCounter += 1
+ startvalue = data.find("\"")
+ if startvalue == -1:
+ startvalue = data.find("\'")
+
+ endvalue = data.find(")")
+ pindata = {}
+ pindata["Name"] = variable
+ pindata["Direction"] = "Out"
+ pindata["Order"] = pinOutCounter
+
+ listdata = data[startvalue:endvalue].split(",")
+ pindata["DataType"] = listdata[0].strip().replace("\"", "").replace("\'", "")
+
+ if len(listdata) >= 2: pindata["DefaultValue"] = listdata[1].strip()
+
+ pindefs["Outputs"][variable] = pindata
+
+ rowcount += 1
+
+ self.functiondict[defname]["PinDefs"] = pindefs
+
+ self.functiondict[defname]["Implement"] = implementdata2
+
+ for variable, data in defs.items():
+ defdata2.append(variable + "=" + data)
+
+ self.functiondict[defname]["Definition"] = defdata2
+
+ def loadPinTable(self, defname):
+ # InputPin
+ pindatatypemodel = QtGui.QStandardItemModel(0, 2)
+ for index, key in enumerate(self.pinDict):
+ pindatatypemodel.setItem(index, 0, QtGui.QStandardItem(str(index)))
+ pindatatypemodel.setItem(index, 1, QtGui.QStandardItem(key))
+
+ if "PinDefs" in self.functiondict[defname]:
+ inputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ inputPinList = []
+ if "Inputs" in self.functiondict[defname]["PinDefs"]:
+ for pindata in self.functiondict[defname]["PinDefs"]["Inputs"]:
+ row = int(self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["Order"]) - 1
+ inputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+ DataTypeValue = ""
+ if "DataType" in self.functiondict[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["DataType"]))
+ DataTypeValue = self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["DataType"]
+ inputPinList.append(DataTypeValue)
+ if "DefaultValue" in self.functiondict[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["DefaultValue"]))
+
+ inputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ outputPinList = []
+ if "Outputs" in self.functiondict[defname]["PinDefs"]:
+ outputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ for rowcount2, pindata in enumerate(self.functiondict[defname]["PinDefs"]["Outputs"]):
+ row = int(self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["Order"]) - 1
+ DataTypeValue = ""
+ if rowcount2 == 0:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem("out"))
+ else:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+
+ if "DataType" in self.functiondict[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["DataType"]))
+ DataTypeValue = self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["DataType"]
+ outputPinList.append(DataTypeValue)
+
+ if "DefaultValue" in self.functiondict[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["DefaultValue"]))
+
+ outputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ self.ui.tblFInputPins.setModel(inputpinlistmodel)
+ self.ui.tblFOutputPins.setModel(outputpinlistmodel)
+
+ if "Inputs" in self.functiondict[defname]["PinDefs"]:
+ for row, data in enumerate(self.functiondict[defname]["PinDefs"]["Inputs"]):
+ self.ui.tblFInputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+ c.setValue(self.functiondict[defname]["PinDefs"]["Inputs"][data]["DataType"], 1)
+ i = self.ui.tblFInputPins.model().index(row, 1)
+ #c.currentIndexChanged2[dict].connect(self.on_lstPinSettings_cmbTableChanged)
+ self.ui.tblFInputPins.setIndexWidget(i, c)
+
+ if "Outputs" in self.functiondict[defname]["PinDefs"]:
+ for row, data in enumerate(self.functiondict[defname]["PinDefs"]["Outputs"]):
+ self.ui.tblFOutputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+ c.setValue(self.functiondict[defname]["PinDefs"]["Outputs"][data]["DataType"], 1)
+ i = self.ui.tblFOutputPins.model().index(row, 1)
+ #c.currentIndexChanged2[dict].connect(self.on_lstTableSettings_cmbTableChanged)
+ self.ui.tblFOutputPins.setIndexWidget(i, c)
+
+ #self.ui.tblFInputPins.resizeColumnsToContents()
+ #self.ui.tblFOutputPins.resizeColumnsToContents()
+
+ #self.ui.tblFInputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+ #self.ui.tblFOutputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+
+ self.initializePinData()
+
+ '''for z in curlystuff2.split(","):
+ itemdata = z.strip().split(":")
+ #print(itemdata[0], itemdata[1].strip())'''
+
+ def on_lstPinSettings_cmbTableChanged(self, int):
+ print(int)
+
+ def initializeform(self):
+
+ self.ui.txtFunctionFileName.setText("")
+ self.ui.txtFunctionName.setText("")
+ #self.ui.tblFInputPins
+ #self.ui.tblFOutputPins
+ self.ui.txtFImplement.setText("")
+ self.ui.txtFDef.setText("")
+ self.ui.txtCodeDescription.setText("")
+ self.ui.txtCode.setText("")
+
+ self.ui.txtMetaCategory.setText("")
+ self.ui.txtMetaKeywords.setText("")
+ self.ui.chkMetaCacheEnabled.setChecked(False)
+
+ def blockPinSignals(self):
+ self.ui.chkPSSupportedDataTypes.blockSignals(True)
+ self.ui.chkPSSupportedDataTypes.blockSignals(True)
+ self.ui.txtPSSupportedDataTypes.blockSignals(True)
+ self.ui.chkPSConstraint.blockSignals(True)
+ self.ui.txtPSConstraint.blockSignals(True)
+ self.ui.chkPSStructConstraint.blockSignals(True)
+ self.ui.txtPSStructConstraint.blockSignals(True)
+ #self.ui.chkPSEnableOptions.blockSignals(True)
+ #self.ui.txtPSEnableOptions.blockSignals(True)
+ self.ui.chkPSDisableOptions.blockSignals(True)
+ self.ui.txtPSDisableOptions.blockSignals(True)
+ self.ui.chkPSInputWidget.blockSignals(True)
+ self.ui.txtPSInputWidget.blockSignals(True)
+ self.ui.chkPSDescription.blockSignals(True)
+ self.ui.txtPSDescription.blockSignals(True)
+ self.ui.chkPSValueList.blockSignals(True)
+ self.ui.txtPSValueList.blockSignals(True)
+ self.ui.chkPSValueRange.blockSignals(True)
+ self.ui.txtPSValueRange.blockSignals(True)
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.blockSignals(True)
+
+ self.ui.chkArraySupported.blockSignals(True)
+ self.ui.chkDictionarySupported.blockSignals(True)
+ self.ui.chkSupportOnlyArrays.blockSignals(True)
+ self.ui.chkAllowMultipleConnections.blockSignals(True)
+ self.ui.chkChangeTypeOnConnection.blockSignals(True)
+ self.ui.chkRenamingEnabled.blockSignals(True)
+ self.ui.chkDynamic.blockSignals(True)
+ self.ui.chkAlwaysPushDirty.blockSignals(True)
+ self.ui.chkStorable.blockSignals(True)
+ self.ui.chkAllowAny.blockSignals(True)
+ self.ui.chkDictionaryElementSupported.blockSignals(True)
+
+ def unblockPinSignals(self):
+ self.ui.chkPSSupportedDataTypes.blockSignals(False)
+ self.ui.txtPSSupportedDataTypes.blockSignals(False)
+ self.ui.chkPSConstraint.blockSignals(False)
+ self.ui.txtPSConstraint.blockSignals(False)
+ self.ui.chkPSStructConstraint.blockSignals(False)
+ self.ui.txtPSStructConstraint.blockSignals(False)
+ #self.ui.chkPSEnableOptions.blockSignals(False)
+ #self.ui.txtPSEnableOptions.blockSignals(False)
+ self.ui.chkPSDisableOptions.blockSignals(False)
+ self.ui.txtPSDisableOptions.blockSignals(False)
+ self.ui.chkPSInputWidget.blockSignals(False)
+ self.ui.txtPSInputWidget.blockSignals(False)
+ self.ui.chkPSDescription.blockSignals(False)
+ self.ui.txtPSDescription.blockSignals(False)
+ self.ui.chkPSValueList.blockSignals(False)
+ self.ui.txtPSValueList.blockSignals(False)
+ self.ui.chkPSValueRange.blockSignals(False)
+ self.ui.txtPSValueRange.blockSignals(False)
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.blockSignals(False)
+
+ self.ui.chkArraySupported.blockSignals(False)
+ self.ui.chkDictionarySupported.blockSignals(False)
+ self.ui.chkSupportOnlyArrays.blockSignals(False)
+ self.ui.chkAllowMultipleConnections.blockSignals(False)
+ self.ui.chkChangeTypeOnConnection.blockSignals(False)
+ self.ui.chkRenamingEnabled.blockSignals(False)
+ self.ui.chkDynamic.blockSignals(False)
+ self.ui.chkAlwaysPushDirty.blockSignals(False)
+ self.ui.chkStorable.blockSignals(False)
+ self.ui.chkAllowAny.blockSignals(False)
+ self.ui.chkDictionaryElementSupported.blockSignals(False)
+
+ def initializePinData(self):
+ self.blockPinSignals()
+ self.ui.chkPSSupportedDataTypes.setChecked(False)
+ self.ui.txtPSSupportedDataTypes.setText("")
+ self.ui.chkPSConstraint.setChecked(False)
+ self.ui.txtPSConstraint.setText("")
+ self.ui.chkPSStructConstraint.setChecked(False)
+ self.ui.txtPSStructConstraint.setText("")
+ #self.ui.chkPSEnableOptions.setChecked(False)
+ #self.ui.txtPSEnableOptions.setText("")
+ self.ui.chkPSDisableOptions.setChecked(False)
+ self.ui.txtPSDisableOptions.setText("")
+ self.ui.chkPSInputWidget.setChecked(False)
+ self.ui.txtPSInputWidget.setText("")
+ self.ui.chkPSDescription.setChecked(False)
+ self.ui.txtPSDescription.setText("")
+ self.ui.chkPSValueList.setChecked(False)
+ self.ui.txtPSValueList.setText("")
+ self.ui.chkPSValueRange.setChecked(False)
+ self.ui.txtPSValueRange.setText("")
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.setText("")
+
+ self.ui.chkArraySupported.setChecked(False)
+ self.ui.chkDictionarySupported.setChecked(False)
+ self.ui.chkSupportOnlyArrays.setChecked(False)
+ self.ui.chkAllowMultipleConnections.setChecked(False)
+ self.ui.chkChangeTypeOnConnection.setChecked(False)
+ self.ui.chkRenamingEnabled.setChecked(False)
+ self.ui.chkDynamic.setChecked(False)
+ self.ui.chkAlwaysPushDirty.setChecked(False)
+ self.ui.chkStorable.setChecked(False)
+ self.ui.chkAllowAny.setChecked(False)
+ self.ui.chkDictionaryElementSupported.setChecked(False)
+ self.unblockPinSignals()
+
+ def writepindata(self):
+ self.functiondict = {}
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSSupportedDataTypes_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["SUPPORTED_DATA_TYPES"] = self.ui.txtPSSupportedDataTypes.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSSupportedDataTypes_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["SUPPORTED_DATA_TYPES"] = self.ui.txtPSSupportedDataTypes.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSConstraint_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["CONSTRAINT"] = self.ui.txtPSConstraint.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSConstraint_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["CONSTRAINT"] = self.ui.txtPSConstraint.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSStructConstraint_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["STRUCT_CONSTRAINT"] = self.ui.txtPSStructConstraint.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSStructConstraint_stateChanged(self):
+ a = self.ui.txtPSStructConstraint.text()
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["STRUCT_CONSTRAINT"] = self.ui.txtPSStructConstraint.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSDisableOptions_stateChanged(self):
+ a = self.ui.chkPSDisableOptions.isChecked()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.setFunctionDirty()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSDisableOptions_textChanged(self):
+ b = self.ui.txtPSDisableOptions.text()
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSInputWidget_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["INPUT_WIDGET_VARIANT"] = self.ui.txtPSInputWidget.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSInputWidget_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["INPUT_WIDGET_VARIANT"] = self.ui.txtPSInputWidget.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSDescription_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DESCRIPTION"] = self.ui.txtPSDescription.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSDescription_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DESCRIPTION"] = self.ui.txtPSDescription.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSValueList_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_LIST"] = self.ui.txtPSValueList.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSValueList_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_LIST"] = self.ui.txtPSValueList.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSValueRange_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_RANGE"] = self.ui.txtPSValueRange.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSValueRange_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_RANGE"] = self.ui.txtPSValueRange.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSDraggerSteps_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DRAGGER_STEPS"] = self.ui.txtPSDraggerSteps.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSDraggerSteps_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DRAGGER_STEPS"] = self.ui.txtPSDraggerSteps.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkArraySupported_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["ArraySupported"] = self.ui.chkArraySupported.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkDictionarySupported_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DictSupported"] = self.ui.chkDictionarySupported.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkSupportOnlyArrays_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["SupportsOnlyArrays"] = self.ui.chkSupportOnlyArrays.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkAllowMultipleConnections_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["AllowMultipleConnections"] = self.ui.chkAllowMultipleConnections.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkChangeTypeOnConnection_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["ChangeTypeOnConnection"] = self.ui.chkChangeTypeOnConnection.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkRenamingEnabled_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["RenamingEnabled"] = self.ui.chkRenamingEnabled.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkDynamic_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["Dynamic"] = self.ui.chkDynamic.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkAlwaysPushDirty_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["AlwaysPushDirty"] = self.ui.chkAlwaysPushDirty.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkStorable_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["Storable"] = self.ui.chkStorable.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkAllowAny_stateChanged(self, value):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["AllowAny"] = self.ui.chkAllowAny.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkDictionaryElementSupported_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DictElementSupported"] = self.ui.chkDictionaryElementSupported.isChecked()
+
+ def pinDictCheck(self, Direction, PinName):
+ if Direction not in self.pindefs:
+ self.pindefs[Direction] = {}
+
+ if PinName not in self.pindefs["Inputs"] and PinName not in self.pindefs["Outputs"]:
+ self.pindefs[Direction][PinName] = {}
+ return(True)
+ else:
+ return(False)
+
+ #@QtCore.pyqtSlot()
+ def on_cmdUpOrderFInputPin_clicked(self):
+ order = self.pindefs["Inputs"][self.selectedPinName]["Order"]
+
+ if order > 1:
+ for key, data in self.pindefs["Inputs"].items():
+ if data["Order"] == order - 1:
+ data["Order"] += 1
+ self.pindefs["Inputs"][self.selectedPinName]["Order"] -= 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdDownOrderFInputPin_clicked(self):
+ order = self.pindefs["Inputs"][self.selectedPinName]["Order"]
+
+ if order < len(self.pindefs["Inputs"]):
+ for key, data in self.pindefs["Inputs"].items():
+ if data["Order"] == order + 1:
+ data["Order"] -= 1
+ self.pindefs["Inputs"][self.selectedPinName]["Order"] += 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdAddFInputPin_clicked(self):
+ self.setFunctionDirty()
+ if self.ui.txtFunctionName.text() != "":
+
+ newPinName = "NewPinName"
+ pinnum = ""
+ counter = 0
+ while not self.pinDictCheck("Inputs", newPinName + pinnum):
+ counter += 1
+ pinnum = "_" + str(counter)
+
+ newPinName = newPinName + pinnum
+
+ self.pindefs["Inputs"][newPinName]["DataType"] = 'AnyPin'
+ self.pindefs["Inputs"][newPinName]["DefaultValue"] = 'None'
+ self.pindefs["Inputs"][newPinName]["Order"] = len(self.pindefs["Inputs"])
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdRemoveFInputPin_clicked(self):
+ self.setFunctionDirty()
+ order = self.pindefs["Inputs"][self.selectedPinName]["Order"]
+ self.pindefs["Inputs"].pop(self.selectedPinName)
+ for key, data in self.pindefs["Inputs"].items():
+ if data["Order"] > order:
+ data["Order"] -= 1
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdUpOrderFOutputPin_clicked(self):
+ order = self.pindefs["Outputs"][self.selectedPinName]["Order"]
+
+ if order > 1:
+ for key, data in self.pindefs["Outputs"].items():
+ if data["Order"] == order - 1:
+ data["Order"] += 1
+ self.pindefs["Outputs"][self.selectedPinName]["Order"] -= 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdDownOrderFOutputPin_clicked(self):
+ order = self.pindefs["Outputs"][self.selectedPinName]["Order"]
+
+ if order < len(self.pindefs["Outputs"]):
+ for key, data in self.pindefs["Outputs"].items():
+ if data["Order"] == order + 1:
+ data["Order"] -= 1
+ self.pindefs["Outputs"][self.selectedPinName]["Order"] += 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdAddFOutputPin_clicked(self):
+ self.setFunctionDirty()
+ if self.ui.txtFunctionName.text() != "":
+ newPinName = "NewPinName"
+ pinnum = ""
+ counter = 0
+ while not self.pinDictCheck("Outputs", newPinName + pinnum):
+ counter += 1
+ pinnum = "_" + str(counter)
+
+ newPinName = newPinName + pinnum
+
+ self.pinDictCheck("Outputs", "NewPinName")
+ self.pindefs["Outputs"][newPinName]["DataType"] = 'AnyPin'
+ self.pindefs["Outputs"][newPinName]["DefaultValue"] = 'None'
+ self.pindefs["Outputs"][newPinName]["Order"] = len(self.pindefs["Outputs"])
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdRemoveFOutputPin_clicked(self):
+ self.setFunctionDirty()
+ order = self.pindefs["Outputs"][self.selectedPinName]["Order"]
+ self.pindefs["Outputs"].pop(self.selectedPinName)
+ for key, data in self.pindefs["Outputs"].items():
+ if data["Order"] > order:
+ data["Order"] -= 1
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+
+ def setFunctionDirty(self):
+ self.ui.cmdSaveFunction.setEnabled(True)
+ self.ui.cmdSaveFunction.setVisible(True)
+ self.ui.cmdSaveFunction.setStyleSheet("background-color: red")
+
+ #@QtCore.pyqtSlot()
+ def on_cmdSaveFunction_clicked(self):
+ self.ui.cmdSaveFunction.setEnabled(False)
+ self.ui.cmdSaveFunction.setVisible(False)
+ self.writeFunction()
+ self.writeFile()
+
+ #@QtCore.pyqtSlot()
+ def on_cmdCreateNewFunction_clicked(self):
+ functionName = self.ui.txtFunctionName.text()
+ if functionName not in self.functiondict:
+
+ self.setFunctionDirty()
+ self.functiondict[functionName] = {}
+
+ self.functiondict[functionName]["PinDefs"] = {}
+ self.functiondict[functionName]["Implement"] = {}
+ self.functiondict[functionName]["Definition"] = {}
+ self.functiondict[functionName]["MetaData"] = {}
+ self.functiondict[functionName]["Code"] = ["Pass"]
+
+ #self.initializeform
+ self.initializePinData
+
+ #self.loadAllFunctions()
+ #self.loadPinTable(deft)
+ else:
+ print("Function Name Taken")
+
+ def renamePin(self, mydict, old_key, new_key):
+ mydict[new_key] = mydict.pop(old_key)
+
+ def loadAllFunctions(self):
+ try:
+ f = open(self.workingFile, "r")
+ for lineitem in f:
+ if lineitem.find("def ") != -1:
+ if lineitem[8] != "_":
+ classnamestart = 7
+ classnameend = lineitem.find("(")
+ if self.workingFile.find(lineitem[classnamestart+1:classnameend]) == -1:
+ functionname = lineitem[classnamestart+1:classnameend]
+ self.functiondict[functionname] = {}
+ self.loadFunctionProperties(self.workingFile, functionname)
+ except:
+ pass
+
+ def initToolDict(self, defname):
+ tooldict = {}
+ tooldict["Author"] = ""
+ tooldict["CopyrightYear"] = ""
+ tooldict["RevisionHistory"] = ""
+ tooldict["Filename"] = ""
+ tooldict["Name"] = ""
+ tooldict["Description"] = ""
+ tooldict["Category"] = ""
+ tooldict["Keywords"] = ""
+ tooldict["ToolTip"] = ""
+ tooldict["KeyboardShortCut"] = ""
+ tooldict["SmallIcon"] = ""
+ tooldict["MediumIcon"] = ""
+ tooldict["LargeIcon"] = ""
+ tooldict["ResourceFolder"] = ""
+ tooldict["PythonCode"] = ""
+ tooldict["PyflowFile"] = ""
+
+ self.tooldict[defname] = tooldict
+ @QtCore.Slot()
+ def on_cmdSaveCommand_clicked(self):
+ filename = self.ui.txtCommandFileName.text()
+ # Write File
+ fname = filename
+ filefullpath = Packages.__path__[0] + "\\"
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ filefullpath = os.path.join(filefullpath, selectedpackage)
+ filefullpath = os.path.join(filefullpath, "Tools")
+ filefullpath = os.path.join(filefullpath, fname)
+
+ Filename = filename.split(".")[0]
+ classline = "class %s(ShelfTool):\n" % (Filename)
+ classline += "#doc string\n\n"
+ classline += " def __init__(self):\n"
+ classline += " super(%s, self).__init__()\n" % (Filename)
+
+ with open(filefullpath, 'w') as f:
+ self.addCopyright(f)
+ # TODO add the prestuff
+ # Revision History
+
+ f.write("from PyFlow.UI.Tool.Tool import ShelfTool\n")
+ f.write("from PyFlow.Core.Common import Direction\n")
+ f.write("from qtpy import QtGui\n")
+ f.write("from PyFlow.Packages.%s.Tools import RESOURCES_DIR\n\n" % (selectedpackage))
+
+ f.write(classline)
+ for variable, code in self.tooldict.items():
+ if code != {}:
+ if variable != "do":
+ f.write(" @staticmethod\n")
+ f.write(" " + "def " + variable + "():\n")
+ f.write(" " + "return " + code["Code"] + ":\n")
+
+ def writeFunction(self):
+
+ defname = self.ui.txtFunctionName.text()
+ implementdata = []
+ defindata = []
+ defoutdata = []
+
+ print("Pin Def", self.pindefs)
+ idata = "@IMPLEMENT_NODE(returns=("
+ # @IMPLEMENT_NODE(returns=('AnyPin', None, {
+ outCounter = 0
+ if "out" in self.pindefs["Outputs"]:
+ data = self.pindefs["Outputs"]["out"]
+ idata += "\'" + data["DataType"].strip() + "\'"
+ if data["DefaultValue"] is not None:
+ idata += ", " + data["DefaultValue"]
+ else:
+ idata += ", None"
+
+ pinspecifiers = ""
+ if "SUPPORTED_DATA_TYPES" in data:
+ pinspecifiers += "PinSpecifiers.SUPPORTED_DATA_TYPES: " + data["SUPPORTED_DATA_TYPES"] + ", "
+ if "CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.CONSTRAINT: " + data["CONSTRAINT"] + ", "
+ if "STRUCT_CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.STRUCT_CONSTRAINT: " + data["STRUCT_CONSTRAINT"] + ", "
+ if "ENABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.ENABLED_OPTIONS: " + data["ENABLED_OPTIONS"] + ", "
+ if "DISABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.DISABLED_OPTIONS: " + data["DISABLED_OPTIONS"] + ", "
+ if "INPUT_WIDGET_VARIANT" in data:
+ pinspecifiers += "PinSpecifiers.INPUT_WIDGET_VARIANT: " + data["INPUT_WIDGET_VARIANT"] + ", "
+ if "DESCRIPTION" in data:
+ pinspecifiers += "PinSpecifiers.DESCRIPTION: \'" + data["DESCRIPTION"] + "\', "
+ if "VALUE_LIST" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_LIST: " + data["VALUE_LIST"] + ", "
+ if "VALUE_RANGE" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_RANGE: " + data["VALUE_RANGE"] + ", "
+ if "DRAGGER_STEPS" in data:
+ pinspecifiers += "PinSpecifiers.DRAGGER_STEPS: " + data["DRAGGER_STEPS"] + ", "
+ if pinspecifiers != "":
+ idata += ", {" + pinspecifiers[:-2] + "})"
+
+ idata += ", "
+
+ implementdata.append(idata)
+ mdata = ""
+ if "CATEGORY" in self.functiondict[defname]["MetaData"]:
+ mdata += "NodeMeta.CATEGORY: " + self.functiondict[defname]["MetaData"]["CATEGORY"] + ", "
+ if "KEYWORDS" in self.functiondict[defname]["MetaData"]:
+ mdata += "NodeMeta.KEYWORDS: " + self.functiondict[defname]["MetaData"]["KEYWORDS"] + ", "
+ if "CACHE_ENABLED" in self.functiondict[defname]["MetaData"]:
+ mdata += "NodeMeta.CACHE_ENABLED: " + self.functiondict[defname]["MetaData"]["CACHE_ENABLED"] + ", "
+
+ implementdata.append("meta={" + mdata[:-2] + "})")
+
+ for pin, data in self.pindefs["Inputs"].items():
+
+ didata = pin + "=("
+ didata += "\'" + data["DataType"].strip() + "\'"
+ if data["DefaultValue"] is not None:
+ didata += ", " + data["DefaultValue"]
+ else:
+ didata += ", None"
+
+ pinspecifiers = ""
+ if "SUPPORTED_DATA_TYPES" in data:
+ pinspecifiers += "PinSpecifiers.SUPPORTED_DATA_TYPES: " + data["SUPPORTED_DATA_TYPES"] + ", "
+ if "CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.CONSTRAINT: " + data["CONSTRAINT"] + ", "
+ if "STRUCT_CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.STRUCT_CONSTRAINT: " + data["STRUCT_CONSTRAINT"] + ", "
+ if "ENABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.ENABLED_OPTIONS: " + data["ENABLED_OPTIONS"] + ", "
+ if "DISABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.DISABLED_OPTIONS: " + data["DISABLED_OPTIONS"] + ", "
+ if "INPUT_WIDGET_VARIANT" in data:
+ pinspecifiers += "PinSpecifiers.INPUT_WIDGET_VARIANT: " + data["INPUT_WIDGET_VARIANT"] + ", "
+ if "DESCRIPTION" in data:
+ pinspecifiers += "PinSpecifiers.DESCRIPTION: " + data["DESCRIPTION"] + ", "
+ if "VALUE_LIST" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_LIST: " + data["VALUE_LIST"] + ", "
+ if "VALUE_RANGE" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_RANGE: " + data["VALUE_RANGE"] + ", "
+ if "DRAGGER_STEPS" in data:
+ pinspecifiers += "PinSpecifiers.DRAGGER_STEPS: " + data["DRAGGER_STEPS"] + ", "
+ if pinspecifiers != "":
+ didata += ", {" + pinspecifiers[:-2] + "}"
+
+ didata += ")"
+ defindata.append(didata)
+
+ outCounter = -1
+ for pin, data in self.pindefs["Outputs"].items():
+ ddata = ""
+ outCounter += 1
+ if outCounter != 0:
+ ddata += pin + "=("
+ ddata += "REF, ("
+
+ ddata += "\'" + data["DataType"].strip() + "\'"
+ if data["DefaultValue"] is not None:
+ ddata += ", " + data["DefaultValue"]
+ else:
+ ddata += ", None"
+
+ pinspecifiers = ""
+ if "SUPPORTED_DATA_TYPES" in data:
+ pinspecifiers += "PinSpecifiers.SUPPORTED_DATA_TYPES: " + data["SUPPORTED_DATA_TYPES"] + ", "
+ if "CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.CONSTRAINT: " + data["CONSTRAINT"] + ", "
+ if "STRUCT_CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.STRUCT_CONSTRAINT: " + data["STRUCT_CONSTRAINT"] + ", "
+ if "ENABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.ENABLED_OPTIONS: " + data["ENABLED_OPTIONS"] + ", "
+ if "DISABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.DISABLED_OPTIONS: " + data["DISABLED_OPTIONS"] + ", "
+ if "INPUT_WIDGET_VARIANT" in data:
+ pinspecifiers += "PinSpecifiers.INPUT_WIDGET_VARIANT: " + data["INPUT_WIDGET_VARIANT"] + ", "
+ if "DESCRIPTION" in data:
+ pinspecifiers += "PinSpecifiers.DESCRIPTION: " + "\'" + data["DESCRIPTION"] + "\', "
+ if "VALUE_LIST" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_LIST: " + data["VALUE_LIST"] + ", "
+ if "VALUE_RANGE" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_RANGE: " + data["VALUE_RANGE"] + ", "
+ if "DRAGGER_STEPS" in data:
+ pinspecifiers += "PinSpecifiers.DRAGGER_STEPS: " + data["DRAGGER_STEPS"] + ", "
+ if pinspecifiers != "":
+ ddata += ", {" + pinspecifiers[:-2] + "}"
+
+ if outCounter == len(self.pindefs["Outputs"]) - 1:
+ ddata = ddata + ")))"
+ else:
+ ddata += "))"
+
+ defoutdata.append(ddata)
+
+ self.functiondict[defname]["Implement"] = implementdata
+ self.functiondict[defname]["Definition"] = defindata + defoutdata
+
+ def writeFile(self):
+ # Write Code
+ filename = self.ui.txtFunctionFileName.text()
+ # Write File
+ '''wizardfile = Wizards.__path__[0] + "\\"
+ filefullpath = os.path.join(wizardfile, "FunctionHeader.txt")
+ codestart = ""
+
+ f = open(filefullpath, "r")
+ for lineitem in f:
+ codestart += lineitem'''
+
+ fname = filename
+ filefullpath = Packages.__path__[0] + "\\"
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ filefullpath = os.path.join(filefullpath, selectedpackage)
+ filefullpath = os.path.join(filefullpath, "FunctionLibraries")
+ filefullpath = os.path.join(filefullpath, fname)
+
+ Filename = filename.split(".")[0]
+ classline = "class %s(FunctionLibraryBase):\n" % (Filename)
+ classline += "#doc string\n\n"
+ classline += " def __init__(self, packageName):\n"
+ classline += " super(%s, self).__init__(packageName)\n" % (Filename)
+
+ with open(filefullpath, 'w') as f:
+ f.write(self.intro)
+ f.write(classline)
+ for variable, code in self.functiondict.items():
+ if code != {}:
+ f.write(" @staticmethod\n")
+ for iitems in code["Implement"]:
+
+ if "meta" in iitems:
+ if len(iitems["meta"]):
+ f.write(" " + iitems + "\n")
+ else:
+ f.write(" " + iitems + "\n")
+ if code["Definition"] == []:
+ f.write(" " + "def " + variable + "():\n")
+ else:
+ for row, ditems in enumerate(code["Definition"]):
+ if row == len(code["Definition"])-1:
+ defend = ":"
+ else:
+ defend = ", "
+
+ if row == 0:
+ f.write(" " + "def " + variable + "("+ ditems + defend + "\n")
+ else:
+ f.write(" " + ditems + defend + "\n")
+
+ #f.write(code["CodeDescription"])
+ for codeline in code["Code"]:
+ f.write(codeline)
+
+ print("Done")
+
+
+ def on_tblFInputPins_Changed(self, index):
+ print("changed")
+ row = self.ui.tblFInputPins.selectionModel().currentIndex().row()
+ column = self.ui.tblFInputPins.selectionModel().currentIndex().column()
+ value = self.ui.tblFInputPins.selectionModel().currentIndex().data(0)
+ print("IP On Change", row, column, value)
+
+ def on_tblFInputPins_clicked(self, index):
+ '''print(index)
+ print("Click", self.ui.tblFInputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.ui.tblFInputPins.model().data(index, QtCore.Qt.UserRole))'''
+ #self.ReferenceNumber_id = self.ui.tblFInputPins.model().index(index.row(), 0).data()
+
+ self.selectedPinDir = "Inputs"
+ self.selectedPinName = self.ui.tblFInputPins.model().index(index.row(), 0).data()
+
+ if self.selectedPinName in self.pindefs[self.selectedPinDir]:
+ self.selectedPinData = self.pindefs[self.selectedPinDir][self.selectedPinName]
+ self.loadPinData(self.selectedPinData)
+
+ def on_tblFInputPins_doubleclicked(self, index):
+ print("Double Click", self.ui.tblFInputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.ui.tblFInputPins.model().data(index, QtCore.Qt.UserRole))
+
+ def on_tblFOutputPins_Changed(self, index):
+ row = self.ui.tblFOutputPins.selectionModel().currentIndex().row()
+ column = self.ui.tblFOutputPins.selectionModel().currentIndex().column()
+ value = self.ui.tblFOutputPins.selectionModel().currentIndex().data(0)
+ print("IP On Change", row, column, value)
+
+ def on_tblFOutputPins_clicked(self, index):
+ print("Click", self.ui.tblFOutputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.ui.tblFOutputPins.model().data(index, QtCore.Qt.UserRole))
+ self.ReferenceNumber_id = self.ui.tblFOutputPins.model().index(index.row(), 0).data()
+ #self.loadProjectdata()
+
+ self.selectedPinDir = "Outputs"
+ self.selectedPinName = self.ui.tblFOutputPins.model().index(index.row(), 0).data()
+
+ if self.selectedPinName in self.pindefs[self.selectedPinDir]:
+ self.selectedPinData = self.pindefs[self.selectedPinDir][self.selectedPinName]
+ self.loadPinData(self.selectedPinData)
+
+ def on_tblFOutputPins_doubleclicked(self, index):
+ print("Double Click", self.tblFOutputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.tblFOutputPins.model().data(index, QtCore.Qt.UserRole))
+
+ def loadPinData(self, data):
+ self.initializePinData()
+ self.blockPinSignals()
+ if "SUPPORTED_DATA_TYPES" in data:
+ self.ui.chkPSSupportedDataTypes.setChecked(True)
+ self.ui.txtPSSupportedDataTypes.setText(data["SUPPORTED_DATA_TYPES"])
+ if "CONSTRAINT" in data:
+ self.ui.chkPSConstraint.setChecked(True)
+ self.ui.txtPSConstraint.setText(data["CONSTRAINT"])
+ if "STRUCT_CONSTRAINT" in data:
+ self.ui.chkPSStructConstraint.setChecked(True)
+ self.ui.txtPSStructConstraint.setText(data["STRUCT_CONSTRAINT"])
+ if "ENABLED_OPTIONS" in data:
+ options = data["ENABLED_OPTIONS"].split("|")
+ for option in options:
+ if "ArraySupported" in option: #: Pin can hold array data structure
+ self.ui.chkArraySupported.setChecked(True)
+ if "DictSupported" in option: #: Pin can hold dict data structure
+ self.ui.chkDictionarySupported.setChecked(True)
+ if "SupportsOnlyArrays" in option: #: Pin will only support other pins with array data structure
+ self.ui.chkSupportOnlyArrays.setChecked(True)
+ if "AllowMultipleConnections" in option: #: This enables pin to allow more that one input connection. See :func:`~PyFlow.Core.Common.connectPins`
+ self.ui.chkAllowMultipleConnections.setChecked(True)
+ if "ChangeTypeOnConnection" in option: #: Used by :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` to determine if it can change its data type on new connection.
+ self.ui.chkChangeTypeOnConnection.setChecked(True)
+ if "RenamingEnabled" in option: #: Determines if pin can be renamed
+ self.ui.chkRenamingEnabled.setChecked(True)
+ if "Dynamic" in option: #: Specifies if pin was created dynamically (during program runtime)
+ self.ui.chkDynamic.setChecked(True)
+ if "AlwaysPushDirty" in option: #: Pin will always be seen as dirty (computation needed)
+ self.ui.chkAlwaysPushDirty.setChecked(True)
+ if "Storable" in option: #: Determines if pin data can be stored when pin serialized
+ self.ui.chkStorable.setChecked(True)
+ if "AllowAny" in option: #: Special flag that allow a pin to be :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin`, which means non typed without been marked as error. By default a :py:class:`PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` need to be initialized with some data type, other defined pin. This flag overrides that. Used in lists and non typed nodes
+ self.ui.chkAllowAny.setChecked(True)
+ if "DictElementSupported" in option: #: Dicts are constructed with :class:`DictElement` objects. So dict pins will only allow other dicts until this flag enabled. Used in :class:`~PyFlow.Packages.PyFlowBase.Nodes.makeDict` node
+ self.ui.chkDictionaryElementSupported.setChecked(True)
+
+ if "DISABLED_OPTIONS" in data:
+ self.ui.chkPSDisableOptions.setChecked(True)
+ self.ui.txtPSDisableOptions.setText(data["DISABLED_OPTIONS"])
+ if "INPUT_WIDGET_VARIANT" in data:
+ self.ui.chkPSInputWidget.setChecked(True)
+ self.ui.txtPSInputWidget.setText(data["INPUT_WIDGET_VARIANT"])
+ if "DESCRIPTION" in data:
+ self.ui.chkPSDescription.setChecked(True)
+ self.ui.txtPSDescription.setText(data["DESCRIPTION"])
+ if "VALUE_LIST" in data:
+ self.ui.chkPSValueList.setChecked(True)
+ self.ui.txtPSValueList.setText(data["VALUE_LIST"])
+ if "VALUE_RANGE" in data:
+ self.ui.chkPSValueRange.setChecked(True)
+ self.ui.txtPSValueRange.setText(data["VALUE_RANGE"])
+ if "DRAGGER_STEPS" in data:
+ self.ui.chkPSDraggerSteps.setChecked(True)
+ self.ui.txtPSDraggerSteps.setText(data["DRAGGER_STEPS"])
+ self.unblockPinSignals()
+
+
+ def loadNodeProperties(self, filefullpath):
+ preamble = ""
+ precode = ""
+ readingimplementdata = -1
+ readingdefdata = 0
+ defdata = ""
+ codedata = []
+ eof = 0
+ defname = ""
+ code = ""
+ self.selectedNodeData = {}
+ self.selectedNodeData[self.selectedNodeDataName] = {}
+
+ addDecorator = ""
+
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ # Reading the parts of the code (Implement, Def, Code)
+ if lineitem.find("def ") != -1 or index == filesize:
+ if lineitem.find("_init_") != -1:
+ defname = "init"
+
+ else:
+ defnamestart = 7
+ defnameend = lineitem.find("(")
+
+ if "@" in codedata[-1]:
+ addDecorator = codedata[-1].replace(" ", "")
+ codedata.pop()
+ self.selectedNodeData[self.selectedNodeDataName][defname] = codedata
+ #Starts the Next Group of code
+ codedata = [addDecorator]
+ else:
+ addDecorator = ""
+ self.selectedNodeData[self.selectedNodeDataName][defname] = codedata
+ codedata = []
+
+ defname = lineitem[defnamestart + 1:defnameend]
+
+ if lineitem.find("class ") != -1:
+ preamble = precode
+ base = ""
+ codedata = []
+ else:
+ codedata.append(lineitem.replace(" ","").replace("\n",""))
+ except:
+ pass
+
+
+ def loadTableProperties(self, filefullpath):
+ className = None
+ tableName = None
+ baseName = None
+
+ fieldName = None
+ fieldDataType = None
+ fieldSize = None
+ fieldOptions = None
+
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ if lineitem.find("__tablename__") != -1:
+ print(lineitem)
+ if lineitem.find("class ") != -1:
+ print(lineitem)
+ codedata = []
+ if className:
+ pass
+ else:
+ codedata.append(lineitem.replace(" ","").replace("\n",""))
+
+ if className:
+ fieldName = None
+ fieldDataType = None
+ fieldSize = None
+ fieldOptions = None
+ except:
+ pass
+
+ def parseNodePins(self):
+ defname = self.selectedNodeDataName
+ self.selectedNodeData[defname]["PinDefs"] = {}
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"] = {}
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"] = {}
+ inputPinOrder = 0
+ outputPinOrder = 0
+ try:
+ for item in self.selectedNodeData[defname]["init"]:
+ if "=" in item:
+ phrase = item.split("=")
+ pinName = phrase[0].replace("self.", "")
+
+ pinOptionsStart = phrase[1].find("(")
+ pinOptionEnd = phrase[1].find(")")
+ if pinOptionsStart != -1:
+ pinOptions = phrase[1][pinOptionsStart:pinOptionEnd]
+
+ '''def createInputPin(self, pinName, dataType, defaultValue=None, callback=None,
+ structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group=""):'''
+
+ '''createOutputPin(self, pinName, dataType, defaultValue=None, structure=StructureType.Single, constraint=None,
+ structConstraint=None, supportedPinDataTypes=[], group=""):'''
+ pinOptionList = pinOptions.split(",")
+
+ pinData = {}
+
+ pinName = pinOptionList[0].replace("\"","").replace("\'","").replace("(","").replace(")","").replace(" ","").strip()
+ pinData["DataType"] = pinOptionList[1][2:-1].replace("\"","").replace("\'","").strip()
+
+ for row, options in enumerate(pinOptionList):
+ if row > 2:
+ if "=" not in options:
+ if row == 2:
+ pinData["DefaultValue"] = options.replace("\"","").replace("\'","").strip()
+ if row == 3:
+ pinData["foo"] = options.replace("\"","").replace("\'","").strip()
+ else:
+ moreoptions = options.split("=")
+ pinData[moreoptions[0]] = moreoptions[0]
+ else:
+ if "headerColor" in phrase[0]:
+ self.selectedNodeData[defname]["HeaderColor"] = phrase[1].strip()
+ self.ui.txtNodeHeaderColor.setText(self.selectedNodeData[defname]["HeaderColor"])
+
+ if 'createOutputPin' in phrase[1]:
+ outputPinOrder += 1
+ self.selectedNodeData[defname]
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pinName] = {}
+ pinData["Order"] = outputPinOrder
+
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pinName] = pinData
+ if 'createInputPin' in phrase[1]:
+ inputPinOrder += 1
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pinName] = {}
+ pinData["Order"] = inputPinOrder
+
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pinName] = pinData
+
+ except:
+ print(self.selectedNodeDataName, "Fail")
+
+ print(self.selectedNodeData[defname])
+ value = ""
+ if "description" in self.selectedNodeData[defname]:
+ for items in self.selectedNodeData[defname]["description"]:
+ if "return" in items or len(value):
+ value += items.replace("return","").replace("\'","").strip()
+
+ self.ui.txtNodeDescription.setText(value)
+
+ if "category" in self.selectedNodeData[defname]:
+ for items in self.selectedNodeData[defname]["category"]:
+ if "return" in items:
+ value = items.replace("return","").replace("\'","").strip()
+ self.ui.txtNodeCategory.setText(value)
+
+ if "keywords" in self.selectedNodeData[defname]:
+ for items in self.selectedNodeData[defname]["keywords"]:
+ if "return" in items:
+ value = items.replace("return","").replace("\'","").replace("[","").replace("]","").strip()
+ self.ui.txtNodeKeyWords.setText(value)
+
+ self.loadNodePinTable()
+
+ def loadNodePinTable(self):
+ # InputPin
+ defname = self.selectedNodeDataName
+ pindatatypemodel = QtGui.QStandardItemModel(0, 2)
+
+ for index, key in enumerate(self.pinDict):
+ pindatatypemodel.setItem(index, 0, QtGui.QStandardItem(str(index)))
+ pindatatypemodel.setItem(index, 1, QtGui.QStandardItem(key))
+ pindatatypemodel.setItem(index+1, 0, QtGui.QStandardItem(str(index+1)))
+ pindatatypemodel.setItem(index+1, 1, QtGui.QStandardItem('ExecPin'))
+
+ if "PinDefs" in self.selectedNodeData[defname]:
+ inputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ inputPinList = []
+ if "Inputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for rowcount1, pindata in enumerate(self.selectedNodeData[defname]["PinDefs"]["Inputs"]):
+ row = int(self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["Order"]) - 1
+ inputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+ DataTypeValue = ""
+ if "DataType" in self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["DataType"]))
+ DataTypeValue = self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["DataType"]
+ inputPinList.append(DataTypeValue)
+ if "DefaultValue" in self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["DefaultValue"]))
+
+ inputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ outputPinList = []
+ outputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ if "Outputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for rowcount2, pindata in enumerate(self.selectedNodeData[defname]["PinDefs"]["Outputs"]):
+ row = int(self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["Order"]) - 1
+ DataTypeValue = ""
+ if rowcount2 == 0:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem("out"))
+ else:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+
+ if "DataType" in self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["DataType"]))
+ DataTypeValue = self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["DataType"]
+ outputPinList.append(DataTypeValue)
+
+ if "DefaultValue" in self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["DefaultValue"]))
+
+ outputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ self.ui.tblNInputPins.setModel(inputpinlistmodel)
+ self.ui.tblNOutputPins.setModel(outputpinlistmodel)
+
+ if "Inputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for row, data in enumerate(self.selectedNodeData[defname]["PinDefs"]["Inputs"]):
+ self.ui.tblNInputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+
+ c.setValue(self.selectedNodeData[defname]["PinDefs"]["Inputs"][data]["DataType"], 1)
+ i = self.ui.tblNInputPins.model().index(row, 1)
+ # c.currentIndexChanged2[dict].connect(self.on_lstPinSettings_cmbTableChanged)
+ self.ui.tblNInputPins.setIndexWidget(i, c)
+
+ if "Outputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for row, data in enumerate(self.selectedNodeData[defname]["PinDefs"]["Outputs"]):
+ self.ui.tblNOutputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+ c.setValue(self.selectedNodeData[defname]["PinDefs"]["Outputs"][data]["DataType"], 1)
+ i = self.ui.tblNOutputPins.model().index(row, 1)
+ # c.currentIndexChanged2[dict].connect(self.on_lstTableSettings_cmbTableChanged)
+ self.ui.tblNOutputPins.setIndexWidget(i, c)
+
+ # self.ui.tblNInputPins.resizeColumnsToContents()
+ # self.ui.tblNOutputPins.resizeColumnsToContents()
+
+ # self.ui.tblNInputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+ # self.ui.tblNOutputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+
+ #self.initializePinData()
+
+ '''for z in curlystuff2.split(","):
+ itemdata = z.strip().split(":")
+ #print(itemdata[0], itemdata[1].strip())'''
+
+
+ def loadPinProperties(self, filefullpath):
+
+ previousitem = ""
+ implementdata = ""
+ readingimplementdata = -1
+ readingdefdata = 0
+ defdata = ""
+ codedata = []
+ eof = 0
+ defname = ""
+ code = ""
+ codedescription = ""
+ NestDict = {}
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ if lineitem.find("class") != -1:
+ self.intro = code
+ precode = code
+ code += lineitem
+ codedata.append(lineitem)
+ #print(lineitem)
+ if lineitem.find("super") != -1:
+ code = ""
+ codedata = []
+
+ if lineitem.find("@staticmethod") != -1 or index == filesize-1:
+ readingdefdata = 0
+ if precode.find("@staticmethod") != -1:
+ NestDict = {}
+ implement2 = implementdata
+ NestDict["Implement"] = implement2.replace("@staticmethod", "")
+ NestDict["Definition"] = defdata
+ NestDict["CodeDescription"] = codedescription
+ NestDict["Code"] = codedata[:-1]
+ self.pindict[defname] = NestDict
+
+ #self.parseFunctionFile(defname)
+ break
+ else:
+ implementdata = ""
+
+ if lineitem.find("def ") != -1 and lineitem.find(" " + defname) != -1:
+ defnamestart = 7
+ defnameend = lineitem.find("(")
+ defname = lineitem[defnamestart + 1:defnameend]
+ readingdefdata = 1
+
+ if readingdefdata == 1:
+ if lineitem.find("def ") != -1:
+ lineitem = lineitem[defnameend+1:]
+ readingimplementdata = 0
+ defdata += lineitem.strip()
+ if defdata[-1] == ":":
+ readingdefdata = 0
+ codedata = []
+ except:
+ pass
+
+
+ def loadPinData(self, filefullpath):
+ codenotes = 0
+ importlist = []
+ importstart = 0
+ classstart = 0
+ super = 0
+ staticmethod = []
+ definition = []
+ codedata = []
+
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ codedata.append(lineitem)
+
+ if lineitem.find("import") != -1:
+ importlist.append(index)
+
+ if lineitem.find("class") != -1:
+ classstart = index
+
+ if lineitem.find("def") != -1:
+ definition.append(index)
+
+ defCount = len(definition)
+ for count, defitem in enumerate(definition):
+ line = codedata[defitem]
+ if count == defCount-1:
+ endCodeBlock = len(codedata)
+ else:
+ endCodeBlock = definition[count+1]-1
+
+ if codedata[defitem - 1].find("@staticmethod") != -1:
+ staticmethod = True
+ else:
+ staticmethod = False
+
+ if codedata[defitem].find("__init__") != -1:
+ if codedata[defitem].find("super") != -1:
+ pass
+
+ if codedata[defitem].find("toolTip") != -1:
+ for row in range(defitem,endCodeBlock):
+ line2 = codedata[row]
+ if codedata[row].find("return") != -1:
+ tooltip = codedata[row][15:]
+ self.ui.txtCommandToolTip.setText(tooltip)
+
+ if codedata[defitem].find("getIcon") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ getIcon = codedata[row][15:]
+ self.ui.txtCommandgetIcon.setText(getIcon)
+
+ if codedata[defitem].find("name") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ name = codedata[row][15:]
+ self.ui.txtCommandName.setText(name)
+
+ if codedata[defitem].find("do") != -1:
+ code = ""
+ for codeline in codedata[defitem:endCodeBlock]:
+ code += codeline
+ self.ui.txtCommandCode.setText(code)
+
+ @QtCore.Slot()
+ def on_cmdCreatePackage_clicked(self):
+ packageRoot = Packages.__path__[0]
+ packagename = self.ui.txtPackageName.text()
+ if packagename == "":
+ return
+ packageFolderPath = os.path.join(packageRoot, packagename)
+ filepath = self.createfolder(packageFolderPath)
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackageResource.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Resource"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackageCommands.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Commands"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackageFunctions.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Functions"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackageWidgets.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Widgets"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackageUI.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "UI"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackagePins.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Pins"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackageTools.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Tools"))
+ self.createfolder(os.path.join(filepath, "res"))
+
+ with open(filepath, "__init__.py", "w") as f:
+ self.addCopyright(f)
+ f.write("import os\n")
+ f.write("RESOURCES_DIR = os.path.dirname(os.path.realpath(__file__)) + \"/res/\"\n")
+
+ if self.ui.chkPackageExporters.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Exporters"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ if self.ui.chkPackageFactories.isChecked():
+ filepath = self.createfolder(os.path.join(packageFolderPath, "Factories"))
+ self.createfile(os.path.join(filepath, "__init__.py"))
+
+ def createfolder(self, folder_name):
+ if not os.path.exists(folder_name):
+ os.makedirs(folder_name)
+ return folder_name
+
+ def createfile(self, file_name):
+ if not os.path.exists(file_name):
+ open(file_name, 'a').close()
+
+ @QtCore.Slot()
+ def on_cmdUpdateInit_clicked(self):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filefullpath = os.path.join(packagepath, "__init__.py")
+
+ fileList = []
+ for file in os.listdir(packagepath):
+ fileList.append(file)
+
+ with open(filefullpath, 'w') as f:
+ self.addIntro(f)
+
+ self.defineFiles(f, "Pins")
+ self.defineFiles(f, "FunctionLibraries")
+ self.defineFiles(f, "Nodes")
+ self.defineFiles(f, "Tools")
+ self.defineFiles(f, "Exporters")
+
+
+ self.defineFiles2(f, "def ", "Factories")
+ self.defineFiles2(f, "class ", "PrefsWidgets")
+
+ self.defineFoo(f, "FunctionLibraries", "_FOO_LIBS")
+ self.defineDict(f, "Nodes", "_NODES")
+ self.defineDict(f, "Pins", "_PINS")
+
+ self.defineOrderedDict(f, "Tools", "_TOOLS")
+ self.defineOrderedDict(f, "Exporters", "_EXPORTERS")
+ self.defineOrderedDict2(f, "PrefsWidgets", "_PREFS_WIDGETS")
+
+ self.createBasePackage(f)
+
+ def loadInit(self, f):
+ '''["Class Imports", "Pins", "Functions", "Nodes", "Factories", "Prefs widgets",
+ "Foo Dict", "Node Dict", "Pin Dict", "Toolbar Dict", "Export", "Preferred Widgets",
+ "Base Package"]'''
+
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ #selectedpackage = self.ui.lstPackages.model().index(self.ui.listPackages.row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ initfile = "__init__.py"
+ filename = os.path.join(packagepath, initfile)
+
+ category = "Introduction"
+ initDict = {}
+ initDict[category] = []
+ try:
+ f = open(filename, "r")
+
+ for index, lineitem in enumerate(f):
+ if "# [" in lineitem:
+ start = lineitem.find("[")
+ stop = lineitem.find("]")
+ category = lineitem[start+1:stop]
+ initDict[category] = [lineitem.replace("\n", "")]
+ else:
+ initDict[category].append(lineitem.replace("\n", ""))
+ except:
+ pass
+
+ for row, item in enumerate(initDict):
+ print(item)
+ for line in initDict[item]:
+ print(" ", line)
+
+ def addCopyright(self, f):
+ f.write("## Copyright " + self.ui.txtCopyrightAuthor.text() + " " + self.ui.dteCopyrightYear.value())
+ f.write(self.ui.txtCopyright.text())
+
+ def addIntro(self, f):
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+
+ f.write("\'\'\'%s\n" % (selectedpackage))
+ f.write("\'\'\'\n")
+ f.write("PACKAGE_NAME = \'%s\'\n" % (selectedpackage))
+
+ f.write("# [Class Imports]\n")
+ f.write("from collections import OrderedDict\n")
+ f.write("from PyFlow.UI.UIInterfaces import IPackage\n")
+
+ f.write("\n")
+
+ def defineFiles(self, f, foldername):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ #f.write("%s = {" % (category))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ #from PyFlow.Packages.PyFlowBase.Pins.AnyPin import AnyPin
+ f.write("from PyFlow.Packages.%s.%s.%s import %s\n"% (selectedpackage, foldername, file2, file2))
+ f.write("\n")
+
+ def defineFiles2(self, f, search, foldername):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+
+ f2 = open(os.path.join(filepath, file), "r")
+ for index, lineitem in enumerate(f2):
+
+ if lineitem[:len(search)] == search:
+ classnamestart = len(search)
+ classnameend = lineitem.find("(")
+ if self.workingFile.find(lineitem[classnamestart + 1:classnameend]) == -1:
+ functionname = lineitem[classnamestart :classnameend]
+ f.write("from PyFlow.Packages.%s.%s.%s import %s\n" % (selectedpackage, foldername, file2, functionname))
+ break
+ #from PyFlow.Packages.PyFlowBase.Pins.AnyPin import AnyPin
+
+ f.write("\n")
+
+ def defineFoo(self, f, foldername, category):
+ "_FOO_LIBS, _NODES, _PINS"
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = {\n" % (category))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f.write(" %s.__name__: %s(PACKAGE_NAME),\n"% (file2, file2))
+ f.write("}\n")
+ f.write("\n")
+
+ def defineDict(self, f, foldername, category):
+ "_FOO_LIBS, _NODES, _PINS"
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = {\n" % (category))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f.write(" %s.__name__: %s,\n"% (file2, file2))
+ f.write("}\n")
+ f.write("\n")
+
+ def defineOrderedDict(self, f, foldername, category):
+ "_TOOLS, _EXPORTERS, _PREFS_WIDGETS"
+ '''_TOOLS = OrderedDict()
+ _TOOLS[CompileTool.__name__] = CompileTool'''
+
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = OrderedDict()\n" % category)
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f.write("%s[%s.__name__] = %s\n"% (category, file2, file2))
+ f.write("\n")
+
+ def defineOrderedDict2(self, f, foldername, category):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+ search = "class "
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = OrderedDict()\n" % category)
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f2 = open(os.path.join(filepath, file), "r")
+ for index, lineitem in enumerate(f2):
+
+ if lineitem[:len(search)] == search:
+ classnamestart = len(search)
+ classnameend = lineitem.find("(")
+ if self.workingFile.find(lineitem[classnamestart + 1:classnameend]) == -1:
+ functionname = lineitem[classnamestart:classnameend]
+
+ f.write("%s[\"%s\"] = %s\n"% (category, file2, functionname))
+ break
+ f.write("\n")
+
+ def createBasePackage(self, f):
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+
+ f.write("# [%s Package]\n" % (selectedpackage))
+ f.write("class %s(IPackage):\n" % (selectedpackage))
+ f.write(" \"\"\"%s package\n"% (selectedpackage))
+ f.write(" \"\"\"\n\n")
+
+ f.write(" def __init__(self):\n")
+ f.write(" super(%s, self).__init__()\n\n" % (selectedpackage))
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetExporters():\n")
+ f.write(" return _EXPORTERS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetFunctionLibraries():\n")
+ f.write(" return _FOO_LIBS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetNodeClasses():\n")
+ f.write(" return _NODES\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetPinClasses():\n")
+ f.write(" return _PINS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetToolClasses():\n")
+ f.write(" return _TOOLS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def UIPinsFactory():\n")
+ f.write(" return createUIPin\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def UINodesFactory():\n")
+ f.write(" return createUINode\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def PinsInputWidgetFactory():\n")
+ f.write(" return getInputWidget\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def PrefsWidgets():\n")
+ f.write(" return _PREFS_WIDGETS")
+
+ def onDone(self):
+ # if we are here, everything is correct
+ self.accept()
\ No newline at end of file
diff --git a/PyFlow/UI/Forms/PackageBuilder/PackageBuilder.ui b/PyFlow/UI/Forms/PackageBuilder/PackageBuilder.ui
new file mode 100644
index 000000000..4f4e8b449
--- /dev/null
+++ b/PyFlow/UI/Forms/PackageBuilder/PackageBuilder.ui
@@ -0,0 +1,2235 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 1086
+ 750
+
+
+
+ Form
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
-
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 1
+
+
+
+
+ -
+
+
+ 0
+
+
+ 0
+
+
-
+
+
-
+
+
+ -
+
+
+ New Package
+
+
+
+ -
+
+
+ Update Package
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Package
+
+
+
-
+
+
+ Resource Folder
+
+
+ true
+
+
+
+ -
+
+
+ Commands (Output Only)
+
+
+ true
+
+
+
+ -
+
+
+ Functions (Package Commands)
+
+
+ true
+
+
+
+ -
+
+
+ Nodes (I/O Commands)
+
+
+ true
+
+
+
+ -
+
+
+ Widgets
+
+
+ true
+
+
+
+ -
+
+
+ UI
+
+
+ true
+
+
+
+ -
+
+
+ Pins
+
+
+ true
+
+
+
+ -
+
+
+ Tools
+
+
+ true
+
+
+
+ -
+
+
+ Exporters
+
+
+ true
+
+
+
+ -
+
+
+ Factories
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 387
+
+
+
+
+
+
+
+
+ Tools
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ New Command
+
+
+
+ -
+
+
+ Save Command
+
+
+
+
+
+ -
+
+
+ 10
+
+
-
+
+
+ 0
+
+
-
+
+
-
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ Singleton
+
+
+
+ -
+
+
+ Keywords
+
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ Filename
+
+
+
+ -
+
+
+ Keyboard ShortCut
+
+
+
+ -
+
+
+ -
+
+
+ Tool Tip
+
+
+
+ -
+
+
+ Category
+
+
+
+ -
+
+
+
+ 16777215
+ 50
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ DockTool
+
+
+
-
+
+
-
+
+
+ On Show
+
+
+
+ -
+
+
+ -
+
+
+ Refresh
+
+
+
+ -
+
+
+ -
+
+
+ Default Dock Area
+
+
+
+ -
+
+
+
+
+
+
+
+
+ ShelfTool
+
+
+
+
+ Form
+
+
+
+
+
+
+ -
+
+
+ Icon
+
+
+
+ -
+
+
+ 10
+
+
-
+
+
+ Small Icon
+
+
+
+ -
+
+
+ Large Icon
+
+
+
+ -
+
+
+
+ 80
+ 80
+
+
+
+
+ 80
+ 80
+
+
+
+
+
+
+
+ -
+
+
+
+ 250
+ 16777215
+
+
+
+
+ -
+
+
+ Medium Icon
+
+
+
+ -
+
+
+
+ 250
+ 16777215
+
+
+
+
+ -
+
+
+
+ 60
+ 60
+
+
+
+
+ 60
+ 60
+
+
+
+
+
+
+
+ -
+
+
+
+ 250
+ 16777215
+
+
+
+
+ -
+
+
+
+ 40
+ 40
+
+
+
+
+ 40
+ 40
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Resource Folder
+
+
+
+ -
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+ -
+
+
+ 1
+
+
+
+ PyFlow
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ New File
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
-
+
+
+ Workspace
+
+
+
+
+
+
+
+
+
+
+
+ Python
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 7
+
+
+
+
+
+
+
+
+ Functions
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ New Command
+
+
+
+ -
+
+
+ Save Command
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+ 75
+ 16777215
+
+
+
+ Create
+
+
+
+ -
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ Create
+
+
+
+ -
+
+
+ -
+
+
+ Filename
+
+
+
+ -
+
+
+ Function Name
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ 4
+
+
+
+ Pins
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Pin Specifiers
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+ Input Widget Variant
+
+
+
+ -
+
+
+ Value List
+
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ Supported Data Types
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Structured Constraint
+
+
+
+ -
+
+
+ Dragger Steps
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Value Range
+
+
+
+ -
+
+
+ Disabled Options
+
+
+
+ -
+
+
+ -
+
+
+ Contraint
+
+
+
+ -
+
+
+ -
+
+
+ Node Type
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Pin Options
+
+
+ -
+
+
-
+
+
+ Supported Only Arrays
+
+
+
+ -
+
+
+ Renaming Enabled
+
+
+
+ -
+
+
+ Allow Multiple Connections
+
+
+
+ -
+
+
+ Dynamic
+
+
+
+ -
+
+
+ Change Type On Connection
+
+
+
+ -
+
+
+ Allow Any
+
+
+
+ -
+
+
+ Storable
+
+
+
+ -
+
+
+ Dictionary Element Supported
+
+
+
+ -
+
+
+ Array Supported
+
+
+
+ -
+
+
+ Dictionary Supported
+
+
+
+ -
+
+
+ Always Push Dirty
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+ Meta
+
+
+ -
+
+
-
+
+
+ Category
+
+
+
+ -
+
+
+ Keywords
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Cache Enabled
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 512
+
+
+
+
+
+
+
+
+ Implement
+
+
+ -
+
+
+
+
+
+
+ Definition
+
+
+ -
+
+
+
+
+
+
+ Code
+
+
+ -
+
+
+
+ 16777215
+ 40
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ false
+
+
+ Save
+
+
+
+
+
+
+
+ Nodes
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ New Command
+
+
+
+ -
+
+
+ Save Command
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Category
+
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ Header Color
+
+
+
+ -
+
+
+ -
+
+
+ Header
+
+
+
+ -
+
+
+ Filename
+
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ New File
+
+
+
+ -
+
+
+ -
+
+
+ Key Words
+
+
+
+ -
+
+
+
+ 16777215
+ 50
+
+
+
+
+
+
+ -
+
+
+ Definition Order
+
+
+
+ -
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ 1
+
+
+
+ Pins
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Pin Options
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ Input Structure
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ Data Types
+
+
+
+ -
+
+
+ Add Type Hint
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ Widgets
+
+
+
+
+ -190
+ 20
+ 822
+ 26
+
+
+
+
+ 0
+
+ -
+
+
+ New Command
+
+
+
+ -
+
+
+ Save Command
+
+
+
+
+
+
+
+
+ UI
+
+
+
+
+ Pins
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ New Command
+
+
+
+ -
+
+
+ Save Command
+
+
+
+
+
+ -
+
+
-
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ Value Pin
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Default Value
+
+
+
+ -
+
+
+ -
+
+
+ New
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Exec Pin
+
+
+
+
+
+ -
+
+
-
+
+
+ Supported Datatype
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Pin Data Type Hint
+
+
+
+
+
+ -
+
+
-
+
+
+ Color
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ Default Value
+
+
+
+ -
+
+
+
+
+ -
+
+
+ 2
+
+
+
+ Pin Connect
+
+
+
-
+
+
+
+
+
+
+ Pin Disconnect
+
+
+ -
+
+
+
+
+
+
+ Process Data
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ Factories
+
+
+
+
+ -200
+ 20
+ 822
+ 26
+
+
+
+
+ 0
+
+ -
+
+
+ New Command
+
+
+
+ -
+
+
+ Save Command
+
+
+
+
+
+
+
+
+ Author
+
+
+ -
+
+
-
+
+
+ David James Lario
+
+
+
+ -
+
+
+
+ 0
+ 0
+ 0
+ 2023
+ 9
+ 14
+
+
+
+
+ 2023
+ 9
+ 14
+
+
+
+ yyyy
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Apache
+
+
+
+ -
+
+
+ PushButton
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 200
+
+
+
+ ## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ Add Revision
+
+
+
+
+
+ -
+
+
+ Revision History
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PyFlow/UI/Forms/PackageBuilder/__init__.py b/PyFlow/UI/Forms/PackageBuilder/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/PyFlow/UI/Forms/RibbonBuilder/RibbonBuilder.ui b/PyFlow/UI/Forms/RibbonBuilder/RibbonBuilder.ui
new file mode 100644
index 000000000..c4499eabe
--- /dev/null
+++ b/PyFlow/UI/Forms/RibbonBuilder/RibbonBuilder.ui
@@ -0,0 +1,3624 @@
+
+
+ ProjectManager
+
+
+
+ 0
+ 0
+ 1225
+ 954
+
+
+
+ Project Manager
+
+
+
+
+
+
+ -
+
+
+ 4
+
+
+
+ Import
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ -
+
+
-
+
+
+ 0
+
+
-
+
+
+ Add Function
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+ Sessions
+
+
+ -
+
+
+ 2
+
+
+
+ Bases
+
+
+
-
+
+
+ -
+
+
+ 0
+
+
-
+
+
-
+
+
+ Engine
+
+
+
+ -
+
+
+ -
+
+
+ Abstract View
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Table Args
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Meta Data
+
+
+
+ -
+
+
+ Default Fields
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Timestamp
+
+
+
+ -
+
+
+ Add Fields
+
+
+
+ -
+
+
+ -
+
+
+ Name
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 59
+ 20
+
+
+
+
+
+
+
+
+ Sessions
+
+
+ -
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
+ Database Type
+
+
+
+ -
+
+
+ Login
+
+
+
+ -
+
+
+ Password
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Schema
+
+
+
+ -
+
+
+ -
+
+
+ Local Bind Part
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ SSH Tunnel Forwarder
+
+
+
+ -
+
+
-
+
+
+ IP Address
+
+
+
+ -
+
+
+ -
+
+
+ Login
+
+
+
+ -
+
+
+ -
+
+
+ Password
+
+
+
+ -
+
+
+ -
+
+
+ Remote Bind Address
+
+
+
+ -
+
+
+ -
+
+
+ Port
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+ Tables
+
+
+ -
+
+
-
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ New Table
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
-
+
+
+ 0
+
+
-
+
+
+ New Field
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+ Record Group
+
+
+ -
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ Create New
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ ID Field
+
+
+
+ -
+
+
+ Display Table
+
+
+
+ -
+
+
+ Create New Item
+
+
+
+ -
+
+
+ Create Dictionary
+
+
+
+ -
+
+
+ List for Display
+
+
+
+ -
+
+
+ List Clicked
+
+
+
+ -
+
+
+ Block Signals
+
+
+
+ -
+
+
+ Clear Records
+
+
+
+ -
+
+
+ Create/Save
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+
+
+
+ -
+
+
+ Group Name
+
+
+
+ -
+
+
+ Form
+
+
+
+ -
+
+
+ Parent Group
+
+
+
+ -
+
+
+ Abbreviation
+
+
+
+ -
+
+
+ Session
+
+
+
+ -
+
+
+ Master Table
+
+
+
+ -
+
+
+ Table Name
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ Add
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+ Widgets
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ New Item
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+ Item
+
+
+
+ -
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Values
+
+
+
-
+
+
+ 10
+
+
-
+
+
+ Set Value
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ Add
+
+
+
+ -
+
+
+
+ 16777215
+ 75
+
+
+
+
+ -
+
+
+ Get Value
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ Add
+
+
+
+ -
+
+
+
+ 16777215
+ 75
+
+
+
+
+ -
+
+
+ Update
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ Add
+
+
+
+ -
+
+
+
+ 16777215
+ 75
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Form Items
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 1
+
+
+
+ Table View
+
+
+
-
+
+
+
+
+
+
+ Tree View
+
+
+ -
+
+
+ Advanced Tree
+
+
+
+ -
+
+
+ Refresh
+
+
+
+ -
+
+
+
+
+
+
+
+
+ 0
+
+ -
+
+
+ 0
+
+
-
+
+
+ New Item
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Button
+
+
+
+ -
+
+
+ ComboBox
+
+
+
+ -
+
+
+ Spin Box
+
+
+
+ -
+
+
+ DateTime
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ TreeView
+
+
+
+ -
+
+
+ Table
+
+
+
+ -
+
+
+ List
+
+
+
+ -
+
+
+ Check Box
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 630
+ 1216
+
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ Item Name
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ UnLocked
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Data Source
+
+
+
-
+
+
-
+
+
+ Data Group
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ New
+
+
+
+
+
+ -
+
+
+ Session
+
+
+
+ -
+
+
+ -
+
+
+ Table
+
+
+
+ -
+
+
+ -
+
+
+ Field
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ false
+
+
+
+ 150
+ 16777215
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Widget Information
+
+
+
-
+
+
-
+
+
+ Widget Type
+
+
+
+ -
+
+
+ -
+
+
+ Widget Name
+
+
+
+ -
+
+
+ -
+
+
+ Label
+
+
+
+ -
+
+
+ -
+
+
+ Filter Check Box
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Widget Data Source
+
+
+
-
+
+
-
+
+
+ Query Record
+
+
+
+ -
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ -
+
+
+ Database Type
+
+
+
+ -
+
+
+ -
+
+
+ View Order
+
+
+
+ -
+
+
+ -
+
+
+ Reference Session
+
+
+
+ -
+
+
+ -
+
+
+ Reference Table
+
+
+
+ -
+
+
+ -
+
+
+ Reference Field
+
+
+
+ -
+
+
+ -
+
+
+ Join Variable
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Database
+
+
+
+ -
+
+
+ Database Value
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Data Access
+
+
+
-
+
+
-
+
+
+ Set String
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ false
+
+
+
+ 200
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ Default Value
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ Restore Default
+
+
+
+
+
+ -
+
+
+ Clear Widget
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Get Value
+
+
+
+ -
+
+
+ Update Value
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 200
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 200
+ 16777215
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Tree View
+
+
+
-
+
+
-
+
+
+ Master Table
+
+
+
+ -
+
+
+ false
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ -
+
+
+ Master Session
+
+
+
+ -
+
+
+ false
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ -
+
+
+ Descriptor Table
+
+
+
+ -
+
+
+ -
+
+
+ Item Table
+
+
+
+ -
+
+
+ Session
+
+
+
+ -
+
+
+ Session Name
+
+
+
+ -
+
+
+ Tree Items
+
+
+
+ -
+
+
+ Header
+
+
+
+ -
+
+
+ Generated Session
+
+
+
+ -
+
+
+ Generated Tree
+
+
+
+ -
+
+
+ Component Table
+
+
+
+ -
+
+
+ Primary Group
+
+
+
+ -
+
+
+ Secondary Group
+
+
+
+ -
+
+
+ -
+
+
+ false
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Branch Builder Table
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form Functions
+
+
+ -
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+ 300
+ 16777215
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ CheckBox
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ Function Template
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ New Function
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+
+
+
+
+
+
+ 0
+
+ -
+
+
+ 0
+
+
-
+
+
+
+ 16777215
+ 200
+
+
+
+
+ -
+
+
+
+ 16777215
+ 200
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+
+ 0
+ 100
+
+
+
+ Output
+
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+
-
+
+
-
+
+
+ -
+
+
+ 1
+
+
+ 12.000000000000000
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ Session
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Table
+
+
+
+ -
+
+
+ -
+
+
+ Field
+
+
+
+ -
+
+
+ -
+
+
+ Record Group
+
+
+
+ -
+
+
+ -
+
+
+ Form
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ true
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Widget
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ AutoSave
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ Save
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TreeView Designer
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ true
+
+
+ 1
+
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ Primary
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 75
+ 16777215
+
+
+
+ Settings
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ true
+
+
+ 1
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+
+ 75
+ 16777215
+
+
+
+ Secondary
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+
+
+
+ -
+
+
+ QAbstractItemView::ExtendedSelection
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ 2
+
+
+
+ Link Manager
+
+
+
-
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+ Export
+
+
+ -
+
+
+ 10
+
+
-
+
+
+ Location
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Internal
+
+
+
+ -
+
+
+ External
+
+
+
+
+
+ -
+
+
+ Primary Key
+
+
+
+ -
+
+
+ Tree Table
+
+
+
+ -
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ true
+
+
+ 1
+
+
+
+ -
+
+
+
+ 75
+ 0
+
+
+
+
+ 75
+ 16777215
+
+
+
+ Local Version
+
+
+ true
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ -
+
+
+ Tags
+
+
+
+ -
+
+
+ -
+
+
+ Session
+
+
+
+ -
+
+
+ Include Root
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Full Tree
+
+
+
+ -
+
+
+ Branch Only
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Create Linked Branch
+
+
+
+ -
+
+
+ Create Full Branch
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Save Branch
+
+
+
+ -
+
+
+ Create Branch
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Remove Branch
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Details
+
+
+ -
+
+
-
+
+
+ Down
+
+
+
+ -
+
+
+ Up
+
+
+
+ -
+
+
+ Refresh
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
+
+
+ -
+
+
+ Settings
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 35
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Item Table id
+
+
+
+ -
+
+
+ -
+
+
+ Order
+
+
+
+ -
+
+
+ -
+
+
+ Link
+
+
+
+ -
+
+
+ Tree ID
+
+
+
+ -
+
+
+ Secondary ID
+
+
+
+ -
+
+
+ -
+
+
+ Primary ID
+
+
+
+ -
+
+
+ Chart Color
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 35
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ Item id
+
+
+
+ -
+
+
+ Level
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 35
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ Parent ID
+
+
+
+ -
+
+
+ Table Parent ID
+
+
+
+ -
+
+
+ ItemTableType id
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Record ID
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 35
+ 16777215
+
+
+
+
+
+
+
+
+ -
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ Tree Path
+
+
+
+ -
+
+
+ -
+
+
+ Tags
+
+
+
+ -
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ MasterTable
+
+
+
+ -
+
+
+ QAbstractItemView::MultiSelection
+
+
+ 0
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+ QAbstractItemView::SingleSelection
+
+
+
+ -
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Add Descriptor
+
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Add Component
+
+
+
+
+
+ -
+
+
-
+
+
+ Descriptor
+
+
+
+ -
+
+
+ -
+
+
+ QAbstractItemView::MultiSelection
+
+
+ 0
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ ADD
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 500
+
+
+
+ QAbstractItemView::SingleSelection
+
+
+
+ -
+
+
-
+
+
+ Add Root
+
+
+
+ -
+
+
+ Add Adjacent
+
+
+
+ -
+
+
+ Add Sub
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Item
+
+
+
+ -
+
+
+ -
+
+
+ QAbstractItemView::MultiSelection
+
+
+ 0
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+ ADD
+
+
+
+
+
+ -
+
+
+ QAbstractItemView::MultiSelection
+
+
+
+ -
+
+
-
+
+
+ Add Adjacent
+
+
+
+ -
+
+
+ Add Sub
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Data Types
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ New Item
+
+
+
+ -
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ 10
+
+
-
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ QT
+
+
+
+ -
+
+
+ Python
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Add
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 75
+
+
+
+
+ -
+
+
+ SQLAlchemy
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Add
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 75
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Add
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 75
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+
+
+
+ false
+
+
+
+
+ toolBar
+
+
+ TopToolBarArea
+
+
+ false
+
+
+
+
+ Connect to Database
+
+
+
+
+ Connect...
+
+
+
+
+
+
+
diff --git a/PyFlow/UI/Forms/RibbonBuilder/__init__.py b/PyFlow/UI/Forms/RibbonBuilder/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/PyFlow/UI/Forms/RibbonBuilder/frmRibbonBuilder.py b/PyFlow/UI/Forms/RibbonBuilder/frmRibbonBuilder.py
new file mode 100644
index 000000000..d9e9dc4d7
--- /dev/null
+++ b/PyFlow/UI/Forms/RibbonBuilder/frmRibbonBuilder.py
@@ -0,0 +1,2847 @@
+from ProjectManager.Packages.ProgramBase.Database.dbFormBuilder import *
+
+#790410062-RC001
+#790410062-RT001
+
+import re
+import sys
+
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import *
+import os
+
+from qtpy import QtUiTools
+
+from ProjectManager import Packages
+basePackagePath = os.path.dirname(os.path.abspath(__file__))
+packageRoot = Packages.__path__[0]
+
+uiFile = os.path.join(basePackagePath, 'frmFormBuilder.ui')
+
+from ProjectManager.Database import DatabaseTools
+from ProjectManager.Packages.ProgramBase.Database.dbBase import *
+from ProjectManager.Packages.ProgramBase.Database.dbMasterTables import *
+from ProjectManager.Packages.ProgramBase.Database.dbAllLists import *
+from ProjectManager.Packages.ProgramBase.Database.dbAllTables import *
+
+from qtalchemy import *
+
+class FormBuilder(QMdiSubWindow):
+
+ def get_filenames(self, directory):
+ file_names = [] # List which will store all of the full filepaths.
+
+ # Walk the tree.
+ for root, directories, files in os.walk(directory):
+ for filename in files:
+ file_names.append(filename) # Add it to the list.
+
+ return file_names # Self-explanatory.
+
+ def get_filepaths(self, directory):
+ """
+ https://realpython.com/working-with-files-in-python/
+
+ This function will generate the file names in a directory
+ tree by walking the tree either top-down or bottom-up. For each
+ directory in the tree rooted at directory top (including top itself),
+ it yields a 3-tuple (dirpath, dirnames, filenames).
+
+ # List all files in a directory using os.listdir
+ basepath = 'my_directory/'
+ for entry in os.listdir(basepath):
+ if os.path.isfile(os.path.join(basepath, entry)):
+ print(entry)
+
+ # List all files in a directory using scandir()
+ basepath = 'my_directory/'
+ with os.scandir(basepath) as entries:
+ for entry in entries:
+ if entry.is_file():
+ print(entry.name)
+
+ from pathlib import Path
+
+ # List all files in directory using pathlib
+ basepath = Path('my_directory/')
+ files_in_basepath = (entry for entry in basepath.iterdir() if entry.is_file())
+ for item in files_in_basepath:
+ print(item.name)
+
+ """
+ file_paths = [] # List which will store all of the full filepaths.
+
+ # Walk the tree.
+ for root, directories, files in os.walk(directory):
+ for filename in files:
+ # Join the two strings in order to form the full filepath.
+ filepath = os.path.join(root, filename)
+ file_paths.append(filepath) # Add it to the list.
+
+ return file_paths # Self-explanatory.
+
+ def __init__(self, main, parent):
+ QMdiSubWindow.__init__(self)
+ self.ui = QtUiTools.QUiLoader().load(uiFile)
+ self.PackageName = "FormBuilder"
+
+ self.instanceDict = main.sqlRegister.instanceDict
+ self.baseDict = main.sqlRegister.instanceDict["ProgramBase"].base
+ self.engineDict = main.sqlRegister.instanceDict["ProgramBase"].engine
+
+ #region loadStuff
+ self.loadBaseList()
+ self.loadSessionList()
+ self.loadMasterTable()
+ #endregion
+
+ #region Initilize Sections
+ self.initRecordGroup()
+ self.init_Widgets()
+ self.init_FormItems()
+ self.init_FunctionTemplate()
+ self.init_FITTemplate()
+ #endregion
+
+ #region SetEvents
+ self.ui.tblTTable.clicked.connect(self.on_tblTTable_clicked)
+ self.ui.tblWidgets.clicked.connect(self.on_tblWidgets_clicked)
+ self.ui.tblRecordGroup.clicked.connect(self.on_tblRecordGroup_clicked)
+ self.ui.tblFormItems.clicked.connect(self.on_tblFormItems_clicked)
+ self.ui.tblFunctionTemplate.clicked.connect(self.on_tblFunctionTemplate_clicked)
+ #self.ui.txtFunctionTemplateCode.textChanged.connect(self.on_txtFunctionTemplateCode_textChanged)
+
+ #self.ui.cmbFITSession.currentIndexChanged.connect(self.on_cmbFITSession_currentIndexChanged)
+
+ self.ui.cmbFormName.currentIndexChanged.connect(self.on_cmbFormName_currentIndexChanged)
+ self.ui.cmdCreateForm.clicked.connect(self.on_cmdCreateForm_clicked)
+ self.ui.lstForms.clicked.connect(self.on_lstForms_clicked)
+
+ self.ui.txtFormPath.setText(basePackagePath)
+ self.ui.cmdCreateFormBaseClass.clicked.connect(self.on_cmdCreateFormBaseClass_clicked)
+ self.ui.cmdFTSaveCode.clicked.connect(self.on_cmdFTSaveCode_clicked)
+ self.ui.spnFunctionOrder.valueChanged.connect(self.on_spnFunctionOrder_valueChanged)
+
+ print("Initiated")
+ #endregion
+
+ def scanTables(self):
+ pass
+
+ def scanForm(self):
+ pass
+
+ def loadTableField(self, Base, table_fullname):
+
+ lario.get_fields_by_tablename(Base, table_fullname)
+
+
+ def loadBaseList(self):
+ qrybaselist = self.instanceDict["ProgramBase"].session.query(SessionBase.id, SessionBase.NameText) #139
+ self.ui.lstBases.setModel(QueryTableModel(qrybaselist))
+ self.ui.lstBases.setModelColumn(1)
+
+ def loadSessionList(self):
+ qrysessionlist = self.instanceDict["ProgramBase"].session.query(SessionNames.id, SessionNames.NameText) #139
+ self.ui.lstSession.setModel(QueryTableModel(qrysessionlist))
+ self.ui.lstSession.setModelColumn(1)
+
+ self.ui.lstTSessions.setModel(QueryTableModel(qrysessionlist))
+ self.ui.lstTSessions.setModelColumn(1)
+
+ def loadMasterTable(self, FilterName=None):
+
+ qryMasterTable = self.instanceDict["ProgramBase"].session.query(MasterTable.id, SessionBase.NameText.label("Base"), SessionNames.NameText.label("Session"), MasterTable.TableName)\
+ .join(SessionBase, MasterTable.Base == SessionBase.id)\
+ .join(SessionNames, MasterTable.Session == SessionNames.id)
+ if FilterName is not None:
+ qryMasterTable = qryMasterTable.filter(MasterTable.TableName.like("%" + str(FilterName) + "%"))
+
+ self.ui.tblTTable.setModel(QueryTableModel(qryMasterTable))
+
+ @QtCore.Slot()
+ def on_tblTTable_clicked(self):
+ data = {}
+ data["row"] = self.ui.tblTTable.selectionModel().currentIndex().row()
+ data["column"] = self.ui.tblTTable.selectionModel().currentIndex().column()
+ data["column"] = self.ui.tblTTable.selectionModel().currentIndex().column()
+ data["Value"] = self.ui.tblTTable.model().index(data["row"], data["column"], None).data()
+ data["Master_id"] = self.ui.tblTTable.model().index(data["row"], 0, None).data()
+
+ BaseName = self.ui.tblTTable.model().index(data["row"], 1, None).data()
+ table_fullname = self.ui.tblTTable.model().index(data["row"], 3, None).data()
+
+ fieldInformation = DatabaseTools.get_fields_by_tablename(bases[BaseName], table_fullname)
+
+ fieldModel = QStandardItemModel(0,2)
+
+ if fieldInformation is not None:
+ for row, key in enumerate(fieldInformation):
+ fieldModel.setItem(row, 0, QStandardItem(str(row)))
+ fieldModel.setItem(row, 1, QStandardItem(str(key)))
+ fieldModel.setItem(row, 2, QStandardItem(str(fieldInformation[key])))
+
+ fieldModel.setHeaderData(0, Qt.Horizontal, 'id', role=Qt.DisplayRole)
+ fieldModel.setHeaderData(1, Qt.Horizontal, 'Field', role=Qt.DisplayRole)
+ fieldModel.setHeaderData(2, Qt.Horizontal, 'DataType', role=Qt.DisplayRole)
+
+ self.ui.tblTFields.setModel(fieldModel)
+ self.ui.tblTFields.resizeColumnsToContents()
+ self.ui.tblTFields.setColumnWidth(0, 0)
+
+ # region Record Group
+ def initRecordGroup(self):
+ self.blockRGSignals(True)
+ self.on_RGTable_load()
+ self.on_RGForm_load()
+ self.on_RGParentGroup_load()
+ self.on_RGSession_load()
+ self.on_RGMasterTable_load()
+ self.blockRGSignals(False)
+
+ def on_RGTable_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroup)
+ self.ui.tblRecordGroup.setModel(QueryTableModel(qryRecord))
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroup.id, RecordGroup.Title)
+ self.ui.cmbRGParentGroup.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbRGParentGroup.setModelColumn(1)
+
+ def on_RGForm_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormInformation.id, FormInformation.FileName)
+ self.ui.cmbRGForm.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbRGForm.setModelColumn(1)
+
+ qryEquipmentProjectRecord = self.instanceDict["ProgramBase"].session.query(ProjectEquipment.id, Equipment.Manufacturer, Equipment.Model,
+ Equipment.Serial, Equipment.Unit) \
+ .join(Equipment, ProjectEquipment.equipment_id == Equipment.id) \
+
+ def on_RGParentGroup_load(self, Group_id=None):
+ if Group_id is not None:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroupParents.id, RecordGroup.Title, RecordGroupParents.ParentFieldID, RecordGroupParents.ChildFieldID)\
+ .join(RecordGroup, RecordGroupParents.ParentGroupID == RecordGroup.id)\
+ .filter(RecordGroupParents.RecordGroupID==Group_id)
+
+ self.ui.tblRGParentGroup.setModel(QueryTableModel(qryRecord))
+ else:
+ self.ui.tblRGParentGroup.setModel(None)
+
+ self.ui.tblRGParentGroup.resizeColumnsToContents()
+ self.ui.tblRGParentGroup.setColumnWidth(0, 0)
+
+ def on_RGSession_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(SessionNames.id, SessionNames.NameText)
+ self.ui.cmbRGSession.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbRGSession.setModelColumn(1)
+
+ def on_RGMasterTable_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable.id, MasterTable.TableName)
+ self.ui.cmbRGMasterTable.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbRGMasterTable.setModelColumn(1)
+
+ @QtCore.Slot()
+ def on_tblRecordGroup_clicked(self):
+ data = {}
+ data["row"] = self.ui.tblRecordGroup.selectionModel().currentIndex().row()
+ data["column"] = self.ui.tblRecordGroup.selectionModel().currentIndex().column()
+ data["column"] = self.ui.tblRecordGroup.selectionModel().currentIndex().column()
+ data["Value"] = self.ui.tblRecordGroup.model().index(data["row"], data["column"], None).data()
+ data["id"] = self.ui.tblRecordGroup.model().index(data["row"], 0, None).data()
+ self.ui.txtRG_id.setText(str(data["id"]))
+ self.initRGDict()
+ self.createRGDictFromDatabase(int(data["id"]))
+ self.loadRGForm(int(data["id"]))
+
+ @QtCore.Slot()
+ def on_cmdCreateNewRecordGroup_clicked(self):
+ self.initRGForm()
+ self.initRGDict()
+ self.instanceDict["ProgramBase"].session.begin()
+ qryRecordGroup = RecordGroup()
+ self.instanceDict["ProgramBase"].session.add(qryRecordGroup)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_RGTable_load()
+ self.ui.txtRG_id.setText(str(qryRecordGroup.id))
+
+ def createsaveRecordGroup(self, record_id = None):
+ currentdatetime = datetime.datetime.now().strftime('%Y:%m:%d %H:%M:%S')
+
+ if record_id is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryRecord = RecordGroup(Date_Created=currentdatetime,
+ Date_Modified=currentdatetime,
+ Date_Accessed=currentdatetime)
+ self.instanceDict["ProgramBase"].session.add(qryRecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroup).filter_by(id=record_id).first()
+ self.instanceDict["ProgramBase"].session.begin()
+
+ if self.RGDict["Title"] != "": qryRecord.Title = self.RGDict["Title"]
+ if self.RGDict["FormID"] != -1: qryRecord.FormID = self.RGDict["FormID"]
+ if self.RGDict["Abbreviation"] != "": qryRecord.Abbreviation = self.RGDict["Abbreviation"]
+ if self.RGDict["SessionID"] != -1: qryRecord.SessionID = self.RGDict["SessionID"]
+ if self.RGDict["TableID"] != -1: qryRecord.TableID = self.RGDict["TableID"]
+ '''qryRecord.IDWidgetName = self.RGDict["IDWidgetName"]
+ qryRecord.IDWidgetType = self.RGDict["IDWidgetType"]
+ qryRecord.NewItemWidgetName = self.RGDict["NewItemWidgetName"]
+ qryRecord.NewItemWidgetType = self.RGDict["NewItemWidgetType"]
+ qryRecord.CreateDictFunction = self.RGDict["CreateDictFunction"]'''
+
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_RGTable_load()
+ return qryRecord.id
+
+ def initRGForm(self):
+ self.blockRGSignals(True)
+ self.ui.txtRG_id.setText("")
+ self.ui.txtRGGroupName.setText("")
+ self.ui.cmbRGForm.setCurrentIndex(-1)
+ self.ui.cmbRGParentGroup.setCurrentIndex(-1)
+ self.ui.txtRGAbbreviation.setText("")
+ self.ui.cmbRGSession.setCurrentIndex(-1)
+ self.ui.cmbRGMasterTable.setCurrentIndex(-1)
+ self.ui.txtRGTableName.setText("")
+ self.ui.txtRGFieldID.setText("")
+ self.ui.txtRGDisplayTable.setText("")
+ self.ui.txtRGCreateNewItem.setText("")
+ self.ui.txtRGCreateDictionary.setText("")
+ self.ui.txtRGListForDisplay.setText("")
+ self.ui.txtRGListLClicked.setText("")
+ self.ui.txtRGBlockSignals.setText("")
+ self.ui.txtRGClearRecords.setText("")
+ self.ui.txtRGCreateSave.setText("")
+ self.blockRGSignals(False)
+
+ def loadRGForm(self, id):
+ self.initRGForm()
+ self.blockRGSignals(True)
+
+ qryRecordGroup = self.instanceDict["ProgramBase"].session.query(RecordGroup).filter_by(id=id).first()
+ self.ui.txtRG_id.setText(str(qryRecordGroup.id))
+ self.ui.txtRGGroupName.setText(qryRecordGroup.Title)
+
+ for row in range(self.ui.cmbRGForm.model().rowCount()):
+ if qryRecordGroup.FormID is not None:
+ if int(self.ui.cmbRGForm.model().index(row, 0, None).data()) == int(qryRecordGroup.FormID):
+ self.ui.cmbRGForm.setCurrentIndex(row)
+ break
+
+ self.ui.txtRGAbbreviation.setText(qryRecordGroup.Abbreviation)
+
+ for row in range(self.ui.cmbRGSession.model().rowCount()):
+ if qryRecordGroup.SessionID is not None:
+ if int(self.ui.cmbRGSession.model().index(row, 0, None).data()) == int(qryRecordGroup.SessionID):
+ self.ui.cmbRGSession.setCurrentIndex(row)
+ break
+
+ for row in range(self.ui.cmbRGMasterTable.model().rowCount()):
+ if qryRecordGroup.TableID is not None:
+ if int(self.ui.cmbRGMasterTable.model().index(row, 0, None).data()) == int(qryRecordGroup.TableID):
+ self.ui.cmbRGMasterTable.setCurrentIndex(row)
+ break
+
+ self.on_RGParentGroup_load(Group_id=qryRecordGroup.id)
+
+ self.ui.txtRGTableName.setText("")
+ self.ui.txtRGFieldID.setText("")
+ self.ui.txtRGDisplayTable.setText("")
+ self.ui.txtRGCreateNewItem.setText("")
+ self.ui.txtRGCreateDictionary.setText("")
+ self.ui.txtRGListForDisplay.setText("")
+ self.ui.txtRGListLClicked.setText("")
+ self.ui.txtRGBlockSignals.setText("")
+ self.ui.txtRGClearRecords.setText("")
+ self.ui.txtRGCreateSave.setText("")
+ self.updateFunctionNames()
+
+ self.blockRGSignals(False)
+
+ def initRGDict(self):
+ #id = int(self.ui.txtRG_id.text())
+ self.RGDict = {}
+ self.RGDict["id"] = -1
+ self.RGDict["RecordGroup"] = ""
+ self.RGDict["FormID"] = -1
+ self.RGDict["Abbreviation"] = ""
+ self.RGDict["ParentGroup"] = []
+ self.RGDict["SessionID"] = -1
+ self.RGDict["TableID"] = -1
+ self.RGDict["IDWidgetName"] = ""
+ self.RGDict["IDWidgetType"] = ""
+ self.RGDict["NewItemWidgetName"] = ""
+ self.RGDict["NewItemWidgetType"] = ""
+ self.RGDict["CreateDictFunction"] = ""
+
+ def createRGDictFromForm(self):
+ id = int(self.ui.self.ui.txtRG_id.text())
+ self.RGDict = {}
+ self.RGDict["id"] = id
+ self.RGDict["RecordGroup"] = self.ui.txtRGGroupName.text()
+ self.RGDict["FormID"] = self.ui.cmbRGForm
+ self.RGDict["Abbreviation"] = self.ui.txtRGAbbreviation
+ self.RGDict["SessionID"] = self.ui.cmbRGSession
+ '''self.RGDict["TableID"] = qryRecord.TableID
+ self.RGDict["IDWidgetName"] = qryRecord.IDWidgetName
+ self.RGDict["IDWidgetType"] = qryRecord.IDWidgetType
+ self.RGDict["NewItemWidgetName"] = qryRecord.NewItemWidgetName
+ self.RGDict["NewItemWidgetType"] = qryRecord.NewItemWidgetType
+ self.RGDict["CreateDictFunction"] = qryRecord.CreateDictFunction'''
+
+ def createRGDictFromDatabase(self, record_id):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroup).filter_by(id=record_id).first()
+
+ self.RGDict = {}
+ self.RGDict["id"] = id
+ self.RGDict["Title"] = qryRecord.Title
+ self.RGDict["FormID"] = qryRecord.FormID
+ self.RGDict["Abbreviation"] = qryRecord.Abbreviation
+ self.RGDict["SessionID"] = qryRecord.SessionID
+
+ self.RGDict["TableID"] = qryRecord.TableID
+ if qryRecord.TableID is not None:
+ qryMainTable = self.instanceDict["ProgramBase"].session.query(MasterTable).filter_by(id=qryRecord.TableID).first()
+ self.RGDict["Table"] = qryMainTable.TableName
+
+ self.RGDict["IDWidgetName"] = qryRecord.IDWidgetName
+ self.RGDict["IDWidgetType"] = qryRecord.IDWidgetType
+ self.RGDict["NewItemWidgetName"] = qryRecord.NewItemWidgetName
+ self.RGDict["NewItemWidgetType"] = qryRecord.NewItemWidgetType
+ self.RGDict["CreateDictFunction"] = qryRecord.CreateDictFunction
+
+ def blockRGSignals(self, Value):
+ self.ui.txtRG_id.blockSignals(Value)
+ self.ui.txtRGGroupName.blockSignals(Value)
+ self.ui.cmbRGForm.blockSignals(Value)
+ self.ui.cmbRGParentGroup.blockSignals(Value)
+ self.ui.txtRGAbbreviation.blockSignals(Value)
+ self.ui.cmbRGSession.blockSignals(Value)
+ self.ui.cmbRGMasterTable.blockSignals(Value)
+ self.ui.txtRGTableName.blockSignals(Value)
+ self.ui.txtRGFieldID.blockSignals(Value)
+ self.ui.txtRGDisplayTable.blockSignals(Value)
+ self.ui.txtRGCreateNewItem.blockSignals(Value)
+ self.ui.txtRGCreateDictionary.blockSignals(Value)
+ self.ui.txtRGListForDisplay.blockSignals(Value)
+ self.ui.txtRGListLClicked.blockSignals(Value)
+ self.ui.txtRGBlockSignals.blockSignals(Value)
+ self.ui.txtRGClearRecords.blockSignals(Value)
+ self.ui.txtRGCreateSave.blockSignals(Value)
+
+ def on_txtRGGroupName_textChanged(self, value):
+ self.RGDict["RecordGroup"] = value
+ capletters = re.findall('([A-Z])', value)
+ abbrv = ""
+ for i in capletters:
+ abbrv += i
+
+ self.ui.txtRGAbbreviation.setText(abbrv)
+
+ if self.ui.chkRGGroupName.isChecked():
+ self.loadRGTable()
+ else:
+ if self.ui.txtRG_id.text() != "":
+ record_id = int(self.ui.txtRG_id.text())
+ self.createsaveRecordGroup(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbRGForm_currentIndexChanged(self, index):
+ if self.ui.txtRG_id.text() != "":
+ self.RGDict["FormID"] = self.ui.cmbRGForm.model().index(index, 0, None).data()
+ print(self.ui.cmbRGForm.model().index(index, 0, None).data())
+ if self.ui.chkRGGroupName.isChecked():
+ self.loadRGTable()
+ else:
+ if self.ui.txtRG_id.text() != "":
+ record_id = int(self.ui.txtRG_id.text())
+ self.createsaveRecordGroup(record_id)
+
+ def on_txtRGAbbreviation_textChanged(self, value):
+ self.RGDict["Abbreviation"] = value
+ if self.ui.chkRGAbbreviation.isChecked():
+ self.loadRGTable()
+ else:
+ if self.ui.txtRG_id.text() != "":
+ record_id = int(self.ui.txtRG_id.text())
+ self.createsaveRecordGroup(record_id)
+
+ self.updateFunctionNames()
+
+ @QtCore.Slot()
+ def on_cmdAddRGParentGroup_clicked(self):
+ id = int(self.ui.txtRG_id.text())
+ if self.ui.cmbRGParentGroup.model().index(self.ui.cmbRGParentGroup.currentIndex(), 0, None).data() is not None:
+ parent_id = int(self.ui.cmbRGParentGroup.model().index(self.ui.cmbRGParentGroup.currentIndex(), 0, None).data())
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroupParents).filter_by(RecordGroupID=id).filter_by(ParentGroupID=parent_id).first()
+ if qryRecord is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryRecord = RecordGroupParents(RecordGroupID=id, ParentGroupID=parent_id)
+ self.instanceDict["ProgramBase"].session.add(qryRecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_RGParentGroup_load(Group_id=id)
+
+ @QtCore.Slot(int)
+ def on_cmbRGSession_currentIndexChanged(self, index):
+ if self.ui.txtRG_id.text() != "":
+ self.RGDict["SessionID"] = self.ui.cmbRGSession.model().index(index, 0, None).data()
+ if self.ui.chkRGSession.isChecked():
+ self.loadRGTable()
+ else:
+ if self.ui.txtRG_id.text() != "":
+ record_id = int(self.ui.txtRG_id.text())
+ self.createsaveRecordGroup(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbRGMasterTable_currentIndexChanged(self, index):
+ if self.ui.txtRG_id.text() != "":
+ self.RGDict["TableID"] = self.ui.cmbRGMasterTable.model().index(index, 0, None).data()
+ if self.ui.chkRGMasterTable.isChecked():
+ self.loadRGTable()
+ else:
+ if self.ui.txtRG_id.text() != "":
+ record_id = int(self.ui.txtRG_id.text())
+ self.createsaveRecordGroup(record_id)
+
+ def on_txtRGTableName_textChanged(self, value):
+ self.RGDict["Abbreviation"] = value
+ if self.ui.chkRGGroupName.isChecked():
+ self.loadRGTable()
+ else:
+ if self.ui.txtRG_id.text() != "":
+ record_id = int(self.ui.txtRG_id.text())
+ self.createsaveRecordGroup(record_id)
+
+ def on_chkRGFieldID_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGDisplayTable_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGCreateNewItem_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGCreateDictionary_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGListForDisplay_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGListLClicked_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGBlockSignals_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGClearRecords_stateChanged(self):
+ self.loadRGTable()
+
+ def on_chkRGCreateSave_stateChanged(self):
+ self.loadRGTable()
+
+ # customFunctions
+ def updateFunctionNames(self):
+
+ abbr = self.RGDict["Abbreviation"]
+ #createsaveRecordGroup
+ #on_tblRecordGroup_clicked
+ '''load_RGTable
+ load_RGForm
+ load_RGParentGroup
+ load_RGSession
+ load_RGMasterTable
+ on_tblRecordGroup_clicked
+ on_cmdCreateNewRG_clicked
+ createsave_RG
+ init_RGForm
+ load_RGForm
+ init_RGDict
+ create_RGDict_fromTable
+ create_RGDict_FromDatabase
+ block_RG_Signals'''
+
+ self.ui.txtRGDisplayTable.setText("tbl" + abbr + "Display")
+ self.ui.txtRGCreateNewItem.setText("createsave" + abbr)
+ self.ui.txtRGCreateDictionary.setText("create" + abbr + "Dict")
+ self.ui.txtRGListForDisplay.setText("tbl" + abbr + "Display")
+ self.ui.txtRGListLClicked.setText("tbl" + abbr + "Display")
+ self.ui.txtRGBlockSignals.setText("block" + abbr + "Signals")
+ self.ui.txtRGClearRecords.setText("init" + abbr + "Form")
+ self.ui.txtRGCreateSave.setText("createSave" + abbr)
+
+ @QtCore.Slot()
+ def on_cmdLoadForms_clicked(self):
+ fpath = QFileDialog.getExistingDirectory()
+ formsearchfolders = self.get_filepaths(fpath)
+
+ for filepath in formsearchfolders:
+ folderpath, filename = os.path.split(filepath)
+ filejunk, file_extension = os.path.splitext(filepath)
+ if file_extension == ".ui":
+ qryForm = self.instanceDict["ProgramBase"].session.query(FormInformation).filter_by(FilePath=folderpath).filter_by(FileName=filename).first()
+ if qryForm is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryForm = FormInformation(FilePath=folderpath, FileName=filename)
+ self.instanceDict["ProgramBase"].session.add(qryForm)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ f = open(filepath, "r")
+ contents = f.read()
+ widgetlist = set(re.findall('\', contents)) #
+ for widgetline in widgetlist:
+ wclass = re.search('class=\".*?\"', widgetline).group()[7:-1]
+ wname = re.search('name=\".*?\"', widgetline).group()[6:-1]
+
+ qryWidgetType = self.instanceDict["ProgramBase"].session.query(WidgetData).filter_by(Title=wclass).first()
+ if qryWidgetType is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryWidgetType = WidgetData(Title=wclass)
+ self.instanceDict["ProgramBase"].session.add(qryWidgetType)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ qryWidgetData = self.instanceDict["ProgramBase"].session.query(WidgetData).filter_by(Title=wclass).first()
+ if qryWidgetData is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryWidgetData = WidgetData(Title=wclass)
+ self.instanceDict["ProgramBase"].session.add(qryWidgetData)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ qryFormWidget = self.instanceDict["ProgramBase"].session.query(FormWidgets).filter_by(Form=qryForm.id).filter_by(WidgetName=wname).first()
+ if qryFormWidget is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryFormWidget = FormWidgets(Form=qryForm.id, WidgetType=qryWidgetData.id, WidgetName=wname)
+ self.instanceDict["ProgramBase"].session.add(qryFormWidget)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_RGForm_load()
+ # end customFunctions
+
+ # endregion
+
+ #region Widgets
+ def init_Widgets(self):
+ self.init_WDict()
+ self.blockWSignals(True)
+ self.on_WTable_load()
+ self.on_WForm_load()
+ self.on_WDatatType_load()
+ self.blockWSignals(False)
+
+ def on_WTable_load(self):
+ qryWidget = self.instanceDict["ProgramBase"].session.query(WidgetData.id, WidgetData.Title, WidgetData.Abbreviation).order_by(WidgetData.Title)
+ self.ui.tblWidgets.setModel(QueryTableModel(qryWidget))
+ self.ui.tblWidgets.resizeColumnsToContents()
+ self.ui.tblWidgets.setColumnWidth(0, 0)
+
+ def on_WDatatType_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(DataType.id, DataType.NameText)
+ self.ui.cmbWSVDataType.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbWSVDataType.setModelColumn(1)
+ self.ui.cmbWGVDataType.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbWGVDataType.setModelColumn(1)
+ self.ui.cmbWUVDataType.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbWUVDataType.setModelColumn(1)
+
+ def on_WForm_load(self):
+ pass
+
+ def on_WSVTable_load(self):
+ id = int(self.ui.txtWidgetID.text())
+ qryrecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.WidgetID, WidgetDataHandling.Command, DataType.NameText.label("DataType"), WidgetDataHandling.DataTypeFormat, WidgetDataHandling.Description, WidgetDataHandling.Preferred)\
+ .join(DataType, WidgetDataHandling.DataType == DataType.id)\
+ .filter(WidgetDataHandling.WidgetID==id, WidgetDataHandling.Direction==1)
+
+ self.ui.tblWSCommands.setModel(QueryTableModel(qryrecord))
+ self.ui.tblWSCommands.resizeColumnsToContents()
+ self.ui.tblWSCommands.setColumnWidth(0, 0)
+ self.ui.tblWSCommands.setColumnWidth(1, 0)
+
+ def on_WGVTable_load(self):
+ id = int(self.ui.txtWidgetID.text())
+ qryrecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.WidgetID, WidgetDataHandling.Command,
+ DataType.NameText.label("DataType"), WidgetDataHandling.DataTypeFormat, WidgetDataHandling.Description,
+ WidgetDataHandling.Preferred)\
+ .join(DataType, WidgetDataHandling.DataType == DataType.id)\
+ .filter(WidgetDataHandling.WidgetID==id, WidgetDataHandling.Direction == 2)
+ self.ui.tblWGCommands.setModel(QueryTableModel(qryrecord))
+ self.ui.tblWGCommands.resizeColumnsToContents()
+ self.ui.tblWGCommands.setColumnWidth(0, 0)
+ self.ui.tblWGCommands.setColumnWidth(1, 0)
+
+ def on_WUVTable_load(self):
+ id = int(self.ui.txtWidgetID.text())
+ qryrecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.WidgetID, WidgetDataHandling.Command,
+ DataType.NameText.label("DataType"), WidgetDataHandling.DataTypeFormat, WidgetDataHandling.Description,
+ WidgetDataHandling.Preferred) \
+ .outerjoin(DataType, WidgetDataHandling.DataType == DataType.id) \
+ .filter(WidgetDataHandling.WidgetID == id, WidgetDataHandling.Direction == 3)
+
+ self.ui.tblWUCommands.setModel(QueryTableModel(qryrecord))
+ self.ui.tblWUCommands.resizeColumnsToContents()
+ self.ui.tblWUCommands.setColumnWidth(0, 0)
+ self.ui.tblWUCommands.setColumnWidth(1, 0)
+
+ def on_cmdCreateNewWidget_clicked(self):
+ pass
+
+ def createsaveWidget(self, record_id=None):
+
+ if record_id is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryRecord = WidgetData()
+ self.instanceDict["ProgramBase"].session.add(qryRecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetData).filter_by(id=record_id).first()
+ self.instanceDict["ProgramBase"].session.begin()
+
+ if self.WDict["Item"] != "": qryRecord.Title = self.WDict["Item"]
+ if self.WDict["Abbrev"] != "": qryRecord.Abbreviation = self.WDict["Abbrev"]
+
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_WTable_load()
+ return qryRecord.id
+
+
+ def blockWSignals(self, value):
+ self.ui.txtWItem.blockSignals(value)
+ self.ui.txtWAbbrev.blockSignals(value)
+
+
+ def init_WidgetForm(self):
+ self.blockWSignals(True)
+ self.ui.txtWItem.setText("")
+ self.ui.txtWAbbrev.setText("")
+ self.ui.txtWSetValue.setText("")
+ self.ui.txtWGetValue.setText("")
+ self.ui.txtWUpdateValue.setText("")
+ self.ui.cmbWSVDataType.setCurrentIndex(-1)
+ self.ui.cmbWGVDataType.setCurrentIndex(-1)
+ self.ui.cmbWUVDataType.setCurrentIndex(-1)
+
+ self.blockWSignals(False)
+
+ def init_WDict(self):
+ self.WDict = {}
+ self.WDict["Item"] = ""
+ self.WDict["Abbrev"] = ""
+
+ def createWDictFromForm(self):
+ self.WDict["Item"] = self.ui.txtWItem.text()
+ self.WDict["Abbrev"] = self.ui.txtWAbbrev.text()
+
+ def createWDictFromDatabase(self, id):
+ qryWidget = self.instanceDict["ProgramBase"].session.query(WidgetData).filter_by(id = int(id)).first()
+ self.WDict["Item"] = qryWidget.Title
+ self.WDict["Abbrev"] = qryWidget.Abbreviation
+
+ @QtCore.Slot()
+ def on_tblWidgets_clicked(self):
+ self.init_WidgetForm()
+ self.blockWSignals(True)
+ data = {}
+ data["row"] = self.ui.tblWidgets.selectionModel().currentIndex().row()
+ data["column"] = self.ui.tblWidgets.selectionModel().currentIndex().column()
+ data["column"] = self.ui.tblWidgets.selectionModel().currentIndex().column()
+ data["Value"] = self.ui.tblWidgets.model().index(data["row"], data["column"], None).data()
+ data["id"] = self.ui.tblWidgets.model().index(data["row"], 0, None).data()
+
+ self.ui.txtWidgetID.setText(str(data["id"]))
+
+ qryWidget = self.instanceDict["ProgramBase"].session.query(WidgetData).filter_by(id = int(data["id"])).first()
+ self.ui.txtWItem.setText(qryWidget.Title)
+ self.ui.txtWAbbrev.setText(qryWidget.Abbreviation)
+
+ self.on_WSVTable_load()
+ self.on_WGVTable_load()
+ self.on_WUVTable_load()
+
+ self.blockWSignals(False)
+
+ @QtCore.Slot()
+ def on_cmdNewWidget_clicked(self):
+ print("hi")
+
+ @QtCore.Slot()
+ def on_cmdWSV_add_clicked(self):
+ id = int(self.ui.txtWidgetID.text())
+ Command = self.ui.txtWSetValue.text()
+ datatype = self.ui.cmbWSVDataType.model().index(self.ui.cmbWSVDataType.currentIndex(), 0, None).data()
+
+ qryrecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling).filter_by(WidgetID=id, Command=Command, Direction=1).first()
+
+ if qryrecord is None:
+ self.instanceDict["ProgramBase"].session.begin(subtransactions=True)
+ qryrecord = WidgetDataHandling(WidgetID=id,
+ Command=Command,
+ Direction=1,
+ DataType=datatype)
+ self.instanceDict["ProgramBase"].session.add(qryrecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_WSVTable_load()
+
+ @QtCore.Slot()
+ def on_cmdWGV_add_clicked(self):
+
+ id = int(self.ui.txtWidgetID.text())
+ Command = self.ui.txtWGetValue.text()
+ datatype = self.ui.cmbWGVDataType.model().index(self.ui.cmbWGVDataType.currentIndex(), 0, None).data()
+
+ qryrecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling).filter_by(WidgetID=id, Command=Command, Direction=1).first()
+
+ if qryrecord is None:
+ self.instanceDict["ProgramBase"].session.begin(subtransactions=True)
+ qryrecord = WidgetDataHandling(WidgetID=id,
+ Command=Command,
+ Direction=2,
+ DataType=datatype)
+ self.instanceDict["ProgramBase"].session.add(qryrecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_WGVTable_load()
+
+ @QtCore.Slot()
+ def on_cmdWUV_add_clicked(self):
+ id = int(self.ui.txtWidgetID.text())
+ Command = self.ui.txtWUpdateValue.text()
+ datatype = self.ui.cmbWUVDataType.model().index(self.ui.cmbWUVDataType.currentIndex(), 0, None).data()
+
+ qryrecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling).filter_by(WidgetID=id, Command=Command, Direction=1).first()
+
+ if qryrecord is None:
+ self.instanceDict["ProgramBase"].session.begin(subtransactions=True)
+ qryrecord = WidgetDataHandling(WidgetID=id,
+ Command=Command,
+ Direction=3,
+ DataType=datatype)
+ self.instanceDict["ProgramBase"].session.add(qryrecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_WUVTable_load()
+
+ @QtCore.Slot(str)
+ def on_txtWAbbrev_textChanged(self, value):
+ self.WDict["Abbrev"] = value
+ if self.ui.chkWItem.isChecked():
+ self.load_WTable()
+ else:
+ if self.ui.txtWidgetID.text() != "":
+ record_id = int(self.ui.txtWidgetID.text())
+ self.createsaveWidget(record_id)
+ #endregion
+
+ #region Form Items
+ def init_FormItems(self):
+ self.blockFISignals(True)
+ self.on_FISession_load()
+ self.on_FITable_load()
+ self.on_cmbFormType_load()
+ self.on_cmbFormName_load()
+ self.on_tblFormItems_load()
+ self.on_tvFormItems_load()
+
+ self.on_FIRSession_load()
+
+ self.on_FIDataGroup_load()
+ self.on_FIWidgetType_load()
+ self.on_FISetValue_load()
+ self.on_FIGetValue_load()
+ self.on_FIUpdateValue_load()
+ self.blockFISignals(False)
+
+ self.init_FIForm()
+
+ def on_FISession_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(SessionNames.id, SessionNames.NameText)
+ self.ui.cmbFISession.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFISession.setModelColumn(1)
+
+ def on_FITable_load(self, Session=None):
+ if Session is None:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable.id, MasterTable.TableName)
+ self.ui.cmbFITable.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFITable.setModelColumn(1)
+ else:
+ queryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable.id, MasterTable.TableName).filter_by(Session=Session).order_by(MasterTable.TableName)
+ self.ui.cmbFITable.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFITable.setModelColumn(1)
+
+ def on_FIRSession_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(SessionNames.id, SessionNames.NameText)
+ self.ui.cmbFIRSession.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIRSession.setModelColumn(1)
+
+ def on_FIRTable_load(self, Session_id=None):
+ self.blockFISignals(True)
+ if Session is None:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable.id, MasterTable.TableName)
+ self.ui.cmbFIRTable.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIRTable.setModelColumn(1)
+ else:
+ queryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable.id, MasterTable.TableName).filter_by(Session=Session_id).order_by(MasterTable.TableName)
+ self.ui.cmbFIRTable.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFIRTable.setModelColumn(1)
+ self.blockFISignals(False)
+
+ def on_FIRField_load(self, Table_id=None):
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation.id, FieldInformation.FieldName).filter_by(TableID=Table_id).order_by(FieldInformation.FieldName)
+ self.blockFISignals(True)
+ if queryRecord.first() is None:
+ qryTable = self.instanceDict["ProgramBase"].session.query(MasterTable).filter_by(id=Table_id).first()
+
+ if qryTable is not None:
+ qryBase = self.instanceDict["ProgramBase"].session.query(SessionBase).filter_by(id=qryTable.Base).first()
+ if qryBase is not None:
+ myColumnList = DatabaseTools.get_fields_by_tablename(bases[qryBase.NameText], qryTable.TableName)
+
+ for key, value in enumerate(myColumnList):
+ qryTableField = self.instanceDict["ProgramBase"].session.query(FieldInformation).filter_by(TableID=Table_id, FieldName=value).first()
+
+ if qryTableField is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryTableField = FieldInformation(TableID=int(Table_id),
+ FieldName=str(value),
+ DataType_Value=str(myColumnList[value]))
+ self.instanceDict["ProgramBase"].session.add(qryTableField)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation.id, FieldInformation.FieldName).filter_by(
+ TableID=Table_id).order_by(FieldInformation.FieldName)
+ self.ui.cmbFIRField.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFIRField.setModelColumn(1)
+ else:
+ self.ui.cmbFIRField.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFIRField.setModelColumn(1)
+
+ self.blockFISignals(False)
+
+
+ def on_cmbFormType_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormDesignPrimaryGroup.id, FormDesignPrimaryGroup.NameText)
+ self.ui.cmbFormType.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFormType.setModelColumn(1)
+ #self.ui.cmbFormType.setCurrentIndex(1)
+
+ @QtCore.Slot(int)
+ def on_cmbFormName_currentIndexChanged(self, index):
+ self.on_tblFormItems_load(index)
+
+ @QtCore.Slot()
+ def on_cmdCreateForm_clicked(self):
+ print("hi")
+
+ def dictFormImport(self, formfileName):
+ importDict = {}
+
+ importDict["re"] = "import re"
+ importDict["sys"] = "import sys"
+ importDict["os"] = "import os"
+
+ importDict["QtGui"] = "from qtpy import QtGui"
+ importDict["QtCore"]= "from qtpy import QtCore"
+ importDict["QtWidgets"] = "from qtpy.QtWidgets import *"
+
+ importDict["QtUiTools"] = "from qtpy import QtUiTools"
+ importDict["qtalchemy"] = "from qtalchemyPySide import *"
+
+ importDict["uiFile"] = "uiFile = os.path.join(basePackagePath, %s)" % (formfileName)
+
+ TableImport = []
+ TableImport.append("from ProjectManager.Database import DatabaseTools")
+ TableImport.append("from ProjectManager.Packages.ProgramBase.Database.dbBase import *")
+ TableImport.append("from ProjectManager.Packages.ProgramBase.Database.dbMasterTables import *")
+ TableImport.append("from ProjectManager.Packages.ProgramBase.Database.dbAllLists import *")
+ TableImport.append("from ProjectManager.Packages.ProgramBase.Database.dbAllTables import *")
+
+ importDict["TableImport"] = TableImport
+
+ return importDict
+
+ @QtCore.Slot()
+ def on_cmdCreateFormBaseClass_clicked(self):
+ formName = self.ui.lstForms.model().index(self.ui.lstForms.currentIndex().row(), 1).data()
+ exportPath = self.ui.txtFormPath.text()
+
+ copyrightyear = "2020-2021"
+ copyrightauthor = "David Lario"
+ copyrightAuthor = "## Copyright %s %s" % (copyrightyear, copyrightauthor)
+ compileDate = datetime.datetime.now().strftime('%Y:%m:%d %H:%M:%S')
+
+ fileName = "frm" + formName
+ filefullpath = Packages.__path__[0] + "\\"
+
+ filefullpath = os.path.join(exportPath, formName + ".py")
+
+ copywritepath = os.path.join(basePackagePath, "gnu.txt")
+
+ importblock = self.dictFormImport(fileName+ ".ui")
+
+ classline = "class %s(QMdiSubWindow):\n" % (fileName)
+ classline += " def __init__(self, packageName):\n"
+ classline += " super(%s, self).__init__(packageName)\n" % (fileName)
+
+ spaces = " "
+ with open(filefullpath, 'w') as f:
+
+ f.write(copyrightAuthor + "\n")
+ f.write("## " + compileDate+ "\n\n")
+
+ f2 = open(copywritepath, "r")
+
+ for index, lineitem in enumerate(f2):
+ if lineitem == '\n':
+ f.write(lineitem)
+ else:
+ f.write("## " + lineitem)
+
+ f.write("\n\n")
+
+ for row, importItem in enumerate(importblock):
+ if importItem == "TableImport":
+ for importItem2 in importblock[importItem]:
+ f.write(importItem2 + "\n")
+ else:
+ f.write(importblock[importItem] + "\n")
+
+ f.write("\n")
+ f.write(classline)
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate.id,
+ FormFunctionTemplate.FunctionName)
+ form_id = 1
+ selectedItems = self.ui.lstFormFunctions.selectionModel().selectedIndexes()
+
+ for itemsselected in selectedItems:
+ template_id = self.ui.lstFormFunctions.model().index(itemsselected.row(), 0).data()
+
+ if len(self.on_GetTemplate(form_id, template_id)):
+
+ f.write(self.on_GetTemplate(form_id, template_id))
+ f.write("\n\n")
+
+ print("Done")
+
+ def on_GetTemplate(self, rg_id, Record_id):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(id=Record_id).first()
+ spaces = " "
+ if qryRecord.Filename is not None:
+ f2 = open(os.path.join(qryRecord.FunctionPath, qryRecord.Filename), "r")
+ self.ui.txtFunctionTemplateCode.setPlainText(f2.read())
+
+
+ #rg_id = self.ui.cmbFITRecordGroup.model().index(self.ui.cmbFITRecordGroup.currentIndex(), 0).data()
+ #frm_id = self.ui.cmbFITRecordGroup.model().index(self.ui.cmbFITRecordGroup.currentIndex(), 0).data()
+
+ self.createRGDictFromDatabase(rg_id)
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(id=Record_id).first()
+ qryFormRecord = self.instanceDict["ProgramBase"].session.query(FormItemData).filter_by(RecordGroupID=int(rg_id)).all()
+
+ if qryRecord.Filename is not None:
+ f = open(os.path.join(qryRecord.FunctionPath, qryRecord.Filename), "r")
+ textbody = ""
+
+ processdelay = False
+ bufferlist = []
+ f2 = [spaces + textline for textline in f] #Add spaces
+
+ for textline in f2:
+ if processdelay == False:
+ if "{BeginWidgetList}" in textline:
+ bufferlist = []
+ processdelay = True
+ else:
+ #Process the Line Item
+ itemlist = set(re.findall('\{.*?\}', textline))
+ for item in itemlist:
+ if "RecordGroup." in item:
+ if item[13:-1] in self.RGDict:
+ #print("found", item[13:-1], self.RGDict[item[13:-1]])
+ itemdictname = self.RGDict[item[13:-1]]
+ textline = textline.replace(item, itemdictname)
+ else:
+ print("not found", self.RGDict)
+ else:
+ print(textline)
+ textbody += textline
+ else:
+ if "{EndWidgetList}" in textline:
+
+ # Process the Block
+ #self.createFIDictFromDatabase(record.id)
+ for record in qryFormRecord:
+ FormRecordDict = record.__dict__
+ '''print(FormRecordDict)'''
+ print(bufferlist)
+ for bufferitem in bufferlist:
+ itemlist = set(re.findall('\{.*?\}', bufferitem))
+ textline = bufferitem
+ for item in itemlist:
+ if "SubFunction" in item:
+ textline = ""
+ FunctionName = re.search('\((.*)\[', bufferitem).group(0)
+ RegionName = re.search('\[\{(.*)\}\]', bufferitem).group(0)
+
+ if "RecordGroup.Form." in RegionName:
+ if RegionName[19:-2] in FormRecordDict:
+ itemdictname = FormRecordDict[RegionName[19:-2]]
+ if RegionName[19:-2] == "WidgetType":
+ qryWidgetType = self.instanceDict["ProgramBase"].session.query(WidgetData).filter_by(id=int(itemdictname)).first()
+ RegionNameValue = qryWidgetType.Title
+
+ sublist = self.SubFunction(FunctionName[1:-1], RegionNameValue)
+ print(FormRecordDict["ItemName"], FunctionName[1:-1], RegionNameValue, bufferitem)
+ for subitem in sublist:
+ #print(subitem)
+ textline = subitem
+ itemlist2 = set(re.findall('\{.*?\}', subitem))
+ for item2 in itemlist2:
+ #print(item2)
+ if "RecordGroup.Form." in item2:
+ if item2[18:-1] in FormRecordDict:
+ itemdictname = FormRecordDict[item2[18:-1]]
+ #print(item2)
+ if itemdictname is not None:
+ textline = textline.replace(item2, itemdictname)
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not FoundS2", FormRecordDict, bufferitem, item, itemdictname)
+
+ elif "RecordGroup." in item2:
+ if item2[13:-1] in self.RGDict:
+ itemdictname = self.RGDict[item2[13:-1]]
+
+ if itemdictname is not None:
+ textline = textline.replace(item2, itemdictname)
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not FoundS2", FormRecordDict, bufferitem, item, itemdictname)
+
+ else:
+ print("not found", item)
+
+ textbody += textline
+ textline = ""
+ textbody += "\n"
+
+ elif "RecordGroup.Form." in item:
+ if item[18:-1] in FormRecordDict:
+ itemdictname = FormRecordDict[item[18:-1]]
+ if itemdictname is not None:
+ textline = textline.replace(item, str(itemdictname))
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not Found1", FormRecordDict, bufferitem, item, itemdictname)
+
+ elif "RecordGroup." in item:
+ #print("test", item[13:-1])
+ if item[13:-1] in self.RGDict:
+ #print("found", item[13:-1], self.RGDict[item[13:-1]])
+ itemdictname = self.RGDict[item[13:-1]]
+ if itemdictname is not None:
+ textline = textline.replace(item, itemdictname)
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not Found2", FormRecordDict, bufferitem, item, itemdictname)
+ else:
+ print("not found", item)
+
+ textbody += textline
+ bufferlist = []
+ processdelay = False
+ textbody += "\n\n"
+ else:
+ #Build the Block
+ bufferlist.append(textline)
+ return textbody
+
+ @QtCore.Slot()
+ def on_lstForms_clicked(self):
+ form_id = self.ui.lstForms.model().index(self.ui.lstForms.currentIndex().row(), 0).data()
+ self.on_lstFormFields_load(form_id)
+
+ def on_lstFormFields_load(self, formIndex=None):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormItemData.id, FormItemData.RecordGroupID, FormItemData.ItemName)
+ if formIndex:
+ qryRecord = qryRecord.filter_by(RecordGroupID=formIndex)
+ self.ui.lstFormFields.setModel(QueryTableModel(qryRecord))
+ self.ui.lstFormFields.setModelColumn(2)
+
+ def on_cmbFormName_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormDesignSecondaryGroup.id, FormDesignSecondaryGroup.NameText)
+ self.ui.cmbFormName.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFormName.setModelColumn(1)
+
+ self.ui.lstForms.setModel(QueryTableModel(qryRecord))
+ self.ui.lstForms.setModelColumn(1)
+
+ def on_tblFormItems_load(self, formIndex=None):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormItemData.id, FormItemData.RecordGroupID, FormItemData.ItemName)
+ if formIndex:
+ qryRecord = qryRecord.filter_by(RecordGroupID=formIndex)
+ self.ui.tblFormItems.setModel(QueryTableModel(qryRecord))
+ self.ui.tblFormItems.setColumnWidth(0,0)
+
+ def initFTTreeView(self):
+ self.tvFTdict = {}
+
+
+ def on_FITreeView_load(self):
+ pass
+
+ def on_cmdAdvanceFormItemsTreeView_clicked(self):
+ self.initFTTreeView()
+ self.NewMeetingManager = TreeBuilderEntity(self.tvFTdict, "tblTaskSettings", sessions, bases, FormBuilder)
+
+ def on_FIDataGroup_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroup.id, RecordGroup.Title)
+ self.ui.cmbFIDataGroup.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIDataGroup.setModelColumn(1)
+
+ def on_FIWidgetType_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetData.id, WidgetData.Title)
+ self.ui.cmbFIWidgetType.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIWidgetType.setModelColumn(1)
+
+ def on_FISetValue_load(self, Widget_id=None):
+ if Widget_id is None:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(Direction=1)
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(WidgetID=Widget_id, Direction=1)
+
+ self.ui.cmbFISetValue.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFISetValue.setModelColumn(1)
+
+ def on_FIGetValue_load(self, Widget_id=None):
+ if Widget_id is None:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(Direction=2)
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(WidgetID=Widget_id, Direction=2)
+
+ self.ui.cmbFIGetValue.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIGetValue.setModelColumn(1)
+
+ def on_FIUpdateValue_load(self, Widget_id=None):
+ if Widget_id is None:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(Direction=3)
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(WidgetID=Widget_id, Direction=3)
+
+ self.ui.cmbFIUpdateValue.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIUpdateValue.setModelColumn(1)
+
+ def on_FIField_load(self, Table_id):
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation.id, FieldInformation.FieldName).filter_by(TableID=Table_id).order_by(FieldInformation.FieldName)
+
+ if queryRecord.first() is None:
+ qryTable = self.instanceDict["ProgramBase"].session.query(MasterTable).filter_by(id=Table_id).first()
+
+ if qryTable is not None:
+ qryBase = self.instanceDict["ProgramBase"].session.query(SessionBase).filter_by(id=qryTable.Base).first()
+ if qryBase is not None:
+ myColumnList = DatabaseTools.get_fields_by_tablename(bases[qryBase.NameText], qryTable.TableName)
+
+ for key, value in enumerate(myColumnList):
+ qryTableField = self.instanceDict["ProgramBase"].session.query(FieldInformation).filter_by(TableID=Table_id, FieldName=value).first()
+
+ if qryTableField is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ print(Table_id, value, myColumnList[value])
+ qryTableField = FieldInformation(TableID=int(Table_id),
+ FieldName=str(value),
+ DataType_Value=str(myColumnList[value]))
+ self.instanceDict["ProgramBase"].session.add(qryTableField)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation.id, FieldInformation.FieldName).filter_by(TableID=Table_id).order_by(FieldInformation.FieldName)
+ self.ui.cmbFIField.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFIField.setModelColumn(1)
+ else:
+ self.ui.cmbFIField.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFIField.setModelColumn(1)
+
+ def on_FIForm_load(self, Record_id):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormItemData).filter_by(id=Record_id).first()
+ self.init_FIDict()
+ self.init_FIForm()
+ self.blockFISignals(True)
+
+ if qryRecord.RecordGroupID is not None:
+ for row in range(self.ui.cmbFIDataGroup.model().rowCount()):
+ if int(self.ui.cmbFIDataGroup.model().index(row, 0, None).data()) == int(qryRecord.RecordGroupID):
+ self.ui.cmbFIDataGroup.setCurrentIndex(row)
+ break
+ self.FormItemDict["RecordGroup"] = {"Record_id": qryRecord.RecordGroupID,
+ "Value": self.ui.cmbFIDataGroup.model().index(row, 1, None).data(),
+ "Row": row}
+
+ qryRecordGroup = self.instanceDict["ProgramBase"].session.query(RecordGroup).filter_by(id=qryRecord.RecordGroupID).first()
+
+ if qryRecordGroup is not None:
+ for row in range(self.ui.cmbFISession.model().rowCount()):
+ if int(self.ui.cmbFISession.model().index(row, 0, None).data()) == int(qryRecordGroup.SessionID):
+ self.ui.cmbFISession.setCurrentIndex(row)
+ break
+
+ if qryRecordGroup.TableID is not None:
+ self.on_FITable_load(Session=qryRecordGroup.SessionID)
+ self.on_FIField_load(qryRecordGroup.TableID)
+
+ for row in range(self.ui.cmbFITable.model().rowCount()):
+ if int(self.ui.cmbFITable.model().index(row, 0, None).data()) == int(qryRecordGroup.TableID):
+ self.ui.cmbFITable.setCurrentIndex(row)
+ break
+ else:
+ self.ui.cmbFITable.setCurrentIndex(-1)
+
+ if qryRecord.WidgetType is not None:
+ for row in range(self.ui.cmbFIWidgetType.model().rowCount()):
+ if int(self.ui.cmbFIWidgetType.model().index(row, 0, None).data()) == int(qryRecord.WidgetType):
+ self.ui.cmbFIWidgetType.setCurrentIndex(row)
+ break
+ self.FormItemDict["WidgetType"] = {"Record_id": qryRecord.WidgetType,
+ "Value": self.ui.cmbFIWidgetType.model().index(row, 1, None).data(),
+ "Row": row}
+
+ if self.FormItemDict["WidgetType"]["Record_id"] == 7:
+ self.ui.gbFITreeView.setMaximumHeight(16777215)
+ else:
+ self.ui.gbFITreeView.setMaximumHeight(0)
+
+ qryRecord3 = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(Direction=3, WidgetID=qryRecord.WidgetType)
+
+ self.ui.cmbFIUpdateValue.setModel(QueryTableModel(qryRecord3))
+ self.ui.cmbFIUpdateValue.setModelColumn(1)
+ self.ui.cmbFIUpdateValue.setCurrentIndex(-1)
+
+ if qryRecord.ItemName is not None:
+ self.ui.txtFIItemName.setText(qryRecord.ItemName)
+ self.FormItemDict["ItemName"] = {"Value": qryRecord.ItemName}
+
+ if qryRecord.DefaultValue is not None:
+ self.ui.txtFIDefaultValue.setText(qryRecord.DefaultValue)
+ self.FormItemDict["DefaultValue"] = {"Value": qryRecord.DefaultValue}
+
+ if qryRecord.WidgetName is not None:
+ self.ui.txtFIWidgetName.setText(qryRecord.WidgetName)
+ self.FormItemDict["WidgetName"] = {"Value": qryRecord.WidgetName}
+
+ self.on_FISetValue_load(qryRecord.WidgetType)
+
+ if qryRecord.SetValue is not None:
+ for row in range(self.ui.cmbFISetValue.model().rowCount()):
+
+ if int(self.ui.cmbFISetValue.model().index(row, 0, None).data()) == int(qryRecord.SetValue):
+ self.ui.cmbFISetValue.setCurrentIndex(row)
+ setString = str(self.ui.cmbFISetValue.model().index(row, 1, None).data())
+ self.FormItemDict["SetValue"] = {"Record_id": qryRecord.SetValue,
+ "Value": setString,
+ "Row": row}
+
+ self.ui.txtFISetString.setText("self.ui." + self.ui.txtFIWidgetName.text() + setString)
+
+ if qryRecord.DefaultValue is not None:
+ self.ui.txtFISetString.setText("self.ui." + self.ui.txtFIWidgetName.text() + "." + setString)
+ newcommand = setString.replace("value", str(qryRecord.DefaultValue))
+ self.ui.txtFIClearWidget.setText("self.ui." + self.ui.txtFIWidgetName.text() + "." + newcommand)
+
+ break
+ else:
+ self.ui.cmbFISetValue.setCurrentIndex(-1)
+
+ self.on_FIGetValue_load()
+ if qryRecord.GetValue is not None:
+ for row in range(self.ui.cmbFIGetValue.model().rowCount()):
+ if int(self.ui.cmbFIGetValue.model().index(row, 0, None).data()) == int(qryRecord.GetValue):
+ self.ui.cmbFIGetValue.setCurrentIndex(row)
+ getString = str(self.ui.cmbFIGetValue.model().index(row, 1, None).data())
+ self.FormItemDict["GetValue"] = {"Record_id": qryRecord.GetValue,
+ "Value": str(self.ui.cmbFIGetValue.model().index(row, 1, None).data()),
+ "Row": row}
+ self.ui.txtFIGetString.setText("self.ui." + self.ui.txtFIWidgetName.text() + "." + getString)
+ break
+
+ if qryRecord.UpdateValue is not None:
+ for row in range(self.ui.cmbFIUpdateValue.model().rowCount()):
+ if int(self.ui.cmbFIUpdateValue.model().index(row, 0, None).data()) == int(qryRecord.UpdateValue):
+ self.ui.cmbFIUpdateValue.setCurrentIndex(row)
+ updateString = str(self.ui.cmbFIUpdateValue.model().index(row, 1, None).data())
+ self.FormItemDict["UpdateValue"] = {"Record_id": qryRecord.UpdateValue,
+ "Value": self.ui.cmbFIUpdateValue.model().index(row, 1, None).data(),
+ "Row": row}
+ self.ui.txtFIUpdateString.setText("self.ui." + self.ui.txtFIWidgetName.text() + "." + updateString)
+ break
+
+
+ if qryRecord.QueryRecord is not None:
+ self.ui.txtFIQueryRecord.setText(qryRecord.QueryRecord)
+ self.FormItemDict["QueryRecord"] = {"Value": qryRecord.QueryRecord}
+
+ if qryRecord.ClearWidget is not None:
+ self.ui.txtFIClearWidget.setText(qryRecord.ClearWidget)
+ self.FormItemDict["ClearWidget"] = {"Value": qryRecord.ClearWidget}
+
+ if qryRecord.FilterCheckBox is not None:
+ self.ui.txtFIFilterCheck.setText(qryRecord.FilterCheckBox)
+ self.FormItemDict["FilterCheck"] = {"Value": qryRecord.FilterCheckBox}
+
+ if qryRecord.Description is not None:
+ self.ui.txtFIDescription.setText(qryRecord.Description)
+ self.FormItemDict["Description"] = {"Value": qryRecord.Description}
+
+ if qryRecord.DatabaseField is not None:
+ for row in range(self.ui.cmbFIField.model().rowCount()):
+ if int(self.ui.cmbFIField.model().index(row, 0, None).data()) == int(qryRecord.DatabaseField):
+ self.ui.cmbFIField.setCurrentIndex(row)
+ self.FormItemDict["DatabaseField"] = {"Record_id": qryRecord.DatabaseField,
+ "Value": self.ui.cmbFIField.model().index(row, 1, None).data(),
+ "Row": row}
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation).filter_by(
+ id=int(qryRecord.DatabaseField)).first()
+ self.ui.txtFIDatabaseFieldType.setText(queryRecord.DataType_Value)
+
+ break
+
+
+ if qryRecord.ViewOrder is not None:
+ self.ui.txtFIViewOrder.setText(str(qryRecord.ViewOrder))
+ self.FormItemDict["ViewOrder"] = {"Value": qryRecord.ViewOrder}
+
+ if qryRecord.Label is not None:
+ self.ui.txtFILabel.setText(qryRecord.Label)
+ self.FormItemDict["Label"] = {"Value": qryRecord.Label}
+
+ if qryRecord.ReferenceSession is not None:
+ for row in range(self.ui.cmbFIRSession.model().rowCount()):
+ if int(self.ui.cmbFIRSession.model().index(row, 0, None).data()) == int(qryRecord.ReferenceSession):
+ self.ui.cmbFIRSession.setCurrentIndex(row)
+ self.FormItemDict["ReferenceSession"] = {"Record_id": qryRecord.ReferenceSession,
+ "Value": self.ui.cmbFIRSession.model().index(row, 1, None).data(),
+ "Row": row}
+ self.on_FIRTable_load(qryRecord.ReferenceSession)
+ break
+
+
+ if qryRecord.ReferenceTable is not None:
+ for row in range(self.ui.cmbFIRTable.model().rowCount()):
+ if int(self.ui.cmbFIRTable.model().index(row, 0, None).data()) == int(qryRecord.ReferenceTable):
+ self.ui.cmbFIRTable.setCurrentIndex(row)
+ self.FormItemDict["ReferenceTable"] = {"Record_id": qryRecord.ReferenceTable,
+ "Value": self.ui.cmbFIRTable.model().index(row, 1, None).data(),
+ "Row": row}
+ self.on_FIRField_load(qryRecord.ReferenceTable)
+ break
+
+
+ if qryRecord.ReferenceField is not None:
+ for row in range(self.ui.cmbFIRField.model().rowCount()):
+ if int(self.ui.cmbFIRField.model().index(row, 0, None).data()) == int(qryRecord.ReferenceField):
+ self.ui.cmbFIRField.setCurrentIndex(row)
+ self.FormItemDict["ReferenceField"] = {"Record_id": qryRecord.ReferenceField,
+ "Value": self.ui.cmbFIRField.model().index(row, 1, None).data(),
+ "Row": row}
+ break
+
+ if qryRecord.JoinVariable is not None:
+ self.ui.txtFIJoinVariable.setText(qryRecord.JoinVariable)
+ self.FormItemDict["JoinVariable"] = {"Value": qryRecord.JoinVariable}
+
+ self.blockFISignals(False)
+
+ def on_tvFormItems_load(self):
+ self.initFTTreeView()
+ self.ui.tvFormItems.setModel(None)
+ '''self.tvFormItemsmodel = TreeViewAlchemy2.linkedtreemodel(self.MasterData, treedicts["FormBuilder"], header="Dave Rocks")
+
+ if False:
+ treepath = []
+ taglist = []
+ MasterTable_id = 29
+ Parent_id = "(0)"
+ GenerateParent_id = 0
+ ItemLevel = -1
+
+ querytreepath = sessions["ProjectManager"].query(FormDesignTree).filter_by(PrimaryGroup_id=1, SecondaryGroup_id=876, ParentTree_id="(0)").first()
+
+ self.tvFormItemsmodel.readtvtreebranch(treepath, taglist, querytreepath.Tree_id, ItemLevel)
+
+ else:
+ self.tvFormItemsmodel = TreeViewAlchemy2.linkedtreemodel(self.MasterData, treedicts["FormBuilder"], header="Dave Rocks")
+ mrilist = []
+ querytreepath = sessions["ProjectManager"].query(FormDesignTree).filter_by(PrimaryGroup_id=1, ParentTree_id="(0)").all()
+
+ for records in querytreepath:
+ SecondaryGroup_id = records.SecondaryGroup_id
+ PrimaryGroup_id = records.PrimaryGroup_id
+ modelrootitem = sessions["ProjectManager"].query(FormDesignTree).filter_by(
+ ParentTree_id="(0)") \
+ .filter_by(PrimaryGroup_id=PrimaryGroup_id) \
+ .filter_by(SecondaryGroup_id=SecondaryGroup_id) \
+ .order_by(self.tvFTdict["TreeItems"].ItemOrder) \
+ .order_by(self.tvFTdict["TreeItems"].DisplayName).first()
+
+ if modelrootitem is not None:
+ data = {}
+ data["root_id"] = modelrootitem.id
+ data["SourcePG"] = PrimaryGroup_id
+ data["SourceSG"] = modelrootitem.SecondaryGroup_id
+ data["DestinationPG"] = None
+ data["DestinationSG"] = None
+
+ mrilist.append(data)
+
+ if mrilist != []:
+ self.tvFormItemsmodel.setupModelData(sessions["ProjectManager"], FormDesignTree, mrilist)
+
+ self.ui.tvFormItems.setModel(self.tvFormItemsmodel)'''
+
+ def on_cmdRefreshFormItemsTreeView_clicked(self):
+ self.createTreeFromData()
+
+ def on_tvFormItems_clicked(self, index):
+ itemselected = index.internalPointer()
+ TreePath = itemselected.TreeClassItem.TreePath
+ # print(itemselected.TreeClassItem.Tree_id, itemselected.TreeClassItem.TreePath)
+ if TreePath != None:
+ sTreePath = DatabaseTools.myListToStr(TreePath)
+ record = self.tvFTdict["Session"].query(self.tvFTdict["TreeItems"]).filter(self.tvFTdict["TreeItems"].TreePath==sTreePath).first()
+ if record:
+ if record.ItemTable_id == 514:
+ self.init_WidgetForm()
+ self.blockWSignals(True)
+ self.ui.txtFormItem_id.setText(str(record.Item_id))
+ self.on_FIForm_load(int(record.Item_id))
+
+ self.blockWSignals(False)
+
+ def createTreeFromData(self):
+ #Root Record Group
+ sessions["ProjectManager"].begin()
+ treerecords = sessions["ProjectManager"].query(FormDesignTree)
+ treerecords.delete(synchronize_session=False)
+ sessions["ProjectManager"].commit()
+
+
+ self.initFTTreeView()
+
+ AddLevel = None
+ self.TVTreeGroup = TreeViewAlchemy2.linkedtreemodel(bases, sessions, self.tvFTdict, header="Tree Stuff")
+
+ #Create the first Record as Blank
+ queryPrimaryGroup = self.tvFTdict["Session"].query(self.tvFTdict["PrimaryGroup"]).all()
+ if queryPrimaryGroup is None:
+ self.tvFTdict["Session"].begin()
+ queryPrimaryGroup = self.tvFTdict["PrimaryGroup"]()
+ self.tvFTdict["Session"].add(queryPrimaryGroup)
+ self.tvFTdict["Session"].commit()
+ self.tvFTdict["Session"].flush()
+
+ # Add Category if Not there
+ queryPrimaryGroup = self.tvFTdict["Session"].query(self.tvFTdict["PrimaryGroup"]).filter_by(NameText="By Form Group").first()
+ if queryPrimaryGroup is None:
+ self.tvFTdict["Session"].begin()
+ queryPrimaryGroup = self.tvFTdict["PrimaryGroup"](NameText="By Form Group")
+ self.tvFTdict["Session"].add(queryPrimaryGroup)
+ self.tvFTdict["Session"].commit()
+ self.tvFTdict["Session"].flush()
+
+ SecondaryGroup_id = self.tvFTdict["Session"].query(self.tvFTdict["SecondaryGroup"]).filter_by(PrimaryGroup_id=queryPrimaryGroup.id).count()
+ # Add Blank Zero Record
+ querySecondaryGroup = self.tvFTdict["Session"].query(self.tvFTdict["SecondaryGroup"]).filter_by(PrimaryGroup_id=queryPrimaryGroup.id).first()
+
+ #Create the First Secondary Group if it does Not Exist
+ if querySecondaryGroup is None:
+ SecondaryGroup_id = 1
+ self.tvFTdict["Session"].begin()
+ querySecondaryGroup = self.tvFTdict["SecondaryGroup"](NameText="", PrimaryGroup_id=queryPrimaryGroup.id, SecondaryGroup_id=SecondaryGroup_id)
+ self.tvFTdict["Session"].add(querySecondaryGroup)
+ self.tvFTdict["Session"].commit()
+ self.tvFTdict["Session"].flush()
+
+ #Build The Tree
+ queryRecordGroup = sessions["ProjectManager"].query(RecordGroup).order_by(RecordGroup.Title).all()
+
+ #Top Level is The Record Groups
+ for record in queryRecordGroup:
+
+ #Each Category is a Separate SecondaryGroup_i
+ SecondaryGroup_id += 1
+ self.tvFTdict["Session"].begin()
+ NewSecondaryGroup = self.tvFTdict["SecondaryGroup"](NameText=record.Title, PrimaryGroup_id=queryPrimaryGroup.id,
+ SecondaryGroup_id=SecondaryGroup_id)
+ self.tvFTdict["Session"].add(NewSecondaryGroup)
+ self.tvFTdict["Session"].commit()
+ self.tvFTdict["Session"].flush()
+
+ newtreeitemdata = self.TVTreeGroup.defaultTreeItem()
+ newtreeitemdata['PrimaryGroup_id'] = 1
+ newtreeitemdata['SecondaryGroup_id'] = SecondaryGroup_id
+ newtreeitemdata['DestinationPG'] = 1
+ newtreeitemdata['DestinationSG'] = SecondaryGroup_id
+
+ newtreeitemdata['ParentTree_id'] = "(0)"
+
+ newtreeitemdata["id"] = record.id
+ newtreeitemdata["ItemTable_id"] = 520
+ newtreeitemdata['Item_id'] = record.id
+ newtreeitemdata["ItemMaster_id"] = 673
+ newtreeitemdata["MasterTable_id"] = 673
+ newtreeitemdata['DisplayName'] = record.Title
+ newtreeitemdata['ItemLevel'] = 0
+ newtreeitemdata['Header'] = None
+
+ GenerateTable = self.tvFTdict["TreeItems"]
+ treedata = self.TVTreeGroup.AddTreeRoot("Ginger", [], newtreeitemdata=newtreeitemdata, LinkedTable=GenerateTable)
+
+ queryRecordGroup = sessions["ProjectManager"].query(FormItemData.id, FormItemData.ItemName, WidgetType.NameText) \
+ .outerjoin(WidgetType, FormItemData.WidgetType == WidgetType.id) \
+ .filter(FormItemData.RecordGroupID == record.id) \
+ .order_by(WidgetType.NameText, FormItemData.ItemName).all()
+
+ parenttreepath = ["(0)"]
+
+ for record2 in queryRecordGroup:
+
+ newtreeitemdata2 = self.TVTreeGroup.defaultTreeItem()
+ newtreeitemdata2['PrimaryGroup_id'] = 1
+ newtreeitemdata2['SecondaryGroup_id'] = SecondaryGroup_id
+ newtreeitemdata2['DestinationPG'] = 1
+ newtreeitemdata2['DestinationSG'] = SecondaryGroup_id
+
+ newtreeitemdata2['id'] = record2.id
+ newtreeitemdata2['TreePath'] = [treedata["Tree_id"]]
+ newtreeitemdata2['ParentTree_id'] = treedata["Tree_id"]
+ newtreeitemdata2["ItemTable_id"] = 514
+ newtreeitemdata2['Item_id'] = record2.id
+ newtreeitemdata2['DisplayName'] = record2.ItemName
+ newtreeitemdata2["ItemMaster_id"] = 673 #Regressed
+ newtreeitemdata2["MasterTable_id"] = 673
+ newtreeitemdata2['ItemLevel'] = 1
+ newtreeitemdata2['Header'] = None
+
+ print(newtreeitemdata2['ParentTree_id'])
+ treeid2 = self.TVTreeGroup.addTreeItem(GenerateTable, newtreeitemdata2)
+
+ self.ui.tvFormItems.setModel(self.TVTreeGroup)
+
+ @QtCore.Slot()
+ def on_tblFormItems_clicked(self):
+ self.init_WidgetForm()
+ self.blockWSignals(True)
+ data = {}
+ data["row"] = self.ui.tblFormItems.selectionModel().currentIndex().row()
+ data["column"] = self.ui.tblFormItems.selectionModel().currentIndex().column()
+ data["column"] = self.ui.tblFormItems.selectionModel().currentIndex().column()
+ data["Value"] = self.ui.tblFormItems.model().index(data["row"], data["column"], None).data()
+ data["id"] = self.ui.tblFormItems.model().index(data["row"], 0, None).data()
+
+ self.ui.txtFormItem_id.setText(str(data["id"]))
+
+ if data["id"] is not None:
+ self.on_FIForm_load(int(data["id"]))
+
+ self.blockWSignals(False)
+
+ @QtCore.Slot()
+ def on_cmdCreateNewFormItem_clicked(self):
+ self.createsaveFormItem(None, None, None)
+
+ @QtCore.Slot()
+ def on_cmdCreateNewFormItem_2_clicked(self):
+ DG_id = int(self.ui.cmbFIDataGroup.model().index(self.ui.cmbFIDataGroup.currentIndex(), 0, None).data())
+ self.createsaveFormItem(None, DG_id, None)
+
+ @QtCore.Slot()
+ def on_cmdCreateNewVCombobox_clicked(self):
+ self.createsaveFormItem(None, None, "ComboBox")
+
+ @QtCore.Slot()
+ def on_cmdCreateNewVSpnBox_clicked(self):
+ self.createsaveFormItem(None, None, "SpnBox")
+
+ @QtCore.Slot()
+ def on_cmdCreateNewVDateTime_clicked(self):
+ self.createsaveFormItem(None, None, "DateTime")
+
+ @QtCore.Slot()
+ def on_cmdCreateNewVTreeView_clicked(self):
+ self.createsaveFormItem(None, None, "TreeView")
+
+ @QtCore.Slot()
+ def on_cmdCreateNewVTable_clicked(self):
+ self.createsaveFormItem(None, None, "Table")
+
+ @QtCore.Slot()
+ def on_cmdCreateNewVList_clicked(self):
+ self.createsaveFormItem(None, None, "List")
+
+ @QtCore.Slot()
+ def on_cmdCreateNewVCheckBox_clicked(self):
+ self.createsaveFormItem(None, None, "CheckBox")
+
+
+ def createsaveFormItem(self, record_id=None, DataGroup_id=None, Widget=None):
+
+ if record_id is None:
+ WidgetType = None
+
+ if Widget == "ComboBox":
+ WidgetType = 6
+
+ if Widget == "SpnBox":
+ WidgetType = 16
+
+ if Widget == "DateTime":
+ WidgetType = 10
+
+ if Widget == "TreeView":
+ WidgetType = 7
+
+ if Widget == "Table":
+ WidgetType = 14
+
+ if Widget == "List":
+ WidgetType = 19
+
+ if Widget == "CheckBox":
+ WidgetType = 2
+
+ self.instanceDict["ProgramBase"].session.begin()
+
+ qryRecord = FormItemData(RecordGroupID=DataGroup_id, WidgetType=WidgetType)
+ self.instanceDict["ProgramBase"].session.add(qryRecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormItemData).filter_by(id=record_id).first()
+ self.instanceDict["ProgramBase"].session.begin()
+
+ if self.FormItemDict["RecordGroup"] is not None: qryRecord.RecordGroupID = self.FormItemDict["RecordGroup"]["Record_id"]
+ if self.FormItemDict["WidgetType"] is not None: qryRecord.WidgetType = self.FormItemDict["WidgetType"]["Record_id"]
+ if self.FormItemDict["ItemName"] is not None: qryRecord.ItemName = str(self.FormItemDict["ItemName"]["Value"])
+ if self.FormItemDict["DefaultValue"] is not None: qryRecord.DefaultValue = str(self.FormItemDict["DefaultValue"]["Value"])
+ if self.FormItemDict["WidgetName"] is not None: qryRecord.WidgetName = str(self.FormItemDict["WidgetName"]["Value"])
+ if self.FormItemDict["SetValue"] is not None: qryRecord.SetValue = self.FormItemDict["SetValue"]["Record_id"]
+ if self.FormItemDict["GetValue"] is not None: qryRecord.GetValue = self.FormItemDict["GetValue"]["Record_id"]
+ if self.FormItemDict["GetString"] is not None: qryRecord.GetString = str(self.FormItemDict["GetString"]["Value"])
+ if self.FormItemDict["UpdateValue"] is not None: qryRecord.UpdateValue = self.FormItemDict["UpdateValue"]["Record_id"]
+ if self.FormItemDict["QueryRecord"] is not None: qryRecord.QueryRecord = str(self.FormItemDict["QueryRecord"]["Value"])
+ #if self.FormItemDict["ClearWidget"] is not None: qryRecord.ClearWidget = str(self.FormItemDict["ClearWidget"]["Value"])
+ if self.FormItemDict["FilterCheck"] is not None: qryRecord.FilterCheckBox = str(self.FormItemDict["FilterCheck"]["Value"])
+ if self.FormItemDict["Description"] is not None: qryRecord.Description = str(self.FormItemDict["Description"]["Value"])
+ if self.FormItemDict["DatabaseField"] is not None: qryRecord.DatabaseField = str(self.FormItemDict["DatabaseField"]["Record_id"])
+ if self.FormItemDict["ViewOrder"] is not None: qryRecord.ViewOrder = str(self.FormItemDict["ViewOrder"]["Value"])
+ if self.FormItemDict["Label"] is not None: qryRecord.Label = str(self.FormItemDict["Label"]["Value"])
+ if self.FormItemDict["ReferenceSession"] is not None: qryRecord.ReferenceSession = str(self.FormItemDict["ReferenceSession"]["Record_id"])
+ if self.FormItemDict["ReferenceTable"] is not None: qryRecord.ReferenceTable = str(self.FormItemDict["ReferenceTable"]["Record_id"])
+ if self.FormItemDict["ReferenceField"] is not None: qryRecord.ReferenceField = str(self.FormItemDict["ReferenceField"]["Record_id"])
+ if self.FormItemDict["JoinVariable"] is not None: qryRecord.JoinVariable = str(self.FormItemDict["JoinVariable"]["Value"])
+
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ self.on_tblFormItems_load()
+ return qryRecord.id
+
+ def init_FIForm(self):
+ self.blockFISignals(True)
+ self.ui.cmbFIDataGroup.setCurrentIndex(-1)
+ self.ui.cmbFIWidgetType.setCurrentIndex(-1)
+ self.ui.txtFIItemName.setText("")
+ self.ui.txtFIDefaultValue.setText("")
+ self.ui.txtFIWidgetName.setText("")
+ self.ui.cmbFISetValue.setCurrentIndex(-1)
+ self.ui.txtFISetString.setText("")
+ self.ui.cmbFIGetValue.setCurrentIndex(-1)
+ self.ui.txtFIGetString.setText("")
+ self.ui.cmbFIUpdateValue.setCurrentIndex(-1)
+ self.ui.txtFIUpdateString.setText("")
+ self.ui.txtFIQueryRecord.setText("")
+ self.ui.txtFIClearWidget.setText("")
+ self.ui.txtFIFilterCheck.setText("")
+ self.ui.txtFIDescription.setText("")
+ self.ui.cmbFITable.setCurrentIndex(-1)
+ self.ui.cmbFIField.setCurrentIndex(-1)
+ self.ui.txtFIViewOrder.setText("")
+ self.ui.txtFILabel.setText("")
+ self.ui.cmbFIRSession.setCurrentIndex(-1)
+ self.ui.cmbFIRTable.setCurrentIndex(-1)
+ self.ui.cmbFIRField.setCurrentIndex(-1)
+ self.ui.txtFIJoinVariable.setText("")
+ self.blockFISignals(False)
+
+ def loadFIForm(self):
+ self.blockFISignals(True)
+ self.ui.cmbFIDataGroup.setCurrentIndex(-1)
+ self.ui.cmbFIWidgetType.setCurrentIndex(-1)
+ self.ui.txtFIItemName.setText("")
+ self.ui.txtFIDefaultValue.setText("")
+ self.ui.txtFIWidgetName.setText("")
+ self.ui.cmbFISetValue.setCurrentIndex(-1)
+ self.ui.txtFISetString.setText("")
+ self.ui.cmbFIGetValue.setCurrentIndex(-1)
+ self.ui.txtFIGetString.setText("")
+ self.ui.cmbFIUpdateValue.setCurrentIndex(-1)
+ self.ui.txtFIUpdateString.setText("")
+ self.ui.txtFIQueryRecord.setText("")
+ self.ui.txtFIClearWidget.setText("")
+ self.ui.txtFIFilterCheck.setText("")
+ self.ui.txtFIDescription.setText("")
+ self.ui.cmbFITable.setCurrentIndex(-1)
+ self.ui.cmbFIField.setCurrentIndex(-1)
+ self.ui.txtFIViewOrder.setText("")
+ self.ui.txtFILabel.setText("")
+ self.ui.cmbFIRSession.setCurrentIndex(-1)
+ self.ui.cmbFIRTable.setCurrentIndex(-1)
+ self.ui.cmbFIRField.setCurrentIndex(-1)
+ self.ui.txtFIJoinVariable.setText("")
+ self.blockFISignals(False)
+
+ def init_FIDict(self):
+ self.FormItemDict = {}
+ self.FormItemDict["RecordGroup"] = None
+ self.FormItemDict["WidgetType"] = None
+ self.FormItemDict["ItemName"] = None
+ self.FormItemDict["DefaultValue"] = None
+ self.FormItemDict["WidgetName"] = None
+ self.FormItemDict["SetValue"] = None
+ self.FormItemDict["GetValue"] = None
+ self.FormItemDict["GetString"] = None
+ self.FormItemDict["UpdateValue"] = None
+ self.FormItemDict["QueryRecord"] = None
+ self.FormItemDict["ClearWidget"] = None
+ self.FormItemDict["FilterCheck"] = None
+ self.FormItemDict["Description"] = None
+ self.FormItemDict["Database"] = None
+ self.FormItemDict["DatabaseField"] = None
+ self.FormItemDict["ViewOrder"] = None
+ self.FormItemDict["Label"] = None
+ self.FormItemDict["ReferenceSession"] = None
+ self.FormItemDict["ReferenceTable"] = None
+ self.FormItemDict["ReferenceField"] = None
+ self.FormItemDict["JoinVariable"] = None
+
+ def createFIDictFromTable(self):
+ pass
+
+ def createFIDictFromDatabase(self, record_id):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroup).filter_by(id=record_id).first()
+ self.init_FIDict()
+ if qryRecord.RecordGroupID is not None: self.FormItemDict["RecordGroup"] = qryRecord.RecordGroupID
+ if qryRecord.WidgetType is not None: self.FormItemDict["WidgetType"] = qryRecord.WidgetType
+ if qryRecord.ItemName is not None: self.FormItemDict["ItemName"] = qryRecord.ItemName
+ if qryRecord.DefaultValue is not None: self.FormItemDict["DefaultValue"] = qryRecord.DefaultValue
+ if qryRecord.WidgetName is not None: self.FormItemDict["WidgetName"] = qryRecord.WidgetName
+ if qryRecord.SetValue is not None: self.FormItemDict["SetValue"] = qryRecord.SetValue
+ if qryRecord.GetValue is not None: self.FormItemDict["GetValue"] = qryRecord.GetValue
+ if qryRecord.GetString is not None: self.FormItemDict["GetString"] = qryRecord.GetString
+ if qryRecord.UpdateValue is not None: self.FormItemDict["UpdateValue"] = qryRecord.UpdateValue
+ if qryRecord.QueryRecord is not None: self.FormItemDict["QueryRecord"] = qryRecord.QueryRecord
+ if qryRecord.ClearWidget is not None: self.FormItemDict["ClearWidget"] = qryRecord.ClearWidget
+ if qryRecord.FilterCheck is not None: self.FormItemDict["FilterCheck"] = qryRecord.FilterCheck
+ if qryRecord.Description is not None: self.FormItemDict["Description"] = qryRecord.Description
+ if qryRecord.Database is not None: self.FormItemDict["Database"] = qryRecord.Database
+ if qryRecord.DatabaseField is not None: self.FormItemDict["DatabaseField"] = qryRecord.DatabaseValue
+ if qryRecord.ViewOrder is not None: self.FormItemDict["ViewOrder"] = qryRecord.ViewOrder
+ if qryRecord.Label is not None: self.FormItemDict["Label"] = qryRecord.Label
+ if qryRecord.ReferenceSession is not None: self.FormItemDict["ReferenceSession"] = qryRecord.ReferenceSession
+ if qryRecord.ReferenceTable is not None: self.FormItemDict["ReferenceTable"] = qryRecord.ReferenceTable
+ if qryRecord.ReferenceField is not None: self.FormItemDict["ReferenceField"] = qryRecord.ReferenceField
+ if qryRecord.JoinVariable is not None: self.FormItemDict["JoinVariable"] = qryRecord.JoinVariable
+
+
+ def blockFISignals(self, Value):
+ self.ui.cmbFIDataGroup.blockSignals(Value)
+ self.ui.cmbFIWidgetType.blockSignals(Value)
+ self.ui.txtFIItemName.blockSignals(Value)
+ self.ui.txtFIDefaultValue.blockSignals(Value)
+ self.ui.txtFIWidgetName.blockSignals(Value)
+ self.ui.cmbFISetValue.blockSignals(Value)
+ self.ui.cmbFIGetValue.blockSignals(Value)
+ self.ui.txtFIGetString.blockSignals(Value)
+ self.ui.cmbFIUpdateValue.blockSignals(Value)
+ self.ui.txtFIQueryRecord.blockSignals(Value)
+ self.ui.txtFIClearWidget.blockSignals(Value)
+ self.ui.txtFIFilterCheck.blockSignals(Value)
+ self.ui.txtFIDescription.blockSignals(Value)
+ self.ui.cmbFITable.blockSignals(Value)
+ self.ui.cmbFIField.blockSignals(Value)
+ self.ui.txtFIViewOrder.blockSignals(Value)
+ self.ui.txtFILabel.blockSignals(Value)
+ self.ui.cmbFIRSession.blockSignals(Value)
+ self.ui.cmbFIRTable.blockSignals(Value)
+ self.ui.cmbFIRField.blockSignals(Value)
+ self.ui.txtFIJoinVariable.blockSignals(Value)
+
+ @QtCore.Slot(int)
+ def on_cmbFIDataGroup_currentIndexChanged(self, value):
+ if self.ui.chkFIDataGroup.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["RecordGroup"] = {"Record_id": self.ui.cmbFIDataGroup.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIDataGroup.model().index(value, 1, None).data(),
+ "Row": value}
+
+ qryRecordGroup = self.instanceDict["ProgramBase"].session.query(RecordGroup).filter_by(id=self.ui.cmbFIDataGroup.model().index(value, 0, None).data()).first()
+ if qryRecordGroup is not None:
+ queryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable).filter_by(Session=qryRecordGroup.SessionID).order_by(MasterTable.TableName)
+ self.ui.cmbFITable.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFITable.setModelColumn(1)
+
+ if qryRecordGroup.SessionID is not None:
+ for row in range(self.ui.cmbFISession.model().rowCount()):
+ if int(self.ui.cmbFISession.model().index(row, 0, None).data()) == int(qryRecordGroup.SessionID):
+ self.ui.cmbFISession.setCurrentIndex(row)
+ break
+
+ self.on_FITable_load(qryRecordGroup.SessionID)
+ if qryRecordGroup.TableID is not None:
+ for row in range(self.ui.cmbFITable.model().rowCount()):
+ if int(self.ui.cmbFITable.model().index(row, 0, None).data()) == int(qryRecordGroup.TableID):
+ self.ui.cmbFITable.setCurrentIndex(row)
+ break
+ else:
+ self.ui.cmbFITable.setCurrentIndex(-1)
+ self.createsaveFormItem(record_id)
+
+
+ @QtCore.Slot(int)
+ def on_cmbFIWidgetType_currentIndexChanged(self, value):
+
+ if self.ui.chkRGMasterTable.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.createsaveFormItem(record_id)
+ self.FormItemDict["WidgetType"] = {"Record_id": self.ui.cmbFIWidgetType.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIWidgetType.model().index(value, 1, None).data(),
+ "Row": value}
+ self.createsaveFormItem(record_id)
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(Direction=2, WidgetID=self.ui.cmbFIWidgetType.model().index(value, 0, None).data())
+
+ self.ui.cmbFIGetValue.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIGetValue.setModelColumn(1)
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(Direction=3, WidgetID=self.ui.cmbFIWidgetType.model().index(value, 0, None).data())
+
+ self.ui.cmbFIUpdateValue.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFIUpdateValue.setModelColumn(1)
+
+ self.updateFormNames()
+
+ @QtCore.Slot(str)
+ def on_txtFIItemName_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["ItemName"] = {"Value": value}
+ self.updateFormNames()
+ #self.createsaveFormItem(record_id)
+
+ def updateFormNames(self):
+ value = self.ui.txtFIItemName.text()
+ rg_id = self.ui.cmbFIDataGroup.model().index(self.ui.cmbFIDataGroup.currentIndex(), 0, None).data()
+ if rg_id is not None:
+ queryDataGroup = self.instanceDict["ProgramBase"].session.query(RecordGroup).filter_by(id=int(rg_id)).first()
+ Abbr = queryDataGroup.Abbreviation
+
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ if self.ui.cmbFIWidgetType.model().index(self.ui.cmbFIWidgetType.currentIndex(), 0, None).data() is not None:
+ queryItemRecord = self.instanceDict["ProgramBase"].session.query(FormItemData).filter_by(id=record_id).first()
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(id=queryItemRecord.SetValue).first()
+ qryRecord2 = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(id=queryItemRecord.GetValue).first()
+ qryRecord3 = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling.id, WidgetDataHandling.Command, WidgetDataHandling.Direction)\
+ .order_by(WidgetDataHandling.Command).filter_by(id=queryItemRecord.UpdateValue).first()
+
+ widgetName = Abbr + value.replace(" ", "")
+
+ self.FormItemDict["WidgetName"] = {"Value": widgetName}
+ self.ui.txtFIWidgetName.setText(widgetName)
+
+ if qryRecord is not None:
+ setString = "self.ui." + widgetName + "." + qryRecord.Command
+ self.FormItemDict["SetString"] = {"Value": setString}
+ self.ui.txtFISetString.setText(setString)
+
+ for row in range(self.ui.cmbFISetValue.model().rowCount()):
+ if int(self.ui.cmbFISetValue.model().index(row, 0, None).data()) == int(queryItemRecord.SetValue):
+ self.ui.cmbFISetValue.setCurrentIndex(row)
+ setString = "self.ui." + widgetName + "." + str(self.ui.cmbFISetValue.model().index(self.ui.cmbFISetValue.currentIndex(), 1, None).data())
+
+ self.FormItemDict["SetString"] = {"Record_id": self.ui.cmbFISetValue.model().index(self.ui.cmbFISetValue.currentIndex(), 0, None).data(),
+ "Value": setString,
+ "Row": row}
+ self.ui.txtFISetString.setText(setString)
+ newcommand = setString.replace("value", value)
+ self.ui.txtFIClearWidget.setText("self.ui." + self.ui.txtFIWidgetName.text() + "." + newcommand)
+ self.FormItemDict["ClearWidget"] = "self.ui." + self.ui.txtFIWidgetName.text() + "." + newcommand
+ break
+
+ if qryRecord2 is not None:
+ for row in range(self.ui.cmbFIGetValue.model().rowCount()):
+ if int(self.ui.cmbFIGetValue.model().index(row, 0, None).data()) == int(queryItemRecord.GetValue):
+ self.ui.cmbFIGetValue.setCurrentIndex(row)
+ getString = "self.ui." + widgetName + "." + str(self.ui.cmbFIGetValue.model().index(self.ui.cmbFIGetValue.currentIndex(), 1, None).data())
+
+ self.FormItemDict["GetString"] = {"Record_id": self.ui.cmbFIGetValue.model().index(self.ui.cmbFIGetValue.currentIndex(), 0, None).data(),
+ "Value": getString,
+ "Row": row}
+ self.ui.txtFIGetString.setText(getString)
+ break
+
+ if qryRecord3 is not None:
+ for row in range(self.ui.cmbFIUpdateValue.model().rowCount()):
+ if int(self.ui.cmbFIUpdateValue.model().index(row, 0, None).data()) == int(queryItemRecord.UpdateValue):
+ self.ui.cmbFIUpdateValue.setCurrentIndex(row)
+ getString = "self.ui." + widgetName + "." + str(self.ui.cmbFIUpdateValue.model().index(self.ui.cmbFIUpdateValue.currentIndex(), 1, None).data())
+
+ self.FormItemDict["GetString"] = {"Record_id": self.ui.cmbFIUpdateValue.model().index(self.ui.cmbFIUpdateValue.currentIndex(), 0, None).data(),
+ "Value": getString,
+ "Row": row}
+ self.ui.txtFIUpdateString.setText(getString)
+ break
+
+ self.FormItemDict["FilterCheck"] = {"Value": "chk" + Abbr + value + "_Filter"}
+ self.ui.txtFIFilterCheck.setText("chk" + Abbr + value + "_Filter")
+ self.ui.txtFILabel.setText("lbl" + Abbr + value + "_Filter")
+
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIDefaultValue_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["DefaultValue"] = {"Value": value}
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormItemData).filter_by(id=record_id).first()
+ qryRecord2 = self.instanceDict["ProgramBase"].session.query(WidgetDataHandling).filter_by(Direction=1, WidgetID=qryRecord.WidgetType).first()
+ if qryRecord.DefaultValue is not None:
+ newcommand = qryRecord2.Command.replace("value", value)
+ self.ui.txtFIClearWidget.setText("self.ui." + self.ui.txtFIWidgetName.text() + "." + newcommand)
+
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIWidgetName_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["WidgetName"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFISetValue_currentIndexChanged(self, value):
+ if self.ui.chkRGMasterTable.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ setString = str(self.ui.cmbFISetValue.model().index(value, 1, None).data())
+ self.FormItemDict["SetValue"] = {"Record_id": self.ui.cmbFISetValue.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFISetValue.model().index(value, 1, None).data(),
+ "Row": value}
+
+ self.ui.txtFISetString.setText(self.ui.txtFIWidgetName.text() + setString)
+ self.createsaveFormItem(record_id)
+
+
+ @QtCore.Slot(int)
+ def on_cmbFIGetValue_currentIndexChanged(self, value):
+ if self.ui.chkRGMasterTable.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ getString = str(self.ui.cmbFIGetValue.model().index(value, 1, None).data())
+ self.FormItemDict["GetValue"] = {"Record_id": self.ui.cmbFIGetValue.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIGetValue.model().index(value, 1, None).data(),
+ "Row": value}
+
+ self.ui.txtFIGetString.setText(self.ui.txtFIWidgetName.text() + getString)
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIGetString_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["GetString"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFIUpdateValue_currentIndexChanged(self, value):
+ if self.ui.chkRGMasterTable.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ updateString = str(self.ui.cmbFIUpdateValue.model().index(value, 1, None).data())
+ self.FormItemDict["UpdateValue"] = {"Record_id": self.ui.cmbFIUpdateValue.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIUpdateValue.model().index(value, 1, None).data(),
+ "Row": value}
+ self.ui.txtFIUpdateString.setText(self.ui.txtFIWidgetName.text() + updateString)
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIQueryRecord_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["QueryRecord"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIClearWidget_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["ClearWidget"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIFilterCheck_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["FilterCheck"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIDescription_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["Description"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFISession_currentIndexChanged(self, value):
+ session_id = self.ui.cmbFISession.model().index(value, 0).data()
+ queryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable).filter_by(Session=session_id).order_by(MasterTable.TableName)
+ self.ui.cmbFITable.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFITable.setModelColumn(1)
+
+ @QtCore.Slot(int)
+ def on_cmbFITable_currentIndexChanged(self, value):
+ table_id = self.ui.cmbFITable.model().index(value, 0, None).data()
+ self.on_FIField_load(table_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFIField_currentIndexChanged(self, value):
+ if self.ui.chkFIDatabaseField.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["DatabaseField"] = {"Record_id": self.ui.cmbFIField.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIField.model().index(value, 1, None).data(),
+ "Row": value}
+
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation).filter_by(id=int(self.ui.cmbFIField.model().index(value, 0, None).data())).first()
+ self.ui.txtFIDatabaseFieldType.setText(queryRecord.DataType_Value)
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFIRSession_currentIndexChanged(self, value):
+ session_id = self.ui.cmbFIRSession.model().index(value, 0, None).data()
+ record_id = int(self.ui.txtFormItem_id.text())
+
+ self.on_FIRTable_load(session_id)
+ self.FormItemDict["ReferenceSession"] = {"Record_id": self.ui.cmbFIRSession.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIRSession.model().index(value, 1, None).data(),
+ "Row": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFIRTable_currentIndexChanged(self, value):
+ table_id = self.ui.cmbFIRTable.model().index(value, 0, None).data()
+ record_id = int(self.ui.txtFormItem_id.text())
+
+ self.on_FIRField_load(table_id)
+ self.FormItemDict["ReferenceTable"] = {"Record_id": self.ui.cmbFIRTable.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIRTable.model().index(value, 1, None).data(),
+ "Row": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFIRField_currentIndexChanged(self, value):
+ table_id = self.ui.cmbFIRField.model().index(value, 0, None).data()
+ record_id = int(self.ui.txtFormItem_id.text())
+
+ #self.on_FIRField_load(table_id)
+ self.FormItemDict["ReferenceField"] = {"Record_id": self.ui.cmbFIRField.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFIRField.model().index(value, 1, None).data(),
+ "Row": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIViewOrder_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["ViewOrder"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFILabel_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["Label"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIReferenceTable_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["ReferenceTable"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+
+ @QtCore.Slot(str)
+ def on_txtFIJoinVariable_textChanged(self, value):
+ if self.ui.chkRGGroupName.isChecked():
+ self.on_tblFormItems_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["JoinVariable"] = {"Value": value}
+ self.createsaveFormItem(record_id)
+ #endregion
+
+ #region Form Functions
+ def on_FFTable_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate.id, FormFunctionTemplate.FunctionName)
+ self.ui.tblFormFunctions.setModel(QueryTableModel(qryRecord))
+ self.ui.tblFormFunctions.setColumnWidth(0,0)
+
+ def on_FFForm_load(self):
+ self.ui.txtFFName.setText("")
+ self.ui.txtFFPath.setText("")
+ self.ui.lstFFVariables.setText("")
+ self.ui.txtFFFunction.setText("")
+
+ def on_tblFormFunction_clicked(self):
+ self.init_WidgetForm()
+ self.blockWSignals(True)
+ data = {}
+ data["row"] = self.ui.tblFormFunctions.selectionModel().currentIndex().row()
+ data["column"] = self.ui.tblFormFunctions.selectionModel().currentIndex().column()
+ data["column"] = self.ui.tblFormFunctions.selectionModel().currentIndex().column()
+ data["Value"] = self.ui.tblFormFunctions.model().index(data["row"], data["column"], None).data()
+ data["id"] = self.ui.tblFormFunctions.model().index(data["row"], 0, None).data()
+
+ self.ui.txtFFID.setText(str(data["id"]))
+
+ self.on_FIFormFunction_load(int(data["id"]))
+
+ def on_cmdCreateNewFormFunction_clicked(self):
+ pass
+
+ def createsaveFormFunction(self, record_id=None):
+ if record_id is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryRecord = FormFunctionTemplate()
+ self.instanceDict["ProgramBase"].session.add(qryRecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(id=record_id).first()
+ self.instanceDict["ProgramBase"].session.begin()
+
+ if self.FormFunctionDict["RecordGroup"] is not None: qryRecord.RecordGroupID = self.FormItemDict["RecordGroup"][
+ "Record_id"]
+
+ def initFFForm(self):
+ pass
+
+ def on_FIFormFunction_load(self, Record_id):
+ pass
+
+ def initFFDict(self):
+ pass
+
+ def createFFDictFromTable(self):
+ pass
+
+ def createFFDictFromDatabase(self):
+ pass
+
+ def blockFFSignals(self):
+ pass
+ #endregion
+
+
+ #region Function Template
+
+ def init_FunctionTemplate(self):
+ self.on_FTTable_load()
+ self.on_lstFTVariables_load()
+ self.on_lstFTProcesses_load()
+ self.ui.cmdFTSaveCode.setEnabled(True)
+ self.on_VariableList_load()
+ self.on_FunctionList_load()
+ self.ui.lstFTVariables.clicked.connect(self.on_lstFTVariables_clicked)
+ self.ui.lstFTProcesses.clicked.connect(self.on_lstFTProcesses_clicked)
+ self.doc = self.ui.txtFunctionTemplateCode.document()
+ self.cursor = QtGui.QTextCursor(self.doc)
+
+ self.on_cmbFITRecordGroup_load()
+
+ def on_FTTable_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate.id, FormFunctionTemplate.FunctionName, FormFunctionTemplate.DefaultOrder).filter_by(SubFunction=0)
+ self.ui.tblFunctionTemplate.setModel(QueryTableModel(qryRecord))
+ self.ui.tblFunctionTemplate.resizeColumnsToContents()
+ self.ui.tblFunctionTemplate.setColumnWidth(0,0)
+
+ self.ui.lstFormFunctions.setModel(QueryTableModel(qryRecord))
+ self.ui.lstFormFunctions.setModelColumn(1)
+
+ def on_VariableList_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(VariableList.id, VariableList.ItemName).order_by(VariableList.ItemName)
+ self.ui.lstFTVariables.setModel(QueryTableModel(qryRecord))
+ self.ui.lstFTVariables.setModelColumn(1)
+
+ def on_FunctionList_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FunctionList.id, FunctionList.ItemName)
+ self.ui.lstFTProcesses.setModel(QueryTableModel(qryRecord))
+ self.ui.lstFTProcesses.setModelColumn(1)
+
+ def on_cmbFITRecordGroup_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(RecordGroup.id, RecordGroup.Title)
+ self.ui.cmbFITRecordGroup.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFITRecordGroup.setModelColumn(1)
+
+ def on_FTForm_load(self):
+ self.ui.txtFTName.setText("")
+ self.ui.txtFTPath.setText("")
+ self.ui.lstFTVariables.setText("")
+ self.ui.txtFTFunction.setText("")
+ self.ui.txtFunctionTemplateCode.setText("")
+ self.ui.chkSubFunction.setCheckState(0)
+ self.FunctionTemplateCode = "Clean"
+
+ # Setup the QTextEdit editor configuration
+ self.ui.txtFunctionTemplateCode.setAutoFormatting(QtGui.QTextEdit.AutoAll)
+ self.ui.txtFunctionTemplateCode.selectionChanged.connect(self.update_format)
+ # Initialize default font size.
+ font = QtGui.QFont('Arial', 12)
+ self.ui.txtFunctionTemplateCode.setFont(font)
+ # We need to repeat the size to init the current format.
+ self.ui.txtFunctionTemplateCode.setFontPointSize(12)
+
+ self.ui.cmdFTSaveCode.setEnabled(True)
+
+ @QtCore.Slot()
+ def on_tblFunctionTemplate_clicked(self):
+ self.init_WidgetForm()
+ self.blockWSignals(True)
+ data = {}
+ data["row"] = self.ui.tblFunctionTemplate.selectionModel().currentIndex().row()
+ data["column"] = self.ui.tblFunctionTemplate.selectionModel().currentIndex().column()
+ data["column"] = self.ui.tblFunctionTemplate.selectionModel().currentIndex().column()
+ data["Value"] = self.ui.tblFunctionTemplate.model().index(data["row"], data["column"], None).data()
+ data["id"] = self.ui.tblFunctionTemplate.model().index(data["row"], 0, None).data()
+
+ self.ui.txtFTID.setText(str(data["id"]))
+
+ self.on_FTForm_load(int(data["id"]))
+
+ self.ui.cmdFTSaveCode.setEnabled(True)
+
+ def on_cmdCreateNewFunctionTemplate_clicked(self):
+ pass
+
+ def createsaveFunctionTemplate(self, record_id=None):
+ if record_id is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryRecord = FormFunctionTemplate()
+ self.instanceDict["ProgramBase"].session.add(qryRecord)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ else:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(id=record_id).first()
+ self.instanceDict["ProgramBase"].session.begin()
+
+ if self.FunctionTemplateDict["RecordGroup"] is not None: qryRecord.RecordGroupID = self.FormItemDict["RecordGroup"][
+ "Record_id"]
+
+ def initFTForm(self):
+ self.ui.txtFTName.setText("")
+ self.ui.txtFTPath.setText("")
+ self.ui.txtFTFilename.setText("")
+
+ def on_FTForm_load(self, Record_id):
+ self.initFTForm()
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(id=Record_id).first()
+
+ if qryRecord.Filename is not None:
+ self.ui.txtFTName.setText(qryRecord.FunctionName)
+ self.ui.txtFTDescription.setText(qryRecord.Description)
+ self.ui.txtFTPath.setText(qryRecord.FunctionPath)
+ self.ui.txtFTFilename.setText(qryRecord.Filename)
+ self.ui.chkSubFunction.setChecked(qryRecord.SubFunction)
+ self.ui.spnFunctionOrder.setValue(qryRecord.DefaultOrder)
+ f2 = open(os.path.join(qryRecord.FunctionPath, qryRecord.Filename), "r")
+ self.ui.txtFunctionTemplateCode.setPlainText(f2.read())
+ self.on_txtFunctionTemplateCode_2_load(Record_id)
+
+ @QtCore.Slot(int)
+ def on_spnFunctionOrder_valueChanged(self, value):
+ print(value)
+
+ @QtCore.Slot()
+ def on_cmdFTSaveCode_clicked(self):
+ filefullpath = self.ui.txtFTPath.text()
+ fname = self.ui.txtFTFilename.text()
+ filefullpath = os.path.join(filefullpath, fname)
+
+ Record_id = self.ui.txtFTID.text()
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(id=Record_id).first()
+
+ if qryRecord.Filename is None:
+ None
+ else:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryRecord.FunctionName = self.ui.txtFTName.text()
+ qryRecord.Description = self.ui.txtFTDescription.text()
+ qryRecord.FunctionPath = self.ui.txtFTPath.text()
+ qryRecord.Filename = self.ui.txtFTFilename.text()
+ qryRecord.SubFunction = self.ui.chkSubFunction.isChecked()
+ qryRecord.DefaultOrder = self.ui.spnFunctionOrder.value()
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ f2 = open(os.path.join(qryRecord.FunctionPath, qryRecord.Filename), "r")
+ self.ui.txtFunctionTemplateCode.setPlainText(f2.read())
+
+ with open(filefullpath, 'w') as f:
+ f.write(self.ui.txtFunctionTemplateCode.toPlainText())
+
+ self.on_txtFunctionTemplateCode_2_load(Record_id)
+
+ self.FunctionTemplateCode = "Clean"
+ self.ui.cmdFTSaveCode.setEnabled(True)
+
+
+ @QtCore.Slot()
+ def on_txtFunctionTemplateCode_textChanged(self):
+ if not self.ui.chkFTAutoSaveCode.checkState():
+ self.FunctionTemplateCode = "Dirty"
+ self.ui.cmdFTSaveCode.setEnabled(True)
+ else:
+ self.on_cmdFTSaveCode_clicked()
+
+ @QtCore.Slot()
+ def on_cmdFTSaveCode_selectionChanged(self):
+ print()
+
+ def on_lstFTVariables_load(self):
+ pass
+
+ def on_lstFTProcesses_load(self):
+ pass
+
+ def initFTDict(self):
+ self.FTDict = {}
+ self.FTDict["FTName"] = ""
+ self.FTDict["FTPath"] = ""
+ self.FTDict["FTFilename"] = ""
+ self.FTDict["txtFunctionTemplateCode"] = ""
+
+ def createFTDictFromTable(self):
+ pass
+
+ def createFTDictFromDatabase(self):
+ pass
+
+ def blockFTSignals(self, Value):
+ self.ui.txtFTName.blockSignals(Value)
+ self.ui.txtFTPath.blockSignals(Value)
+ self.ui.txtFTFilename.blockSignals(Value)
+ self.ui.txtFunctionTemplateCode.blockSignals(Value)
+
+ @QtCore.Slot()
+ def on_lstFTVariables_clicked(self):
+
+ newtext = str(self.ui.lstFTVariables.model().index(self.ui.lstFTVariables.currentIndex().row(), 1).data())
+ self.ui.txtFunctionTemplateCode.textCursor().insertText("{" + newtext + "}")
+
+ @QtCore.Slot()
+ def on_lstFTProcesses_clicked(self):
+ self.ui.txtFTDescription.setText(str(self.ui.lstFTProcesses.model().index(self.ui.lstFTProcesses.currentIndex().row(), 1).data()))
+
+ newtext = str(self.ui.lstFTVariables.model().index(self.ui.lstFTVariables.currentIndex().row(), 1).data())
+ self.ui.txtFunctionTemplateCode.textCursor().insertText("{" + newtext + "}")
+
+ def on_txtFunctionTemplateCode_2_load(self, Record_id):
+
+ rg_id = self.ui.cmbFITRecordGroup.model().index(self.ui.cmbFITRecordGroup.currentIndex(), 0).data()
+ frm_id = self.ui.cmbFITRecordGroup.model().index(self.ui.cmbFITRecordGroup.currentIndex(), 0).data()
+
+ self.createRGDictFromDatabase(rg_id)
+
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(id=Record_id).first()
+ qryFormRecord = self.instanceDict["ProgramBase"].session.query(FormItemData).filter_by(RecordGroupID=int(3)).all()
+
+ if qryRecord.Filename is not None:
+ self.ui.txtFTName.setText(qryRecord.FunctionName)
+ self.ui.txtFTDescription.setText(qryRecord.Description)
+ self.ui.txtFTPath.setText(qryRecord.FunctionPath)
+ self.ui.txtFTFilename.setText(qryRecord.Filename)
+
+ f = open(os.path.join(qryRecord.FunctionPath, qryRecord.Filename), "r")
+ textbody = ""
+
+ processdelay = False
+ bufferlist = []
+ ignore = False
+ findcase = None
+
+ for textline in f:
+ if processdelay == False:
+ if "{BeginWidgetList}" in textline:
+ bufferdict = {}
+ bufferlist = []
+ processdelay = True
+ else:
+ #Process the Line Item
+ itemlist = set(re.findall('\{.*?\}', textline))
+ for item in itemlist:
+ if "RecordGroup." in item:
+ if item[13:-1] in self.RGDict:
+ #print("found", item[13:-1], self.RGDict[item[13:-1]])
+ itemdictname = self.RGDict[item[13:-1]]
+ textline = textline.replace(item, itemdictname)
+ else:
+ print("not found", self.RGDict)
+ else:
+ print(textline)
+ textbody += textline
+ else:
+ if "{EndWidgetList}" in textline:
+
+ # Process the Block
+ #self.createFIDictFromDatabase(record.id)
+ for record in qryFormRecord:
+ FormRecordDict = record.__dict__
+ '''print(FormRecordDict)'''
+
+
+ for bufferitem in bufferlist:
+ itemlist = set(re.findall('\{.*?\}', bufferitem))
+ textline = bufferitem
+ for item in itemlist:
+ if "SubFunction" in item:
+ textline = ""
+ FunctionName = re.search('\((.*)\[', bufferitem).group(0)
+ RegionName = re.search('\[\{(.*)\}\]', bufferitem).group(0)
+
+ if "RecordGroup.Form." in RegionName:
+ if RegionName[19:-2] in FormRecordDict:
+ itemdictname = FormRecordDict[RegionName[19:-2]]
+ if RegionName[19:-2] == "WidgetType":
+ qryWidgetType = self.instanceDict["ProgramBase"].session.query(WidgetData).filter_by(id=int(itemdictname)).first()
+ RegionNameValue = qryWidgetType.Title
+
+ sublist = self.SubFunction(FunctionName[1:-1], RegionNameValue)
+ print(FormRecordDict["ItemName"], FunctionName[1:-1], RegionNameValue, bufferitem)
+ for subitem in sublist:
+ #print(subitem)
+ textline = subitem
+ itemlist2 = set(re.findall('\{.*?\}', subitem))
+ for item2 in itemlist2:
+ #print(item2)
+ if "RecordGroup.Form." in item2:
+ if item2[18:-1] in FormRecordDict:
+ itemdictname = FormRecordDict[item2[18:-1]]
+ #print(item2)
+ if itemdictname is not None:
+ textline = textline.replace(item2, itemdictname)
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not FoundS2", FormRecordDict, bufferitem, item, itemdictname)
+
+ elif "RecordGroup." in item2:
+ if item2[13:-1] in self.RGDict:
+ itemdictname = self.RGDict[item2[13:-1]]
+
+ if itemdictname is not None:
+ textline = textline.replace(item2, itemdictname)
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not FoundS2", FormRecordDict, bufferitem, item, itemdictname)
+
+ else:
+ print("not found", item)
+
+ textbody += textline
+ textline = ""
+ textbody += "\n"
+
+ elif "RecordGroup.Form." in item:
+ if item[18:-1] in FormRecordDict:
+ itemdictname = FormRecordDict[item[18:-1]]
+ if itemdictname is not None:
+ textline = textline.replace(item, str(itemdictname))
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not Found1", FormRecordDict, bufferitem, item, itemdictname)
+
+ elif "RecordGroup." in item:
+ #print("test", item[13:-1])
+ if item[13:-1] in self.RGDict:
+ #print("found", item[13:-1], self.RGDict[item[13:-1]])
+ itemdictname = self.RGDict[item[13:-1]]
+ if itemdictname is not None:
+ textline = textline.replace(item, itemdictname)
+ else:
+ textline = textline.replace("}", "(" + FormRecordDict["ItemName"] + ")}")
+ print("Item Not Found2", FormRecordDict, bufferitem, item, itemdictname)
+ else:
+ print("not found", item)
+
+ elif "Select Case WidgetType" in item:
+ pass
+
+ textbody += textline
+ bufferlist = []
+ processdelay = False
+ else:
+ #Build the Block
+ if "If WidgetType" in textline:
+ ignore = True
+ findcase = textline[len("{If WidgetType"):-1]
+ caselines = []
+
+ if ignore == False:
+ bufferlist.append(textline)
+ else:
+ if "End If" in textline:
+ #This only works with on position for choices
+ bufferdict[findcase] = caselines
+ ignore = False
+
+
+ self.ui.txtFunctionTemplateCode_2.setPlainText(textbody)
+ record_id = 2
+ #self.createRGDictFromDatabase(record_id)
+ '''
+ self.RGDict
+ id
+ RecordGroup
+ FormID
+ Abbreviation
+ SessionID
+ TableID
+ IDWidgetName
+ IDWidgetType
+ NewItemWidgetName
+ NewItemWidgetType
+ CreateDictFunction'''
+
+ def init_FITTemplate(self):
+ self.blockFITSignals(True)
+ self.on_FITSession_load()
+ self.on_FITTable_load()
+ self.on_FITField_load()
+
+ #self.on_FITDataGroup_load()
+ self.on_FITWidgetType_load()
+
+ self.blockFITSignals(False)
+
+ def blockFITSignals(self, Value):
+ self.ui.cmbFITSession.blockSignals(Value)
+ self.ui.cmbFITTable.blockSignals(Value)
+ self.ui.cmbFITField.blockSignals(Value)
+ self.ui.cmbFITForm.blockSignals(Value)
+ self.ui.cmbFITWidgetName.blockSignals(Value)
+ self.ui.lstFITWidgetName.blockSignals(Value)
+ self.ui.cmbFITWidgetType.blockSignals(Value)
+
+ def on_FITSession_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(SessionNames.id, SessionNames.NameText)
+ self.ui.cmbFITSession.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFITSession.setModelColumn(1)
+
+ def on_FITTable_load(self, Session_id=None):
+ self.blockFITSignals(True)
+ if Session is None:
+ qryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable.id, MasterTable.TableName)
+ self.ui.cmbFITTable.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFITTable.setModelColumn(1)
+ else:
+ queryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable.id, MasterTable.TableName).filter_by(Session=Session_id).order_by(MasterTable.TableName)
+ self.ui.cmbFITTable.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFITTable.setModelColumn(1)
+ self.blockFITSignals(False)
+
+ def on_FITField_load(self, Table_id=None):
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation.id, FieldInformation.FieldName).filter_by(TableID=Table_id).order_by(FieldInformation.FieldName)
+ self.blockFITSignals(True)
+ if queryRecord.first() is None:
+ qryTable = self.instanceDict["ProgramBase"].session.query(MasterTable).filter_by(id=Table_id).first()
+
+ if qryTable is not None:
+ qryBase = self.instanceDict["ProgramBase"].session.query(SessionBase).filter_by(id=qryTable.Base).first()
+ if qryBase is not None:
+ myColumnList = DatabaseTools.get_fields_by_tablename(bases[qryBase.NameText], qryTable.TableName)
+
+ for key, value in enumerate(myColumnList):
+ qryTableField = self.instanceDict["ProgramBase"].session.query(FieldInformation).filter_by(TableID=Table_id, FieldName=value).first()
+
+ if qryTableField is None:
+ self.instanceDict["ProgramBase"].session.begin()
+ qryTableField = FieldInformation(TableID=int(Table_id),
+ FieldName=str(value),
+ DataType_Value=str(myColumnList[value]))
+ self.instanceDict["ProgramBase"].session.add(qryTableField)
+ self.instanceDict["ProgramBase"].session.commit()
+ self.instanceDict["ProgramBase"].session.flush()
+
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation.id, FieldInformation.FieldName).filter_by(
+ TableID=Table_id).order_by(FieldInformation.FieldName)
+ self.ui.cmbFITField.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFITField.setModelColumn(1)
+ else:
+ self.ui.cmbFITField.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFITField.setModelColumn(1)
+
+ self.blockFITSignals(False)
+
+ def on_FITWidgetType_load(self):
+ qryRecord = self.instanceDict["ProgramBase"].session.query(WidgetData.id, WidgetData.Title)
+ self.ui.cmbFITWidgetType.setModel(QueryTableModel(qryRecord))
+ self.ui.cmbFITWidgetType.setModelColumn(1)
+
+
+ @QtCore.Slot(int)
+ def on_cmbFITSession_currentIndexChanged(self, value):
+ session_id = self.ui.cmbFITSession.model().index(value, 0).data()
+ queryRecord = self.instanceDict["ProgramBase"].session.query(MasterTable).filter_by(Session=session_id).order_by(MasterTable.TableName)
+ self.ui.cmbFITTable.setModel(QueryTableModel(queryRecord))
+ self.ui.cmbFITTable.setModelColumn(1)
+
+ self.ui.on_FITTable_load(session_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFITTable_currentIndexChanged(self, value):
+ table_id = self.ui.cmbFITTable.model().index(value, 0, None).data()
+ self.on_FITField_load(table_id)
+
+ @QtCore.Slot(int)
+ def on_cmbFITField_currentIndexChanged(self, value):
+ if self.ui.chkFIDatabaseField.isChecked():
+ self.on_FITTable_load()
+ else:
+ if self.ui.txtFormItem_id.text() != "":
+ record_id = int(self.ui.txtFormItem_id.text())
+ self.FormItemDict["DatabaseField"] = {"Record_id": self.ui.cmbFITField.model().index(value, 0, None).data(),
+ "Value": self.ui.cmbFITField.model().index(value, 1, None).data(),
+ "Row": value}
+
+ queryRecord = self.instanceDict["ProgramBase"].session.query(FieldInformation).filter_by(id=int(self.ui.cmbFITField.model().index(value, 0, None).data())).first()
+ self.ui.txtFITDatabaseFieldType.setText(queryRecord.DataType_Value)
+ self.createsaveFormItem(record_id)
+
+
+ #endregion
+
+ def SubFunction(self, FunctionName, RegionName):
+ filename = FunctionName + ".txt"
+ qryRecord = self.instanceDict["ProgramBase"].session.query(FormFunctionTemplate).filter_by(Filename=filename).first()
+ if qryRecord.Filename is not None:
+ self.ui.txtFTName.setText(qryRecord.FunctionName)
+ self.ui.txtFTDescription.setText(qryRecord.Description)
+ self.ui.txtFTPath.setText(qryRecord.FunctionPath)
+ self.ui.txtFTFilename.setText(qryRecord.Filename)
+
+ f = open(os.path.join(qryRecord.FunctionPath, qryRecord.Filename), "r")
+ RegionList = []
+ InRegion = False
+ for textline in f:
+ if InRegion == False:
+ if "region " in textline and RegionName in textline:
+ InRegion = True
+ else:
+ if "endregion" in textline:
+ break
+ else:
+ RegionList.append(textline)
+
+ return(RegionList)
+
+def main():
+ app = QApplication(sys.argv)
+ widget = FormBuilder()
+ widget.show()
+ sys.exit(app.exec_())
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/PyFlow/UI/Forms/TextEditor/TextEditor.py b/PyFlow/UI/Forms/TextEditor/TextEditor.py
new file mode 100644
index 000000000..76acbbd48
--- /dev/null
+++ b/PyFlow/UI/Forms/TextEditor/TextEditor.py
@@ -0,0 +1,116 @@
+from PySide6.QtWidgets import QApplication, QMessageBox, QFileDialog
+from qtpy.QtCore import *
+from qtpy.QtGui import *
+
+from qtpy.QtWidgets import QTextEdit
+#import mdi_rc
+
+import uuid
+
+class MdiChild(QTextEdit):
+ sequenceNumber = 1
+ def __init__(self):
+ super(MdiChild, self).__init__()
+ self.PackageName = "TextEditor"
+ self.setAttribute(Qt.WA_DeleteOnClose)
+ self.isUntitled = True
+ self.uuid = uuid.uuid4()
+ self.supportedSoftwares = ["any"]
+ def newFile(self):
+ self.isUntitled = True
+ self.curFile = "document%d.txt" % MdiChild.sequenceNumber
+ MdiChild.sequenceNumber += 1
+ self.setWindowTitle(self.curFile + '[*]')
+
+ self.document().contentsChanged.connect(self.documentWasModified)
+
+ def loadFile(self, fileName):
+ file = QFile(fileName)
+ if not file.open(QFile.ReadOnly | QFile.Text):
+ QMessageBox.warning(self, "MDI",
+ "Cannot read file %s:\n%s." % (fileName, file.errorString()))
+ return False
+
+ instr = QTextStream(file)
+ QApplication.setOverrideCursor(Qt.WaitCursor)
+ self.setPlainText(instr.readAll())
+ QApplication.restoreOverrideCursor()
+
+ self.setCurrentFile(fileName)
+
+ self.document().contentsChanged.connect(self.documentWasModified)
+
+ return True
+
+ @staticmethod
+ def supportedSoftwares():
+ return ["any"]
+
+ def save(self):
+ if self.isUntitled:
+ return self.saveAs()
+ else:
+ return self.saveFile(self.curFile)
+
+ def saveAs(self):
+ fileName, _ = QFileDialog.getSaveFileName(self, "Save As", self.curFile)
+ if not fileName:
+ return False
+
+ return self.saveFile(fileName)
+
+ def saveFile(self, fileName):
+ file = QFile(fileName)
+
+ if not file.open(QFile.WriteOnly | QFile.Text):
+ QMessageBox.warning(self, "MDI",
+ "Cannot write file %s:\n%s." % (fileName, file.errorString()))
+ return False
+
+ outstr = QTextStream(file)
+ QApplication.setOverrideCursor(Qt.WaitCursor)
+ outstr << self.toPlainText()
+ QApplication.restoreOverrideCursor()
+
+ self.setCurrentFile(fileName)
+ return True
+
+ def userFriendlyCurrentFile(self):
+ return self.strippedName(self.curFile)
+
+ def currentFile(self):
+ return self.curFile
+
+ def closeEvent(self, event):
+ if self.maybeSave():
+ event.accept()
+ else:
+ event.ignore()
+
+ def documentWasModified(self):
+ self.setWindowModified(self.document().isModified())
+
+ def maybeSave(self):
+ if self.document().isModified():
+ ret = QMessageBox.warning(self, "MDI",
+ "'%s' has been modified.\nDo you want to save your "
+ "changes?" % self.userFriendlyCurrentFile(),
+ QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
+
+ if ret == QMessageBox.Save:
+ return self.save()
+
+ if ret == QMessageBox.Cancel:
+ return False
+
+ return True
+
+ def setCurrentFile(self, fileName):
+ self.curFile = QFileInfo(fileName).canonicalFilePath()
+ self.isUntitled = False
+ self.document().setModified(False)
+ self.setWindowModified(False)
+ self.setWindowTitle(self.userFriendlyCurrentFile() + "[*]")
+
+ def strippedName(self, fullFileName):
+ return QFileInfo(fullFileName).fileName()
\ No newline at end of file
diff --git a/PyFlow/UI/Forms/TextEditor/__init__.py b/PyFlow/UI/Forms/TextEditor/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/PyFlow/UI/Forms/__init__.py b/PyFlow/UI/Forms/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/PyFlow/UI/Tool/Tool.py b/PyFlow/UI/Tool/Tool.py
index 9e35f7c8b..57bda7cde 100644
--- a/PyFlow/UI/Tool/Tool.py
+++ b/PyFlow/UI/Tool/Tool.py
@@ -13,10 +13,9 @@
## limitations under the License.
-from nine import str
import uuid
-from Qt import QtWidgets
-from Qt import QtGui, QtCore
+from qtpy import QtWidgets
+from qtpy import QtGui, QtCore
from PyFlow.UI.Utils.stylesheet import editableStyleSheet
@@ -129,6 +128,7 @@ def name():
class ShelfTool(ToolBase):
"""Base class for shelf tools
"""
+
def __init__(self):
super(ShelfTool, self).__init__()
@@ -140,18 +140,64 @@ def getIcon():
return QtGui.QIcon.fromTheme("go-home")
def do(self):
- print(self.name(), "called!", self.canvas)
+ print(self.name(), "called!", self.canvas) # TODO: there is no 'canvas' yet
+
+class FormTool(QtWidgets.QMdiSubWindow, ToolBase):
+ """Base class for form tools
+ """
+ def __init__(self):
+ ToolBase.__init__(self)
+ QtWidgets.QMdiSubWindow.__init__(self)
+ self.setToolTip(self.toolTip())
+ self.setObjectName(self.uniqueName())
+
+ def supportedSoftwares():
+ """Under what software to work
+ """
+ return ["any"]
+
+ @staticmethod
+ def isSingleton():
+ return False
+ def onShow(self):
+ super(FormTool, self).onShow()
+ self.setWindowTitle(self.name())
+
+ def contextMenuBuilder(self):
+ return None
+
+ @staticmethod
+ def getIcon():
+ return None
+
+ def restoreState(self, settings):
+ super(FormTool, self).restoreState(settings)
+ self.setObjectName(self.uniqueName())
+
+ def closeEvent(self, event):
+ self.onDestroy()
+ self.parent().unregisterToolInstance(self)
+ event.accept()
+
+ def do(self):
+ print(self.name(), "called!", self.canvas)
class DockTool(QtWidgets.QDockWidget, ToolBase):
"""Base class for dock tools
"""
+
def __init__(self):
ToolBase.__init__(self)
QtWidgets.QDockWidget.__init__(self)
self.setToolTip(self.toolTip())
- self.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures)
- self.setAllowedAreas(QtCore.Qt.BottomDockWidgetArea | QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.RightDockWidgetArea | QtCore.Qt.TopDockWidgetArea)
+ #self.setFeatures(QtWidgets.QDockWidget.AllDockWidgetFeatures)
+ self.setAllowedAreas(
+ QtCore.Qt.BottomDockWidgetArea
+ | QtCore.Qt.LeftDockWidgetArea
+ | QtCore.Qt.RightDockWidgetArea
+ | QtCore.Qt.TopDockWidgetArea
+ )
self.setObjectName(self.uniqueName())
self.setTitleBarWidget(DockTitleBar(self))
self.setFloating(False)
@@ -184,7 +230,6 @@ def closeEvent(self, event):
def addButton(self, button):
self.titleBarWidget().addButton(button)
-
class DockTitleBar(QtWidgets.QWidget):
def __init__(self, dockWidget, renamable=False):
super(DockTitleBar, self).__init__(dockWidget)
@@ -193,7 +238,7 @@ def __init__(self, dockWidget, renamable=False):
self.layout().setContentsMargins(0, 0, 0, 1)
self.buttonsLay = QtWidgets.QHBoxLayout()
self.buttonsLay.setSpacing(1)
- self.buttonsLay.setMargin(1)
+ self.buttonsLay.setContentsMargins(1,1,1,1)
self.box = QtWidgets.QGroupBox("")
self.box.setLayout(self.buttonsLay)
self.box.setObjectName("Docked")
@@ -213,7 +258,7 @@ def __init__(self, dockWidget, renamable=False):
self.buttonSize = QtCore.QSize(14, 14)
self.dockButton = QtWidgets.QToolButton(self)
- self.dockButton.setIcon(QtGui.QIcon(':/split_window.png'))
+ self.dockButton.setIcon(QtGui.QIcon(":/split_window.png"))
self.dockButton.setMaximumSize(self.buttonSize)
self.dockButton.setAutoRaise(True)
self.dockButton.clicked.connect(self.toggleFloating)
@@ -221,7 +266,7 @@ def __init__(self, dockWidget, renamable=False):
self.closeButton = QtWidgets.QToolButton(self)
self.closeButton.setMaximumSize(self.buttonSize)
self.closeButton.setAutoRaise(True)
- self.closeButton.setIcon(QtGui.QIcon(':/close_window.png'))
+ self.closeButton.setIcon(QtGui.QIcon(":/close_window.png"))
self.closeButton.clicked.connect(self.closeParent)
self.buttonsLay.addSpacing(2)
@@ -257,11 +302,11 @@ def finishEdit(self):
def onFeaturesChanged(self, features):
if not features & QtWidgets.QDockWidget.DockWidgetVerticalTitleBar:
self.closeButton.setVisible(
- features & QtWidgets.QDockWidget.DockWidgetClosable)
+ features & QtWidgets.QDockWidget.DockWidgetClosable == QtWidgets.QDockWidget.DockWidgetClosable)
self.dockButton.setVisible(
- features & QtWidgets.QDockWidget.DockWidgetFloatable)
+ features & QtWidgets.QDockWidget.DockWidgetFloatable == QtWidgets.QDockWidget.DockWidgetFloatable)
else:
- raise ValueError('vertical title bar not supported')
+ raise ValueError("vertical title bar not supported")
def setTitle(self, title):
self.titleLabel.setText(title)
@@ -271,13 +316,18 @@ def ChangeFloatingStyle(self, state):
if not state:
self.box.setStyleSheet(editableStyleSheet().getStyleSheet())
else:
- self.box.setStyleSheet("""QGroupBox{
+ self.box.setStyleSheet(
+ """QGroupBox{
background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
stop: 0 %s,
stop: 0.6 %s,
- stop: 1.0 %s);}""" % ("rgba%s" % str(editableStyleSheet().ButtonsColor.getRgb()),
- "rgba%s" % str(editableStyleSheet().BgColorBright.getRgb()),
- "rgba%s" % str(editableStyleSheet().BgColorBright.getRgb())))
+ stop: 1.0 %s);}"""
+ % (
+ "rgba%s" % str(editableStyleSheet().ButtonsColor.getRgb()),
+ "rgba%s" % str(editableStyleSheet().BgColorBright.getRgb()),
+ "rgba%s" % str(editableStyleSheet().BgColorBright.getRgb()),
+ )
+ )
def update(self, *args, **kwargs):
self.ChangeFloatingStyle(self.parent().isFloating())
diff --git a/PyFlow/UI/Tool/__init__.py b/PyFlow/UI/Tool/__init__.py
index cab427f2d..76e99e3f2 100644
--- a/PyFlow/UI/Tool/__init__.py
+++ b/PyFlow/UI/Tool/__init__.py
@@ -14,6 +14,7 @@
from collections import defaultdict
+
__REGISTERED_TOOLS = defaultdict(list)
diff --git a/PyFlow/UI/UIInterfaces.py b/PyFlow/UI/UIInterfaces.py
index 49ccdf2e9..c78c6fda3 100644
--- a/PyFlow/UI/UIInterfaces.py
+++ b/PyFlow/UI/UIInterfaces.py
@@ -15,6 +15,7 @@
class IUINode(object):
"""docstring for IUINode."""
+
def __init__(self):
super(IUINode, self).__init__()
@@ -27,6 +28,7 @@ def serializationHook(self):
class IPropertiesViewSupport(object):
"""docstring for IPropertiesViewSupport."""
+
def __init__(self):
super(IPropertiesViewSupport, self).__init__()
@@ -39,24 +41,29 @@ class IDataExporter(object):
Editor data can be exported/imported to/from arbitrary formats
"""
+
def __init__(self):
super(IDataExporter, self).__init__()
@staticmethod
def creationDateString():
- raise NotImplementedError('creationDateString method of IDataExporter is not implemented')
+ raise NotImplementedError(
+ "creationDateString method of IDataExporter is not implemented"
+ )
@staticmethod
def version():
- raise NotImplementedError('version method of IDataExporter is not implemented')
+ raise NotImplementedError("version method of IDataExporter is not implemented")
@staticmethod
def displayName():
- raise NotImplementedError('displayName method of IDataExporter is not implemented')
+ raise NotImplementedError(
+ "displayName method of IDataExporter is not implemented"
+ )
@staticmethod
def toolTip():
- return ''
+ return ""
@staticmethod
def createImporterMenu():
@@ -68,11 +75,11 @@ def createExporterMenu():
@staticmethod
def doImport(pyFlowInstance):
- raise NotImplementedError('doImport method of IDataExporter is not implemented')
+ raise NotImplementedError("doImport method of IDataExporter is not implemented")
@staticmethod
def doExport(pyFlowInstance):
- raise NotImplementedError('doExport method of IDataExporter is not implemented')
+ raise NotImplementedError("doExport method of IDataExporter is not implemented")
class IPackage(object):
@@ -80,6 +87,7 @@ class IPackage(object):
Will be instantiated and used to create registered entities.
"""
+
def __init__(self):
super(IPackage, self).__init__()
@@ -89,7 +97,7 @@ def GetExporters():
:rtype: dict(str, class)
"""
- raise NotImplementedError('GetExporters method of IPackage is not implemented')
+ raise NotImplementedError("GetExporters method of IPackage is not implemented")
@staticmethod
def GetFunctionLibraries():
@@ -97,7 +105,9 @@ def GetFunctionLibraries():
:rtype: dict(str, object)
"""
- raise NotImplementedError('GetFunctionLibraries method of IPackage is not implemented')
+ raise NotImplementedError(
+ "GetFunctionLibraries method of IPackage is not implemented"
+ )
@staticmethod
def GetNodeClasses():
@@ -105,7 +115,9 @@ def GetNodeClasses():
:rtype: dict(str, class)
"""
- raise NotImplementedError('GetNodeClasses method of IPackage is not implemented')
+ raise NotImplementedError(
+ "GetNodeClasses method of IPackage is not implemented"
+ )
@staticmethod
def GetPinClasses():
@@ -113,7 +125,7 @@ def GetPinClasses():
:rtype: dict(str, class)
"""
- raise NotImplementedError('GetPinClasses method of IPackage is not implemented')
+ raise NotImplementedError("GetPinClasses method of IPackage is not implemented")
@staticmethod
def GetToolClasses():
@@ -121,7 +133,9 @@ def GetToolClasses():
:rtype: dict(str, class)
"""
- raise NotImplementedError('GetToolClasses method of IPackage is not implemented')
+ raise NotImplementedError(
+ "GetToolClasses method of IPackage is not implemented"
+ )
@staticmethod
def UIPinsFactory():
diff --git a/PyFlow/UI/Utils/ConvexHull.py b/PyFlow/UI/Utils/ConvexHull.py
index 01ebc9fbb..44abbc4ce 100644
--- a/PyFlow/UI/Utils/ConvexHull.py
+++ b/PyFlow/UI/Utils/ConvexHull.py
@@ -21,7 +21,7 @@ def convex_hull(points):
def cross(o, a, b):
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
- # Build lower hull
+ # Build lower hull
lower = []
for p in points:
while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0:
@@ -36,5 +36,5 @@ def cross(o, a, b):
upper.append(p)
# Concatenation of the lower and upper hulls gives the convex hull.
- # Last point of each list is omitted because it is repeated at the beginning of the other list.
- return lower[:-1] + upper[:-1]
\ No newline at end of file
+ # Last point of each list is omitted because it is repeated at the beginning of the other list.
+ return lower[:-1] + upper[:-1]
diff --git a/PyFlow/UI/Utils/PythonSyntax.py b/PyFlow/UI/Utils/PythonSyntax.py
index bfe5c6288..3712a2107 100644
--- a/PyFlow/UI/Utils/PythonSyntax.py
+++ b/PyFlow/UI/Utils/PythonSyntax.py
@@ -13,8 +13,8 @@
## limitations under the License.
-from Qt.QtCore import QRegExp
-from Qt.QtGui import QColor, QTextCharFormat, QFont, QSyntaxHighlighter
+from qtpy.QtCore import QRegularExpression
+from qtpy.QtGui import QColor, QTextCharFormat, QFont, QSyntaxHighlighter
try:
# python 2 support
import __builtin__ as builtins
@@ -22,7 +22,7 @@
import builtins
-def format(color, style=''):
+def format(color, style=""):
"""Return a QTextCharFormat with the given attributes.
"""
_color = QColor()
@@ -30,9 +30,9 @@ def format(color, style=''):
_format = QTextCharFormat()
_format.setForeground(_color)
- if 'bold' in style:
+ if "bold" in style:
_format.setFontWeight(QFont.Bold)
- if 'italic' in style:
+ if "italic" in style:
_format.setFontItalic(True)
return _format
@@ -40,49 +40,94 @@ def format(color, style=''):
# Syntax styles that can be shared by all languages
STYLES = {
- 'keyword': format('darkMagenta'),
- 'operator': format('white', 'bold'),
- 'brace': format('darkGray'),
- 'defclass': format('cyan', 'bold'),
- 'string': format('yellow'),
- 'string2': format('yellow'),
- 'comment': format('gray', 'italic'),
- 'self': format('green', 'italic'),
- 'numbers': format('brown'),
- 'dataAccess': format('orange'),
- 'builtinFunction': format('cyan')
+ "keyword": format("darkMagenta"),
+ "operator": format("white", "bold"),
+ "brace": format("darkGray"),
+ "defclass": format("cyan", "bold"),
+ "string": format("yellow"),
+ "string2": format("yellow"),
+ "comment": format("gray", "italic"),
+ "self": format("green", "italic"),
+ "numbers": format("brown"),
+ "dataAccess": format("orange"),
+ "builtinFunction": format("cyan"),
}
-class PythonHighlighter (QSyntaxHighlighter):
+class PythonHighlighter(QSyntaxHighlighter):
"""Syntax highlighter for the Python language.
"""
+
# Python keywords
keywords = [
- 'and', 'assert', 'break', 'class', 'continue', 'def',
- 'del', 'elif', 'else', 'except', 'exec', 'finally',
- 'for', 'from', 'global', 'if', 'import', 'in',
- 'is', 'lambda', 'not', 'or', 'pass', 'raise',
- 'return', 'try', 'while', 'yield', 'None', 'True', 'False',
+ "and",
+ "assert",
+ "break",
+ "class",
+ "continue",
+ "def",
+ "del",
+ "elif",
+ "else",
+ "except",
+ "exec",
+ "finally",
+ "for",
+ "from",
+ "global",
+ "if",
+ "import",
+ "in",
+ "is",
+ "lambda",
+ "not",
+ "or",
+ "pass",
+ "raise",
+ "return",
+ "try",
+ "while",
+ "yield",
+ "None",
+ "True",
+ "False",
]
# Python operators
operators = [
- '=',
+ "=",
# Comparison
- '==', '!=', '<', '<=', '>', '>=',
+ "==",
+ "!=",
+ "<",
+ "<=",
+ ">",
+ ">=",
# Arithmetic
- '\+', '-', '\*', '/', '//', '\%', '\*\*',
+ "\+",
+ "-",
+ "\*",
+ "/",
+ "//",
+ "\%",
+ "\*\*",
# In-place
- '\+=', '-=', '\*=', '/=', '\%=',
+ "\+=",
+ "-=",
+ "\*=",
+ "/=",
+ "\%=",
# Bitwise
- '\^', '\|', '\&', '\~', '>>', '<<',
+ "\^",
+ "\|",
+ "\&",
+ "\~",
+ ">>",
+ "<<",
]
# Python braces
- braces = [
- '\{', '\}', '\(', '\)', '\[', '\]',
- ]
+ braces = ["\{", "\}", "\(", "\)", "\[", "\]"]
def __init__(self, document):
QSyntaxHighlighter.__init__(self, document)
@@ -90,48 +135,48 @@ def __init__(self, document):
# Multi-line strings (expression, flag, style)
# FIXME: The triple-quotes in these two lines will mess up the
# syntax highlighting from this point onward
- self.tri_single = (QRegExp("'''"), 1, STYLES['string2'])
- self.tri_double = (QRegExp('"""'), 2, STYLES['string2'])
+ self.tri_single = (QRegularExpression("'''"), 1, STYLES['string2'])
+ self.tri_double = (QRegularExpression('"""'), 2, STYLES['string2'])
rules = []
# Keyword, operator, and brace rules
- rules += [(r'\b%s\b' % w, 0, STYLES['keyword']) for w in PythonHighlighter.keywords]
- rules += [(r'%s' % o, 0, STYLES['operator']) for o in PythonHighlighter.operators]
- rules += [(r'%s' % b, 0, STYLES['brace']) for b in PythonHighlighter.braces]
- rules += [(r'%s' % b, 0, STYLES['builtinFunction']) for b in dir(builtins)]
+ rules += [
+ (r"\b%s\b" % w, 0, STYLES["keyword"]) for w in PythonHighlighter.keywords
+ ]
+ rules += [
+ (r"%s" % o, 0, STYLES["operator"]) for o in PythonHighlighter.operators
+ ]
+ rules += [(r"%s" % b, 0, STYLES["brace"]) for b in PythonHighlighter.braces]
+ rules += [(r"%s" % b, 0, STYLES["builtinFunction"]) for b in dir(builtins)]
# All other rules
rules += [
# 'self'
- (r'\bself\b', 0, STYLES['self']),
- (r'\bsetData\b', 0, STYLES['dataAccess']),
- (r'\bgetData\b', 0, STYLES['dataAccess']),
- (r'\bsetClean\b', 0, STYLES['dataAccess']),
- (r'\bsetDirty\b', 0, STYLES['dataAccess']),
- (r'\bcurrentData\b', 0, STYLES['dataAccess']),
-
+ (r"\bself\b", 0, STYLES["self"]),
+ (r"\bsetData\b", 0, STYLES["dataAccess"]),
+ (r"\bgetData\b", 0, STYLES["dataAccess"]),
+ (r"\bsetClean\b", 0, STYLES["dataAccess"]),
+ (r"\bsetDirty\b", 0, STYLES["dataAccess"]),
+ (r"\bcurrentData\b", 0, STYLES["dataAccess"]),
# Double-quoted string, possibly containing escape sequences
- (r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES['string']),
+ (r'"[^"\\]*(\\.[^"\\]*)*"', 0, STYLES["string"]),
# Single-quoted string, possibly containing escape sequences
- (r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES['string']),
-
+ (r"'[^'\\]*(\\.[^'\\]*)*'", 0, STYLES["string"]),
# 'def' followed by an identifier
- (r'\bdef\b\s*(\w+)', 1, STYLES['defclass']),
+ (r"\bdef\b\s*(\w+)", 1, STYLES["defclass"]),
# 'class' followed by an identifier
- (r'\bclass\b\s*(\w+)', 1, STYLES['defclass']),
-
+ (r"\bclass\b\s*(\w+)", 1, STYLES["defclass"]),
# From '#' until a newline
- (r'#[^\n]*', 0, STYLES['comment']),
-
+ (r"#[^\n]*", 0, STYLES["comment"]),
# Numeric literals
- (r'\b[+-]?[0-9]+[lL]?\b', 0, STYLES['numbers']),
- (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, STYLES['numbers']),
- (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, STYLES['numbers']),
+ (r"\b[+-]?[0-9]+[lL]?\b", 0, STYLES["numbers"]),
+ (r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b", 0, STYLES["numbers"]),
+ (r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b", 0, STYLES["numbers"]),
]
- # Build a QRegExp for each pattern
- self.rules = [(QRegExp(pat), index, fmt) for (pat, index, fmt) in rules]
+ # Build a QRegularExpression for each pattern
+ self.rules = [(QRegularExpression(pat), index, fmt) for (pat, index, fmt) in rules]
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
@@ -153,11 +198,11 @@ def highlightBlock(self, text):
# Do multi-line strings
in_multiline = self.match_multiline(text, *self.tri_single)
if not in_multiline:
- in_multiline = self.match_multiline(text, *self.tri_double)
+ self.match_multiline(text, *self.tri_double)
def match_multiline(self, text, delimiter, in_state, style):
"""Do highlighting of multi-line strings. ``delimiter`` should be a
- ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
+ ``QRegularExpression`` for triple-single-quotes or triple-double-quotes, and
``in_state`` should be a unique integer to represent the corresponding
state changes when inside those strings. Returns True if we're still
inside a multi-line string when this function is finished.
diff --git a/PyFlow/UI/Utils/stylesheet.py b/PyFlow/UI/Utils/stylesheet.py
index a94bf3910..4d3f79d31 100644
--- a/PyFlow/UI/Utils/stylesheet.py
+++ b/PyFlow/UI/Utils/stylesheet.py
@@ -18,13 +18,9 @@
import json
import os
-from nine import IS_PYTHON2
-if IS_PYTHON2:
- from aenum import IntEnum
-else:
- from enum import IntEnum
+from enum import IntEnum
-from Qt import QtGui, QtWidgets, QtCore
+from qtpy import QtGui, QtWidgets, QtCore
from PyFlow.Core.Common import SingletonDecorator
from PyFlow.ConfigManager import ConfigManager
@@ -34,12 +30,14 @@
STYLE_PATH = os.path.join(FILE_DIR, "style.css")
THEMES_PATH = os.path.join(os.path.dirname(FILE_DIR), "Themes")
+
class ConnectionTypes(IntEnum):
Cubic = 0
Circuit = 1
ComplexCircuit = 2
Linear = 3
+
class Colors:
AbsoluteBlack = QtGui.QColor(0, 0, 0, 255)
DarkGray = QtGui.QColor(60, 60, 60)
@@ -55,8 +53,9 @@ class Colors:
Yellow = QtGui.QColor(255, 211, 25)
Orange = QtGui.QColor(209, 84, 0)
+
@SingletonDecorator
-class editableStyleSheet():
+class editableStyleSheet:
def __init__(self, appInstance=None):
self.appInstance = appInstance
@@ -96,7 +95,7 @@ def __init__(self, appInstance=None):
self.ConnectionRoundness = [5]
self.ConnectionOffset = [20]
- self.storeDeffaults()
+ self.storeDefaults()
self.presets = {}
self.loadPresets(THEMES_PATH)
try:
@@ -109,7 +108,7 @@ def __init__(self, appInstance=None):
except:
pass
- def storeDeffaults(self):
+ def storeDefaults(self):
for name, obj in inspect.getmembers(self):
if isinstance(obj, QtGui.QColor):
obj.default = obj.getRgb()
@@ -175,7 +174,12 @@ def updateApp(self):
if topWindow:
topWindow.setStyleSheet(self.getStyleSheet())
for widget in topWindow.allWidgets():
- widget.update()
+ if isinstance(widget, QtWidgets.QListView):
+ widget.viewport().update()
+ elif isinstance(widget, QtWidgets.QHeaderView):
+ widget.viewport().update()
+ else:
+ widget.update()
def getStyleSheet(self):
MainColor_Lighter = QtGui.QColor(self.MainColor)
@@ -183,28 +187,26 @@ def getStyleSheet(self):
ButtonG1 = self.ButtonsColor.lighter(120)
ButtonG3 = self.ButtonsColor.darker(110)
InputFieldHover = self.InputFieldColor.lighter(200)
- with open(STYLE_PATH, 'r') as f:
+ with open(STYLE_PATH, "r") as f:
styleString = f.read()
- return styleString % ("rgba%s" % str(self.TextColor.getRgb()),
- "rgba%s" % str(self.BgColor.getRgb()),
- "rgba%s" % str(self.BgColorDarker.getRgb()),
- "rgba%s" % str(self.BgColorBright.getRgb()),
- "rgba%s" % str(self.MainColor.getRgb()),
- "rgba%s" % str(MainColor_Lighter.getRgb()),
- "rgba%s" % str(MainColor_Lighter.getRgb()),
- "rgba%s" % str(self.BorderColor.getRgb()),
- "rgba%s" % str(
- self.InputFieldColor.getRgb()),
- "rgba%s" % str(InputFieldHover.getRgb()),
- "rgba%s" % str(MainColor_Lighter.getRgb()),
- "rgba%s" % str(
- self.TextSelectedColor.getRgb()),
- "rgba%s" % str(ButtonG1.getRgb()),
- "rgba%s" % str(self.ButtonsColor.getRgb()),
- "rgba%s" % str(ButtonG3.getRgb()),
- "rgba%s" % str(QtGui.QColor(
- 0, 0, 0, 100).getRgb())
- )
+ return styleString % (
+ "rgba%s" % str(self.TextColor.getRgb()),
+ "rgba%s" % str(self.BgColor.getRgb()),
+ "rgba%s" % str(self.BgColorDarker.getRgb()),
+ "rgba%s" % str(self.BgColorBright.getRgb()),
+ "rgba%s" % str(self.MainColor.getRgb()),
+ "rgba%s" % str(MainColor_Lighter.getRgb()),
+ "rgba%s" % str(MainColor_Lighter.getRgb()),
+ "rgba%s" % str(self.BorderColor.getRgb()),
+ "rgba%s" % str(self.InputFieldColor.getRgb()),
+ "rgba%s" % str(InputFieldHover.getRgb()),
+ "rgba%s" % str(MainColor_Lighter.getRgb()),
+ "rgba%s" % str(self.TextSelectedColor.getRgb()),
+ "rgba%s" % str(ButtonG1.getRgb()),
+ "rgba%s" % str(self.ButtonsColor.getRgb()),
+ "rgba%s" % str(ButtonG3.getRgb()),
+ "rgba%s" % str(QtGui.QColor(0, 0, 0, 100).getRgb()),
+ )
def getSliderStyleSheet(self, name):
@@ -227,7 +229,9 @@ def getSliderStyleSheet(self, name):
QSlider::handle:horizontal {
width: 1px;
}
- """ % "rgba%s" % str(self.MainColor.getRgb()),
+ """
+ % "rgba%s"
+ % str(self.MainColor.getRgb()),
"sliderStyleSheetB": """
QSlider::groove:horizontal {
border: 1px solid #bbb;
@@ -278,7 +282,9 @@ def getSliderStyleSheet(self, name):
border-radius: 2px;
height : 10;
}
- """ % "rgba%s" % str(self.MainColor.getRgb()),
+ """
+ % "rgba%s"
+ % str(self.MainColor.getRgb()),
"sliderStyleSheetC": """
QSlider,QSlider:disabled,QSlider:focus{
background: qcolor(0,0,0,0); }
@@ -296,7 +302,9 @@ def getSliderStyleSheet(self, name):
QSlider::handle:horizontal:hover {
border: 2.25px solid %s;
}
- """ % "rgba%s" % str(self.MainColor.getRgb()),
+ """
+ % "rgba%s"
+ % str(self.MainColor.getRgb()),
"draggerstyleSheet": """
QGroupBox{
border: 0.5 solid darkgrey;
@@ -320,7 +328,9 @@ def getSliderStyleSheet(self, name):
border: 0 solid transparent;
color: white;
}
- """ % "rgba%s" % str(self.MainColor.getRgb()),
+ """
+ % "rgba%s"
+ % str(self.MainColor.getRgb()),
"timeStyleSheet": """
QSlider,QSlider:disabled,QSlider:focus{
background: qcolor(0,0,0,0); }
@@ -332,6 +342,8 @@ def getSliderStyleSheet(self, name):
background: %s;
width: 3px;
}
- """ % "rgba%s" % str(self.MainColor.getRgb())
+ """
+ % "rgba%s"
+ % str(self.MainColor.getRgb()),
}
return Styles[name]
diff --git a/PyFlow/UI/Views/NodeBox.py b/PyFlow/UI/Views/NodeBox.py
index 52beb5170..aacba29d8 100644
--- a/PyFlow/UI/Views/NodeBox.py
+++ b/PyFlow/UI/Views/NodeBox.py
@@ -15,15 +15,12 @@
import json
import os
-import weakref
-try:
- from inspect import getfullargspec as getargspec
-except:
- from inspect import getargspec
+import uuid
+from inspect import getfullargspec
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import *
from PyFlow import GET_PACKAGES
from PyFlow import GET_PACKAGE_PATH
@@ -42,12 +39,16 @@ def __init__(self, parent, events=True):
self.setParent(parent)
self._events = events
self.parent = parent
- self.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
+ self.setLocale(
+ QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates)
+ )
self.setObjectName("le_nodes")
- style = "border-radius: 2px;" +\
- "font-size: 14px;" +\
- "border-style: outset;" +\
- "border-width: 1px;"
+ style = (
+ "border-radius: 2px;"
+ + "font-size: 14px;"
+ + "border-style: outset;"
+ + "border-width: 1px;"
+ )
self.setStyleSheet(style)
self.setPlaceholderText("enter node name..")
@@ -56,12 +57,21 @@ class NodeBoxTreeWidget(QTreeWidget):
showInfo = QtCore.Signal(object)
hideInfo = QtCore.Signal()
- def __init__(self, parent, canvas, bNodeInfoEnabled=True, useDragAndDrop=True, bGripsEnabled=True):
+ def __init__(
+ self,
+ parent,
+ canvas,
+ bNodeInfoEnabled=True,
+ useDragAndDrop=True,
+ bGripsEnabled=True,
+ ):
super(NodeBoxTreeWidget, self).__init__(parent)
- style = "border-radius: 2px;" +\
- "font-size: 14px;" +\
- "border-style: outset;" +\
- "border-width: 1px;"
+ style = (
+ "border-radius: 2px;"
+ + "font-size: 14px;"
+ + "border-style: outset;"
+ + "border-width: 1px;"
+ )
self.setStyleSheet(style)
self.bGripsEnabled = bGripsEnabled
self.canvas = canvas
@@ -98,21 +108,29 @@ def _isCategoryExists(self, category_name, categories):
return True
if not bFound:
for c in categories:
- sepCatNames = c.split('|')
+ sepCatNames = c.split("|")
if len(sepCatNames) == 1:
if category_name == c:
return True
else:
for i in range(0, len(sepCatNames)):
- c = '|'.join(sepCatNames)
+ c = "|".join(sepCatNames)
if category_name == c:
return True
sepCatNames.pop()
return False
- def insertNode(self, nodeCategoryPath, name, doc=None, libName=None, bPyNode=False, bCompoundNode=False):
- nodePath = nodeCategoryPath.split('|')
- categoryPath = ''
+ def insertNode(
+ self,
+ nodeCategoryPath,
+ name,
+ doc=None,
+ libName=None,
+ bPyNode=False,
+ bCompoundNode=False,
+ ):
+ nodePath = nodeCategoryPath.split("|")
+ categoryPath = ""
# walk from tree top to bottom, creating folders if needed
# also writing all paths in dict to avoid duplications
for folderId in range(0, len(nodePath)):
@@ -125,17 +143,22 @@ def insertNode(self, nodeCategoryPath, name, doc=None, libName=None, bPyNode=Fal
rootFolderItem.setFlags(QtCore.Qt.ItemIsEnabled)
rootFolderItem.setText(0, folderName)
rootFolderItem.setBackground(
- folderId, editableStyleSheet().BgColorBright)
+ folderId, editableStyleSheet().BgColorBright
+ )
self.categoryPaths[categoryPath] = rootFolderItem
else:
parentCategoryPath = categoryPath
- categoryPath += '|{}'.format(folderName)
+ categoryPath += "|{}".format(folderName)
if categoryPath not in self.categoryPaths:
- childCategoryItem = QTreeWidgetItem(self.categoryPaths[parentCategoryPath])
+ childCategoryItem = QTreeWidgetItem(
+ self.categoryPaths[parentCategoryPath]
+ )
childCategoryItem.setFlags(QtCore.Qt.ItemIsEnabled)
childCategoryItem.bCategory = True
childCategoryItem.setText(0, folderName)
- childCategoryItem.setBackground(0, editableStyleSheet().BgColorBright.lighter(150))
+ childCategoryItem.setBackground(
+ 0, editableStyleSheet().BgColorBright.lighter(150)
+ )
self.categoryPaths[categoryPath] = childCategoryItem
# create node under constructed folder
# TODO: Subclass QTreeWidgetItem to not create dynamic attributes. Below code is ugly
@@ -148,7 +171,7 @@ def insertNode(self, nodeCategoryPath, name, doc=None, libName=None, bPyNode=Fal
nodeItem.docString = doc
return nodeItem
- def refresh(self, pattern='', pinDirection=None, pinStructure=StructureType.Single):
+ def refresh(self, pattern="", pinDirection=None, pinStructure=StructureType.Single):
self.clear()
self.categoryPaths = {}
@@ -163,21 +186,23 @@ def refresh(self, pattern='', pinDirection=None, pinStructure=StructureType.Sing
for name, foo in foos.items():
foo = foo
libName = foo.__annotations__["lib"]
- fooArgNames = getargspec(foo).args
+ fooArgNames = getfullargspec(foo).args
fooInpTypes = set()
fooOutTypes = set()
fooInpStructs = set()
fooOutStructs = set()
- if foo.__annotations__['nodeType'] == NodeTypes.Callable:
- fooInpTypes.add('ExecPin')
- fooOutTypes.add('ExecPin')
+ if foo.__annotations__["nodeType"] == NodeTypes.Callable:
+ fooInpTypes.add("ExecPin")
+ fooOutTypes.add("ExecPin")
fooInpStructs.add(StructureType.Single)
fooOutStructs.add(StructureType.Single)
# consider return type if not None
- if foo.__annotations__['return'] is not None:
- fooOutTypes.add(foo.__annotations__['return'][0])
- fooOutStructs.add(findStructFromValue(foo.__annotations__['return'][1]))
+ if foo.__annotations__["return"] is not None:
+ fooOutTypes.add(foo.__annotations__["return"][0])
+ fooOutStructs.add(
+ findStructFromValue(foo.__annotations__["return"][1])
+ )
for index in range(len(fooArgNames)):
dType = foo.__annotations__[fooArgNames[index]]
@@ -186,43 +211,65 @@ def refresh(self, pattern='', pinDirection=None, pinStructure=StructureType.Sing
fooInpTypes.add(dType[0])
fooInpStructs.add(findStructFromValue(dType[1]))
- nodeCategoryPath = "{0}|{1}".format(package_name, foo.__annotations__['meta'][NodeMeta.CATEGORY])
- keywords = foo.__annotations__['meta'][NodeMeta.KEYWORDS]
- checkString = name + nodeCategoryPath + ''.join(keywords)
+ nodeCategoryPath = "{0}|{1}".format(
+ package_name, foo.__annotations__["meta"][NodeMeta.CATEGORY]
+ )
+ keywords = foo.__annotations__["meta"][NodeMeta.KEYWORDS]
+ checkString = name + nodeCategoryPath + "".join(keywords)
if pattern.lower() in checkString.lower():
# create all nodes items if clicked on canvas
if dataType is None:
self.suggestionsEnabled = False
- self.insertNode(nodeCategoryPath, name, foo.__doc__, libName)
+ self.insertNode(
+ nodeCategoryPath, name, foo.__doc__, libName
+ )
else:
self.suggestionsEnabled = True
if pinDirection == PinDirection.Output:
if pinStructure != StructureType.Multi:
hasMultiPins = StructureType.Multi in fooInpStructs
- if dataType in fooInpTypes and (pinStructure in fooInpStructs or hasMultiPins):
- self.insertNode(nodeCategoryPath, name, foo.__doc__, libName)
+ if dataType in fooInpTypes and (
+ pinStructure in fooInpStructs or hasMultiPins
+ ):
+ self.insertNode(
+ nodeCategoryPath, name, foo.__doc__, libName
+ )
elif dataType in fooInpTypes:
- self.insertNode(nodeCategoryPath, name, foo.__doc__, libName)
+ self.insertNode(
+ nodeCategoryPath, name, foo.__doc__, libName
+ )
else:
if pinStructure != StructureType.Multi:
hasMultiPins = StructureType.Multi in fooOutStructs
- if dataType in fooOutTypes and (pinStructure in fooOutStructs or hasMultiPins):
- self.insertNode(nodeCategoryPath, name, foo.__doc__, libName)
+ if dataType in fooOutTypes and (
+ pinStructure in fooOutStructs or hasMultiPins
+ ):
+ self.insertNode(
+ nodeCategoryPath, name, foo.__doc__, libName
+ )
elif dataType in fooOutTypes:
- self.insertNode(nodeCategoryPath, name, foo.__doc__, libName)
+ self.insertNode(
+ nodeCategoryPath, name, foo.__doc__, libName
+ )
# class based nodes
for node_class in package.GetNodeClasses().values():
- if node_class.__name__ in ('setVar', 'getVar'):
+ if node_class.__name__ in ("setVar", "getVar"):
continue
nodeCategoryPath = "{0}|{1}".format(package_name, node_class.category())
- checkString = node_class.__name__ + nodeCategoryPath + ''.join(node_class.keywords())
+ checkString = (
+ node_class.__name__
+ + nodeCategoryPath
+ + "".join(node_class.keywords())
+ )
if pattern.lower() not in checkString.lower():
continue
if dataType is None:
- self.insertNode(nodeCategoryPath, node_class.__name__, node_class.description())
+ self.insertNode(
+ nodeCategoryPath, node_class.__name__, node_class.description()
+ )
else:
# if pressed pin is output pin
# filter by nodes input types
@@ -230,19 +277,39 @@ def refresh(self, pattern='', pinDirection=None, pinStructure=StructureType.Sing
if pinDirection == PinDirection.Output:
if pinStructure != StructureType.Multi:
hasMultiPins = StructureType.Multi in hints.inputStructs
- if dataType in hints.inputTypes and (pinStructure in hints.inputStructs or hasMultiPins):
- self.insertNode(nodeCategoryPath, node_class.__name__, node_class.description())
+ if dataType in hints.inputTypes and (
+ pinStructure in hints.inputStructs or hasMultiPins
+ ):
+ self.insertNode(
+ nodeCategoryPath,
+ node_class.__name__,
+ node_class.description(),
+ )
elif dataType in hints.inputTypes:
- self.insertNode(nodeCategoryPath, node_class.__name__, node_class.description())
+ self.insertNode(
+ nodeCategoryPath,
+ node_class.__name__,
+ node_class.description(),
+ )
else:
# if pressed pin is input pin
# filter by nodes output types
if pinStructure != StructureType.Multi:
hasMultiPins = StructureType.Multi in hints.outputStructs
- if dataType in hints.outputTypes and (pinStructure in hints.outputStructs or hasMultiPins):
- self.insertNode(nodeCategoryPath, node_class.__name__, node_class.description())
+ if dataType in hints.outputTypes and (
+ pinStructure in hints.outputStructs or hasMultiPins
+ ):
+ self.insertNode(
+ nodeCategoryPath,
+ node_class.__name__,
+ node_class.description(),
+ )
elif dataType in hints.outputTypes:
- self.insertNode(nodeCategoryPath, node_class.__name__, node_class.description())
+ self.insertNode(
+ nodeCategoryPath,
+ node_class.__name__,
+ node_class.description(),
+ )
# populate exported py nodes
packagePath = GET_PACKAGE_PATH(package_name)
@@ -255,7 +322,7 @@ def refresh(self, pattern='', pinDirection=None, pinStructure=StructureType.Sing
p = os.path.normpath(path)
folders = p.split(os.sep)
index = folders.index("PyNodes")
- categorySuffix = '|'.join(folders[index:])
+ categorySuffix = "|".join(folders[index:])
category = "{0}|{1}".format(package_name, categorySuffix)
self.insertNode(category, pyNodeName, bPyNode=True)
@@ -268,12 +335,16 @@ def refresh(self, pattern='', pinDirection=None, pinStructure=StructureType.Sing
if extension == ".compound":
compoundsRoot = os.path.normpath(path)
fullCompoundPath = os.path.join(compoundsRoot, f)
- with open(fullCompoundPath, 'r') as compoundFile:
+ with open(fullCompoundPath, "r") as compoundFile:
data = json.load(compoundFile)
compoundCategoryName = data["category"]
compoundNodeName = data["name"]
- category = "{0}|{1}|{2}".format(package_name, "Compounds", compoundCategoryName)
- self.insertNode(category, compoundNodeName, bCompoundNode=True)
+ category = "{0}|{1}|{2}".format(
+ package_name, "Compounds", compoundCategoryName
+ )
+ self.insertNode(
+ category, compoundNodeName, bCompoundNode=True
+ )
# expand all categories
if dataType is not None:
@@ -312,14 +383,14 @@ def mousePressEvent(self, event):
return
jsonTemplate = NodeBase.jsonTemplate()
- jsonTemplate['package'] = packageName
- jsonTemplate['lib'] = libName
- jsonTemplate['type'] = pressed_text
- jsonTemplate['name'] = pressed_text
- jsonTemplate['uuid'] = str(uuid.uuid4())
- jsonTemplate['meta']['label'] = pressed_text
- jsonTemplate['bPyNode'] = bPyNode
- jsonTemplate['bCompoundNode'] = bCompoundNode
+ jsonTemplate["package"] = packageName
+ jsonTemplate["lib"] = libName
+ jsonTemplate["type"] = pressed_text
+ jsonTemplate["name"] = pressed_text
+ jsonTemplate["uuid"] = str(uuid.uuid4())
+ jsonTemplate["meta"]["label"] = pressed_text
+ jsonTemplate["bPyNode"] = bPyNode
+ jsonTemplate["bCompoundNode"] = bCompoundNode
if self.canvas.pressedPin is not None and self.bGripsEnabled:
a = self.canvas.mapToScene(self.canvas.mouseReleasePos)
@@ -349,21 +420,22 @@ def mousePressEvent(self, event):
pressed_text = json.dumps(jsonTemplate)
mime_data.setText(pressed_text)
drag.setMimeData(mime_data)
- drag.exec_()
+ drag.exec()
def update(self):
for category in self.categoryPaths.values():
if not category.parent():
- category.setBackground(
- 0, editableStyleSheet().BgColorBright)
+ category.setBackground(0, editableStyleSheet().BgColorBright)
else:
category.setBackground(
- 0, editableStyleSheet().BgColorBright.lighter(150))
+ 0, editableStyleSheet().BgColorBright.lighter(150)
+ )
super(NodeBoxTreeWidget, self).update()
class NodeBoxSizeGrip(QSizeGrip):
"""docstring for NodeBoxSizeGrip."""
+
def __init__(self, parent=None):
super(NodeBoxSizeGrip, self).__init__(parent)
@@ -384,7 +456,14 @@ def paintEvent(self, event):
class NodesBox(QFrame):
"""doc string for NodesBox"""
- def __init__(self, parent, canvas, bNodeInfoEnabled=True, bGripsEnabled=True, bUseDragAndDrop=False):
+ def __init__(
+ self,
+ parent,
+ canvas,
+ bNodeInfoEnabled=True,
+ bGripsEnabled=True,
+ bUseDragAndDrop=False,
+ ):
super(NodesBox, self).__init__(parent)
self.setContentsMargins(0, 0, 0, 0)
self.setFrameStyle(QFrame.Panel | QFrame.Raised)
@@ -432,7 +511,9 @@ def __init__(self, parent, canvas, bNodeInfoEnabled=True, bGripsEnabled=True, bU
self.splitter.addWidget(self.nodeInfoWidget)
self.nodeInfoWidget.setVisible(bNodeInfoEnabled)
- self.treeWidget = NodeBoxTreeWidget(self, canvas, bNodeInfoEnabled, bUseDragAndDrop, bGripsEnabled)
+ self.treeWidget = NodeBoxTreeWidget(
+ self, canvas, bNodeInfoEnabled, bUseDragAndDrop, bGripsEnabled
+ )
self.treeWidget.setObjectName("nodeBoxTreeWidget")
self.treeWidget.headerItem().setText(0, "1")
self.verticalLayout.addWidget(self.treeWidget)
@@ -459,12 +540,14 @@ def sizeHint(self):
return QtCore.QSize(500, 300)
def expandCategory(self):
- for i in self.treeWidget.categoryPaths:
- self.treeWidget.setItemExpanded(
- self.treeWidget.categoryPaths[i], True)
+ for i in range(self.treeWidget.topLevelItemCount()):
+ item = self.treeWidget.topLevelItem(i)
+ if item.text(0) in self.treeWidget.categoryPaths:
+ index = self.treeWidget.indexFromItem(item)
+ self.treeWidget.setExpanded(index, True)
def leTextChanged(self):
- if self.lineEdit.text() == '':
+ if self.lineEdit.text() == "":
self.lineEdit.setPlaceholderText("enter node name..")
self.treeWidget.refresh()
return
diff --git a/PyFlow/UI/Views/PinWidget_ui.py b/PyFlow/UI/Views/PinWidget_ui.py
index 03d37f4d2..56e8eb095 100644
--- a/PyFlow/UI/Views/PinWidget_ui.py
+++ b/PyFlow/UI/Views/PinWidget_ui.py
@@ -8,13 +8,15 @@
#
# WARNING! All changes made in this file will be lost!
-from Qt import QtCompat, QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(168, 75)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy = QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
@@ -26,7 +28,9 @@ def setupUi(self, Form):
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.lePinName = QtWidgets.QLineEdit(Form)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy = QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lePinName.sizePolicy().hasHeightForWidth())
@@ -36,7 +40,9 @@ def setupUi(self, Form):
self.lePinName.setObjectName("lePinName")
self.horizontalLayout.addWidget(self.lePinName)
self.cbType = QtWidgets.QComboBox(Form)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy = QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.cbType.sizePolicy().hasHeightForWidth())
@@ -49,7 +55,9 @@ def setupUi(self, Form):
self.cbHideLabel = QtWidgets.QCheckBox(Form)
self.cbHideLabel.setObjectName("cbHideLabel")
self.horizontalLayout_2.addWidget(self.cbHideLabel)
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ spacerItem = QtWidgets.QSpacerItem(
+ 40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum
+ )
self.horizontalLayout_2.addItem(spacerItem)
self.verticalLayout.addLayout(self.horizontalLayout_2)
@@ -57,8 +65,8 @@ def setupUi(self, Form):
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
- Form.setWindowTitle(QtCompat.translate("Form", "Form", None, -1))
- self.lePinName.setText(QtCompat.translate("Form", "pinName", None, -1))
- self.cbHideLabel.setToolTip(QtCompat.translate("Form", "should hide label", None, -1))
- self.cbHideLabel.setText(QtCompat.translate("Form", "hide label", None, -1))
+ Form.setWindowTitle("Form")
+ self.lePinName.setText("pinName")
+ self.cbHideLabel.setToolTip("should hide label")
+ self.cbHideLabel.setText("hide label")
diff --git a/PyFlow/UI/Views/VariableForm_ui.py b/PyFlow/UI/Views/VariableForm_ui.py
index 71a89046a..68306756c 100644
--- a/PyFlow/UI/Views/VariableForm_ui.py
+++ b/PyFlow/UI/Views/VariableForm_ui.py
@@ -8,7 +8,7 @@
#
# WARNING! All changes made in this file will be lost!
-from Qt import QtCompat, QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
@@ -24,7 +24,9 @@ def setupUi(self, Form):
self.labelName = QtWidgets.QLabel(Form)
self.labelName.setObjectName("labelName")
self.horizontalLayout.addWidget(self.labelName)
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ spacerItem = QtWidgets.QSpacerItem(
+ 40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum
+ )
self.horizontalLayout.addItem(spacerItem)
self.pbKill = QtWidgets.QPushButton(Form)
self.pbKill.setMaximumSize(QtCore.QSize(40, 16777215))
@@ -36,6 +38,6 @@ def setupUi(self, Form):
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
- Form.setWindowTitle(QtCompat.translate("Form", "Form", None, -1))
- self.labelName.setText(QtCompat.translate("Form", "var name", None, -1))
+ Form.setWindowTitle("Form")
+ self.labelName.setText("var name")
diff --git a/PyFlow/UI/Views/VariablesWidget.py b/PyFlow/UI/Views/VariablesWidget.py
index 19bdcea49..faefa1623 100644
--- a/PyFlow/UI/Views/VariablesWidget.py
+++ b/PyFlow/UI/Views/VariablesWidget.py
@@ -13,13 +13,10 @@
## limitations under the License.
-from nine import str
import json
-from types import MethodType
-import uuid
-from Qt import QtCore, QtGui
-from Qt.QtWidgets import (
+from qtpy import QtCore, QtGui
+from qtpy.QtWidgets import (
QListWidget,
QListWidgetItem,
QWidget,
@@ -29,7 +26,6 @@
from PyFlow.UI.EditorHistory import EditorHistory
from PyFlow.UI.Canvas.UIVariable import UIVariable
from PyFlow.UI.Views.VariablesWidget_ui import Ui_Form
-from PyFlow.UI.Canvas.UICommon import clearLayout
from PyFlow.Core.Common import *
VARIABLE_TAG = "VAR"
@@ -38,6 +34,7 @@
class VariablesListWidget(QListWidget):
"""docstring for VariablesListWidget."""
+
def __init__(self, parent=None):
super(VariablesListWidget, self).__init__(parent)
self.setDragDropMode(QAbstractItemView.InternalMove)
@@ -83,7 +80,7 @@ def onGraphChanged(self, *args, **kwargs):
self.actualize()
def clear(self):
- """Does not removes any variable. UI only
+ """Does not remove any variable. UI only
"""
self.listWidget.clear()
@@ -102,8 +99,14 @@ def createVariableWrapperAndAddToList(self, rawVariable):
self.listWidget.setItemWidget(item, uiVariable)
return uiVariable
- def createVariable(self, dataType=str('BoolPin'), accessLevel=AccessLevel.public, uid=None):
- rawVariable = self.pyFlowInstance.graphManager.get().activeGraph().createVariable(dataType=dataType, accessLevel=accessLevel, uid=uid)
+ def createVariable(
+ self, dataType="BoolPin", accessLevel=AccessLevel.public, uid=None
+ ):
+ rawVariable = (
+ self.pyFlowInstance.graphManager.get()
+ .activeGraph()
+ .createVariable(dataType=dataType, accessLevel=accessLevel, uid=uid)
+ )
uiVariable = self.createVariableWrapperAndAddToList(rawVariable)
EditorHistory().saveState("Create variable", modify=True)
return uiVariable
diff --git a/PyFlow/UI/Views/VariablesWidget_ui.py b/PyFlow/UI/Views/VariablesWidget_ui.py
index f65ddc4be..6eab1f99e 100644
--- a/PyFlow/UI/Views/VariablesWidget_ui.py
+++ b/PyFlow/UI/Views/VariablesWidget_ui.py
@@ -8,7 +8,7 @@
#
# WARNING! All changes made in this file will be lost!
-from Qt import QtCompat, QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
@@ -23,7 +23,9 @@ def setupUi(self, Form):
self.label = QtWidgets.QLabel(Form)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
- spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ spacerItem = QtWidgets.QSpacerItem(
+ 40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum
+ )
self.horizontalLayout.addItem(spacerItem)
self.pbNewVar = QtWidgets.QPushButton(Form)
self.pbNewVar.setMaximumSize(QtCore.QSize(50, 16777215))
@@ -31,7 +33,9 @@ def setupUi(self, Form):
self.horizontalLayout.addWidget(self.pbNewVar)
self.verticalLayout.addLayout(self.horizontalLayout)
self.wListWidget = QtWidgets.QWidget(Form)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
+ sizePolicy = QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.wListWidget.sizePolicy().hasHeightForWidth())
@@ -50,7 +54,7 @@ def setupUi(self, Form):
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
- Form.setWindowTitle(QtCompat.translate("Form", "Form", None, -1))
- self.label.setText(QtCompat.translate("Form", "Create var", None, -1))
- self.pbNewVar.setText(QtCompat.translate("Form", "+", None, -1))
+ Form.setWindowTitle("Form")
+ self.label.setText("Create var")
+ self.pbNewVar.setText( "+")
diff --git a/PyFlow/UI/Widgets/BlueprintCanvas.py b/PyFlow/UI/Widgets/BlueprintCanvas.py
index 2c7d24ac0..cc43c5d59 100644
--- a/PyFlow/UI/Widgets/BlueprintCanvas.py
+++ b/PyFlow/UI/Widgets/BlueprintCanvas.py
@@ -13,26 +13,18 @@
## limitations under the License.
-from nine import str
-import random
from copy import deepcopy
import json
import uuid
-import weakref
from collections import Counter
from functools import partial
-try:
- from inspect import getfullargspec as getargspec
-except:
- from inspect import getargspec
-from Qt import QtCore
-from Qt import QtGui
-from Qt import QtWidgets
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy import QtWidgets
+from qtpy.QtWidgets import *
from PyFlow.UI.EditorHistory import EditorHistory
-from PyFlow.UI.Utils.stylesheet import Colors
from PyFlow.UI.Canvas.CanvasBase import CanvasBase
from PyFlow.UI.Canvas.UICommon import *
from PyFlow.UI.Canvas.SelectionRect import SelectionRect
@@ -47,35 +39,35 @@
from PyFlow.Core.PinBase import PinBase
from PyFlow.Core.NodeBase import NodeBase
from PyFlow.Input import InputManager, InputAction, InputActionType
-from PyFlow.UI.Views.VariablesWidget import (
- VARIABLE_TAG,
- VARIABLE_DATA_TAG
-)
+from PyFlow.UI.Views.VariablesWidget import VARIABLE_TAG, VARIABLE_DATA_TAG
from PyFlow import getRawNodeInstance
from PyFlow.Core.Common import *
-from PyFlow.UI.Utils.stylesheet import editableStyleSheet
-
def getNodeInstance(jsonTemplate, canvas, parentGraph=None):
- nodeClassName = jsonTemplate['type']
- nodeName = jsonTemplate['name']
- packageName = jsonTemplate['package']
- if 'lib' in jsonTemplate:
- libName = jsonTemplate['lib']
+ nodeClassName = jsonTemplate["type"]
+ packageName = jsonTemplate["package"]
+ if "lib" in jsonTemplate:
+ libName = jsonTemplate["lib"]
else:
libName = None
kwargs = {}
# if get var or set var, construct additional keyword arguments
- if jsonTemplate['type'] in ('getVar', 'setVar'):
- kwargs['var'] = canvas.graphManager.findVariableByUid(uuid.UUID(jsonTemplate['varUid']))
-
- raw_instance = getRawNodeInstance(nodeClassName, packageName=packageName, libName=libName, **kwargs)
- raw_instance.uid = uuid.UUID(jsonTemplate['uuid'])
- assert(raw_instance is not None), "Node {0} not found in package {1}".format(nodeClassName, packageName)
+ if jsonTemplate["type"] in ("getVar", "setVar"):
+ kwargs["var"] = canvas.graphManager.findVariableByUid(
+ uuid.UUID(jsonTemplate["varUid"])
+ )
+
+ raw_instance = getRawNodeInstance(
+ nodeClassName, packageName=packageName, libName=libName, **kwargs
+ )
+ raw_instance.uid = uuid.UUID(jsonTemplate["uuid"])
+ assert raw_instance is not None, "Node {0} not found in package {1}".format(
+ nodeClassName, packageName
+ )
instance = getUINodeInstance(raw_instance)
canvas.addNode(instance, jsonTemplate, parentGraph=parentGraph)
return instance
@@ -115,13 +107,19 @@ def __init__(self, graphManager, pyFlowInstance=None):
self.hoveredReroutes = []
self.realTimeLine = QGraphicsPathItem(None, self.scene())
- self.realTimeLine.name = 'RealTimeLine'
- self.realTimeLineInvalidPen = QtGui.QPen(self._realTimeLineInvalidPen, 2.0, QtCore.Qt.SolidLine)
- self.realTimeLineNormalPen = QtGui.QPen(self._realTimeLineNormalPen, 2.0, QtCore.Qt.DashLine)
- self.realTimeLineValidPen = QtGui.QPen(self._realTimeLineValidPen, 2.0, QtCore.Qt.SolidLine)
+ self.realTimeLine.name = "RealTimeLine"
+ self.realTimeLineInvalidPen = QtGui.QPen(
+ self._realTimeLineInvalidPen, 2.0, QtCore.Qt.SolidLine
+ )
+ self.realTimeLineNormalPen = QtGui.QPen(
+ self._realTimeLineNormalPen, 2.0, QtCore.Qt.DashLine
+ )
+ self.realTimeLineValidPen = QtGui.QPen(
+ self._realTimeLineValidPen, 2.0, QtCore.Qt.SolidLine
+ )
self.realTimeLine.setPen(self.realTimeLineNormalPen)
self._drawRealtimeLine = False
- self._sortcuts_enabled = True
+ self._shortcuts_enabled = True
self.autoPanController = AutoPanController()
self.node_box = NodesBox(self.getApp(), self, bUseDragAndDrop=True)
@@ -160,6 +158,7 @@ def onGraphChanged(self, newGraph):
def nodeShapeUpdater():
for node in self.nodes.values():
node.updateNodeShape()
+
QtCore.QTimer.singleShot(100, nodeShapeUpdater)
def setSelectedNodesCollapsed(self, collapsed=True):
@@ -182,32 +181,46 @@ def collapseSelectedNodesToCompound(self):
outputPins = list()
outputConnectionList = dict()
for wire in wires:
- if wire.source().owningNode().isSelected() and not wire.destination().owningNode().isSelected():
+ if (
+ wire.source().owningNode().isSelected()
+ and not wire.destination().owningNode().isSelected()
+ ):
if wire.destination() not in outputPins:
outputPins.append(wire.destination())
- outputConnectionList[wire.destination()] = [[wire.source().owningNode().name, wire.source().name]]
+ outputConnectionList[wire.destination()] = [
+ [wire.source().owningNode().name, wire.source().name]
+ ]
else:
- outputConnectionList[wire.destination()].append([wire.source().owningNode().name, wire.source().name])
-
- if not wire.source().owningNode().isSelected() and wire.destination().owningNode().isSelected():
+ outputConnectionList[wire.destination()].append(
+ [wire.source().owningNode().name, wire.source().name]
+ )
+
+ if (
+ not wire.source().owningNode().isSelected()
+ and wire.destination().owningNode().isSelected()
+ ):
if wire.source() not in inputPins:
inputPins.append(wire.source())
- inputConnectionList[wire.source()] = [[wire.destination().owningNode().name, wire.destination().name]]
+ inputConnectionList[wire.source()] = [
+ [wire.destination().owningNode().name, wire.destination().name]
+ ]
else:
- inputConnectionList[wire.source()].append([wire.destination().owningNode().name, wire.destination().name])
+ inputConnectionList[wire.source()].append(
+ [wire.destination().owningNode().name, wire.destination().name]
+ )
nodes = self.copyNodes(toClipBoard=False)
for node in selectedNodes:
node._rawNode.kill()
compoundTemplate = NodeBase.jsonTemplate()
- compoundTemplate['package'] = 'PyFlowBase'
- compoundTemplate['type'] = 'compound'
- compoundTemplate['name'] = 'compound'
- compoundTemplate['uuid'] = str(uuid.uuid4())
- compoundTemplate['meta']['label'] = 'compound'
- compoundTemplate['x'] = selectedNodesRect.center().x()
- compoundTemplate['y'] = selectedNodesRect.center().y()
+ compoundTemplate["package"] = "PyFlowBase"
+ compoundTemplate["type"] = "compound"
+ compoundTemplate["name"] = "compound"
+ compoundTemplate["uuid"] = str(uuid.uuid4())
+ compoundTemplate["meta"]["label"] = "compound"
+ compoundTemplate["x"] = selectedNodesRect.center().x()
+ compoundTemplate["y"] = selectedNodesRect.center().y()
uiCompoundNode = self._createNode(compoundTemplate)
activeGraphName = self.graphManager.activeGraph().name
@@ -219,13 +232,13 @@ def collapseSelectedNodesToCompound(self):
if len(inputPins) > 0:
graphInputsTemplate = NodeBase.jsonTemplate()
- graphInputsTemplate['package'] = 'PyFlowBase'
- graphInputsTemplate['type'] = 'graphInputs'
- graphInputsTemplate['name'] = 'graphInputs'
- graphInputsTemplate['uuid'] = str(uuid.uuid4())
- graphInputsTemplate['meta']['label'] = 'graphInputs'
- graphInputsTemplate['x'] = selectedNodesRect.left() - 100
- graphInputsTemplate['y'] = selectedNodesRect.center().y()
+ graphInputsTemplate["package"] = "PyFlowBase"
+ graphInputsTemplate["type"] = "graphInputs"
+ graphInputsTemplate["name"] = "graphInputs"
+ graphInputsTemplate["uuid"] = str(uuid.uuid4())
+ graphInputsTemplate["meta"]["label"] = "graphInputs"
+ graphInputsTemplate["x"] = selectedNodesRect.left() - 100
+ graphInputsTemplate["y"] = selectedNodesRect.center().y()
graphInputs = self._createNode(graphInputsTemplate)
for o in inputPins:
@@ -238,13 +251,13 @@ def collapseSelectedNodesToCompound(self):
if len(outputPins) > 0:
graphOutputsTemplate = NodeBase.jsonTemplate()
- graphOutputsTemplate['package'] = 'PyFlowBase'
- graphOutputsTemplate['type'] = 'graphOutputs'
- graphOutputsTemplate['name'] = 'graphOutputs'
- graphOutputsTemplate['uuid'] = str(uuid.uuid4())
- graphOutputsTemplate['meta']['label'] = 'graphOutputs'
- graphOutputsTemplate['x'] = selectedNodesRect.right() + 100
- graphOutputsTemplate['y'] = selectedNodesRect.center().y()
+ graphOutputsTemplate["package"] = "PyFlowBase"
+ graphOutputsTemplate["type"] = "graphOutputs"
+ graphOutputsTemplate["name"] = "graphOutputs"
+ graphOutputsTemplate["uuid"] = str(uuid.uuid4())
+ graphOutputsTemplate["meta"]["label"] = "graphOutputs"
+ graphOutputsTemplate["x"] = selectedNodesRect.right() + 100
+ graphOutputsTemplate["y"] = selectedNodesRect.center().y()
graphOutputs = self._createNode(graphOutputsTemplate)
for i in outputPins:
@@ -267,18 +280,30 @@ def connectPins(compoundNode, inputs, outputs):
self.connectPinsInternal(i, exposedPin)
EditorHistory().saveState("Collapse to compound", modify=True)
- QtCore.QTimer.singleShot(1, lambda: connectPins(uiCompoundNode, inputPins, outputPins))
+ QtCore.QTimer.singleShot(
+ 1, lambda: connectPins(uiCompoundNode, inputPins, outputPins)
+ )
self.graphManager.selectGraphByName(activeGraphName)
def populateMenu(self):
- self.actionCollapseSelectedNodes = self.menu.addAction("Collapse selected nodes")
- self.actionCollapseSelectedNodes.triggered.connect(lambda: self.setSelectedNodesCollapsed(True))
+ self.actionCollapseSelectedNodes = self.menu.addAction(
+ "Collapse selected nodes"
+ )
+ self.actionCollapseSelectedNodes.triggered.connect(
+ lambda: self.setSelectedNodesCollapsed(True)
+ )
self.actionExpandSelectedNodes = self.menu.addAction("Expand selected nodes")
- self.actionExpandSelectedNodes.triggered.connect(lambda: self.setSelectedNodesCollapsed(False))
+ self.actionExpandSelectedNodes.triggered.connect(
+ lambda: self.setSelectedNodesCollapsed(False)
+ )
- self.actionCollapseSelectedNodesToCompound = self.menu.addAction("Collapse to compound")
- self.actionCollapseSelectedNodesToCompound.triggered.connect(self.collapseSelectedNodesToCompound)
+ self.actionCollapseSelectedNodesToCompound = self.menu.addAction(
+ "Collapse to compound"
+ )
+ self.actionCollapseSelectedNodesToCompound.triggered.connect(
+ self.collapseSelectedNodesToCompound
+ )
def plot(self):
self.graphManager.plot()
@@ -286,8 +311,12 @@ def plot(self):
def location(self):
return self.graphManager.location()
- def createVariable(self, dataType='AnyPin', accessLevel=AccessLevel.public, uid=None):
- return self.graphManager.activeGraph().createVariable(dataType=dataType, accessLevel=accessLevel, uid=uid)
+ def createVariable(
+ self, dataType="AnyPin", accessLevel=AccessLevel.public, uid=None
+ ):
+ return self.graphManager.activeGraph().createVariable(
+ dataType=dataType, accessLevel=accessLevel, uid=uid
+ )
@property
def nodes(self):
@@ -325,7 +354,7 @@ def getAllNodes(self):
def showNodeBox(self, pinDirection=None, pinStructure=StructureType.Single):
self.node_box.show()
self.node_box.move(QtGui.QCursor.pos())
- self.node_box.treeWidget.refresh('', pinDirection, pinStructure)
+ self.node_box.treeWidget.refresh("", pinDirection, pinStructure)
self.node_box.lineEdit.blockSignals(True)
self.node_box.lineEdit.setText("")
self.node_box.lineEdit.blockSignals(False)
@@ -335,7 +364,7 @@ def hideNodeBox(self):
self.node_box.hide()
self.node_box.lineEdit.clear()
- def shoutDown(self, *args, **kwargs):
+ def shutDown(self, *args, **kwargs):
self.scene().clear()
self._UIConnections.clear()
self.hideNodeBox()
@@ -359,21 +388,21 @@ def Tick(self, deltaTime):
e.Tick()
def isShortcutsEnabled(self):
- return self._sortcuts_enabled
+ return self._shortcuts_enabled
- def disableSortcuts(self):
- self._sortcuts_enabled = False
+ def disableShortcuts(self):
+ self._shortcuts_enabled = False
- def enableSortcuts(self):
- self._sortcuts_enabled = True
+ def enableShortcuts(self):
+ self._shortcuts_enabled = True
def onNewFile(self, keepRoot=True):
self.getApp().undoStack.clear()
- self.shoutDown()
+ self.shutDown()
def getPinByFullName(self, full_name):
- node_name = full_name.split('.')[0]
- pinName = full_name.split('.')[1]
+ node_name = full_name.split(".")[0]
+ pinName = full_name.split(".")[1]
node = self.findNode(node_name)
if node:
Pin = node.getPinSG(pinName)
@@ -397,11 +426,13 @@ def frameAllNodes(self):
self.frameRect(self.getNodesRect())
def getNodesRect(self, selected=False, activeGraphOnly=True):
- return self.getItemsRect(cls=UINodeBase, bSelectedOnly=selected, bVisibleOnly=activeGraphOnly)
+ return self.getItemsRect(
+ cls=UINodeBase, bSelectedOnly=selected, bVisibleOnly=activeGraphOnly
+ )
def selectedNodes(self):
allNodes = self.getAllNodes()
- assert(None not in allNodes), "Bad nodes!"
+ assert None not in allNodes, "Bad nodes!"
return [i for i in allNodes if i.isSelected()]
def selectedConnections(self):
@@ -426,7 +457,13 @@ def killSelectedNodes(self):
def keyPressEvent(self, event):
modifiers = event.modifiers()
- currentInputAction = InputAction("temp", InputActionType.Keyboard, "temp", key=event.key(), modifiers=modifiers)
+ currentInputAction = InputAction(
+ "temp",
+ InputActionType.Keyboard,
+ "temp",
+ key=event.key(),
+ modifiers=modifiers,
+ )
self.currentPressedKey = event.key()
if self.isShortcutsEnabled():
@@ -510,38 +547,42 @@ def duplicateNodes(self):
self.pasteNodes(data=copiedJson)
EditorHistory().saveState("Duplicate nodes", modify=True)
- def makeSerializedNodesUnique(self, nodes, extra=[]):
+ def makeSerializedNodesUnique(self, nodes, extra=None):
+ if extra is None:
+ extra = []
copiedNodes = deepcopy(nodes)
# make names unique
renameData = {}
existingNames = self.graphManager.getAllNames() + extra
for node in copiedNodes:
- newName = getUniqNameFromList(existingNames, node['name'])
+ newName = getUniqNameFromList(existingNames, node["name"])
existingNames.append(newName)
- renameData[node['name']] = newName
+ renameData[node["name"]] = newName
# rename old name in header data
- node["wrapper"]["headerHtml"] = node["wrapper"]["headerHtml"].replace(node['name'], newName)
- node['name'] = newName
- node['uuid'] = str(uuid.uuid4())
- for inp in node['inputs']:
- inp['fullName'] = '{0}_{1}'.format(node['name'], inp['name'])
- inp['uuid'] = str(uuid.uuid4())
- for out in node['outputs']:
- out['fullName'] = '{0}_{1}'.format(node['name'], out['name'])
- out['uuid'] = str(uuid.uuid4())
+ node["wrapper"]["headerHtml"] = node["wrapper"]["headerHtml"].replace(
+ node["name"], newName
+ )
+ node["name"] = newName
+ node["uuid"] = str(uuid.uuid4())
+ for inp in node["inputs"]:
+ inp["fullName"] = "{0}_{1}".format(node["name"], inp["name"])
+ inp["uuid"] = str(uuid.uuid4())
+ for out in node["outputs"]:
+ out["fullName"] = "{0}_{1}".format(node["name"], out["name"])
+ out["uuid"] = str(uuid.uuid4())
# update connections
for node in copiedNodes:
- for out in node['outputs']:
- for linkedToData in out['linkedTo']:
+ for out in node["outputs"]:
+ for linkedToData in out["linkedTo"]:
lhsNodeName = linkedToData["lhsNodeName"]
rhsNodeName = linkedToData["rhsNodeName"]
if lhsNodeName in renameData:
linkedToData["lhsNodeName"] = renameData[lhsNodeName]
if rhsNodeName in renameData:
linkedToData["rhsNodeName"] = renameData[rhsNodeName]
- for inp in node['inputs']:
- for linkedToData in inp['linkedTo']:
+ for inp in node["inputs"]:
+ for linkedToData in inp["linkedTo"]:
lhsNodeName = linkedToData["lhsNodeName"]
rhsNodeName = linkedToData["rhsNodeName"]
if lhsNodeName in renameData:
@@ -550,8 +591,10 @@ def makeSerializedNodesUnique(self, nodes, extra=[]):
linkedToData["rhsNodeName"] = renameData[rhsNodeName]
for node in copiedNodes:
- if node['type'] == 'compound':
- node['graphData']['nodes'] = self.makeSerializedNodesUnique(node['graphData']['nodes'], extra=existingNames)
+ if node["type"] == "compound":
+ node["graphData"]["nodes"] = self.makeSerializedNodesUnique(
+ node["graphData"]["nodes"], extra=existingNames
+ )
return copiedNodes
def cutNodes(self):
@@ -579,7 +622,7 @@ def copyNodes(self, toClipBoard=True):
for outJson in nodeJson["outputs"]:
outJson["linkedTo"] = []
for inpJson in nodeJson["inputs"]:
- for link in (inpJson["linkedTo"]):
+ for link in inpJson["linkedTo"]:
if inpJson["dataType"] == "ExecPin":
if link["lhsNodeName"] not in serializedNodeNames:
inpJson["linkedTo"].remove(link)
@@ -593,7 +636,6 @@ def copyNodes(self, toClipBoard=True):
def pasteNodes(self, move=True, data=None):
if not data:
- nodes = None
try:
nodes = json.loads(QApplication.clipboard().text())
except json.JSONDecodeError as err:
@@ -604,9 +646,10 @@ def pasteNodes(self, move=True, data=None):
existingNames = self.graphManager.getAllNames()
nodes = self.makeSerializedNodesUnique(nodes, extra=existingNames)
- diff = QtCore.QPointF(self.mapToScene(self.mousePos)) - QtCore.QPointF(nodes[0]["x"], nodes[0]["y"])
+ diff = QtCore.QPointF(self.mapToScene(self.mousePos)) - QtCore.QPointF(
+ nodes[0]["x"], nodes[0]["y"]
+ )
self.clearSelection()
- newNodes = {}
nodesData = deepcopy(nodes)
createdNodes = {}
@@ -626,8 +669,8 @@ def pasteNodes(self, move=True, data=None):
n.setPos(n.scenePos() + diff)
for nodeJson in nodesData:
- for inpPinJson in nodeJson['inputs']:
- linkDatas = inpPinJson['linkedTo']
+ for inpPinJson in nodeJson["inputs"]:
+ linkDatas = inpPinJson["linkedTo"]
for linkData in linkDatas:
try:
lhsNode = self.findNode(linkData["lhsNodeName"])
@@ -639,9 +682,11 @@ def pasteNodes(self, move=True, data=None):
rhsPin = rhsNode.orderedInputs[rhsNodePinId]
connected = connectPins(lhsPin, rhsPin)
if connected:
- self.createUIConnectionForConnectedPins(lhsPin.getWrapper()(), rhsPin.getWrapper()())
+ self.createUIConnectionForConnectedPins(
+ lhsPin.getWrapper()(), rhsPin.getWrapper()()
+ )
except Exception as e:
- print(inpPinJson['fullName'], "not found")
+ print(inpPinJson["fullName"], "not found")
continue
# Hacks here!!
@@ -717,7 +762,11 @@ def nodeFromInstance(self, instance):
if isinstance(instance, UINodeBase):
return instance
node = instance
- while (isinstance(node, QGraphicsItem) or isinstance(node, QGraphicsWidget) or isinstance(node, QGraphicsProxyWidget)) and node.parentItem():
+ while (
+ isinstance(node, QGraphicsItem)
+ or isinstance(node, QGraphicsWidget)
+ or isinstance(node, QGraphicsProxyWidget)
+ ) and node.parentItem():
node = node.parentItem()
if isinstance(node, UINodeBase):
return node
@@ -726,39 +775,45 @@ def nodeFromInstance(self, instance):
def getRerouteNode(self, pos, connection=None):
nodeClassName = "reroute"
- if connection and connection.drawSource._rawPin.isExec() and connection.drawDestination._rawPin.isExec():
+ if (
+ connection
+ and connection.drawSource._rawPin.isExec()
+ and connection.drawDestination._rawPin.isExec()
+ ):
nodeClassName = "rerouteExecs"
else:
if self.pressedPin and self.pressedPin.isExec():
nodeClassName = "rerouteExecs"
- rerouteNode = self.spawnNode(nodeClassName, self.mapToScene(pos).x(), self.mapToScene(pos).y())
+ rerouteNode = self.spawnNode(
+ nodeClassName, self.mapToScene(pos).x(), self.mapToScene(pos).y()
+ )
rerouteNode.translate(-rerouteNode.boundingRect().center().x(), -5)
return rerouteNode
def getInputNode(self):
nodeTemplate = NodeBase.jsonTemplate()
- nodeTemplate['package'] = "PyFlowBase"
- nodeTemplate['lib'] = None
- nodeTemplate['type'] = "graphInputs"
- nodeTemplate['name'] = "graphInputs"
- nodeTemplate['x'] = self.boundingRect.left() + 50
- nodeTemplate['y'] = self.boundingRect.center().y() + 50
- nodeTemplate['uuid'] = str(uuid.uuid4())
- nodeTemplate['meta']['label'] = "Inputs"
+ nodeTemplate["package"] = "PyFlowBase"
+ nodeTemplate["lib"] = None
+ nodeTemplate["type"] = "graphInputs"
+ nodeTemplate["name"] = "graphInputs"
+ nodeTemplate["x"] = self.boundingRect.left() + 50
+ nodeTemplate["y"] = self.boundingRect.center().y() + 50
+ nodeTemplate["uuid"] = str(uuid.uuid4())
+ nodeTemplate["meta"]["label"] = "Inputs"
node = self.createNode(nodeTemplate)
node.translate(-20, 0)
return node
def getOutputNode(self):
nodeTemplate = NodeBase.jsonTemplate()
- nodeTemplate['package'] = "PyFlowBase"
- nodeTemplate['lib'] = None
- nodeTemplate['type'] = "graphOutputs"
- nodeTemplate['name'] = "graphOutputs"
- nodeTemplate['x'] = self.boundingRect.width() - 50
- nodeTemplate['y'] = self.boundingRect.center().y() + 50
- nodeTemplate['uuid'] = str(uuid.uuid4())
- nodeTemplate['meta']['label'] = "Outputs"
+ nodeTemplate["package"] = "PyFlowBase"
+ nodeTemplate["lib"] = None
+ nodeTemplate["type"] = "graphOutputs"
+ nodeTemplate["name"] = "graphOutputs"
+ nodeTemplate["x"] = self.boundingRect.width() - 50
+ nodeTemplate["y"] = self.boundingRect.center().y() + 50
+ nodeTemplate["uuid"] = str(uuid.uuid4())
+ nodeTemplate["meta"]["label"] = "Outputs"
node = self.createNode(nodeTemplate)
node.translate(-20, 0)
return node
@@ -779,20 +834,37 @@ def validateConnections(self, graph):
srcNode = connection.source().owningNode()
if srcNode.isUnderActiveGraph():
comment = srcNode.owningCommentNode
- if comment is not None and comment.collapsed and not srcNode.isVisible():
- connection.sourcePositionOverride = comment.getRightSideEdgesPoint
+ if (
+ comment is not None
+ and comment.collapsed
+ and not srcNode.isVisible()
+ ):
+ connection.sourcePositionOverride = (
+ comment.getRightSideEdgesPoint
+ )
# override dst endpoint to comment right side if connected
# node is hidden and under collapsed comment
dstNode = connection.destination().owningNode()
if dstNode.isUnderActiveGraph():
comment = dstNode.owningCommentNode
- if comment is not None and comment.collapsed and not dstNode.isVisible():
- connection.destinationPositionOverride = comment.getLeftSideEdgesPoint
+ if (
+ comment is not None
+ and comment.collapsed
+ and not dstNode.isVisible()
+ ):
+ connection.destinationPositionOverride = (
+ comment.getLeftSideEdgesPoint
+ )
if connection.isUnderCollapsedComment():
connection.hide()
- if not connection.source().owningNode().isUnderActiveGraph() or not connection.destination().owningNode().isUnderActiveGraph():
+ if (
+ not connection.source().owningNode().isUnderActiveGraph()
+ or not connection.destination()
+ .owningNode()
+ .isUnderActiveGraph()
+ ):
connection.hide()
checked.add(connection)
@@ -837,16 +909,31 @@ def mousePressEvent(self, event):
self.pressedPin = self.findPinNearPosition(event.pos())
modifiers = event.modifiers()
self.mousePressPose = event.pos()
- expandComments = False
- currentInputAction = InputAction("temp", "temp", InputActionType.Mouse, event.button(), modifiers=modifiers)
- if any([not self.pressed_item,
- isinstance(self.pressed_item, UIConnection) and modifiers != QtCore.Qt.AltModifier,
- isinstance(self.pressed_item, UINodeBase) and node.isCommentNode and not node.collapsed,
- isinstance(node, UINodeBase) and (node.resizable and node.shouldResize(self.mapToScene(event.pos()))["resize"])]):
+ currentInputAction = InputAction(
+ "temp", "temp", InputActionType.Mouse, event.button(), modifiers=modifiers
+ )
+ if any(
+ [
+ not self.pressed_item,
+ isinstance(self.pressed_item, UIConnection)
+ and modifiers != QtCore.Qt.AltModifier,
+ isinstance(self.pressed_item, UINodeBase)
+ and node.isCommentNode
+ and not node.collapsed,
+ isinstance(node, UINodeBase)
+ and (
+ node.resizable
+ and node.shouldResize(self.mapToScene(event.pos()))["resize"]
+ ),
+ ]
+ ):
self.resizing = False
# Create branch on B + LMB
- if self.currentPressedKey is not None and event.button() == QtCore.Qt.LeftButton:
+ if (
+ self.currentPressedKey is not None
+ and event.button() == QtCore.Qt.LeftButton
+ ):
if self.currentPressedKey == QtCore.Qt.Key_B:
spawnPos = self.mapToScene(self.mousePressPose)
node = self.spawnNode("branch", spawnPos.x(), spawnPos.y())
@@ -857,27 +944,51 @@ def mousePressEvent(self, event):
self.resizing = node.bResize
node.setSelected(False)
if not self.resizing:
- if isinstance(self.pressed_item, UIConnection) and modifiers == QtCore.Qt.NoModifier and event.button() == QtCore.Qt.LeftButton:
+ if (
+ isinstance(self.pressed_item, UIConnection)
+ and modifiers == QtCore.Qt.NoModifier
+ and event.button() == QtCore.Qt.LeftButton
+ ):
closestPin = self.findPinNearPosition(event.pos(), 20)
if closestPin is not None:
if closestPin.direction == PinDirection.Input:
- self.pressed_item.destinationPositionOverride = lambda: self.mapToScene(self.mousePos)
+ self.pressed_item.destinationPositionOverride = lambda: self.mapToScene(
+ self.mousePos
+ )
elif closestPin.direction == PinDirection.Output:
- self.pressed_item.sourcePositionOverride = lambda: self.mapToScene(self.mousePos)
+ self.pressed_item.sourcePositionOverride = lambda: self.mapToScene(
+ self.mousePos
+ )
self.reconnectingWires.add(self.pressed_item)
- elif event.button() == QtCore.Qt.LeftButton and modifiers in [QtCore.Qt.NoModifier, QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier, QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier]:
+ elif event.button() == QtCore.Qt.LeftButton and modifiers in [
+ QtCore.Qt.NoModifier,
+ QtCore.Qt.ShiftModifier,
+ QtCore.Qt.ControlModifier,
+ QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ ]:
self.manipulationMode = CanvasManipulationMode.SELECT
- self._selectionRect = SelectionRect(graph=self, mouseDownPos=self.mapToScene(event.pos()), modifiers=modifiers)
+ self._selectionRect = SelectionRect(
+ graph=self,
+ mouseDownPos=self.mapToScene(event.pos()),
+ modifiers=modifiers,
+ )
self._selectionRect.selectFullyIntersectedItems = True
self._mouseDownSelection = [node for node in self.selectedNodes()]
- self._mouseDownConnectionsSelection = [node for node in self.selectedConnections()]
- if modifiers not in [QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier]:
+ self._mouseDownConnectionsSelection = [
+ node for node in self.selectedConnections()
+ ]
+ if modifiers not in [
+ QtCore.Qt.ShiftModifier,
+ QtCore.Qt.ControlModifier,
+ ]:
self.clearSelection()
else:
- if hasattr(self, "_selectionRect") and self._selectionRect is not None:
+ if (
+ hasattr(self, "_selectionRect")
+ and self._selectionRect is not None
+ ):
self._selectionRect.destroy()
self._selectionRect = None
- LeftPaning = event.button() == QtCore.Qt.LeftButton and modifiers == QtCore.Qt.AltModifier
if currentInputAction in InputManager()["Canvas.Pan"]:
self.manipulationMode = CanvasManipulationMode.PAN
self._lastPanPoint = self.mapToScene(event.pos())
@@ -887,35 +998,62 @@ def mousePressEvent(self, event):
self._lastSceneRect = self.sceneRect()
self._lastSceneCenter = self._lastSceneRect.center()
self._lastScenePos = self.mapToScene(event.pos())
- self._lastOffsetFromSceneCenter = self._lastScenePos - self._lastSceneCenter
+ self._lastOffsetFromSceneCenter = (
+ self._lastScenePos - self._lastSceneCenter
+ )
self.node_box.hide()
else:
- if not isinstance(self.pressed_item, NodesBox) and self.node_box.isVisible():
+ if (
+ not isinstance(self.pressed_item, NodesBox)
+ and self.node_box.isVisible()
+ ):
self.node_box.hide()
self.node_box.lineEdit.clear()
- if isinstance(self.pressed_item, UIPinBase) and not type(self.pressed_item) is PinGroup:
- if event.button() == QtCore.Qt.LeftButton and modifiers == QtCore.Qt.NoModifier:
- self.pressed_item.topLevelItem().setFlag(QGraphicsItem.ItemIsMovable, False)
- self.pressed_item.topLevelItem().setFlag(QGraphicsItem.ItemIsSelectable, False)
+ if (
+ isinstance(self.pressed_item, UIPinBase)
+ and not type(self.pressed_item) is PinGroup
+ ):
+ if (
+ event.button() == QtCore.Qt.LeftButton
+ and modifiers == QtCore.Qt.NoModifier
+ ):
+ self.pressed_item.topLevelItem().setFlag(
+ QGraphicsItem.ItemIsMovable, False
+ )
+ self.pressed_item.topLevelItem().setFlag(
+ QGraphicsItem.ItemIsSelectable, False
+ )
self._drawRealtimeLine = True
self.autoPanController.start()
- elif event.button() == QtCore.Qt.LeftButton and modifiers == QtCore.Qt.ControlModifier:
+ elif (
+ event.button() == QtCore.Qt.LeftButton
+ and modifiers == QtCore.Qt.ControlModifier
+ ):
for wire in self.pressed_item.uiConnectionList:
if self.pressed_item.direction == PinDirection.Input:
- wire.destinationPositionOverride = lambda: self.mapToScene(self.mousePos)
+ wire.destinationPositionOverride = lambda: self.mapToScene(
+ self.mousePos
+ )
elif self.pressed_item.direction == PinDirection.Output:
- wire.sourcePositionOverride = lambda: self.mapToScene(self.mousePos)
+ wire.sourcePositionOverride = lambda: self.mapToScene(
+ self.mousePos
+ )
self.reconnectingWires.add(wire)
if currentInputAction in InputManager()["Canvas.DisconnectPin"]:
self.removeEdgeCmd(self.pressed_item.connections)
self._drawRealtimeLine = False
else:
- if isinstance(self.pressed_item, UIConnection) and modifiers == QtCore.Qt.AltModifier:
+ if (
+ isinstance(self.pressed_item, UIConnection)
+ and modifiers == QtCore.Qt.AltModifier
+ ):
rerouteNode = self.getRerouteNode(event.pos(), self.pressed_item)
self.clearSelection()
rerouteNode.setSelected(True)
for inp in rerouteNode.UIinputs.values():
- if canConnectPins(self.pressed_item.source()._rawPin, inp._rawPin):
+ if canConnectPins(
+ self.pressed_item.source()._rawPin, inp._rawPin
+ ):
drawPin = self.pressed_item.drawSource
if self.pressed_item.source().isExec():
self.pressed_item.kill()
@@ -925,7 +1063,9 @@ def mousePressEvent(self, event):
break
for out in rerouteNode.UIoutputs.values():
drawPin = self.pressed_item.drawDestination
- if canConnectPins(out._rawPin, self.pressed_item.destination()._rawPin):
+ if canConnectPins(
+ out._rawPin, self.pressed_item.destination()._rawPin
+ ):
self.connectPins(out, self.pressed_item.destination())
for connection in out.connections:
connection.drawDestination = drawPin
@@ -959,15 +1099,22 @@ def mousePressEvent(self, event):
else:
if modifiers in [QtCore.Qt.NoModifier, QtCore.Qt.AltModifier]:
super(BlueprintCanvas, self).mousePressEvent(event)
- if modifiers == QtCore.Qt.ControlModifier and event.button() == QtCore.Qt.LeftButton:
+ if (
+ modifiers == QtCore.Qt.ControlModifier
+ and event.button() == QtCore.Qt.LeftButton
+ ):
node.setSelected(not node.isSelected())
if modifiers == QtCore.Qt.ShiftModifier:
node.setSelected(True)
- if currentInputAction in InputManager()["Canvas.DragNodes"] and isinstance(self.pressed_item, UINodeBase):
+ if currentInputAction in InputManager()[
+ "Canvas.DragNodes"
+ ] and isinstance(self.pressed_item, UINodeBase):
self.manipulationMode = CanvasManipulationMode.MOVE
if self.pressed_item.objectName() == "MouseLocked":
super(BlueprintCanvas, self).mousePressEvent(event)
- elif currentInputAction in InputManager()["Canvas.DragNodes"] and isinstance(self.pressed_item.topLevelItem(), UINodeBase):
+ elif currentInputAction in InputManager()[
+ "Canvas.DragNodes"
+ ] and isinstance(self.pressed_item.topLevelItem(), UINodeBase):
isComment = self.pressed_item.topLevelItem().isCommentNode
if isComment:
self.manipulationMode = CanvasManipulationMode.MOVE
@@ -978,10 +1125,16 @@ def mousePressEvent(self, event):
def updateReroutes(self, event, showPins=False):
tolerance = 9 * self.currentViewScale()
- mouseRect = QtCore.QRect(QtCore.QPoint(event.pos().x() - tolerance, event.pos().y() - tolerance),
- QtCore.QPoint(event.pos().x() + tolerance, event.pos().y() + tolerance))
+ mouseRect = QtCore.QRect(
+ QtCore.QPoint(event.pos().x() - tolerance, event.pos().y() - tolerance),
+ QtCore.QPoint(event.pos().x() + tolerance, event.pos().y() + tolerance),
+ )
hoverItems = self.items(mouseRect)
- self.hoveredReroutes += [node for node in hoverItems if isinstance(node, UINodeBase) and node.isReroute()]
+ self.hoveredReroutes += [
+ node
+ for node in hoverItems
+ if isinstance(node, UINodeBase) and node.isReroute()
+ ]
for node in self.hoveredReroutes:
if showPins:
if node in hoverItems:
@@ -1028,16 +1181,26 @@ def mouseMoveEvent(self, event):
p1 = self.pressed_item.scenePos() + self.pressed_item.pinCenter()
p2 = self.mapToScene(self.mousePos)
- mouseRect = QtCore.QRect(QtCore.QPoint(event.pos().x() - 5, event.pos().y() - 4),
- QtCore.QPoint(event.pos().x() + 5, event.pos().y() + 4))
+ mouseRect = QtCore.QRect(
+ QtCore.QPoint(event.pos().x() - 5, event.pos().y() - 4),
+ QtCore.QPoint(event.pos().x() + 5, event.pos().y() + 4),
+ )
hoverItems = self.items(mouseRect)
hoveredPins = [pin for pin in hoverItems if isinstance(pin, UIPinBase)]
if len(hoveredPins) > 0:
item = hoveredPins[0]
- if isinstance(item, UIPinBase) and isinstance(self.pressed_item, UIPinBase):
- canBeConnected = canConnectPins(self.pressed_item._rawPin, item._rawPin)
- self.realTimeLine.setPen(self.realTimeLineValidPen if canBeConnected else self.realTimeLineInvalidPen)
+ if isinstance(item, UIPinBase) and isinstance(
+ self.pressed_item, UIPinBase
+ ):
+ canBeConnected = canConnectPins(
+ self.pressed_item._rawPin, item._rawPin
+ )
+ self.realTimeLine.setPen(
+ self.realTimeLineValidPen
+ if canBeConnected
+ else self.realTimeLineInvalidPen
+ )
if canBeConnected:
p2 = item.scenePos() + item.pinCenter()
else:
@@ -1047,13 +1210,16 @@ def mouseMoveEvent(self, event):
multiply = 3
path = QtGui.QPainterPath()
path.moveTo(p1)
- path.cubicTo(QtCore.QPoint(p1.x() + distance / multiply, p1.y()),
- QtCore.QPoint(p2.x() - distance / 2, p2.y()), p2)
+ path.cubicTo(
+ QtCore.QPoint(p1.x() + distance / multiply, p1.y()),
+ QtCore.QPoint(p2.x() - distance / 2, p2.y()),
+ p2,
+ )
self.realTimeLine.setPath(path)
if modifiers == QtCore.Qt.AltModifier:
self._drawRealtimeLine = False
if self.realTimeLine in self.scene().items():
- self.removeItemByName('RealTimeLine')
+ self.removeItemByName("RealTimeLine")
rerouteNode = self.getRerouteNode(event.pos())
self.clearSelection()
rerouteNode.setSelected(True)
@@ -1082,43 +1248,67 @@ def mouseMoveEvent(self, event):
# handle nodes
for node in nodes:
if node in self._mouseDownSelection:
- if node.isSelected() and self._selectionRect.collidesWithItem(node):
+ if node.isSelected() and self._selectionRect.collidesWithItem(
+ node
+ ):
node.setSelected(False)
- elif not node.isSelected() and not self._selectionRect.collidesWithItem(node):
+ elif not node.isSelected() and not self._selectionRect.collidesWithItem(
+ node
+ ):
node.setSelected(True)
else:
- if not node.isSelected() and self._selectionRect.collidesWithItem(node):
+ if not node.isSelected() and self._selectionRect.collidesWithItem(
+ node
+ ):
node.setSelected(True)
- elif node.isSelected() and not self._selectionRect.collidesWithItem(node):
+ elif node.isSelected() and not self._selectionRect.collidesWithItem(
+ node
+ ):
if node not in self._mouseDownSelection:
node.setSelected(False)
# handle connections
for wire in self.connections.values():
if wire in self._mouseDownConnectionsSelection:
- if wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ if wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
wire.setSelected(False)
- elif not wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ elif not wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
wire.setSelected(True)
else:
- if not wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ if not wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
wire.setSelected(True)
- elif wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ elif wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
if wire not in self._mouseDownConnectionsSelection:
wire.setSelected(False)
elif modifiers == QtCore.Qt.ShiftModifier:
for node in nodes:
- if not node.isSelected() and self._selectionRect.collidesWithItem(node):
+ if not node.isSelected() and self._selectionRect.collidesWithItem(
+ node
+ ):
node.setSelected(True)
- elif node.isSelected() and not self._selectionRect.collidesWithItem(node):
+ elif node.isSelected() and not self._selectionRect.collidesWithItem(
+ node
+ ):
if node not in self._mouseDownSelection:
node.setSelected(False)
for wire in self.connections.values():
- if not wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ if not wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
wire.setSelected(True)
- elif wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ elif wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
if wire not in self._mouseDownConnectionsSelection:
wire.setSelected(False)
@@ -1128,28 +1318,37 @@ def mouseMoveEvent(self, event):
node.setSelected(False)
for wire in self.connections.values():
- if QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ if QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
wire.setSelected(False)
else:
self.clearSelection()
for node in nodes:
# if node not in [self.inputsItem,self.outputsItem]:
- if not node.isSelected() and self._selectionRect.collidesWithItem(node):
+ if not node.isSelected() and self._selectionRect.collidesWithItem(
+ node
+ ):
node.setSelected(True)
- elif node.isSelected() and not self._selectionRect.collidesWithItem(node):
+ elif node.isSelected() and not self._selectionRect.collidesWithItem(
+ node
+ ):
node.setSelected(False)
for wire in self.connections.values():
- if not wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ if not wire.isSelected() and QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
wire.setSelected(True)
- elif wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(self._selectionRect, wire):
+ elif wire.isSelected() and not QtWidgets.QGraphicsWidget.collidesWithItem(
+ self._selectionRect, wire
+ ):
wire.setSelected(False)
elif self.manipulationMode == CanvasManipulationMode.MOVE:
# TODO: do not change object names. Rewrite with flag e.g. `bMovementLocked`
if self.pressed_item.objectName() == "MouseLocked":
super(BlueprintCanvas, self).mouseMoveEvent(event)
else:
- newPos = self.mapToScene(event.pos())
scaledDelta = mouseDelta / self.currentViewScale()
selectedNodes = self.selectedNodes()
@@ -1158,17 +1357,34 @@ def mouseMoveEvent(self, event):
node.translate(scaledDelta.x(), scaledDelta.y())
if node.isReroute() and modifiers == QtCore.Qt.AltModifier:
- mouseRect = QtCore.QRect(QtCore.QPoint(event.pos().x() - 1, event.pos().y() - 1),
- QtCore.QPoint(event.pos().x() + 1, event.pos().y() + 1))
+ mouseRect = QtCore.QRect(
+ QtCore.QPoint(event.pos().x() - 1, event.pos().y() - 1),
+ QtCore.QPoint(event.pos().x() + 1, event.pos().y() + 1),
+ )
hoverItems = self.items(mouseRect)
newOuts = []
newIns = []
for item in hoverItems:
if isinstance(item, UIConnection):
- if list(node.UIinputs.values())[0].connections and list(node.UIoutputs.values())[0].connections:
- if item.source() == list(node.UIinputs.values())[0].connections[0].source():
- newOuts.append([item.destination(), item.drawDestination])
- if item.destination() == list(node.UIoutputs.values())[0].connections[0].destination():
+ if (
+ list(node.UIinputs.values())[0].connections
+ and list(node.UIoutputs.values())[0].connections
+ ):
+ if (
+ item.source()
+ == list(node.UIinputs.values())[0]
+ .connections[0]
+ .source()
+ ):
+ newOuts.append(
+ [item.destination(), item.drawDestination]
+ )
+ if (
+ item.destination()
+ == list(node.UIoutputs.values())[0]
+ .connections[0]
+ .destination()
+ ):
newIns.append([item.source(), item.drawSource])
for out in newOuts:
self.connectPins(list(node.UIoutputs.values())[0], out[0])
@@ -1177,7 +1393,6 @@ def mouseMoveEvent(self, event):
elif self.manipulationMode == CanvasManipulationMode.PAN:
self.pan(mouseDelta)
elif self.manipulationMode == CanvasManipulationMode.ZOOM:
- zoomFactor = 1.0
if mouseDelta.x() > 0:
zoomFactor = 1.0 + mouseDelta.x() / 100.0
else:
@@ -1187,7 +1402,6 @@ def mouseMoveEvent(self, event):
delta = self.mousePos - self.mousePressPose
if delta.manhattanLength() > 15:
self.manipulationMode = CanvasManipulationMode.MOVE
- selectedNodes = self.selectedNodes()
copiedNodes = self.copyNodes(toClipBoard=False)
self.pasteNodes(move=False, data=copiedNodes)
scaledDelta = delta / self.currentViewScale()
@@ -1201,10 +1415,14 @@ def mouseMoveEvent(self, event):
def findPinNearPosition(self, scenePos, tolerance=3):
tolerance = tolerance * self.currentViewScale()
- rect = QtCore.QRect(QtCore.QPoint(scenePos.x() - tolerance, scenePos.y() - tolerance),
- QtCore.QPoint(scenePos.x() + tolerance, scenePos.y() + tolerance))
+ rect = QtCore.QRect(
+ QtCore.QPoint(scenePos.x() - tolerance, scenePos.y() - tolerance),
+ QtCore.QPoint(scenePos.x() + tolerance, scenePos.y() + tolerance),
+ )
items = self.items(rect)
- pins = [i for i in items if isinstance(i, UIPinBase) and type(i) is not PinGroup]
+ pins = [
+ i for i in items if isinstance(i, UIPinBase) and type(i) is not PinGroup
+ ]
if len(pins) > 0:
return pins[0]
return None
@@ -1219,7 +1437,10 @@ def mouseReleaseEvent(self, event):
self.released_item = self.itemAt(event.pos())
self.releasedPin = self.findPinNearPosition(event.pos())
- if self.manipulationMode == CanvasManipulationMode.MOVE and len(self.selectedNodes()) > 0:
+ if (
+ self.manipulationMode == CanvasManipulationMode.MOVE
+ and len(self.selectedNodes()) > 0
+ ):
EditorHistory().saveState("Move nodes", modify=True)
if len(self.reconnectingWires) > 0:
@@ -1253,26 +1474,44 @@ def mouseReleaseEvent(self, event):
if self._drawRealtimeLine:
self._drawRealtimeLine = False
if self.realTimeLine in self.scene().items():
- self.removeItemByName('RealTimeLine')
+ self.removeItemByName("RealTimeLine")
if self.manipulationMode == CanvasManipulationMode.SELECT:
self._selectionRect.destroy()
self._selectionRect = None
- if event.button() == QtCore.Qt.RightButton and modifiers == QtCore.Qt.NoModifier:
+ if (
+ event.button() == QtCore.Qt.RightButton
+ and modifiers == QtCore.Qt.NoModifier
+ ):
# show nodebox only if drag is small and no items under cursor
- if self.pressed_item is None or (isinstance(self.pressed_item, UINodeBase) and self.nodeFromInstance(self.pressed_item).isCommentNode):
+ if self.pressed_item is None or (
+ isinstance(self.pressed_item, UINodeBase)
+ and self.nodeFromInstance(self.pressed_item).isCommentNode
+ ):
if modifiers == QtCore.Qt.NoModifier:
- dragDiff = self.mapToScene(self.mousePressPose) - self.mapToScene(event.pos())
+ dragDiff = self.mapToScene(self.mousePressPose) - self.mapToScene(
+ event.pos()
+ )
if all([abs(i) < 0.4 for i in [dragDiff.x(), dragDiff.y()]]):
self.showNodeBox()
- elif event.button() == QtCore.Qt.RightButton and modifiers == QtCore.Qt.ControlModifier:
+ elif (
+ event.button() == QtCore.Qt.RightButton
+ and modifiers == QtCore.Qt.ControlModifier
+ ):
self.menu.exec_(QtGui.QCursor.pos())
elif event.button() == QtCore.Qt.LeftButton and self.releasedPin is None:
- if isinstance(self.pressed_item, UIPinBase) and not self.resizing and modifiers == QtCore.Qt.NoModifier:
+ if (
+ isinstance(self.pressed_item, UIPinBase)
+ and not self.resizing
+ and modifiers == QtCore.Qt.NoModifier
+ ):
if not type(self.pressed_item) is PinGroup:
# suggest nodes that can be connected to pressed pin
- self.showNodeBox(self.pressed_item.direction, self.pressed_item._rawPin.getCurrentStructure())
+ self.showNodeBox(
+ self.pressed_item.direction,
+ self.pressed_item._rawPin.getCurrentStructure(),
+ )
self.manipulationMode = CanvasManipulationMode.NONE
if not self.resizing:
p_itm = self.pressedPin
@@ -1286,7 +1525,10 @@ def mouseReleaseEvent(self, event):
do_connect = False
break
if p_itm and r_itm:
- if p_itm.__class__.__name__ == UIPinBase.__name__ and r_itm.__class__.__name__ == UIPinBase.__name__:
+ if (
+ p_itm.__class__.__name__ == UIPinBase.__name__
+ and r_itm.__class__.__name__ == UIPinBase.__name__
+ ):
if cycleCheck(p_itm, r_itm):
# print('cycles are not allowed')
do_connect = False
@@ -1300,7 +1542,15 @@ def mouseReleaseEvent(self, event):
releasedNode = self.nodeFromInstance(self.released_item)
pressedNode = self.nodeFromInstance(self.pressed_item)
manhattanLengthTest = (self.mousePressPose - event.pos()).manhattanLength() <= 2
- if all([event.button() == QtCore.Qt.LeftButton, releasedNode is not None, pressedNode is not None, pressedNode == releasedNode, manhattanLengthTest]):
+ if all(
+ [
+ event.button() == QtCore.Qt.LeftButton,
+ releasedNode is not None,
+ pressedNode is not None,
+ pressedNode == releasedNode,
+ manhattanLengthTest,
+ ]
+ ):
# check if clicking on node action button
if self.released_item is not None:
if isinstance(self.released_item.parentItem(), NodeActionButtonBase):
@@ -1313,7 +1563,11 @@ def mouseReleaseEvent(self, event):
self.updateReroutes(event, False)
def removeItemByName(self, name):
- [self.scene().removeItem(i) for i in self.scene().items() if hasattr(i, 'name') and i.name == name]
+ [
+ self.scene().removeItem(i)
+ for i in self.scene().items()
+ if hasattr(i, "name") and i.name == name
+ ]
def tryFillPropertiesView(self, obj):
if isinstance(obj, IPropertiesViewSupport):
@@ -1331,38 +1585,46 @@ def dragEnterEvent(self, event):
if url.isLocalFile():
filePath = url.toLocalFile()
if filePath.endswith(".pygraph"):
- with open(filePath, 'r') as f:
+ with open(filePath, "r") as f:
data = json.load(f)
if "fileVersion" in data:
event.accept()
- self.dropCallback = partial(self.getApp().loadFromFileChecked, filePath)
+ self.dropCallback = partial(
+ self.getApp().loadFromFileChecked, filePath
+ )
return
elif filePath.endswith(".compound"):
- with open(filePath, 'r') as f:
+ with open(filePath, "r") as f:
data = json.load(f)
def spawnCompoundFromData(data):
mousePos = self.mapToScene(self.mousePos)
- compound = self.spawnNode("compound", mousePos.x(), mousePos.y())
+ compound = self.spawnNode(
+ "compound", mousePos.x(), mousePos.y()
+ )
compound.assignData(data)
+
event.accept()
self.dropCallback = partial(spawnCompoundFromData, data)
return
elif filePath.endswith(".pynode"):
- with open(filePath, 'r') as f:
+ with open(filePath, "r") as f:
data = f.read()
def spawnPyNodeFromData(data):
mousePos = self.mapToScene(self.mousePos)
- compound = self.spawnNode("pythonNode", mousePos.x(), mousePos.y())
+ compound = self.spawnNode(
+ "pythonNode", mousePos.x(), mousePos.y()
+ )
compound.tryApplyNodeData(data)
+
event.accept()
self.dropCallback = partial(spawnPyNodeFromData, data)
return
- elif event.mimeData().hasFormat('text/plain'):
+ elif event.mimeData().hasFormat("text/plain"):
scenePos = self.mapToScene(event.pos())
event.accept()
- mime = str(event.mimeData().text())
+ mime = event.mimeData().text()
jsonData = json.loads(mime)
if VARIABLE_TAG in jsonData:
@@ -1373,14 +1635,14 @@ def spawnPyNodeFromData(data):
name = nodeType
nodeTemplate = NodeBase.jsonTemplate()
- nodeTemplate['package'] = packageName
- nodeTemplate['lib'] = libName
- nodeTemplate['type'] = nodeType
- nodeTemplate['name'] = name
- nodeTemplate['x'] = scenePos.x()
- nodeTemplate['y'] = scenePos.y()
- nodeTemplate['meta']['label'] = nodeType
- nodeTemplate['uuid'] = str(uuid.uuid4())
+ nodeTemplate["package"] = packageName
+ nodeTemplate["lib"] = libName
+ nodeTemplate["type"] = nodeType
+ nodeTemplate["name"] = name
+ nodeTemplate["x"] = scenePos.x()
+ nodeTemplate["y"] = scenePos.y()
+ nodeTemplate["meta"]["label"] = nodeType
+ nodeTemplate["uuid"] = str(uuid.uuid4())
try:
self.tempnode.isTemp = False
self.tempnode = None
@@ -1398,13 +1660,17 @@ def dragMoveEvent(self, event):
scenePos = self.mapToScene(self.mousePos)
if self.dropCallback is not None:
event.accept()
- elif event.mimeData().hasFormat('text/plain'):
+ elif event.mimeData().hasFormat("text/plain"):
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
if self.tempnode:
- self.tempnode.setPos((self.tempnode.w / -2) + scenePos.x()+5, scenePos.y()+5)
- mouseRect = QtCore.QRect(QtCore.QPoint(scenePos.x() - 1, scenePos.y() - 1),
- QtCore.QPoint(scenePos.x() + 1, scenePos.y() + 1))
+ self.tempnode.setPos(
+ (self.tempnode.w / -2) + scenePos.x() + 5, scenePos.y() + 5
+ )
+ mouseRect = QtCore.QRect(
+ QtCore.QPoint(scenePos.x() - 1, scenePos.y() - 1),
+ QtCore.QPoint(scenePos.x() + 1, scenePos.y() + 1),
+ )
hoverItems = self.scene().items(mouseRect)
for item in hoverItems:
if isinstance(item, UIConnection):
@@ -1444,7 +1710,7 @@ def dropEvent(self, event):
x = scenePos.x()
y = scenePos.y()
- if event.mimeData().hasFormat('text/plain'):
+ if event.mimeData().hasFormat("text/plain"):
jsonData = json.loads(event.mimeData().text())
# try load mime data text as json
@@ -1458,17 +1724,21 @@ def dropEvent(self, event):
varData = jsonData[VARIABLE_DATA_TAG]
def varGetterCreator():
- n = self.spawnNode("getVar", x, y, payload={"varUid": varData["uuid"]})
+ n = self.spawnNode(
+ "getVar", x, y, payload={"varUid": varData["uuid"]}
+ )
n.updateNodeShape()
def varSetterCreator():
- n = self.spawnNode("setVar", x, y, payload={"varUid": varData["uuid"]})
+ n = self.spawnNode(
+ "setVar", x, y, payload={"varUid": varData["uuid"]}
+ )
n.updateNodeShape()
if modifiers == QtCore.Qt.NoModifier:
m = QMenu()
- getterAction = m.addAction('Get')
- setterAction = m.addAction('Set')
+ getterAction = m.addAction("Get")
+ setterAction = m.addAction("Set")
getterAction.triggered.connect(varGetterCreator)
setterAction.triggered.connect(varSetterCreator)
m.exec_(QtGui.QCursor.pos(), None)
@@ -1481,10 +1751,10 @@ def varSetterCreator():
else:
packageName = jsonData["package"]
nodeType = jsonData["type"]
- libName = jsonData['lib']
+ libName = jsonData["lib"]
name = nodeType
- dropItem = self.nodeFromInstance(self.itemAt(scenePos.toPoint()))
- #if not dropItem or (isinstance(dropItem, UINodeBase) and dropItem.isCommentNode or dropItem.isTemp) or isinstance(dropItem, UIPinBase) or isinstance(dropItem, UIConnection):
+ # dropItem = self.nodeFromInstance(self.itemAt(scenePos.toPoint()))
+ # if not dropItem or (isinstance(dropItem, UINodeBase) and dropItem.isCommentNode or dropItem.isTemp) or isinstance(dropItem, UIPinBase) or isinstance(dropItem, UIConnection):
#######################################################
# Drop from nodebox by R. Scharf-Wildenhain, 2022-07-22
# dropItem = self.nodeFromInstance(self.itemAt(scenePos.toPoint()))
@@ -1492,20 +1762,26 @@ def varSetterCreator():
dropItem = self.itemAt(event.pos())
# if not dropItem or (isinstance(dropItem, UINodeBase) and dropItem.isCommentNode or dropItem.isTemp) or isinstance(dropItem, UIPinBase) or isinstance(dropItem, UIConnection):
- if dropNode or not dropNode or \
- (isinstance(dropItem, UINodeBase) and (dropItem.isCommentNode or dropItem.isTemp)) or \
- isinstance(dropItem, UIPinBase) or \
- isinstance(dropItem, UIConnection):
- #######################################################
+ if (
+ dropNode
+ or not dropNode
+ or (
+ isinstance(dropItem, UINodeBase)
+ and (dropItem.isCommentNode or dropItem.isTemp)
+ )
+ or isinstance(dropItem, UIPinBase)
+ or isinstance(dropItem, UIConnection)
+ ):
+ #######################################################
nodeTemplate = NodeBase.jsonTemplate()
- nodeTemplate['package'] = packageName
- nodeTemplate['lib'] = libName
- nodeTemplate['type'] = nodeType
- nodeTemplate['name'] = name
- nodeTemplate['x'] = x
- nodeTemplate['y'] = y
- nodeTemplate['meta']['label'] = nodeType
- nodeTemplate['uuid'] = str(uuid.uuid4())
+ nodeTemplate["package"] = packageName
+ nodeTemplate["lib"] = libName
+ nodeTemplate["type"] = nodeType
+ nodeTemplate["name"] = name
+ nodeTemplate["x"] = x
+ nodeTemplate["y"] = y
+ nodeTemplate["meta"]["label"] = nodeType
+ nodeTemplate["uuid"] = str(uuid.uuid4())
if self.tempnode:
self.tempnode.updateOwningCommentNode()
self.tempnode.isTemp = False
@@ -1520,7 +1796,9 @@ def varSetterCreator():
dropItem = it
break
node.eventDropOnCanvas()
- EditorHistory().saveState("Create node {}".format(node.name), modify=True)
+ EditorHistory().saveState(
+ "Create node {}".format(node.name), modify=True
+ )
else:
node = self.createNode(nodeTemplate)
@@ -1549,7 +1827,9 @@ def varSetterCreator():
self.connectPins(dropItem.source(), inp)
break
for out in nodeOutputs.values():
- if canConnectPins(out._rawPin, dropItem.destination()._rawPin):
+ if canConnectPins(
+ out._rawPin, dropItem.destination()._rawPin
+ ):
self.connectPins(out, dropItem.destination())
break
super(BlueprintCanvas, self).dropEvent(event)
@@ -1557,8 +1837,8 @@ def varSetterCreator():
def _createNode(self, jsonTemplate):
# Check if this node is variable get/set. Variables created in child graphs are not visible to parent ones
# Stop any attempt to disrupt variable scope. Even if we accidentally forgot this check, GraphBase.addNode will fail
- if jsonTemplate['type'] in ['getVar', 'setVar']:
- var = self.graphManager.findVariableByUid(uuid.UUID(jsonTemplate['varUid']))
+ if jsonTemplate["type"] in ["getVar", "setVar"]:
+ var = self.graphManager.findVariableByUid(uuid.UUID(jsonTemplate["varUid"]))
variableLocation = var.location()
graphLocation = self.graphManager.location()
if len(variableLocation) > len(graphLocation):
@@ -1568,25 +1848,29 @@ def _createNode(self, jsonTemplate):
return None
nodeInstance = getNodeInstance(jsonTemplate, self)
- assert(nodeInstance is not None), "Node instance is not found!"
+ assert nodeInstance is not None, "Node instance is not found!"
nodeInstance.setPos(jsonTemplate["x"], jsonTemplate["y"])
# set pins data
- for inpJson in jsonTemplate['inputs']:
- pin = nodeInstance.getPinSG(inpJson['name'], PinSelectionGroup.Inputs)
+ for inpJson in jsonTemplate["inputs"]:
+ pin = nodeInstance.getPinSG(inpJson["name"], PinSelectionGroup.Inputs)
if pin:
- pin.uid = uuid.UUID(inpJson['uuid'])
+ pin.uid = uuid.UUID(inpJson["uuid"])
try:
- pin.setData(json.loads(inpJson['value'], cls=pin.jsonDecoderClass()))
+ pin.setData(
+ json.loads(inpJson["value"], cls=pin.jsonDecoderClass())
+ )
except:
pin.setData(pin.defaultValue())
- for outJson in jsonTemplate['outputs']:
- pin = nodeInstance.getPinSG(outJson['name'], PinSelectionGroup.Outputs)
+ for outJson in jsonTemplate["outputs"]:
+ pin = nodeInstance.getPinSG(outJson["name"], PinSelectionGroup.Outputs)
if pin:
- pin.uid = uuid.UUID(outJson['uuid'])
+ pin.uid = uuid.UUID(outJson["uuid"])
try:
- pin.setData(json.loads(outJson['value'], cls=pin.jsonDecoderClass()))
+ pin.setData(
+ json.loads(outJson["value"], cls=pin.jsonDecoderClass())
+ )
except:
pin.setData(pin.defaultValue())
@@ -1594,10 +1878,14 @@ def _createNode(self, jsonTemplate):
def createNode(self, jsonTemplate, **kwargs):
nodeInstance = self._createNode(jsonTemplate)
- EditorHistory().saveState("Create node {}".format(nodeInstance.name), modify=True)
+ EditorHistory().saveState(
+ "Create node {}".format(nodeInstance.name), modify=True
+ )
return nodeInstance
- def spawnNode(self, nodeClass, x, y, payload={}):
+ def spawnNode(self, nodeClass, x, y, payload=None):
+ if payload is None:
+ payload = {}
packageName = None
for pkgName, pkg in GET_PACKAGES().items():
if nodeClass in pkg.GetNodeClasses():
@@ -1665,7 +1953,7 @@ def addNode(self, uiNode, jsonTemplate, parentGraph=None):
uiNode.canvasRef = weakref.ref(self)
self.scene().addItem(uiNode)
- assert(jsonTemplate is not None)
+ assert jsonTemplate is not None
if uiNode._rawNode.graph is None:
# if added from node box
@@ -1673,14 +1961,14 @@ def addNode(self, uiNode, jsonTemplate, parentGraph=None):
else:
# When copy paste compound node. we are actually pasting a tree of graphs
# So we need to put each node under correct graph
- assert(parentGraph is not None), "Parent graph is invalid"
+ assert parentGraph is not None, "Parent graph is invalid"
parentGraph.addNode(uiNode._rawNode, jsonTemplate)
uiNode.postCreate(jsonTemplate)
def createUIConnectionForConnectedPins(self, srcUiPin, dstUiPin):
- assert(srcUiPin is not None)
- assert(dstUiPin is not None)
+ assert srcUiPin is not None
+ assert dstUiPin is not None
if srcUiPin.direction == PinDirection.Input:
srcUiPin, dstUiPin = dstUiPin, srcUiPin
uiConnection = UIConnection(srcUiPin, dstUiPin, self)
@@ -1738,6 +2026,7 @@ def eventFilter(self, object, event):
class BlueprintCanvasWidget(QWidget):
"""docstring for BlueprintCanvasWidget."""
+
def __init__(self, graphManager, pyFlowInstance, parent=None):
super(BlueprintCanvasWidget, self).__init__(parent)
self.manager = graphManager
@@ -1778,25 +2067,31 @@ def __init__(self, graphManager, pyFlowInstance, parent=None):
self.manager.graphChanged.connect(self.updateGraphTreeLocation)
- self.canvas.requestFillProperties.connect(self.pyFlowInstance.onRequestFillProperties)
- self.canvas.requestClearProperties.connect(self.pyFlowInstance.onRequestClearProperties)
+ self.canvas.requestFillProperties.connect(
+ self.pyFlowInstance.onRequestFillProperties
+ )
+ self.canvas.requestClearProperties.connect(
+ self.pyFlowInstance.onRequestClearProperties
+ )
- rxLettersAndNumbers = QtCore.QRegExp('^[a-zA-Z0-9]*$')
- nameValidator = QtGui.QRegExpValidator(rxLettersAndNumbers, self.leCompoundName)
+ rxLettersAndNumbers = QtCore.QRegularExpression('^[a-zA-Z0-9]*$')
+ nameValidator = QtGui.QRegularExpressionValidator(rxLettersAndNumbers, self.leCompoundName)
self.leCompoundName.setValidator(nameValidator)
self.leCompoundName.returnPressed.connect(self.onActiveCompoundNameAccepted)
- rxLetters = QtCore.QRegExp('[a-zA-Z]+(\|[a-zA-Z]+)*')
- categoryValidator = QtGui.QRegExpValidator(rxLetters, self.leCompoundCategory)
+ rxLetters = QtCore.QRegularExpression('[a-zA-Z]+(\|[a-zA-Z]+)*')
+ categoryValidator = QtGui.QRegularExpressionValidator(rxLetters, self.leCompoundCategory)
self.leCompoundCategory.setValidator(categoryValidator)
- self.leCompoundCategory.returnPressed.connect(self.onActiveCompoundCategoryAccepted)
+ self.leCompoundCategory.returnPressed.connect(
+ self.onActiveCompoundCategoryAccepted
+ )
self.updateGraphTreeLocation()
self.pyFlowInstance.fileBeenLoaded.connect(self.onFileBeenLoaded)
def shoutDown(self):
- self.canvas.shoutDown()
+ self.canvas.shutDown()
def Tick(self, delta):
self.canvas.Tick(delta)
diff --git a/PyFlow/UI/Widgets/EditPropertiesWidget.py b/PyFlow/UI/Widgets/EditPropertiesWidget.py
index 874456b5c..ee4704b2f 100644
--- a/PyFlow/UI/Widgets/EditPropertiesWidget.py
+++ b/PyFlow/UI/Widgets/EditPropertiesWidget.py
@@ -13,7 +13,7 @@
## limitations under the License.
-from Qt import QtWidgets, QtCore
+from qtpy import QtWidgets, QtCore
import sys
import collections
@@ -63,7 +63,7 @@ def model_to_dict(self):
return d
-if __name__ == '__main__':
+if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
form = EditPropertiesTreeWidget()
form.addNormal("Normal")
diff --git a/PyFlow/UI/Widgets/EnumComboBox.py b/PyFlow/UI/Widgets/EnumComboBox.py
index e9e4a136e..544398d9d 100644
--- a/PyFlow/UI/Widgets/EnumComboBox.py
+++ b/PyFlow/UI/Widgets/EnumComboBox.py
@@ -13,17 +13,19 @@
## limitations under the License.
-from Qt import QtCore, QtGui
-from Qt.QtWidgets import QComboBox, QCompleter
+from qtpy import QtCore, QtGui
+from qtpy.QtWidgets import QComboBox, QCompleter
class EnumComboBox(QComboBox):
changeCallback = QtCore.Signal(str)
textChangedCallback = QtCore.Signal(str)
- def __init__(self, values=[], parent=None):
+ def __init__(self, values=None, parent=None):
super(EnumComboBox, self).__init__(parent)
+ if values is None:
+ values = []
self.setFocusPolicy(QtCore.Qt.StrongFocus)
self.setEditable(True)
self.completer = QCompleter(self)
@@ -33,7 +35,7 @@ def __init__(self, values=[], parent=None):
self.pFilterModel = QtCore.QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
- self.setInsertPolicy(self.NoInsert)
+ #self.setInsertPolicy(self.NoInsert)
self.completer.setPopup(self.view())
@@ -84,7 +86,7 @@ def setTextIfCompleterIsClicked(self, text):
if __name__ == "__main__":
import sys
- from Qt.QtWidgets import QApplication
+ from qtpy.QtWidgets import QApplication
a = QApplication(sys.argv)
def clb(string):
diff --git a/PyFlow/UI/Widgets/FileDialog.py b/PyFlow/UI/Widgets/FileDialog.py
index 8fa70a94b..b1806b2dc 100644
--- a/PyFlow/UI/Widgets/FileDialog.py
+++ b/PyFlow/UI/Widgets/FileDialog.py
@@ -1,12 +1,13 @@
-from Qt import QtCore
-from Qt.QtWidgets import *
+from qtpy.QtWidgets import *
+
class FileDialog(QFileDialog):
"""docstring for ExecInputWidget"""
- def __init__(self, mode="all", multifile=False, parent=None, **kwds):
- super(FileDialog, self).__init__(parent=parent, **kwds)
+
+ def __init__(self, mode="all", multifile=False, parent=None, **kwargs):
+ super(FileDialog, self).__init__(parent=parent, **kwargs)
self.setOption(QFileDialog.DontUseNativeDialog, True)
- if mode == "all":
+ if mode == "all":
self.setFileMode(QFileDialog.Directory)
for but in self.findChildren(QPushButton):
if "open" in but.text().lower() or "choose" in but.text().lower():
@@ -18,7 +19,7 @@ def __init__(self, mode="all", multifile=False, parent=None, **kwds):
elif mode == "directory":
self.setFileMode(QFileDialog.DirectoryOnly)
-
+
if multifile:
self.listView = self.findChild(QListView)
if self.listView:
@@ -28,4 +29,4 @@ def __init__(self, mode="all", multifile=False, parent=None, **kwds):
self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
def chooseClicked(self):
- QDialog.accept(self)
\ No newline at end of file
+ QDialog.accept(self)
diff --git a/PyFlow/UI/Widgets/GraphEditor_ui.py b/PyFlow/UI/Widgets/GraphEditor_ui.py
index e1aa43b03..2a47961b8 100644
--- a/PyFlow/UI/Widgets/GraphEditor_ui.py
+++ b/PyFlow/UI/Widgets/GraphEditor_ui.py
@@ -8,7 +8,7 @@
#
# WARNING! All changes made in this file will be lost!
-from Qt import QtCompat, QtCore, QtGui, QtWidgets
+from qtpy import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
@@ -17,9 +17,15 @@ def setupUi(self, MainWindow):
MainWindow.resize(863, 543)
MainWindow.setDocumentMode(True)
MainWindow.setDockNestingEnabled(True)
- MainWindow.setDockOptions(QtWidgets.QMainWindow.AllowNestedDocks|QtWidgets.QMainWindow.AllowTabbedDocks|QtWidgets.QMainWindow.AnimatedDocks)
+ MainWindow.setDockOptions(
+ QtWidgets.QMainWindow.AllowNestedDocks
+ | QtWidgets.QMainWindow.AllowTabbedDocks
+ | QtWidgets.QMainWindow.AnimatedDocks
+ )
self.centralwidget = QtWidgets.QWidget(MainWindow)
- self.centralwidget.setLocale(QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates))
+ self.centralwidget.setLocale(
+ QtCore.QLocale(QtCore.QLocale.English, QtCore.QLocale.UnitedStates)
+ )
self.centralwidget.setObjectName("centralwidget")
self.gridLayout_3 = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout_3.setContentsMargins(1, 1, 1, 1)
@@ -31,10 +37,14 @@ def setupUi(self, MainWindow):
self.gridLayout.setContentsMargins(1, 1, 1, 1)
self.gridLayout.setObjectName("gridLayout")
self.widgetCurrentGraphPath = QtWidgets.QWidget(self.SceneWidget)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy = QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.widgetCurrentGraphPath.sizePolicy().hasHeightForWidth())
+ sizePolicy.setHeightForWidth(
+ self.widgetCurrentGraphPath.sizePolicy().hasHeightForWidth()
+ )
self.widgetCurrentGraphPath.setSizePolicy(sizePolicy)
self.widgetCurrentGraphPath.setObjectName("widgetCurrentGraphPath")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widgetCurrentGraphPath)
@@ -51,10 +61,14 @@ def setupUi(self, MainWindow):
self.SceneLayout.setObjectName("SceneLayout")
self.gridLayout.addLayout(self.SceneLayout, 4, 0, 1, 1)
self.CompoundPropertiesWidget = QtWidgets.QWidget(self.SceneWidget)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
+ sizePolicy = QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.CompoundPropertiesWidget.sizePolicy().hasHeightForWidth())
+ sizePolicy.setHeightForWidth(
+ self.CompoundPropertiesWidget.sizePolicy().hasHeightForWidth()
+ )
self.CompoundPropertiesWidget.setSizePolicy(sizePolicy)
self.CompoundPropertiesWidget.setObjectName("CompoundPropertiesWidget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.CompoundPropertiesWidget)
@@ -92,8 +106,8 @@ def setupUi(self, MainWindow):
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
- MainWindow.setWindowTitle(QtCompat.translate("MainWindow", "PyFlow", None, -1))
- self.label_2.setText(QtCompat.translate("MainWindow", "Name:", None, -1))
- self.label.setText(QtCompat.translate("MainWindow", "Category:", None, -1))
- self.toolBar.setWindowTitle(QtCompat.translate("MainWindow", "toolBar", None, -1))
+ MainWindow.setWindowTitle( "PyFlow")
+ self.label_2.setText( "Name:")
+ self.label.setText( "Category:")
+ self.toolBar.setWindowTitle( "toolBar")
diff --git a/PyFlow/UI/Widgets/InputActionWidget.py b/PyFlow/UI/Widgets/InputActionWidget.py
index 306a277e9..46972ee76 100644
--- a/PyFlow/UI/Widgets/InputActionWidget.py
+++ b/PyFlow/UI/Widgets/InputActionWidget.py
@@ -13,8 +13,8 @@
## limitations under the License.
-from Qt.QtWidgets import *
-from Qt import QtCore, QtGui
+from qtpy.QtWidgets import *
+from qtpy import QtCore, QtGui
from PyFlow.Input import InputActionType
from PyFlow.UI.Widgets.KeyboardModifiersCapture import KeyboardModifiersCaptureWidget
@@ -24,6 +24,7 @@
class InputActionWidget(QWidget):
"""docstring for InputActionWidget."""
+
def __init__(self, parent=None, inputActionRef=None):
super(InputActionWidget, self).__init__(parent)
self.currentActionRef = inputActionRef
diff --git a/PyFlow/UI/Widgets/InputWidgets.py b/PyFlow/UI/Widgets/InputWidgets.py
index d7ce7ecf7..b41b600dd 100644
--- a/PyFlow/UI/Widgets/InputWidgets.py
+++ b/PyFlow/UI/Widgets/InputWidgets.py
@@ -13,15 +13,9 @@
## limitations under the License.
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import QWidget
-from Qt.QtWidgets import QGridLayout
-from Qt.QtWidgets import QHBoxLayout
-from Qt.QtWidgets import QSpacerItem
-from Qt.QtWidgets import QSizePolicy
-from Qt.QtWidgets import QPushButton
-from Qt.QtWidgets import QMenu
+from qtpy.QtWidgets import QWidget
+from qtpy.QtWidgets import QHBoxLayout
+from qtpy.QtWidgets import QMenu
from PyFlow.Core.Common import *
@@ -30,23 +24,22 @@
class IInputWidget(object):
- def __init__(self):
+ def __init__(self,parent= None):
super(IInputWidget, self).__init__()
def widgetVariant(self):
return "DefaultWidget"
def getWidget(self):
- raise NotImplementedError(
- "getWidget of IInputWidget is not implemented")
+ raise NotImplementedError("getWidget of IInputWidget is not implemented")
def setWidget(self, widget):
- raise NotImplementedError(
- "setWidget of IInputWidget is not implemented")
+ raise NotImplementedError("setWidget of IInputWidget is not implemented")
def blockWidgetSignals(self, bLock=False):
raise NotImplementedError(
- "blockWidgetSignals of IInputWidget is not implemented")
+ "blockWidgetSignals of IInputWidget is not implemented"
+ )
class InputWidgetRaw(QWidget, IInputWidget):
@@ -54,7 +47,7 @@ class InputWidgetRaw(QWidget, IInputWidget):
This type of widget can be used as a base class for complex ui generated by designer
"""
- def __init__(self, parent=None, dataSetCallback=None, defaultValue=None, **kwds):
+ def __init__(self, parent=None, dataSetCallback=None, defaultValue=None, **kwargs):
super(InputWidgetRaw, self).__init__(parent=parent)
self._defaultValue = defaultValue
# function with signature void(object)
@@ -74,18 +67,18 @@ def setWidget(self, widget):
self._widget = widget
def getWidget(self):
- assert(self._widget is not None)
+ assert self._widget is not None
return self._widget
def onResetValue(self):
self.setWidgetValue(self._defaultValue)
def setWidgetValue(self, value):
- '''to widget'''
+ """to widget"""
pass
def widgetValueUpdated(self, value):
- '''from widget'''
+ """from widget"""
pass
def contextMenuEvent(self, event):
@@ -98,12 +91,15 @@ class InputWidgetSingle(InputWidgetRaw):
It consists of horizontal layout widget itself and reset button.
"""
- def __init__(self, parent=None, dataSetCallback=None, defaultValue=None, **kwds):
- super(InputWidgetSingle, self).__init__(parent=parent, dataSetCallback=dataSetCallback,
- defaultValue=defaultValue, **kwds)
+ def __init__(self, parent=None, dataSetCallback=None, defaultValue=None, **kwargs):
+ super(InputWidgetSingle, self).__init__(
+ parent=parent,
+ dataSetCallback=dataSetCallback,
+ defaultValue=defaultValue,
+ **kwargs,
+ )
self.horizontalLayout = QHBoxLayout(self)
self.horizontalLayout.setObjectName("horizontalLayout")
- spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self._index = 0
self._widget = None
self.senderPin = None
@@ -121,10 +117,18 @@ def REGISTER_UI_INPUT_WIDGET_PIN_FACTORY(packageName, factory):
UI_INPUT_WIDGET_PINS_FACTORIES[packageName] = factory
-def createInputWidget(dataType, dataSetter, defaultValue=None, widgetVariant=DEFAULT_WIDGET_VARIANT, **kwds):
+def createInputWidget(
+ dataType,
+ dataSetter,
+ defaultValue=None,
+ widgetVariant=DEFAULT_WIDGET_VARIANT,
+ **kwargs,
+):
pinInputWidget = None
for packageName, factory in UI_INPUT_WIDGET_PINS_FACTORIES.items():
- pinInputWidget = factory(dataType, dataSetter, defaultValue, widgetVariant, **kwds)
+ pinInputWidget = factory(
+ dataType, dataSetter, defaultValue, widgetVariant, **kwargs
+ )
if pinInputWidget is not None:
return pinInputWidget
return pinInputWidget
diff --git a/PyFlow/UI/Widgets/KeyCapture.py b/PyFlow/UI/Widgets/KeyCapture.py
index 858a64def..b2ede2511 100644
--- a/PyFlow/UI/Widgets/KeyCapture.py
+++ b/PyFlow/UI/Widgets/KeyCapture.py
@@ -13,12 +13,13 @@
## limitations under the License.
-from Qt.QtWidgets import *
-from Qt import QtCore, QtGui
+from qtpy.QtWidgets import *
+from qtpy import QtCore, QtGui
class KeyCaptureWidget(QPushButton):
"""docstring for KeyCaptureWidget."""
+
captured = QtCore.Signal(object)
def __init__(self, parent=None):
@@ -27,7 +28,9 @@ def __init__(self, parent=None):
self._currentKey = None
self.setText("NoKey")
self.setCheckable(True)
- self.setToolTip("Left mouse button to start capture.
Modifiers will not be accepted.")
+ self.setToolTip(
+ "Left mouse button to start capture.
Modifiers will not be accepted."
+ )
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.actionReset = QAction("Reset", None)
@@ -75,6 +78,7 @@ def keyPressEvent(self, event):
if __name__ == "__main__":
import sys
+
a = QApplication(sys.argv)
w = KeyCaptureWidget()
diff --git a/PyFlow/UI/Widgets/KeyboardModifiersCapture.py b/PyFlow/UI/Widgets/KeyboardModifiersCapture.py
index 60fc9db45..481b024ca 100644
--- a/PyFlow/UI/Widgets/KeyboardModifiersCapture.py
+++ b/PyFlow/UI/Widgets/KeyboardModifiersCapture.py
@@ -13,12 +13,13 @@
## limitations under the License.
-from Qt.QtWidgets import *
-from Qt import QtCore, QtGui
+from qtpy.QtWidgets import *
+from qtpy import QtCore, QtGui
class KeyboardModifiersCaptureWidget(QPushButton):
"""docstring for KeyboardModifiersCaptureWidget."""
+
captured = QtCore.Signal(object)
def __init__(self, parent=None):
@@ -27,7 +28,9 @@ def __init__(self, parent=None):
self.setText("NoModifier")
self.bCapturing = False
self.setCheckable(True)
- self.setToolTip("Left click to start capturing.
Enter to accept.
Esc to clear")
+ self.setToolTip(
+ "Left click to start capturing.
Enter to accept.
Esc to clear"
+ )
self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.actionReset = QAction("Reset", None)
@@ -43,7 +46,7 @@ def resetToDefault(self):
def modifiersToString(modifiers):
if modifiers == QtCore.Qt.KeyboardModifier.NoModifier:
return "NoModifier"
- return QtGui.QKeySequence(modifiers).toString()[:-1]
+ return QtGui.QKeySequence(modifiers).toString()[:-2]
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.MouseButton.LeftButton:
@@ -82,6 +85,7 @@ def keyPressEvent(self, event):
if __name__ == "__main__":
import sys
+
a = QApplication(sys.argv)
w = KeyboardModifiersCaptureWidget()
diff --git a/PyFlow/UI/Widgets/MouseButtonCapture.py b/PyFlow/UI/Widgets/MouseButtonCapture.py
index 89cb3afa9..f8f0e5df3 100644
--- a/PyFlow/UI/Widgets/MouseButtonCapture.py
+++ b/PyFlow/UI/Widgets/MouseButtonCapture.py
@@ -13,21 +13,24 @@
## limitations under the License.
-from Qt.QtWidgets import *
-from Qt import QtCore, QtGui
+from qtpy.QtWidgets import *
+from qtpy import QtCore, QtGui
class MouseButtonCaptureWidget(QPushButton):
"""docstring for MouseButtonCaptureWidget."""
+
captured = QtCore.Signal(object)
def __init__(self, parent=None):
super(MouseButtonCaptureWidget, self).__init__(parent)
self._currentButton = QtCore.Qt.MouseButton.NoButton
- self.setText(self._currentButton.name.decode('utf-8'))
+ self.setText(self._currentButton.name)
self.bCapturing = False
self.setCheckable(True)
- self.setToolTip("Esc will set button to NoButton clear.
Left mouse button will initiate capturing")
+ self.setToolTip(
+ "Esc will set button to NoButton clear.
Left mouse button will initiate capturing"
+ )
@property
def currentButton(self):
@@ -36,7 +39,7 @@ def currentButton(self):
@currentButton.setter
def currentButton(self, btn):
self._currentButton = btn
- self.setText(self._currentButton.name.decode('utf-8'))
+ self.setText(self._currentButton.name)
self.setChecked(False)
self.bCapturing = False
self.captured.emit(self._currentButton)
@@ -60,6 +63,7 @@ def mousePressEvent(self, event):
if __name__ == "__main__":
import sys
+
a = QApplication(sys.argv)
w = MouseButtonCaptureWidget()
diff --git a/PyFlow/UI/Widgets/PreferencesWindow.py b/PyFlow/UI/Widgets/PreferencesWindow.py
index fe021e082..103bd20bb 100644
--- a/PyFlow/UI/Widgets/PreferencesWindow.py
+++ b/PyFlow/UI/Widgets/PreferencesWindow.py
@@ -13,10 +13,9 @@
## limitations under the License.
-from nine import str
-from Qt.QtWidgets import *
-from Qt import QtCore, QtGui
+from qtpy.QtWidgets import *
+from qtpy import QtCore, QtGui
from PyFlow.ConfigManager import ConfigManager
from PyFlow.Core.Common import SingletonDecorator
@@ -24,6 +23,7 @@
class CategoryButton(QPushButton):
"""docstring for CategoryButton."""
+
def __init__(self, icon=None, text="test", parent=None):
super(CategoryButton, self).__init__(text, parent)
self.setMinimumHeight(30)
@@ -33,6 +33,7 @@ def __init__(self, icon=None, text="test", parent=None):
class CategoryWidgetBase(QScrollArea):
"""docstring for CategoryWidgetBase."""
+
def __init__(self, parent=None):
super(CategoryWidgetBase, self).__init__(parent)
self.setWidgetResizable(True)
@@ -50,6 +51,7 @@ def onShow(self, settings):
@SingletonDecorator
class PreferencesWindow(QMainWindow):
"""docstring for PreferencesWindow."""
+
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.resize(600, 400)
@@ -145,12 +147,16 @@ def savePreferences(self):
def addCategory(self, name, widget):
categoryButton = CategoryButton(text=name)
- self.categoriesVerticalLayout.insertWidget(self.categoriesVerticalLayout.count() - 2, categoryButton)
+ self.categoriesVerticalLayout.insertWidget(
+ self.categoriesVerticalLayout.count() - 2, categoryButton
+ )
widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
index = self.stackedWidget.addWidget(widget)
self._indexes[name] = (index, widget)
self.categoryButtons[index] = categoryButton
- categoryButton.clicked.connect(lambda checked=False, idx=index: self.switchCategoryContent(idx))
+ categoryButton.clicked.connect(
+ lambda checked=False, idx=index: self.switchCategoryContent(idx)
+ )
def switchCategoryContent(self, index):
self.stackedWidget.setCurrentIndex(index)
diff --git a/PyFlow/UI/Widgets/PropertiesFramework.py b/PyFlow/UI/Widgets/PropertiesFramework.py
index bda5d2ac0..cd0a80792 100644
--- a/PyFlow/UI/Widgets/PropertiesFramework.py
+++ b/PyFlow/UI/Widgets/PropertiesFramework.py
@@ -13,16 +13,16 @@
## limitations under the License.
-from nine import str
from PyFlow.UI.Canvas.UICommon import clearLayout
from PyFlow.UI.Widgets.EditPropertiesWidget import EditPropertiesTreeWidget
-from Qt import QtWidgets
-from Qt import QtCore, QtGui
+from qtpy import QtWidgets
+from qtpy import QtCore, QtGui
# Framework
class HeadButton(QtWidgets.QPushButton):
"""docstring for HeadButton."""
+
def __init__(self, parent=None, maxHeight=25):
super(HeadButton, self).__init__(parent)
self.setObjectName(self.__class__.__name__)
@@ -33,7 +33,9 @@ def __init__(self, parent=None, maxHeight=25):
class CollapsibleWidget(QtWidgets.QWidget):
"""Has content widget and button on top to hide or show content"""
- def __init__(self, parent=None, headName="Collapse", noSpacer=True, collapsed=False):
+ def __init__(
+ self, parent=None, headName="Collapse", noSpacer=True, collapsed=False
+ ):
super(CollapsibleWidget, self).__init__(parent)
self.setObjectName(self.__class__.__name__)
self.setupUi()
@@ -66,21 +68,35 @@ def setupUi(self):
self.mainVLayout.addWidget(self.pbHead)
self.setMinimumHeight(30)
self.ContentWidget = QtWidgets.QWidget(self)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+ sizePolicy = QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred
+ )
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.ContentWidget.sizePolicy().hasHeightForWidth())
+ sizePolicy.setHeightForWidth(
+ self.ContentWidget.sizePolicy().hasHeightForWidth()
+ )
self.ContentWidget.setSizePolicy(sizePolicy)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred))
+ self.setSizePolicy(
+ QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred
+ )
+ )
self.ContentWidget.setObjectName("ContentWidget")
self.ContentWidget.setContentsMargins(10, 0, 0, 0)
self.mainVLayout.addWidget(self.ContentWidget)
- self.spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.spacerItem = QtWidgets.QSpacerItem(
+ 20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding
+ )
self.mainVLayout.addItem(self.spacerItem)
self.setWindowTitle(self.objectName())
self.pbHead.setStyleSheet(self.pbHead.styleSheet() + "\nText-align:left;")
- self.contentHiddenIcon = self.pbHead.style().standardIcon(QtWidgets.QStyle.SP_TitleBarUnshadeButton)
- self.contentVisibleIcon = self.pbHead.style().standardIcon(QtWidgets.QStyle.SP_TitleBarShadeButton)
+ self.contentHiddenIcon = self.pbHead.style().standardIcon(
+ QtWidgets.QStyle.SP_TitleBarUnshadeButton
+ )
+ self.contentVisibleIcon = self.pbHead.style().standardIcon(
+ QtWidgets.QStyle.SP_TitleBarShadeButton
+ )
self.updateIcon()
def addWidget(self, widget):
@@ -123,7 +139,16 @@ def setCollapsed(self, bCollapsed=False):
class PropertyEntry(QtWidgets.QWidget):
"""docstring for PropertyEntry."""
- def __init__(self, label, widget, parent=None, hideLabel=False, maxLabelWidth=None, toolTip=""):
+
+ def __init__(
+ self,
+ label,
+ widget,
+ parent=None,
+ hideLabel=False,
+ maxLabelWidth=None,
+ toolTip="",
+ ):
super(PropertyEntry, self).__init__(parent)
self.label = label
self.layout = QtWidgets.QHBoxLayout(self)
@@ -133,7 +158,11 @@ def __init__(self, label, widget, parent=None, hideLabel=False, maxLabelWidth=No
label.setStyleSheet("font: bold")
label.setToolTip(toolTip)
if not maxLabelWidth:
- label.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred))
+ label.setSizePolicy(
+ QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Preferred
+ )
+ )
else:
label.setMaximumWidth(maxLabelWidth)
self.layout.addWidget(label)
@@ -145,8 +174,17 @@ def getLabel(self):
class CollapsibleFormWidget(CollapsibleWidget):
- def __init__(self, parent=None, headName="Collapse", noSpacer=True, collapsed=False, hideLabels=False):
- super(CollapsibleFormWidget, self).__init__(parent, headName=headName, noSpacer=noSpacer, collapsed=collapsed)
+ def __init__(
+ self,
+ parent=None,
+ headName="Collapse",
+ noSpacer=True,
+ collapsed=False,
+ hideLabels=False,
+ ):
+ super(CollapsibleFormWidget, self).__init__(
+ parent, headName=headName, noSpacer=noSpacer, collapsed=collapsed
+ )
self.hideLabels = hideLabels
self.Layout = QtWidgets.QVBoxLayout(self.ContentWidget)
self.Layout.setObjectName("CollapseWidgetFormLayout")
@@ -170,28 +208,31 @@ def isAllWidgetsHidden(self):
return count == hidden
def filterContent(self, pattern):
- count = self.Layout.count()
for key, value in self.entryNames.items():
if isinstance(value, PropertyEntry):
value.setVisible(pattern.lower() in value.getLabel().lower())
for key, value in self.groups.items():
- if isinstance(value, CollapSibleGoupBox):
+ if isinstance(value, CollapsibleGroupBox):
if value.isAllWidgetsHidden():
value.hide()
else:
value.show()
value.setCollapsed(False)
- def insertWidget(self, index=0, label=None, widget=None, maxLabelWidth=None, group=None):
+ def insertWidget(
+ self, index=0, label=None, widget=None, maxLabelWidth=None, group=None
+ ):
if widget is None or isinstance(widget, CollapsibleWidget):
return False
if group is not None and group != "":
if group in self.groups:
groupW = self.groups[group]
else:
- groupW = CollapSibleGoupBox(group)
+ groupW = CollapsibleGroupBox(group)
self.groups[group] = groupW
- entry = PropertyEntry(str(label), widget, hideLabel=self.hideLabels, maxLabelWidth=maxLabelWidth)
+ entry = PropertyEntry(
+ str(label), widget, hideLabel=self.hideLabels, maxLabelWidth=maxLabelWidth
+ )
self.propertyNames[label] = widget
self.entryNames[label] = entry
if group is None or group == "":
@@ -208,10 +249,16 @@ def addWidget(self, label=None, widget=None, maxLabelWidth=None, group=None):
if group in self.groups:
groupW = self.groups[group]
else:
- groupW = CollapSibleGoupBox(group)
+ groupW = CollapsibleGroupBox(group)
self.groups[group] = groupW
self.propertyNames[label] = widget
- entry = PropertyEntry(str(label), widget, hideLabel=self.hideLabels, maxLabelWidth=maxLabelWidth, toolTip=widget.toolTip())
+ entry = PropertyEntry(
+ str(label),
+ widget,
+ hideLabel=self.hideLabels,
+ maxLabelWidth=maxLabelWidth,
+ toolTip=widget.toolTip(),
+ )
self.entryNames[label] = entry
if group is None or group == "":
self.Layout.addWidget(entry)
@@ -226,10 +273,10 @@ def getWidgetByName(self, name):
else:
return None
-class CollapSibleGoupBox(QtWidgets.QWidget):
- def __init__(self,name):
- super(CollapSibleGoupBox, self).__init__()
+class CollapsibleGroupBox(QtWidgets.QWidget):
+ def __init__(self, name):
+ super(CollapsibleGroupBox, self).__init__()
# widgets
self.controlGroup = QtWidgets.QGroupBox()
@@ -243,8 +290,7 @@ def __init__(self,name):
self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())
# signals
- self.controlGroup.toggled.connect(
- lambda: self.toggleCollapsed())
+ self.controlGroup.toggled.connect(lambda: self.toggleCollapsed())
# layout
self.mainLayout = QtWidgets.QGridLayout(self)
@@ -259,11 +305,11 @@ def isAllWidgetsHidden(self):
hidden += 1
return count == hidden
- def insertWidget(self,index,widget):
- self.groupLayout.insertWidget(index,widget)
+ def insertWidget(self, index, widget):
+ self.groupLayout.insertWidget(index, widget)
self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())
- def addWidget(self,widget):
+ def addWidget(self, widget):
self.groupLayout.addWidget(widget)
self.controlGroup.setFixedHeight(self.controlGroup.sizeHint().height())
@@ -281,8 +327,10 @@ def setCollapsed(self, bCollapsed=False):
else:
self.controlGroup.setFixedHeight(30)
+
class PropertiesWidget(QtWidgets.QWidget):
"""docstring for PropertiesWidget."""
+
spawnDuplicate = QtCore.Signal()
def __init__(self, parent=None, searchByHeaders=False):
@@ -293,8 +341,10 @@ def __init__(self, parent=None, searchByHeaders=False):
self.mainLayout.setContentsMargins(2, 2, 2, 2)
self.searchBox = QtWidgets.QLineEdit(self)
self.searchBox.setObjectName("lineEdit")
- self.searchBox.setPlaceholderText(str("search..."))
- self.searchBox.textChanged.connect(self.filterByHeaders if searchByHeaders else self.filterByHeadersAndFields)
+ self.searchBox.setPlaceholderText("search...")
+ self.searchBox.textChanged.connect(
+ self.filterByHeaders if searchByHeaders else self.filterByHeadersAndFields
+ )
self.searchBoxWidget = QtWidgets.QWidget()
self.searchBoxLayout = QtWidgets.QHBoxLayout(self.searchBoxWidget)
self.searchBoxLayout.setContentsMargins(1, 1, 1, 1)
@@ -307,12 +357,12 @@ def __init__(self, parent=None, searchByHeaders=False):
# self.settingsMenu.addAction(self.editPropertiesAction)
# self.settingsButton.setMenu(self.settingsMenu)
# self.editPropertiesAction.triggered.connect(self.showPropertyEditor)
- #self.settingsButton.clicked.connect(self.spawnDuplicate.emit)
+ # self.settingsButton.clicked.connect(self.spawnDuplicate.emit)
# self.settingsButton.setPopupMode(QtWidgets.QToolButton.InstantPopup)
self.lockCheckBox = QtWidgets.QToolButton()
self.lockCheckBox.setCheckable(True)
- self.lockCheckBox.setIcon(QtGui.QIcon(':/unlocked.png'))
+ self.lockCheckBox.setIcon(QtGui.QIcon(":/unlocked.png"))
self.lockCheckBox.toggled.connect(self.changeLockIcon)
self.searchBoxLayout.addWidget(self.lockCheckBox)
self.tearOffCopy = QtWidgets.QToolButton()
@@ -324,16 +374,22 @@ def __init__(self, parent=None, searchByHeaders=False):
self.contentLayout = QtWidgets.QVBoxLayout()
self.contentLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
self.mainLayout.addLayout(self.contentLayout)
- self.spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.spacerItem = QtWidgets.QSpacerItem(
+ 20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding
+ )
self.mainLayout.addItem(self.spacerItem)
self.mainLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
- self.setSizePolicy(QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding))
+ self.setSizePolicy(
+ QtWidgets.QSizePolicy(
+ QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
+ )
+ )
- def changeLockIcon(self,checked):
+ def changeLockIcon(self, checked):
if checked:
- self.lockCheckBox.setIcon(QtGui.QIcon(':/locked.png'))
+ self.lockCheckBox.setIcon(QtGui.QIcon(":/locked.png"))
else:
- self.lockCheckBox.setIcon(QtGui.QIcon(':/unlocked.png'))
+ self.lockCheckBox.setIcon(QtGui.QIcon(":/unlocked.png"))
def setLockCheckBoxVisible(self, bVisible):
self.lockCheckBox.setVisible(bVisible)
@@ -377,7 +433,7 @@ def clear(self):
self.searchBoxWidget.hide()
self.lockCheckBox.setChecked(False)
- def insertWidget(self, collapsibleWidget,index):
+ def insertWidget(self, collapsibleWidget, index):
if not self.isLocked():
if isinstance(collapsibleWidget, CollapsibleFormWidget):
self.searchBoxWidget.show()
@@ -397,30 +453,28 @@ def showPropertyEditor(self):
folders = {}
for i in range(count):
item = self.contentLayout.itemAt(i)
- w = item.widget()
+ w = item.widget()
if w:
- if w.title() in ["Inputs"]:
- for key,group in w.groups.items():
+ if w.title() in ["Inputs"]:
+ for key, group in w.groups.items():
if key not in folders:
folders[key] = {}
- #for e in range(group.groupLayout.count()):
+ # for e in range(group.groupLayout.count()):
# w = group.groupLayout.itemAt(e).widget()
# folders[key][w.getLabel()] = group.groupLayout.itemAt(e).widget()
for fold in folders:
- folder = tree.addFolder(fold)
- #for widg in folders[fold]:
- # child = tree.addNormal(widg,folder)
+ tree.addFolder(fold)
d = QtWidgets.QDialog()
d.setLayout(QtWidgets.QHBoxLayout())
d.layout().addWidget(tree)
d.exec_()
- newOrder = tree.model_to_dict()
if __name__ == "__main__":
import sys
+
app = QtWidgets.QApplication(sys.argv)
s = QtWidgets.QScrollArea()
diff --git a/PyFlow/UI/Widgets/QtSliders.py b/PyFlow/UI/Widgets/QtSliders.py
index 9980eb8e9..0d8dd5301 100644
--- a/PyFlow/UI/Widgets/QtSliders.py
+++ b/PyFlow/UI/Widgets/QtSliders.py
@@ -15,8 +15,7 @@
from copy import copy
import sys
-import struct
-from Qt import QtGui, QtCore, QtWidgets
+from qtpy import QtGui, QtCore, QtWidgets
from PyFlow.UI.Canvas.UICommon import SessionDescriptor
from PyFlow.UI.Utils.stylesheet import editableStyleSheet, Colors
@@ -24,8 +23,8 @@
from PyFlow.Core import structs
-FLOAT_SLIDER_DRAG_STEPS = [100.0, 10.0, 1.0, 0.1, 0.01, 0.001]
-INT_SLIDER_DRAG_STEPS = [100.0, 10.0, 1.0]
+FLOAT_SLIDER_DRAG_STEPS = (100.0, 10.0, 1.0, 0.1, 0.01, 0.001)
+INT_SLIDER_DRAG_STEPS = (100.0, 10.0, 1.0)
class inputDragger(QtWidgets.QWidget):
@@ -59,7 +58,9 @@ def __init__(self, parent, factor, *args, **kwargs):
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.frame.layout().addWidget(self.label)
self.layout().addWidget(self.frame)
- self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("draggerstyleSheet"))
+ self.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("draggerstyleSheet")
+ )
self.size = 35
self.setMinimumHeight(self.size)
self.setMinimumWidth(self.size)
@@ -72,14 +73,20 @@ def __init__(self, parent, factor, *args, **kwargs):
def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.HoverEnter:
- self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("draggerstyleSheetHover"))
+ self.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("draggerstyleSheetHover")
+ )
self.parent.activeDrag = self
for drag in self.parent.drags:
if drag != self:
- drag.setStyleSheet(editableStyleSheet().getSliderStyleSheet("draggerstyleSheet"))
+ drag.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("draggerstyleSheet")
+ )
if event.type() == QtCore.QEvent.HoverLeave:
if event.pos().y() > self.height() or event.pos().y() < 0:
- self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("draggerstyleSheet"))
+ self.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("draggerstyleSheet")
+ )
if event.type() == QtCore.QEvent.MouseMove:
self.parent.eventFilter(self, event)
@@ -119,7 +126,9 @@ def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.MouseMove:
if self.activeDrag:
modifiers = event.modifiers()
- self.activeDrag.setStyleSheet(editableStyleSheet().getSliderStyleSheet("draggerstyleSheetHover"))
+ self.activeDrag.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("draggerstyleSheetHover")
+ )
if self.initialPos is None:
self.initialPos = event.globalPos()
deltaX = event.globalPos().x() - self.initialPos.x()
@@ -130,9 +139,16 @@ def eventFilter(self, object, event):
if modifiers == QtCore.Qt.NoModifier and deltaX % 4 == 0:
self.increment.emit(v)
- if modifiers in [QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier] and deltaX % 8 == 0:
+ if (
+ modifiers
+ in [QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier]
+ and deltaX % 8 == 0
+ ):
self.increment.emit(v)
- if modifiers == QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier and deltaX % 32 == 0:
+ if (
+ modifiers == QtCore.Qt.ShiftModifier | QtCore.Qt.ControlModifier
+ and deltaX % 32 == 0
+ ):
self.increment.emit(v)
self.lastDeltaX = deltaX
@@ -140,7 +156,7 @@ def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.MouseButtonRelease:
self.hide()
self.lastDeltaX = 0
- del(self)
+ del self
return False
@@ -158,11 +174,19 @@ class slider(QtWidgets.QSlider):
Extends:
QtWidgets.QSlider
"""
+
editingFinished = QtCore.Signal()
valueIncremented = QtCore.Signal(object)
floatValueChanged = QtCore.Signal(object)
- def __init__(self, parent=None, draggerSteps=INT_SLIDER_DRAG_STEPS, sliderRange=[-100, 100], *args, **kwargs):
+ def __init__(
+ self,
+ parent=None,
+ draggerSteps=INT_SLIDER_DRAG_STEPS,
+ sliderRange=(-100, 100),
+ *args,
+ **kwargs,
+ ):
super(slider, self).__init__(parent, **kwargs)
self.sliderRange = sliderRange
self.setFocusPolicy(QtCore.Qt.StrongFocus)
@@ -185,33 +209,62 @@ def mousePressEvent(self, event):
self.startDragpos = event.pos()
if event.button() == QtCore.Qt.MidButton:
if self.draggers is None:
- self.draggers = draggers(self, self.isFloat, draggerSteps=self.draggerSteps)
+ self.draggers = draggers(
+ self, self.isFloat, draggerSteps=self.draggerSteps
+ )
self.draggers.increment.connect(self.valueIncremented.emit)
self.draggers.show()
if self.isFloat:
- self.draggers.move(self.mapToGlobal(QtCore.QPoint(event.pos().x() - 1, event.pos().y() - self.draggers.height() / 2)))
+ self.draggers.move(
+ self.mapToGlobal(
+ QtCore.QPoint(
+ event.pos().x() - 1,
+ event.pos().y() - self.draggers.height() / 2,
+ )
+ )
+ )
else:
- self.draggers.move(self.mapToGlobal(QtCore.QPoint(event.pos().x() - 1, event.pos().y() - (self.draggers.height() - self.draggers.height() / 6))))
-
- elif event.button() == self.LeftButton and event.modifiers() not in [QtCore.Qt.ControlModifier, QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier]:
+ self.draggers.move(
+ self.mapToGlobal(
+ QtCore.QPoint(
+ event.pos().x() - 1,
+ event.pos().y()
+ - (self.draggers.height() - self.draggers.height() / 6),
+ )
+ )
+ )
+
+ elif event.button() == self.LeftButton and event.modifiers() not in [
+ QtCore.Qt.ControlModifier,
+ QtCore.Qt.ShiftModifier,
+ QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ ]:
butts = QtCore.Qt.MouseButtons(self.MidButton)
- nevent = QtGui.QMouseEvent(event.type(), event.pos(),
- self.MidButton, butts,
- event.modifiers())
+ nevent = QtGui.QMouseEvent(
+ event.type(), event.pos(), self.MidButton, butts, event.modifiers()
+ )
super(slider, self).mousePressEvent(nevent)
- elif event.modifiers() in [QtCore.Qt.ControlModifier, QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier]:
+ elif event.modifiers() in [
+ QtCore.Qt.ControlModifier,
+ QtCore.Qt.ShiftModifier,
+ QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ ]:
st_slider = QtWidgets.QStyleOptionSlider()
st_slider.initFrom(self)
st_slider.orientation = self.orientation()
- available = self.style().pixelMetric(QtWidgets.QStyle.PM_SliderSpaceAvailable, st_slider, self)
- xloc = QtWidgets.QStyle.sliderPositionFromValue(self.minimum(), self.maximum(), super(slider, self).value(), available)
+ available = self.style().pixelMetric(
+ QtWidgets.QStyle.PM_SliderSpaceAvailable, st_slider, self
+ )
+ xloc = QtWidgets.QStyle.sliderPositionFromValue(
+ self.minimum(), self.maximum(), super(slider, self).value(), available
+ )
butts = QtCore.Qt.MouseButtons(self.MidButton)
newPos = QtCore.QPointF()
newPos.setX(xloc)
- nevent = QtGui.QMouseEvent(event.type(), newPos,
- self.MidButton, butts,
- event.modifiers())
+ nevent = QtGui.QMouseEvent(
+ event.type(), newPos, self.MidButton, butts, event.modifiers()
+ )
self.startDragpos = newPos
self.realStartDragpos = event.pos()
super(slider, self).mousePressEvent(nevent)
@@ -224,19 +277,25 @@ def mouseMoveEvent(self, event):
deltaX = event.pos().x() - self.realStartDragpos.x()
deltaY = event.pos().y() - self.realStartDragpos.y()
newPos = QtCore.QPointF()
- if event.modifiers() in [QtCore.Qt.ControlModifier, QtCore.Qt.ShiftModifier, QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier]:
+ if event.modifiers() in [
+ QtCore.Qt.ControlModifier,
+ QtCore.Qt.ShiftModifier,
+ QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier,
+ ]:
if event.modifiers() == QtCore.Qt.ControlModifier:
newPos.setX(self.startDragpos.x() + deltaX / 2)
newPos.setY(self.startDragpos.y() + deltaY / 2)
elif event.modifiers() == QtCore.Qt.ShiftModifier:
newPos.setX(self.startDragpos.x() + deltaX / 4)
newPos.setY(self.startDragpos.y() + deltaY / 4)
- elif event.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier:
+ elif (
+ event.modifiers() == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier
+ ):
newPos.setX(self.startDragpos.x() + deltaX / 8)
newPos.setY(self.startDragpos.y() + deltaY / 8)
- nevent = QtGui.QMouseEvent(event.type(), newPos,
- event.button(), event.buttons(),
- event.modifiers())
+ nevent = QtGui.QMouseEvent(
+ event.type(), newPos, event.button(), event.buttons(), event.modifiers()
+ )
super(slider, self).mouseMoveEvent(nevent)
self.setValue(self.value() - self.deltaValue)
else:
@@ -259,8 +318,17 @@ def keyPressEvent(self, event):
class DoubleSlider(slider):
doubleValueChanged = QtCore.Signal(float)
- def __init__(self, parent=None, sliderRange=(-100.0, 100.0), defaultValue=0.0, dencity=1000000, draggerSteps=FLOAT_SLIDER_DRAG_STEPS):
- super(DoubleSlider, self).__init__(parent, draggerSteps=draggerSteps, sliderRange=sliderRange)
+ def __init__(
+ self,
+ parent=None,
+ sliderRange=(-100.0, 100.0),
+ defaultValue=0.0,
+ dencity=1000000,
+ draggerSteps=FLOAT_SLIDER_DRAG_STEPS,
+ ):
+ super(DoubleSlider, self).__init__(
+ parent, draggerSteps=draggerSteps, sliderRange=sliderRange
+ )
self.isFloat = True
self._dencity = abs(dencity)
self.setOrientation(QtCore.Qt.Horizontal)
@@ -303,11 +371,25 @@ def setMappedValue(self, value, blockSignals=False):
def mapValue(self, inValue):
# convert slider int value to slider float range value
- return mapRangeUnclamped(inValue, self.minimum(), self.maximum(), self.sliderRange[0], self.sliderRange[1])
+ return mapRangeUnclamped(
+ inValue,
+ self.minimum(),
+ self.maximum(),
+ self.sliderRange[0],
+ self.sliderRange[1],
+ )
def unMapValue(self, outValue):
# convert mapped float value to slider integer
- return int(mapRangeUnclamped(outValue, self.sliderRange[0], self.sliderRange[1], self.minimum(), self.maximum()))
+ return int(
+ mapRangeUnclamped(
+ outValue,
+ self.sliderRange[0],
+ self.sliderRange[1],
+ self.minimum(),
+ self.maximum(),
+ )
+ )
def onInternalValueChanged(self, x):
mappedValue = self.mapValue(x)
@@ -322,9 +404,19 @@ class valueBox(QtWidgets.QDoubleSpinBox):
Extends:
QtWidgets.QDoubleSpinBox
"""
+
valueIncremented = QtCore.Signal(object)
- def __init__(self, labelText="", type="float", buttons=False, decimals=3, draggerSteps=FLOAT_SLIDER_DRAG_STEPS, *args, **kwargs):
+ def __init__(
+ self,
+ labelText="",
+ type="float",
+ buttons=False,
+ decimals=3,
+ draggerSteps=FLOAT_SLIDER_DRAG_STEPS,
+ *args,
+ **kwargs,
+ ):
"""
:param type: Choose if create a float or int spinBox, defaults to "float"
:type type: str, optional
@@ -338,7 +430,7 @@ def __init__(self, labelText="", type="float", buttons=False, decimals=3, dragge
:type **kwargs: [type]
"""
super(valueBox, self).__init__(*args, **kwargs)
- self.labelFont = QtGui.QFont('Serif', 10, QtGui.QFont.Bold)
+ self.labelFont = QtGui.QFont("Serif", 10, QtGui.QFont.Bold)
self.labelText = labelText
self.draggerSteps = copy(draggerSteps)
self.isFloat = type == "float"
@@ -348,7 +440,9 @@ def __init__(self, labelText="", type="float", buttons=False, decimals=3, dragge
self.setDecimals(decimals)
if not buttons:
self.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
- self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA"))
+ self.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA")
+ )
self.lineEdit().installEventFilter(self)
self.installEventFilter(self)
self.setFocusPolicy(QtCore.Qt.StrongFocus)
@@ -379,17 +473,35 @@ def eventFilter(self, object, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
if event.button() == QtCore.Qt.MiddleButton:
if self.draggers is None:
- self.draggers = draggers(self, self.isFloat, draggerSteps=self.draggerSteps)
+ self.draggers = draggers(
+ self, self.isFloat, draggerSteps=self.draggerSteps
+ )
self.draggers.increment.connect(self.onValueIncremented)
self.draggers.show()
if self.isFloat:
- self.draggers.move(self.mapToGlobal(QtCore.QPoint(event.pos().x() - 1, event.pos().y() - self.draggers.height() / 2)))
+ self.draggers.move(
+ self.mapToGlobal(
+ QtCore.QPoint(
+ event.pos().x() - 1,
+ event.pos().y() - self.draggers.height() / 2,
+ )
+ )
+ )
else:
- self.draggers.move(self.mapToGlobal(QtCore.QPoint(event.pos().x() - 1, event.pos().y() - self.draggers.height() + 15)))
+ self.draggers.move(
+ self.mapToGlobal(
+ QtCore.QPoint(
+ event.pos().x() - 1,
+ event.pos().y() - self.draggers.height() + 15,
+ )
+ )
+ )
return False
def update(self):
- self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA"))
+ self.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA")
+ )
super(valueBox, self).update()
@@ -399,9 +511,20 @@ class pyf_Slider(QtWidgets.QWidget):
Signals:
:valueChanged: Signal emitted when slider or valueBox value changes, int/float
"""
+
valueChanged = QtCore.Signal(object)
- def __init__(self, parent, type="float", style=0, name=None, sliderRange=(-100.0, 100.0), defaultValue=0.0, draggerSteps=FLOAT_SLIDER_DRAG_STEPS, *args):
+ def __init__(
+ self,
+ parent,
+ type="float",
+ style=0,
+ name=None,
+ sliderRange=(-100.0, 100.0),
+ defaultValue=0.0,
+ draggerSteps=FLOAT_SLIDER_DRAG_STEPS,
+ *args,
+ ):
"""
:param parent: Parent Widget
:type parent: QtWidgets.QWidget
@@ -423,7 +546,12 @@ def __init__(self, parent, type="float", style=0, name=None, sliderRange=(-100.0
self.type = type
if self.type == "float":
- self.sld = DoubleSlider(self, defaultValue=defaultValue, sliderRange=sliderRange, draggerSteps=draggerSteps)
+ self.sld = DoubleSlider(
+ self,
+ defaultValue=defaultValue,
+ sliderRange=sliderRange,
+ draggerSteps=draggerSteps,
+ )
if self.type == "int":
self.sld = slider(self, sliderRange=sliderRange)
self.sld.valueIncremented.connect(self.incrementValue)
@@ -451,9 +579,13 @@ def __init__(self, parent, type="float", style=0, name=None, sliderRange=(-100.0
self.styleSheetType = style
if self.styleSheetType == 0:
self.layout().setSpacing(0)
- self.sld.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA"))
+ self.sld.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA")
+ )
elif self.styleSheetType == 1:
- self.sld.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetB"))
+ self.sld.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetB")
+ )
self.sld.valueChanged.connect(self.sliderValueChanged)
self.input.valueChanged.connect(self.valBoxValueChanged)
@@ -461,7 +593,13 @@ def __init__(self, parent, type="float", style=0, name=None, sliderRange=(-100.0
self._value = 0.0
def sliderValueChanged(self, x):
- outValue = mapRangeUnclamped(x, self.sld.minimum(), self.sld.maximum(), self.input.minimum(), self.input.maximum())
+ outValue = mapRangeUnclamped(
+ x,
+ self.sld.minimum(),
+ self.sld.maximum(),
+ self.input.minimum(),
+ self.input.maximum(),
+ )
self.input.blockSignals(True)
self.input.setValue(outValue)
self.input.blockSignals(False)
@@ -469,7 +607,13 @@ def sliderValueChanged(self, x):
def valBoxValueChanged(self, x):
val = self.input.value()
- sv = mapRangeUnclamped(val, self.input.minimum(), self.input.maximum(), self.sld.minimum(), self.sld.maximum())
+ sv = mapRangeUnclamped(
+ val,
+ self.input.minimum(),
+ self.input.maximum(),
+ self.sld.minimum(),
+ self.sld.maximum(),
+ )
self.sld.blockSignals(True)
self.sld.setValue(int(sv))
self.sld.blockSignals(False)
@@ -478,9 +622,13 @@ def valBoxValueChanged(self, x):
def update(self):
if self.styleSheetType == 0:
self.layout().setSpacing(0)
- self.sld.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA"))
+ self.sld.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetA")
+ )
elif self.styleSheetType == 1:
- self.sld.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetB"))
+ self.sld.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetB")
+ )
super(pyf_Slider, self).update()
@property
@@ -536,7 +684,7 @@ def setRange(self, min, max):
self.setMinimum(min)
self.setMaximum(max)
- def setDisplayMinimun(self, value):
+ def setDisplayMinimum(self, value):
"""Sets the Minimum value for display options, real min value don't touched, if current value is less than this display value,Widget automatically recalculates minDisplay
:param value: New Display MinValue
@@ -593,17 +741,25 @@ class pyf_HueSlider(DoubleSlider):
Extends:
:obj: `DoubleSlider`
"""
+
def __init__(self, parent, *args):
"""
:param parent: Parent QtWidget
:type parent: QtWidgets.QWidget
"""
- super(pyf_HueSlider, self).__init__(parent=parent, sliderRange=(0.0, 1.0), draggerSteps=[0.1, 0.01, 0.001], *args)
+ super(pyf_HueSlider, self).__init__(
+ parent=parent,
+ sliderRange=(0.0, 1.0),
+ draggerSteps=[0.1, 0.01, 0.001],
+ *args,
+ )
self.parent = parent
self.color = QtGui.QColor()
self.color.setHslF(0, 1, 0.5, 1)
self.defColor = self.color.name()
- self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetC"))
+ self.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetC")
+ )
self.light = 0.5
def setColor(self, color):
@@ -613,7 +769,9 @@ def setColor(self, color):
:type color: [float,float,float]
"""
if isinstance(color, list) and len(color) == 3:
- self.color = QtGui.QColor(color[0] * 255.0, color[1] * 255.0, color[2] * 255.0)
+ self.color = QtGui.QColor(
+ color[0] * 255.0, color[1] * 255.0, color[2] * 255.0
+ )
self.defColor = self.color.name()
self.update()
@@ -662,7 +820,9 @@ def drawWidget(self, qp):
gradient = QtGui.QLinearGradient(0, 0, w, h)
for i in range(11):
hue = self.getHue(i * 0.1)
- gradient.setColorAt(i * 0.1, QtGui.QColor(hue[0] * 255, hue[1] * 255, hue[2] * 255))
+ gradient.setColorAt(
+ i * 0.1, QtGui.QColor(hue[0] * 255, hue[1] * 255, hue[2] * 255)
+ )
qp.setBrush(QtGui.QBrush(gradient))
@@ -670,25 +830,38 @@ def drawWidget(self, qp):
class pyf_GradientSlider(DoubleSlider):
- """Custom Slider to select a color by Non Editable gradient
+ """Custom Slider to select a color by non-editable gradient
# Extends:
# :obj: `DoubleSlider`
"""
- def __init__(self, parent, color1=[0, 0, 0], color2=[255, 255, 255], sliderRange=(0.0, 255.0), draggerSteps=[5.0, 1.0, 0.25], *args):
+
+ def __init__(
+ self,
+ parent,
+ color1=(0, 0, 0),
+ color2=(255, 255, 255),
+ sliderRange=(0.0, 255.0),
+ draggerSteps=(5.0, 1.0, 0.25),
+ *args,
+ ):
"""
:param parent: Parent QtWidget
:type parent: QtWidgets.QWidget
- :param color1: Start Color in range 0-255 , defaults to [0, 0, 0]
- :type color1: [int,int,int], optional
- :param color2: End Color in range 0-255, defaults to [255, 255, 255]
- :type color2: [int,int,int], optional
+ :param color1: Start Color in range 0-255 , defaults to (0, 0, 0)
+ :type color1: (int,int,int), optional
+ :param color2: End Color in range 0-255, defaults to (255, 255, 255)
+ :type color2: (int,int,int), optional
"""
- super(pyf_GradientSlider, self).__init__(parent=parent, sliderRange=sliderRange, draggerSteps=draggerSteps, *args)
+ super(pyf_GradientSlider, self).__init__(
+ parent=parent, sliderRange=sliderRange, draggerSteps=draggerSteps, *args
+ )
self.parent = parent
self.color1 = QtGui.QColor(color1[0], color1[1], color1[2])
self.color2 = QtGui.QColor(color2[0], color2[1], color2[2])
- self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("sliderStyleSheetC"))
+ self.setStyleSheet(
+ editableStyleSheet().getSliderStyleSheet("sliderStyleSheetC")
+ )
def getColor(self):
"""Computes the current Color
@@ -730,13 +903,16 @@ class pyf_ColorSlider(QtWidgets.QWidget):
Signals:
:valueChanged: Signal emitted when any of the sliders/valueBoxes changes
"""
+
valueChanged = QtCore.Signal(list)
- def __init__(self, parent=None, startColor=[0, 0, 0], type="float", alpha=False, h=50, *args):
+ def __init__(
+ self, parent=None, startColor=(0, 0, 0), type="float", alpha=False, h=50, *args
+ ):
"""
:param parent: Parent Widget
:type parent: QtWidgets.QWidget
- :param startColor: Initialization color, defaults to [0, 0, 0]
+ :param startColor: Initialization color, defaults to (0, 0, 0)
:type startColor: list(float/int), optional
:param type: Choose if create a float or int Slider, defaults to "float"
:type type: str, optional
@@ -805,7 +981,16 @@ def __init__(self, parent=None, startColor=[0, 0, 0], type="float", alpha=False,
else:
self.ASlider.hide()
- for i in [self.RSlider, self.GSlider, self.BSlider, self.ASlider, self.RBox, self.GBox, self.BBox, self.ABox]:
+ for i in [
+ self.RSlider,
+ self.GSlider,
+ self.BSlider,
+ self.ASlider,
+ self.RBox,
+ self.GBox,
+ self.BBox,
+ self.ABox,
+ ]:
i.setMaximumHeight(h / (len(inpList) + 1))
i.setMinimumHeight(h / (len(inpList) + 1))
@@ -824,7 +1009,15 @@ def __init__(self, parent=None, startColor=[0, 0, 0], type="float", alpha=False,
self.defaultColor = startColor
if isinstance(startColor, list) and len(startColor) >= 3:
self.setColor(startColor)
- self.Color.setStyleSheet(self.styleSheetString % (self.RSlider.mappedValue(), self.GSlider.mappedValue(), self.BSlider.mappedValue(), self.ASlider.mappedValue()))
+ self.Color.setStyleSheet(
+ self.styleSheetString
+ % (
+ self.RSlider.mappedValue(),
+ self.GSlider.mappedValue(),
+ self.BSlider.mappedValue(),
+ self.ASlider.mappedValue(),
+ )
+ )
self._menu = QtWidgets.QMenu()
self.actionReset = self._menu.addAction("ResetValue")
self.actionReset.triggered.connect(self.onResetValue)
@@ -846,8 +1039,20 @@ def setColor(self, color):
self.ASlider.setMappedValue(color[3])
def colorChanged(self, value):
- self.Color.setStyleSheet(self.styleSheetString % (self.RSlider.mappedValue(), self.GSlider.mappedValue(), self.BSlider.mappedValue(), self.ASlider.mappedValue()))
- valueList = [self.RSlider.mappedValue(), self.GSlider.mappedValue(), self.BSlider.mappedValue()]
+ self.Color.setStyleSheet(
+ self.styleSheetString
+ % (
+ self.RSlider.mappedValue(),
+ self.GSlider.mappedValue(),
+ self.BSlider.mappedValue(),
+ self.ASlider.mappedValue(),
+ )
+ )
+ valueList = [
+ self.RSlider.mappedValue(),
+ self.GSlider.mappedValue(),
+ self.BSlider.mappedValue(),
+ ]
if self.alpha:
valueList.append(self.ASlider.mappedValue())
if self.type == "int":
@@ -856,14 +1061,48 @@ def colorChanged(self, value):
def showColorDialog(self):
if self.alpha:
- color = QtWidgets.QColorDialog.getColor(options=QtWidgets.QColorDialog.ShowAlphaChannel)
+ color = QtWidgets.QColorDialog.getColor(
+ options=QtWidgets.QColorDialog.ShowAlphaChannel
+ )
else:
color = QtWidgets.QColorDialog.getColor()
if color.isValid():
- self.RSlider.setMappedValue(mapRangeUnclamped(color.redF(), 0.0, 1.0, self.RSlider.sliderRange[0], self.RSlider.sliderRange[1]))
- self.GSlider.setMappedValue(mapRangeUnclamped(color.greenF(), 0.0, 1.0, self.GSlider.sliderRange[0], self.GSlider.sliderRange[1]))
- self.BSlider.setMappedValue(mapRangeUnclamped(color.blueF(), 0.0, 1.0, self.BSlider.sliderRange[0], self.BSlider.sliderRange[1]))
- self.ASlider.setMappedValue(mapRangeUnclamped(color.alphaF(), 0.0, 1.0, self.ASlider.sliderRange[0], self.ASlider.sliderRange[1]))
+ self.RSlider.setMappedValue(
+ mapRangeUnclamped(
+ color.redF(),
+ 0.0,
+ 1.0,
+ self.RSlider.sliderRange[0],
+ self.RSlider.sliderRange[1],
+ )
+ )
+ self.GSlider.setMappedValue(
+ mapRangeUnclamped(
+ color.greenF(),
+ 0.0,
+ 1.0,
+ self.GSlider.sliderRange[0],
+ self.GSlider.sliderRange[1],
+ )
+ )
+ self.BSlider.setMappedValue(
+ mapRangeUnclamped(
+ color.blueF(),
+ 0.0,
+ 1.0,
+ self.BSlider.sliderRange[0],
+ self.BSlider.sliderRange[1],
+ )
+ )
+ self.ASlider.setMappedValue(
+ mapRangeUnclamped(
+ color.alphaF(),
+ 0.0,
+ 1.0,
+ self.ASlider.sliderRange[0],
+ self.ASlider.sliderRange[1],
+ )
+ )
def contextMenuEvent(self, event):
self._menu.exec_(event.globalPos())
@@ -873,7 +1112,7 @@ class pyf_timeline(QtWidgets.QSlider):
def __init__(self, parent, *args):
super(pyf_timeline, self).__init__(parent=parent, *args)
self.parent = parent
- self.cachedFrmaes = []
+ self.cachedFrames = []
self.missingFrames = []
self.hover = False
self.hoverPos = None
@@ -883,16 +1122,14 @@ def __init__(self, parent, *args):
self.origMax = self.maximum()
self.oriMin = self.minimum()
self.setOrientation(QtCore.Qt.Horizontal)
- self.setStyleSheet(editableStyleSheet(
- ).getSliderStyleSheet("timeStyleSheet"))
+ self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("timeStyleSheet"))
self.setMouseTracking(True)
self.setPageStep(1)
self.setMinimumSize(1, 40)
self.installEventFilter(self)
def update(self):
- self.setStyleSheet(editableStyleSheet(
- ).getSliderStyleSheet("timeStyleSheet"))
+ self.setStyleSheet(editableStyleSheet().getSliderStyleSheet("timeStyleSheet"))
super(pyf_timeline, self).update()
def setRange(self, min, max, setOrig=True):
@@ -902,7 +1139,7 @@ def setRange(self, min, max, setOrig=True):
return super(pyf_timeline, self).setRange(min, max)
def setCached(self, cached):
- self.cachedFrmaes = cached
+ self.cachedFrames = cached
def setMissing(self, missing):
self.missingFrames = missing
@@ -916,19 +1153,18 @@ def paintEvent(self, event):
super(pyf_timeline, self).paintEvent(event)
def drawWidget(self, qp):
- font = QtGui.QFont('Serif', 7, QtGui.QFont.Light)
+ font = QtGui.QFont("Serif", 7, QtGui.QFont.Light)
qp.setFont(font)
w = self.width()
h = self.height()
- nb = (self.maximum() - self.minimum())
+ nb = self.maximum() - self.minimum()
if nb == 0:
return
fStep = float(w) / nb
step = max(1, int(round(fStep)))
- pen = QtGui.QPen(QtGui.QColor(200, 200, 200), 1,
- QtCore.Qt.SolidLine)
+ pen = QtGui.QPen(QtGui.QColor(200, 200, 200), 1, QtCore.Qt.SolidLine)
qp.setPen(pen)
qp.setBrush(QtCore.Qt.NoBrush)
@@ -938,9 +1174,11 @@ def drawWidget(self, qp):
metrics = qp.fontMetrics()
fh = metrics.height()
for e, i in enumerate(range(0, pxNb, step)):
- pos = self.style().sliderPositionFromValue(self.minimum(), self.maximum(), r[e], self.width())
+ pos = self.style().sliderPositionFromValue(
+ self.minimum(), self.maximum(), r[e], self.width()
+ )
half = h / 2
- if r[e] in self.cachedFrmaes:
+ if r[e] in self.cachedFrames:
qp.setPen(QtGui.QColor(0, 255, 0))
qp.setBrush(QtGui.QColor(0, 255, 0))
qp.drawRect(pos - (fStep / 2), half + 5, fStep, 1.5)
@@ -956,22 +1194,27 @@ def drawWidget(self, qp):
s = 4
text = r[e]
fw = metrics.horizontalAdvance(str(text))
- qp.drawText((pos) - fw / 2, h - fh / 3, str(text))
+ qp.drawText(pos - fw / 2, h - fh / 3, str(text))
else:
s = 1.5
qp.drawLine(pos, half + s, pos, half - s)
- pos = self.style().sliderPositionFromValue(self.minimum(), self.maximum(), self.value(), self.width())
+ pos = self.style().sliderPositionFromValue(
+ self.minimum(), self.maximum(), self.value(), self.width()
+ )
fw = metrics.horizontalAdvance("0")
qp.setPen(editableStyleSheet().MainColor)
if self.value() > self.maximum() - (self.maximum() / 2):
fw += metrics.horizontalAdvance(str(self.value()))
fw *= -1
- qp.drawText((pos) + fw, 0 + fh, str(self.value()))
+ qp.drawText(pos + fw, 0 + fh, str(self.value()))
if self.hover:
val = self.style().sliderValueFromPosition(
- self.minimum(), self.maximum(), self.hoverPos.x(), self.width())
+ self.minimum(), self.maximum(), self.hoverPos.x(), self.width()
+ )
if val != self.value():
- pos = self.style().sliderPositionFromValue(self.minimum(), self.maximum(), val, self.width())
+ pos = self.style().sliderPositionFromValue(
+ self.minimum(), self.maximum(), val, self.width()
+ )
fw = metrics.horizontalAdvance("0")
if val > self.maximum() - (self.maximum() / 2):
fw += metrics.horizontalAdvance(str(val))
@@ -981,24 +1224,33 @@ def drawWidget(self, qp):
pen2 = QtGui.QPen(color2, 2, QtCore.Qt.SolidLine)
qp.setPen(pen2)
qp.drawLine(pos, 0, pos, h)
- qp.drawText((pos) + fw, 0 + fh, str(val))
+ qp.drawText(pos + fw, 0 + fh, str(val))
qp.setPen(pen)
def mousePressEvent(self, event):
if event.modifiers() == QtCore.Qt.AltModifier:
self.PressPos = event.globalPos()
self.MovePos = event.globalPos()
- if event.button() == QtCore.Qt.LeftButton and event.modifiers() != QtCore.Qt.AltModifier:
+ if (
+ event.button() == QtCore.Qt.LeftButton
+ and event.modifiers() != QtCore.Qt.AltModifier
+ ):
butts = QtCore.Qt.MouseButtons(QtCore.Qt.MidButton)
- nevent = QtGui.QMouseEvent(event.type(), QtCore.QPointF(event.pos()), QtCore.QPointF(
- event.globalPos()), QtCore.Qt.MidButton, butts, event.modifiers())
+ nevent = QtGui.QMouseEvent(
+ event.type(),
+ QtCore.QPointF(event.pos()),
+ QtCore.QPointF(event.globalPos()),
+ QtCore.Qt.MidButton,
+ butts,
+ event.modifiers(),
+ )
super(pyf_timeline, self).mousePressEvent(nevent)
elif event.modifiers() != QtCore.Qt.AltModifier:
super(pyf_timeline, self).mousePressEvent(event)
def wheelEvent(self, event):
- newMin = self.minimum() + (round(120 / event.delta()))
- newMax = self.maximum() - (round(120 / event.delta()))
+ newMin = self.minimum() + (round(120 / event.angleDelta().y()))
+ newMax = self.maximum() - (round(120 / event.angleDelta().y()))
distance = newMax - newMin
if distance > 0:
self.setRange(newMin, newMax)
@@ -1019,7 +1271,7 @@ def mouseMoveEvent(self, event):
if event.buttons() in [QtCore.Qt.MidButton, QtCore.Qt.LeftButton]:
globalPos = event.globalPos()
diff = globalPos - self.MovePos
- a = (self.width() / (self.maximum() - self.minimum()))
+ a = self.width() / (self.maximum() - self.minimum())
if abs(diff.x()) > a:
self.MovePos = globalPos
newMin = self.minimum() - (1 * (diff.x() / abs(diff.x())))
@@ -1034,6 +1286,7 @@ class uiTick(QtWidgets.QGraphicsWidget):
""" UiElement For Ramp Widgets.
Holds a :obj:`PyFlow.Core.structs.Tick` inside with U,V attributes and expand it to use colors in V instead of floats for use in gradient sliders """
+
def __init__(self, raw_tick, parent=None):
"""
:param raw_tick: Input Core Tick
@@ -1143,6 +1396,7 @@ class pyf_RampSpline(QtWidgets.QGraphicsView):
:tickMoved: Signal emitted when a UiTick element moved
:tickRemoved: Signal emitted when a UiTick element deleted
"""
+
tickClicked = QtCore.Signal(object)
tickAdded = QtCore.Signal(object)
tickChanged = QtCore.Signal(object)
@@ -1199,7 +1453,7 @@ def updateFromRaw(self):
def __getitem__(self, index):
"""
- :param index: What UiTick to get, orderer by U
+ :param index: What UiTick to get, ordered by U
:type index: int
:returns: Ui Tick
:rtype: :obj:`UiTick`
@@ -1269,7 +1523,7 @@ def setU(self, u, index=-1):
:param u: New x position
:type u: float
- :param index: Index of the tick to set the value in, orderer by current X position, if -1 will try to set value in all selected Ticks, defaults to -1
+ :param index: Index of the tick to set the value in, ordered by current X position, if -1 will try to set value in all selected Ticks, defaults to -1
:type index: int, optional
"""
if index in range(0, len(self.items()) - 1):
@@ -1290,7 +1544,7 @@ def setV(self, v, index=-1):
:param v: New y position
:type v: float
- :param index: Index of the tick to set the value in, orderer by current X position, if -1 will try to set value in all selected Ticks, defaults to -1
+ :param index: Index of the tick to set the value in, ordered by current X position, if -1 will try to set value in all selected Ticks, defaults to -1
:type index: int, optional
"""
if index in range(0, len(self.items()) - 1):
@@ -1307,7 +1561,7 @@ def setV(self, v, index=-1):
self.computeDisplayPoints()
def evaluateAt(self, value):
- """Computes the result of the interpolation for the guiven U value
+ """Computes the result of the interpolation for the given U value
:param value: x position to evaluate at
:type value: float
@@ -1317,18 +1571,33 @@ def evaluateAt(self, value):
return self._rawRamp.evaluateAt(value, self.bezier)
def computeItemU(self, item):
- return max(min(item.scenePos().x() / (self.frameSize().width() - self.itemSize), 1), 0)
+ return max(
+ min(item.scenePos().x() / (self.frameSize().width() - self.itemSize), 1), 0
+ )
def computeItemV(self, item):
- return max(min(1 - (item.scenePos().y() / (self.frameSize().height() - self.itemSize)), 1), 0)
+ return max(
+ min(
+ 1 - (item.scenePos().y() / (self.frameSize().height() - self.itemSize)),
+ 1,
+ ),
+ 0,
+ )
def updateItemPos(self, item):
- item.setPos(item.getU() * (self.sceneRect().width() - self.itemSize), (1 - item.getV()) * (self.sceneRect().height() - self.itemSize))
+ item.setPos(
+ item.getU() * (self.sceneRect().width() - self.itemSize),
+ (1 - item.getV()) * (self.sceneRect().height() - self.itemSize),
+ )
def resizeEvent(self, event):
super(pyf_RampSpline, self).resizeEvent(event)
- self.scene().setSceneRect(0, 0, self.frameSize().width(), self.frameSize().height())
- self.fitInView(0, 0, self.scene().sceneRect().width(), 60, QtCore.Qt.IgnoreAspectRatio)
+ self.scene().setSceneRect(
+ 0, 0, self.frameSize().width(), self.frameSize().height()
+ )
+ self.fitInView(
+ 0, 0, self.scene().sceneRect().width(), 60, QtCore.Qt.IgnoreAspectRatio
+ )
for item in self.items():
self.updateItemPos(item)
item.update()
@@ -1354,17 +1623,17 @@ def mousePressEvent(self, event):
self.computeDisplayPoints()
self.tickRemoved.emit()
elif event.button() == QtCore.Qt.LeftButton and not self.pressed_item:
- raw_item = self._rawRamp.addItem(0, 0)
- item = uiTick(raw_item)
- item._width = item._height = 6
- self._scene.addItem(item)
- item.setPos(self.mapToScene(event.pos()))
- item.setU(self.computeItemU(item))
- item.setV(self.computeItemV(item))
- self.updateItemPos(item)
- self.pressed_item = item
- self.computeDisplayPoints()
- self.tickAdded.emit(item)
+ raw_item = self._rawRamp.addItem(0, 0)
+ item = uiTick(raw_item)
+ item._width = item._height = 6
+ self._scene.addItem(item)
+ item.setPos(self.mapToScene(event.pos()))
+ item.setU(self.computeItemU(item))
+ item.setV(self.computeItemV(item))
+ self.updateItemPos(item)
+ self.pressed_item = item
+ self.computeDisplayPoints()
+ self.tickAdded.emit(item)
self.clearSelection()
if self.pressed_item:
self.pressed_item.setSelected(True)
@@ -1393,43 +1662,84 @@ def mouseReleaseEvent(self, event):
def getCornerPoints(self, corner=0):
if corner == 0:
- return [QtCore.QPointF(self.itemSize / 2, self.frameSize().height() - self.itemSize / 2),
- QtCore.QPointF(self.itemSize / 2, self.sortedItems()[0].scenePos().y() - self.mapToScene(QtCore.QPoint(-1.5, -1.5)).y())]
+ return [
+ QtCore.QPointF(
+ self.itemSize / 2, self.frameSize().height() - self.itemSize / 2
+ ),
+ QtCore.QPointF(
+ self.itemSize / 2,
+ self.sortedItems()[0].scenePos().y()
+ - self.mapToScene(QtCore.QPoint(-1.5, -1.5)).y(),
+ ),
+ ]
else:
- return [QtCore.QPointF(self.frameSize().width() - self.itemSize / 2, self.sortedItems()[-1].scenePos().y() - self.mapToScene(QtCore.QPoint(-1.5, -1.5)).y()),
- QtCore.QPointF(self.frameSize().width() - self.itemSize / 2, self.frameSize().height() - self.itemSize / 2)]
+ return [
+ QtCore.QPointF(
+ self.frameSize().width() - self.itemSize / 2,
+ self.sortedItems()[-1].scenePos().y()
+ - self.mapToScene(QtCore.QPoint(-1.5, -1.5)).y(),
+ ),
+ QtCore.QPointF(
+ self.frameSize().width() - self.itemSize / 2,
+ self.frameSize().height() - self.itemSize / 2,
+ ),
+ ]
def computeDisplayPoints(self, nonLinearRes=50):
items = self.sortedItems()
points = []
if len(items):
for item in items:
- points.append(item.scenePos() - self.mapToScene(QtCore.QPoint(-1.5, -1.5)))
+ points.append(
+ item.scenePos() - self.mapToScene(QtCore.QPoint(-1.5, -1.5))
+ )
if self.bezier:
bezierPoints = []
numSteps = nonLinearRes
for k in range(numSteps):
t = float(k) / (numSteps - 1)
- x = int(self._rawRamp.interpolateBezier([p.x() for p in points], 0, len(items) - 1, t))
- y = int(self._rawRamp.interpolateBezier([p.y() for p in points], 0, len(items) - 1, t))
+ x = int(
+ self._rawRamp.interpolateBezier(
+ [p.x() for p in points], 0, len(items) - 1, t
+ )
+ )
+ y = int(
+ self._rawRamp.interpolateBezier(
+ [p.y() for p in points], 0, len(items) - 1, t
+ )
+ )
bezierPoints.append(QtCore.QPointF(x, y))
points = bezierPoints
points = self.getCornerPoints(0) + points + self.getCornerPoints(1)
self.displayPoints = points
def drawBackground(self, painter, rect):
- painter.fillRect(rect.adjusted(self.itemSize / 2, self.itemSize / 2, -self.itemSize / 2, -self.itemSize / 2), editableStyleSheet().InputFieldColor)
+ painter.fillRect(
+ rect.adjusted(
+ self.itemSize / 2,
+ self.itemSize / 2,
+ -self.itemSize / 2,
+ -self.itemSize / 2,
+ ),
+ editableStyleSheet().InputFieldColor,
+ )
painter.setBrush(QtGui.QColor(0, 0, 0, 0))
painter.setPen(QtGui.QColor(0, 0, 0, 255))
- painter.drawRect(rect.adjusted(self.itemSize / 2, self.itemSize / 2, -self.itemSize / 2, -self.itemSize / 2))
+ painter.drawRect(
+ rect.adjusted(
+ self.itemSize / 2,
+ self.itemSize / 2,
+ -self.itemSize / 2,
+ -self.itemSize / 2,
+ )
+ )
items = self.sortedItems()
- val = self.mapToScene(QtCore.QPoint(-1.5, -1.5))
if len(items):
painter.setBrush(QtGui.QColor(100, 100, 100))
painter.drawPolygon(self.displayPoints, QtCore.Qt.WindingFill)
- else:
- b = editableStyleSheet().InputFieldColor
+ # else:
+ # b = editableStyleSheet().InputFieldColor
class pyf_RampColor(pyf_RampSpline):
@@ -1447,6 +1757,7 @@ class pyf_RampColor(pyf_RampSpline):
Extends:
:obj: `pyf_RampSpline`
"""
+
colorClicked = QtCore.Signal(list)
def __init__(self, raw_ramp, parent=None, bezier=True):
@@ -1463,12 +1774,12 @@ def values(self):
"""
return [x.getColor().getRgbF() for x in self.sortedItems()]
- def addItem(self, u=0, v=[0, 0, 0], raw_item=None):
+ def addItem(self, u=0, v=(0, 0, 0), raw_item=None):
"""Adds a new Item to the ramp
:param u: X position for the item, defaults to 0
:type u: float, optional
- :param v: color value for the item, defaults to [0,0,0]
+ :param v: color value for the item, defaults to (0,0,0)
:type v: [float,float,float], optional
:param raw_item: Existing :obj:`PyFlow.Core.structs.Tick` to link with, if none, one new created , defaults to None
:type raw_item: :obj:`PyFlow.Core.structs.Tick`, optional
@@ -1489,7 +1800,7 @@ def setColor(self, color, index=-1):
:param color: New color
:type color: [float,float,float]
- :param index: Index of the tick to set the value in, orderer by current X position, if -1 will try to set value in all selected Ticks, defaults to -1
+ :param index: Index of the tick to set the value in, ordered by current X position, if -1 will try to set value in all selected Ticks, defaults to -1
:type index: int, optional
"""
if index in range(0, len(self.items()) - 1):
@@ -1508,8 +1819,12 @@ def updateItemPos(self, item):
def resizeEvent(self, event):
super(pyf_RampColor, self).resizeEvent(event)
- self.scene().setSceneRect(0, 0, self.frameSize().width(), self.frameSize().height())
- self.fitInView(0, 0, self.scene().sceneRect().width(), 15, QtCore.Qt.IgnoreAspectRatio)
+ self.scene().setSceneRect(
+ 0, 0, self.frameSize().width(), self.frameSize().height()
+ )
+ self.fitInView(
+ 0, 0, self.scene().sceneRect().width(), 15, QtCore.Qt.IgnoreAspectRatio
+ )
for item in self.items():
self.updateItemPos(item)
item.update()
@@ -1528,8 +1843,9 @@ def mousePressEvent(self, event):
self.tickRemoved.emit()
else:
if not self.pressed_item:
- color = self.evaluateAt(self.mapToScene(
- event.pos()).x() / self.frameSize().width())
+ color = self.evaluateAt(
+ self.mapToScene(event.pos()).x() / self.frameSize().width()
+ )
raw_item = self._rawRamp.addItem(0, color)
item = uiTick(raw_item)
item._width = 10
@@ -1545,7 +1861,9 @@ def mousePressEvent(self, event):
if self.pressed_item:
self.pressed_item.setSelected(True)
self.tickClicked.emit(self.pressed_item)
- self.colorClicked.emit([(x + 0.5) for x in self.pressed_item.getColor().getRgb()])
+ self.colorClicked.emit(
+ [(x + 0.5) for x in self.pressed_item.getColor().getRgb()]
+ )
self.valueClicked.emit(self.pressed_item.getU(), self.pressed_item.getV())
self.scene().update()
@@ -1575,16 +1893,23 @@ def drawBackground(self, painter, rect):
t = float(k) / (numSteps - 1)
color = []
for i in range(len(items[0].getV())):
- color.append(self._rawRamp.interpolateBezier([p.getV()[i] for p in items], 0, len(items) - 1, t))
- x = self._rawRamp.interpolateBezier([p.getU() for p in items], 0, len(items) - 1, t)
- b.setColorAt(x, QtGui.QColor().fromRgb(color[0], color[1], color[2]))
+ color.append(
+ self._rawRamp.interpolateBezier(
+ [p.getV()[i] for p in items], 0, len(items) - 1, t
+ )
+ )
+ x = self._rawRamp.interpolateBezier(
+ [p.getU() for p in items], 0, len(items) - 1, t
+ )
+ b.setColorAt(
+ x, QtGui.QColor().fromRgb(color[0], color[1], color[2])
+ )
else:
b = editableStyleSheet().InputFieldColor
painter.fillRect(rect, b)
class testWidg(QtWidgets.QWidget):
-
def __init__(self, parent=None):
super(testWidg, self).__init__(parent)
@@ -1615,8 +1940,7 @@ def __init__(self, parent=None):
self.layout().addWidget(ramp2)
-if __name__ == '__main__':
- import os
+if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
diff --git a/PyFlow/UI/Widgets/SelectPinDialog.py b/PyFlow/UI/Widgets/SelectPinDialog.py
index 79754d297..a233f679f 100644
--- a/PyFlow/UI/Widgets/SelectPinDialog.py
+++ b/PyFlow/UI/Widgets/SelectPinDialog.py
@@ -13,8 +13,8 @@
## limitations under the License.
-from Qt import QtWidgets
-from Qt import QtGui, QtCore
+from qtpy import QtWidgets
+from qtpy import QtGui, QtCore
from PyFlow.UI.Canvas.Painters import PinPainter
from PyFlow import findPinClassByType, getAllPinClasses
@@ -30,6 +30,7 @@ def __init__(self):
def getLodValueFromCurrentScale(self, lod):
return 1
+
def getCanvasLodValueFromCurrentScale(self):
return 1
@@ -57,6 +58,7 @@ def hasConnections(self):
class _PinWidget(QtWidgets.QWidget):
"""docstring for _PinWidget."""
+
def __init__(self, dataType, parent=None):
super(_PinWidget, self).__init__(parent)
self.dataType = dataType
@@ -77,7 +79,9 @@ def __init__(self, dataType, parent=None):
self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
def sizeHint(self):
- textWidth = QtGui.QFontMetrics(self._font).horizontalAdvance(self.dataType) + _PIN_SIZE
+ textWidth = (
+ QtGui.QFontMetrics(self._font).horizontalAdvance(self.dataType) + _PIN_SIZE
+ )
textHeight = max(QtGui.QFontMetrics(self._font).height(), _PIN_SIZE + 6)
return QtCore.QSize(textWidth, textHeight)
@@ -125,11 +129,12 @@ def paintEvent(self, event):
class _PinsListWidget(QtWidgets.QListWidget):
"""docstring for _PinsListWidget."""
+
returnPressed = QtCore.Signal()
- def __init__(self, parent=None,validPins=None):
+ def __init__(self, parent=None, validPins=None):
super(_PinsListWidget, self).__init__()
- self.populate(pattern="",validPins=validPins)
+ self.populate(pattern="", validPins=validPins)
def filterContent(self, pattern):
self.clear()
@@ -145,7 +150,11 @@ def keyPressEvent(self, event):
self.returnPressed.emit()
super(_PinsListWidget, self).keyPressEvent(event)
- def populate(self, pattern="", validPins=[pin.__name__ for pin in getAllPinClasses()]):
+ def populate(
+ self, pattern="", validPins=None
+ ):
+ if validPins is None:
+ validPins = [pin.__name__ for pin in getAllPinClasses()]
for pinClass in getAllPinClasses():
className = pinClass.__name__
if className in validPins:
@@ -159,7 +168,8 @@ def populate(self, pattern="", validPins=[pin.__name__ for pin in getAllPinClass
class SelectPinDialog(QtWidgets.QDialog):
"""docstring for SelectPinDialog."""
- def __init__(self, parent=None,validPins=None):
+
+ def __init__(self, parent=None, validPins=None):
super(SelectPinDialog, self).__init__(None)
self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint)
self.setWindowTitle("Select pin")
diff --git a/PyFlow/UI/Widgets/TextEditDialog.py b/PyFlow/UI/Widgets/TextEditDialog.py
index 8d14c37cf..2d398932c 100644
--- a/PyFlow/UI/Widgets/TextEditDialog.py
+++ b/PyFlow/UI/Widgets/TextEditDialog.py
@@ -13,15 +13,16 @@
## limitations under the License.
-from Qt import QtCore, QtGui
-from Qt.QtWidgets import QDialog
-from Qt.QtWidgets import QVBoxLayout
-from Qt.QtWidgets import QDialogButtonBox
-from Qt.QtWidgets import QTextEdit
+from qtpy import QtCore, QtGui
+from qtpy.QtWidgets import QDialog
+from qtpy.QtWidgets import QVBoxLayout
+from qtpy.QtWidgets import QDialogButtonBox
+from qtpy.QtWidgets import QTextEdit
class TextEditingField(QTextEdit):
"""docstring for TextEditingField."""
+
accepted = QtCore.Signal()
def __init__(self, parent=None):
@@ -29,7 +30,10 @@ def __init__(self, parent=None):
def keyPressEvent(self, event):
super(TextEditingField, self).keyPressEvent(event)
- if event.modifiers() == QtCore.Qt.ControlModifier and event.key() == QtCore.Qt.Key_Return:
+ if (
+ event.modifiers() == QtCore.Qt.ControlModifier
+ and event.key() == QtCore.Qt.Key_Return
+ ):
self.accepted.emit()
@@ -46,7 +50,8 @@ def __init__(self, font, textColor, parent=None):
self.te.setTextColor(textColor)
self.layout.addWidget(self.te)
self.buttons = QDialogButtonBox(
- QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self)
+ QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self
+ )
self.buttons.accepted.connect(self.onAccept)
self.buttons.rejected.connect(self.onReject)
self.layout.addWidget(self.buttons)
diff --git a/PyFlow/UI/__init__.py b/PyFlow/UI/__init__.py
index aa15c2700..ed018641f 100644
--- a/PyFlow/UI/__init__.py
+++ b/PyFlow/UI/__init__.py
@@ -15,6 +15,7 @@
import sys
import os
+
fileDir = os.path.dirname(__file__)
fileDir = fileDir.replace("\\", "/")
sys.path.append(fileDir)
diff --git a/PyFlow/UI/resources.py b/PyFlow/UI/resources.py
index a2eae5360..51171731d 100644
--- a/PyFlow/UI/resources.py
+++ b/PyFlow/UI/resources.py
@@ -7,7 +7,7 @@
#
# WARNING! All changes made in this file will be lost!
-from PySide2 import QtCore
+from qtpy import QtCore
qt_resource_data = b"\
\x00\x00\x02\x19\
@@ -75672,10 +75672,17 @@
\x00\x00\x00$\x00\x00\x00\x00\x00\x01\x00\x00\x02\x1d\
"
+
def qInitResources():
- QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qRegisterResourceData(
+ 0x01, qt_resource_struct, qt_resource_name, qt_resource_data
+ )
+
def qCleanupResources():
- QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qUnregisterResourceData(
+ 0x01, qt_resource_struct, qt_resource_name, qt_resource_data
+ )
+
qInitResources()
diff --git a/PyFlow/Wizards/ClassDialogueBase.py b/PyFlow/Wizards/ClassDialogueBase.py
new file mode 100644
index 000000000..4605b02a2
--- /dev/null
+++ b/PyFlow/Wizards/ClassDialogueBase.py
@@ -0,0 +1,173 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+from qtpy import QtCore
+from qtpy import QtGui
+
+
+from qtpy.QtWidgets import *
+from docutils import core
+
+
+def rst2html(rst):
+ if rst is not None:
+ return core.publish_string(rst, writer_name="html").decode("utf-8")
+ return ""
+
+
+class ClassDialogueBase(QDialog):
+ """docstring for WizardDialogueBase."""
+ def __init__(self, parent=None):
+ super(ClassDialogueBase, self).__init__(parent)
+ self.setWindowTitle("Package wizard")
+ self.setWindowIcon(QtGui.QIcon(":LogoBpApp.png"))
+ self.resize(700, 500)
+ self.mainLayout = QVBoxLayout(self)
+ self.mainLayout.setObjectName("mainLayout")
+ self.mainLayout.setSpacing(1)
+ self.mainLayout.setContentsMargins(1, 1, 1, 1)
+ self.progress = QProgressBar()
+ self.progress.setTextVisible(False)
+ self.progress.setObjectName("progress")
+ self.progress.setRange(0, 100)
+ self.mainLayout.addWidget(self.progress)
+ self.stackWidget = QStackedWidget()
+ self.stackWidget.currentChanged.connect(self.updateMessage)
+
+ # message section
+ self.messageLayout = QHBoxLayout()
+ self.messageWidget = QLabel()
+ self.messageWidget.setTextFormat(QtCore.Qt.RichText)
+ self.messageWidget.setWordWrap(True)
+ self.messageWidget.setAlignment(QtCore.Qt.AlignCenter)
+ font = QtGui.QFont("Consolas", 20)
+ self.messageWidget.setFont(font)
+ '''wizardImage = QLabel("test")
+ wizardImage.setPixmap(QtGui.QPixmap(":wizard-cat.png").scaled(250, 250))
+ wizardImage.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
+ self.messageLayout.addWidget(wizardImage)'''
+ self.messageLayout.addWidget(self.messageWidget)
+ self.mainLayout.addLayout(self.messageLayout)
+
+ # add user input section
+ # ...
+ self.messages = {}
+ self.pageValidationHooks = {}
+ self.errorMessages = {}
+ self.pageEnterCallbacks = {}
+ self.addGreetPage()
+ self.populate()
+ self.addFinalPage()
+
+ self.mainLayout.addWidget(self.stackWidget)
+
+ # add navigation buttons
+ # ...
+ self.navigationLayout = QHBoxLayout()
+ self.navigationLayout.setObjectName("navigationLayout")
+ self.navigationLayout.setContentsMargins(5, 1, 5, 5)
+ self.goBackButton = QPushButton("Go back")
+ self.goBackButton.clicked.connect(self.onGoBack)
+ self.goForwardButton = QPushButton("Go forward")
+ self.goForwardButton.clicked.connect(self.onGoForward)
+ self.navigationLayout.addWidget(self.goBackButton)
+ spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Maximum)
+ self.navigationLayout.addItem(spacerItem)
+ self.navigationLayout.addWidget(self.goForwardButton)
+ self.mainLayout.addLayout(self.navigationLayout)
+
+ self.updateNavigationVisibility()
+ self.updateMessage(0)
+
+ def updateMessage(self, pageNum):
+ widget = self.stackWidget.widget(pageNum)
+ if widget in self.messages:
+ self.setMessageRst(self.messages[widget])
+ return
+ if pageNum == 0:
+ return
+ if pageNum == self.stackWidget.count() - 1:
+ return
+
+ def addGreetPage(self):
+ self.addPageWidget(QWidget(), "Updating the Package")
+
+ def addFinalPage(self):
+ self.addPageWidget(QWidget(), "Have fun!")
+
+ def updateProgress(self):
+ numStates = self.stackWidget.count() - 1
+ chunk = 100 / numStates
+ self.progress.setValue(chunk * self.stackWidget.currentIndex())
+
+ def updateNavigationVisibility(self):
+ self.goBackButton.show()
+ self.goForwardButton.show()
+ self.goForwardButton.setText("Go forward")
+ if self.stackWidget.currentIndex() == 0:
+ self.goBackButton.hide()
+ if self.stackWidget.currentIndex() == self.stackWidget.count() - 1:
+ self.goForwardButton.setText("Done")
+ self.updateProgress()
+
+ def onGoBack(self):
+ futureIndex = self.stackWidget.currentIndex() - 1
+ self.stackWidget.setCurrentIndex(futureIndex)
+ self.setMessageRst(self.messages[self.stackWidget.currentWidget()])
+ self.updateNavigationVisibility()
+
+ def onDone(self):
+ self.accept()
+
+ def onGoForward(self):
+ futureIndex = self.stackWidget.currentIndex() + 1
+ isCurrentPageValid = self.pageValidationHooks[self.stackWidget.currentWidget()]()
+ if isCurrentPageValid:
+ self.stackWidget.setCurrentIndex(futureIndex)
+ self.updateNavigationVisibility()
+ self.pageValidationHooks[self.stackWidget.currentWidget()]()
+ self.pageEnterCallbacks[self.stackWidget.currentWidget()]()
+ if futureIndex == self.stackWidget.count():
+ self.onDone()
+ else:
+ self.setMessageRst(self.errorMessages[self.stackWidget.currentWidget()])
+
+ def addPageWidget(self, widget, messageRst, errorMessageRst="Somethig is wrong!", validationHook=lambda: True, pageEnterCallback=lambda: None):
+ self.stackWidget.addWidget(widget)
+ self.messages[widget] = messageRst
+ self.pageValidationHooks[widget] = validationHook
+ self.errorMessages[widget] = errorMessageRst
+ self.pageEnterCallbacks[widget] = pageEnterCallback
+
+ def populate(self):
+ pass
+
+ def setMessageRst(self, rst):
+ self.messageWidget.setText(rst2html(rst))
+
+ @staticmethod
+ def run():
+ instance = ClassDialogueBase()
+ instance.exec_()
+
+
+if __name__ == "__main__":
+ import sys
+ app = QApplication(sys.argv)
+
+ w = ClassDialogueBase()
+ w.show()
+
+ sys.exit(app.exec_())
diff --git a/PyFlow/Wizards/Forms/ClassManager - Import.ui b/PyFlow/Wizards/Forms/ClassManager - Import.ui
new file mode 100644
index 000000000..0c206f08f
--- /dev/null
+++ b/PyFlow/Wizards/Forms/ClassManager - Import.ui
@@ -0,0 +1,1412 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 931
+ 848
+
+
+
+ Dialog
+
+
+ -
+
+
-
+
+
+ -
+
+
+
+ 30
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
-
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ Update Init
+
+
+
+ -
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 1
+
+
+
+
+ -
+
+
+ 3
+
+
+
+ Exporters
+
+
+
+
+ Factories
+
+
+
+
+ Functions
+
+
+
-
+
+
+ 0
+
+
-
+
+
+
+ 75
+ 16777215
+
+
+
+ Create
+
+
+
+ -
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ Create
+
+
+
+ -
+
+
+ -
+
+
+ Filename
+
+
+
+ -
+
+
+ Function Name
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ 0
+
+
+
+ Pins
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Pin Specifiers
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ -
+
+
+ Input Widget Variant
+
+
+
+ -
+
+
+ Value List
+
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ Supported Data Types
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ -
+
+
+ Structured Constraint
+
+
+
+ -
+
+
+ Dragger Steps
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Value Range
+
+
+
+ -
+
+
+ Disabled Options
+
+
+
+ -
+
+
+ -
+
+
+ Contraint
+
+
+
+ -
+
+
+ -
+
+
+ Node Type
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Pin Options
+
+
+ -
+
+
-
+
+
+ Supported Only Arrays
+
+
+
+ -
+
+
+ Renaming Enabled
+
+
+
+ -
+
+
+ Allow Multiple Connections
+
+
+
+ -
+
+
+ Dynamic
+
+
+
+ -
+
+
+ Change Type On Connection
+
+
+
+ -
+
+
+ Allow Any
+
+
+
+ -
+
+
+ Storable
+
+
+
+ -
+
+
+ Dictionary Element Supported
+
+
+
+ -
+
+
+ Array Supported
+
+
+
+ -
+
+
+ Dictionary Supported
+
+
+
+ -
+
+
+ Always Push Dirty
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+ Meta
+
+
+ -
+
+
-
+
+
+ Category
+
+
+
+ -
+
+
+ Keywords
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Cache Enabled
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 512
+
+
+
+
+
+
+
+
+ Implement
+
+
+ -
+
+
+
+
+
+
+ Definition
+
+
+ -
+
+
+
+
+
+
+ Code
+
+
+ -
+
+
+
+ 16777215
+ 40
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+ false
+
+
+ false
+
+
+ Save
+
+
+
+
+
+
+
+ Nodes
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Category
+
+
+
+ -
+
+
+ Description
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ Header Color
+
+
+
+ -
+
+
+ -
+
+
+ Header
+
+
+
+ -
+
+
+ Filename
+
+
+
+ -
+
+
+
+ 75
+ 16777215
+
+
+
+ New File
+
+
+
+ -
+
+
+ -
+
+
+ Key Words
+
+
+
+ -
+
+
+
+ 16777215
+ 50
+
+
+
+
+
+
+ -
+
+
+ Definition Order
+
+
+
+ -
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Pins
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ U
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ D
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Pin Options
+
+
+
-
+
+
+ 0
+
+
-
+
+
+ Input Structure
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ Data Types
+
+
+
+ -
+
+
+ Add Type Hint
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ Widgets
+
+
+
+
+ Pins
+
+
+ -
+
+
-
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ Value Pin
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Default Value
+
+
+
+ -
+
+
+ -
+
+
+ New
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Exec Pin
+
+
+
+
+
+ -
+
+
-
+
+
+ Supported Datatype
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Pin Data Type Hint
+
+
+
+
+
+ -
+
+
-
+
+
+ Color
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ ...
+
+
+
+
+
+ -
+
+
+ Default Value
+
+
+
+ -
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Pin Connect
+
+
+
-
+
+
+
+
+
+
+ Pin Disconnect
+
+
+ -
+
+
+
+
+
+
+ Process Data
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ Tools
+
+
+
+
+ UI
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/PyFlow/Wizards/Forms/NodeandFunctionDesigner.ui b/PyFlow/Wizards/Forms/NodeandFunctionDesigner.ui
new file mode 100644
index 000000000..27fac0b8c
--- /dev/null
+++ b/PyFlow/Wizards/Forms/NodeandFunctionDesigner.ui
@@ -0,0 +1,263 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 536
+ 421
+
+
+
+ Dialog
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 25
+ 16777215
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/PyFlow/Wizards/PackageBuilder.py b/PyFlow/Wizards/PackageBuilder.py
new file mode 100644
index 000000000..0753a0e14
--- /dev/null
+++ b/PyFlow/Wizards/PackageBuilder.py
@@ -0,0 +1,2317 @@
+## Copyright 2023 David Lario
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import os
+import shutil
+#import subprocess
+import uuid
+import inspect
+import importlib
+
+from PyFlow import Packages
+
+from qtpy import QtGui
+from qtpy import QtCore
+from qtpy.QtWidgets import *
+
+from qtpy.uic import loadUiType, loadUi
+from qtpy import QtUiTools
+from blinker import Signal
+
+
+path = os.path.dirname(os.path.abspath(__file__))
+packageRoot = Packages.__path__[0]
+
+uiFile = os.path.join(path, 'PackageBuilder.ui')
+#WindowTemplate, TemplateBaseClass = loadUiType(uiFile)
+RESOURCES_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "UI/resources/")
+
+from qtpy.QtCore import QItemSelectionModel #Because Pyside hijacked from pyqt
+from qtpy.QtCore import QSortFilterProxyModel, QRegularExpression, QModelIndex
+from qtpy.QtGui import QStandardItemModel, QStandardItem
+from PyFlow.Wizards.WizardDialogueBase import WizardDialogueBase
+from PyFlow.UI.Tool.Tool import FormTool
+
+class PackageBuilder(QMdiSubWindow):
+ def __init__(self, main, parent):
+ QMdiSubWindow.__init__(self)
+
+ self.ui = QtUiTools.QUiLoader().load(uiFile, self)
+ self.PackageName = "PackageBuilder"
+ self.actionRegister = None
+
+ self.defaultfolderlist = ["Commands", "FunctionLibraries", "Nodes", "Pins", "PrefsWidgets", "Tools", "UI"]
+ packageRoot = Packages.__path__[0]
+ self.ui.txtPackageFolderLocation.setText(packageRoot)
+
+ packagelistmodel = QtGui.QStandardItemModel(0, 1)
+ rowcount = 0
+ self.functiondict = {}
+ self.pindict = {}
+
+ self.pindefs = {}
+ self.pindefs["Inputs"] = {}
+ self.pindefs["Outputs"] = {}
+
+ self.codeDict = {}
+ self.codeList = []
+ self.pindata = {}
+ self.selectedPinName = ""
+ self.selectedPinDir = ""
+ self.workingFile = ""
+
+ for directories in os.listdir(packageRoot):
+ if directories[1] != "_":
+ packagelistmodel.setItem(rowcount, 0, QtGui.QStandardItem(directories))
+ rowcount += 1
+
+ self.packagelistModelproxy = QtCore.QSortFilterProxyModel(self)
+ self.packagelistModelproxy.setSourceModel(packagelistmodel)
+
+ self.ui.lstPackages.setModel(self.packagelistModelproxy)
+ self.ui.lstPackages.setModelColumn(1)
+ self.ui.lstPackages.clicked.connect(self.onSelectPackage)
+
+ self.ui.cmdOpenPackageFolder.clicked.connect(self.onOpenPackageFolder)
+ self.ui.txtPackageFilter.textChanged.connect(self.onChangeFilterValue)
+ self.ui.tvPackageItems.header().hide()
+
+ #self.ui.tblFInputPins.selectionModel().selectionChanged.connect(self.on_tblFInputPins_Changed)
+ self.ui.tblFInputPins.clicked.connect(self.on_tblFInputPins_clicked)
+
+ #self.ui.tblFOutputPins.selectionModel().selectionChanged.connect(self.on_tblFOutputPins_Changed)
+ self.ui.tblFOutputPins.clicked.connect(self.on_tblFOutputPins_clicked)
+ self.ui.tvPackageItems.itemClicked.connect(self.on_tvPackageItems_clicked)
+
+ self.onPinScan()
+
+ @staticmethod
+ def supportedSoftwares():
+ """Under what software to work
+ """
+ return ["any"]
+
+
+ def getMenuOrder(self):
+ menuOrder = ["Tools", "Windows", "Help"]
+ return menuOrder
+
+ def getMenuLayout(self):
+ menuDict = {}
+ separatorCount = 0
+ packageList = []
+ menuDict["Packages"] = packageList
+
+ fileMenuList = []
+ menuDict["File"] = fileMenuList
+
+ packageToolList = []
+ packageToolList.append({"Action": "Add Action", "Package": "PackageBuilder", "PackageGroup": "ProgramBase","Instance": self, "Command": "PackageBuilder"})
+ menuDict["Tools"] = packageToolList
+
+ windowMenuList = []
+ menuDict["Windows"] = windowMenuList
+
+ helpMenuList = []
+ helpMenuList.append({"Action": "Add Action", "Package": "ProgramBase", "PackageGroup": "ProgramBase", "Instance": self, "Command": "About"})
+ helpMenuList.append({"Action": "Add Action", "Package": "ProgramBase", "PackageGroup": "ProgramBase", "Instance": self, "Command": "HomePage"})
+ menuDict["Help"] = helpMenuList
+
+ return menuDict
+
+ def getRibbonOrder(self):
+ ribbonOrder = ["Tools"]
+ return ribbonOrder
+
+ def getRibbonLayout(self):
+ ribbonBarDict = {}
+ ribbonItems = []
+ ribbonItems.append({"Bar": "ProgramBase", "Section": "Tools", "Widget": "Small Button", "SmallIcon": RESOURCES_DIR + "new_file_icon.png", "LargeIcon": RESOURCES_DIR + "new_file_icon.png", "Action": "Add Action", "Package": "ProgramBase", "PackageGroup": "ProgramBase","Instance": self, "Command": "ToolBar"})
+ ribbonBarDict["Tools"] = ribbonItems
+ return ribbonBarDict
+
+ def getToolBarLayout(self):
+
+ toolBarDict = {}
+ pyFlowToolBar = []
+ pyFlowToolBar.append({"Bar": "Bar 1", "Section": "Section 1", "Widget": "Small Button", "Action": "Add Action", "Package": "PyFlow", "PackageGroup": "PyFlow", "Instance": self, "Command": "NewFile"})
+ toolBarDict["File"] = pyFlowToolBar
+
+ return toolBarDict
+
+ def onChangeFilterValue(self, text):
+ #self.packagelistModelproxy.setFilterKeyColumn(text)
+ search = QtCore.QRegExp(str(text), QtCore.Qt.CaseInsensitive, QtCore.QRegExp.Wildcard)
+ self.packagelistModelproxy.setFilterRegExp(search)
+
+ def onSelectPackage(self, index):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(index.row(), 0).data()
+ #selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex(), 0, None).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ self.ui.tvPackageItems.clear()
+ CommandList = {}
+ FunctionList = {}
+ PrefsWidgetsList = {}
+
+ for directories in os.listdir(packagepath):
+ if directories[1] != "_":
+ parent = QTreeWidgetItem(self.ui.tvPackageItems)
+ parent.setText(0, directories)
+ #parent.setFlags(parent.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable)
+ filepath = os.path.join(packagepath, directories)
+ for file in os.listdir(filepath):
+ if file[1] != "_" and file[-3:]==".py":
+ child = QTreeWidgetItem(parent)
+ #child.clicked.connect(self.on_tvPackageItems_clicked)
+ #child.setFlags(child.flags() | QtCore.Qt.ItemIsUserCheckable)
+ child.setText(0, file[:-3])
+ #child.setCheckState(0, QtCore.Qt.Unchecked)
+ filefullpath = os.path.join(filepath, file)
+
+ try:
+ f = open(filefullpath, "r")
+ for lineitem in f:
+ if directories[1] == "FunctionLibrary":
+ if len(lineitem) > 10:
+ if lineitem[:5] == "class":
+ classnamestart = lineitem.find(" ")
+ classnameend = lineitem.find("(")
+ #child2 = QTreeWidgetItem(child)
+ #child2.setFlags(child2.flags() | QtCore.Qt.ItemIsUserCheckable)
+ #child2.setText(0, lineitem[classnamestart+1:classnameend])
+ #child2.setCheckState(0, QtCore.Qt.Unchecked)
+ if lineitem.find("def ") != -1:
+ if lineitem[8] != "_":
+ classnamestart = 7
+ classnameend = lineitem.find("(")
+ child2 = QTreeWidgetItem(child)
+ #child2.setFlags(child2.flags() | QtCore.Qt.ItemIsUserCheckable)
+ classname = lineitem[classnamestart+1:classnameend]
+ if file.find(lineitem[classnamestart+1:classnameend]) == -1:
+ functionname = lineitem[classnamestart+1:classnameend]
+ child2.setText(0, functionname)
+ #child2.setCheckState(0, QtCore.Qt.Unchecked)
+ except:
+ pass
+
+
+ def packageScan(self):
+ print(f"Class name: {cls.__name__}")
+ print("Methods:")
+ methods = inspect.getmembers(cls, predicate=inspect.isfunction)
+ for _, method in methods:
+ print(method)
+
+ def analyze_package(package_name):
+ try:
+ package = importlib.import_module(package_name)
+ except ImportError:
+ print(f"Error: Package '{package_name}' not found.")
+ return
+
+ classes = inspect.getmembers(package, predicate=inspect.isclass)
+ functions = inspect.getmembers(package, predicate=inspect.isfunction)
+
+ print(f"Package name: {package_name}\n")
+ '''print("Classes:")
+ for _, cls in classes:
+ print_class_info(cls)
+
+ print("Functions:")
+ for _, function in functions:
+ print_function_info(function)'''
+
+ def onPinScan(self):
+ packageRoot = Packages.__path__[0]
+ self.pinDict = {}
+ for root, dirs, files in os.walk(packageRoot, topdown=False):
+ for name in files:
+ directories = os.path.join(root, name)
+ if "Pin.py" in name:
+ PinName = name.replace(".py", "")
+ self.pinDict[PinName] = directories
+
+ @QtCore.Slot(QTreeWidgetItem, int)
+ def on_tvPackageItems_clicked(self, it, col):
+ parents = []
+ current_item = it
+ current_parent = current_item.parent()
+
+ # Walk up the tree and collect all parent items of this item
+ while not current_parent is None:
+ parents.insert(0, current_parent.text(col))
+ current_item = current_parent
+ current_parent = current_item.parent()
+
+ filefullpath = Packages.__path__[0] + "\\"
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ selecteditem = it.text(col) + ".py"
+ filefullpath = os.path.join(filefullpath, selectedpackage)
+ for items in parents:
+ filefullpath = os.path.join(filefullpath, items)
+
+ filefullpath = os.path.join(filefullpath, selecteditem)
+
+ if filefullpath.find("\\Commands") != -1:
+
+ #todo: check if package level was selected
+ deft = it.text(col)
+ self.workingFile = filefullpath
+ self.initializeform
+ self.initializePinData
+ filename = filefullpath.split("\\")[-1]
+ self.ui.txtFunctionFileName.setText(filename)
+ self.ui.txtFunctionName.setText(deft)
+ self.ui.twPackage.setCurrentIndex(1)
+ implementdata = ""
+ definitiondata = ""
+ codedata = ""
+
+ self.loadCodeData(filefullpath)
+
+
+ if filefullpath.find("\\FunctionLibraries") != -1:
+ self.loadAllFunctions()
+ #todo: check if package level was selected
+ deft = it.text(col)
+ self.workingFile = filefullpath
+ self.initializeform
+ self.initializePinData
+ filename = filefullpath.split("\\")[-1]
+ self.ui.txtFunctionFileName.setText(filename)
+ self.ui.txtFunctionName.setText(deft)
+
+ self.loadAllFunctions()
+ implementdata = ""
+ definitiondata = ""
+ codedata = ""
+ self.ui.twPackage.setCurrentIndex(2)
+
+ try:
+ for idata in self.functiondict[deft]["Implement"]:
+ implementdata += idata
+ self.ui.txtFImplement.setText(implementdata)
+
+ for ddata in self.functiondict[deft]["Definition"]:
+ definitiondata += ddata
+ self.ui.txtFDef.setText(definitiondata)
+
+ code = ""
+ for codeline in self.functiondict[deft]["Code"]:
+ code += codeline
+ self.ui.txtCode.setText(code)
+
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.setText("")
+
+ if "CATEGORY" in self.functiondict[deft]["MetaData"]:
+ self.ui.txtMetaCategory.setText(self.functiondict[deft]["MetaData"]["CATEGORY"])
+ if "KEYWORDS" in self.functiondict[deft]["MetaData"]:
+ self.ui.txtMetaKeywords.setText(self.functiondict[deft]["MetaData"]["KEYWORDS"])
+ if "CacheEnabled" in self.functiondict[deft]["MetaData"]:
+ self.ui.chkMetaCacheEnabled.setChecked(self.functiondict[deft]["MetaData"]["CacheEnabled"])
+
+ self.pindefs = self.functiondict[deft]["PinDefs"]
+
+ if deft in self.functiondict:
+ self.loadPinTable(deft)
+
+ except:
+ pass
+
+ if filefullpath.find("\\Nodes") != -1:
+ deft = it.text(col)
+ self.ui.txtNodeFileName.setText(deft)
+ self.selectedNodeDataName = deft.replace(".py", "")
+ #filefullpath = os.path.join(filefullpath, deft)
+ self.loadNodeProperties(filefullpath)
+ self.parseNodePins()
+ self.ui.twPackage.setCurrentIndex(3)
+
+ if filefullpath.find("\\Pins") != -1:
+ self.loadPinProperties(filefullpath)
+ print(self.pindict)
+
+ if filefullpath.find("\\SQL") != -1:
+ deft = it.text(col)
+ self.loadTableProperties(filefullpath)
+ self.ui.twPackage.setCurrentIndex(8)
+
+ def onOpenPackageFolder(self):
+
+ #print(Packages.__path__[0] + "\\")
+ os.startfile(Packages.__path__[0])
+ #subprocess.Popen(r'explorer' + Packages.__path__[0] + "\\")
+
+
+ def loadFunctionProperties(self, filefullpath, functionname):
+
+ previousitem = ""
+ implementdata = ""
+ readingimplementdata = -1
+ readingdefdata = 0
+ defdata = ""
+ codedata = []
+ eof = 0
+ defname = ""
+ code = ""
+ codedescription = ""
+ NestDict = {}
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ if lineitem.find("class") != -1:
+ self.intro = code
+ precode = code
+ code += lineitem
+ codedata.append(lineitem)
+ #print(lineitem)
+ if lineitem.find("super") != -1:
+ code = ""
+ codedata = []
+
+ if lineitem.find("@staticmethod") != -1 or index == filesize-1:
+ readingdefdata = 0
+ if defname == functionname:
+ if precode.find("@staticmethod") != -1:
+ NestDict = {}
+ implement2 = implementdata
+ NestDict["Implement"] = implement2.replace("@staticmethod", "")
+ NestDict["Definition"] = defdata
+ NestDict["CodeDescription"] = codedescription
+ NestDict["Code"] = codedata[:-1]
+ self.functiondict[functionname] = NestDict
+
+ self.parseFunctionFile(functionname)
+ break
+ else:
+ implementdata = ""
+
+ if lineitem.find("def ") != -1 and lineitem.find(" " + functionname) != -1:
+ defnamestart = 7
+ defnameend = lineitem.find("(")
+ defname = lineitem[defnamestart + 1:defnameend]
+ readingdefdata = 1
+
+ if readingdefdata == 1:
+ if lineitem.find("def ") != -1:
+ lineitem = lineitem[defnameend+1:]
+ readingimplementdata = 0
+ defdata += lineitem.strip()
+ if defdata[-1] == ":":
+ readingdefdata = 0
+ codedata = []
+
+ if (lineitem.find("@IMPLEMENT_NODE") != -1) or readingimplementdata == 1:
+ implementdata += lineitem.strip()
+ readingimplementdata = 1
+
+ '''if "\'\'\'" in lineitem or "\"\"\"" in lineitem and readingdefdata == 0:
+ codedescription += lineitem[8:]
+ else:
+ codedata.append(lineitem[8:])'''
+ except:
+ pass
+
+
+ def loadCodeData(self, filefullpath):
+ codenotes = 0
+ importlist = []
+ importstart = 0
+ classstart = 0
+ super = 0
+ staticmethod = []
+ definition = []
+ codedata = []
+
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ codedata.append(lineitem)
+
+ if lineitem.find("import") != -1:
+ importlist.append(index)
+
+ if lineitem.find("class") != -1:
+ classstart = index
+
+ if lineitem.find("def") != -1:
+ definition.append(index)
+
+ defCount = len(definition)
+ for count, defitem in enumerate(definition):
+ line = codedata[defitem]
+ if count == defCount-1:
+ endCodeBlock = len(codedata)
+ else:
+ endCodeBlock = definition[count+1]-1
+
+ if codedata[defitem - 1].find("@staticmethod") != -1:
+ staticmethod = True
+ else:
+ staticmethod = False
+
+ if codedata[defitem].find("__init__") != -1:
+ if codedata[defitem].find("super") != -1:
+ pass
+
+ if codedata[defitem].find("toolTip") != -1:
+ for row in range(defitem,endCodeBlock):
+ line2 = codedata[row]
+ if codedata[row].find("return") != -1:
+ tooltip = codedata[row][15:]
+ self.ui.txtCommandToolTip.setText(tooltip)
+
+ if codedata[defitem].find("getIcon") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ getIcon = codedata[row][15:]
+ self.ui.txtCommandgetIcon.setText(getIcon)
+
+ if codedata[defitem].find("name") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ name = codedata[row][15:]
+ self.ui.txtCommandName.setText(name)
+
+ if codedata[defitem].find("do") != -1:
+ code = ""
+ for codeline in codedata[defitem:endCodeBlock]:
+ code += codeline
+ self.ui.txtCommandCode.setText(code)
+
+ def parseFunctionFile(self, defname):
+ #https://nedbatchelder.com/text/python-parsers.html
+ #Maybe use Code Parser
+
+ ''' Function Dictionary Structure
+ ["Name"] - Function Name
+ ["Order"] - Order in Appearance
+ ["Meta"] - Meta Data
+ ["Inputs"] - Pin Input List
+ ["Variable"] - Input Pin Name
+ ["DataType"] - Input Pin Data Type
+ ["DefaultValue"] - Input Pin Default Value
+ ["PinSpecifiers"] - Input Pin Options
+ ["Outputs"]
+ ["Variable"] - Output Pin Name
+ ["DataType"] - Output Pin Data Type
+ ["DefaultValue"] - Output Pin Default Value
+ ["PinSpecifiers"] - Output Pin Options
+ '''
+
+ implementdata = self.functiondict[defname]["Implement"]
+
+ '''IMPLEMENT_NODE(func=None, returns=(DataType, DefaultValue, PinSpecficiers), meta={NodeMeta.CATEGORY: 'Default', NodeMeta.KEYWORDS: []}, nodeType=NodeTypes.Pure):'''
+ istyle = "kwarg"
+ ikeywords = ["func", "returns", "meta"]
+
+ '''PinSpecifiers = {PinSpecifiers.List: PinOptions.ArraySupported | PinOptions.AllowAny | PinOptions.DictElementSupported, PinSpecifiers.Value: "1"}'''
+ parseparameters = {"start":"{", "end": "}", "delimination": ","}
+ valuetype = {"\"": "Value", "|": "List"}
+
+ '''meta = {NodeMeta.CATEGORY: 'Utils', NodeMeta.KEYWORDS: ['id'], NodeMeta.CACHE_ENABLED: False}'''
+ parseparameters = {"start": "{", "end": "}", "delimination": ","}
+ valuetype = {"\"": "Value", "[]": "List"}
+
+ defdata = self.functiondict[defname]["Definition"]
+ style = "csv"
+ pos = ["DataType", "DefaultValue", "PinSpecifiers"]
+
+ codedata = self.functiondict[defname]["Code"]
+
+ implementdata2 = []
+ defdata2 = []
+
+ code = ""
+ for line, linedata in enumerate(codedata):
+ if "staticmethod" not in linedata:
+ if line == 0:
+ output = linedata.replace("\"", "").replace("\'","")[8:]
+ self.ui.txtCodeDescription.setText(output)
+ else:
+ code += linedata[8:]
+
+ self.functiondict[defname]["Code"] = codedata
+
+ #Extracting Information from Implement
+ itemdict = {}
+ itemdict["FunctionName"] = defname
+
+ idata = {}
+ pos = 1
+
+ while pos <= len(implementdata)-1:
+ if implementdata[pos] == "=":
+ pos2 = pos
+ while implementdata[pos2] != "(" and implementdata[pos2] != ",":
+ pos2 -= 1
+ variable = implementdata[pos2 + 1:pos].strip()
+ pos3 = pos + 1
+ while implementdata[pos3] != "=" and pos3 != len(implementdata) - 1:
+ pos3 += 1
+ if pos3 != len(implementdata) - 1:
+ pos4 = pos3
+ while implementdata[pos4] != ",":
+ pos4 -= 1
+ settings = implementdata[pos + 1:pos4].strip()
+ else:
+ settings = implementdata[pos + 1:len(implementdata) - 1].strip()
+ pos4 = len(implementdata) - 1
+ idata[variable] = settings.strip()
+ pos = pos4
+ pos += 1
+
+ bracketstart = implementdata.find("returns") + len("returns") + 2
+ bracketend = bracketstart
+ bracketcount = 1
+ while bracketend < len(implementdata) and bracketcount != 0:
+ if implementdata[bracketend:bracketend + 1] == "(":
+ bracketcount += 1
+ if implementdata[bracketend:bracketend + 1] == ")":
+ bracketcount -= 1
+ bracketend += 1
+ bracketstuff = implementdata[bracketstart:bracketend-1]
+ #implementdata = implementdata.replace(bracketstuff,"bracketstuff")
+
+ curlbracketstart = implementdata.find("meta") + len("meta") + 2
+ curlbracketend = curlbracketstart
+ bracketcount = 1
+ metadata = {}
+
+ if curlbracketstart != -1:
+ while curlbracketend < len(implementdata) and bracketcount != 0:
+ if implementdata[curlbracketend:curlbracketend + 1] == "{":
+ bracketcount += 1
+ if implementdata[curlbracketend:curlbracketend + 1] == "}":
+ bracketcount -= 1
+ curlbracketend += 1
+
+ metalist = implementdata[curlbracketstart:curlbracketend-1]
+ for y in metalist.split(","):
+ itemdata = y.strip().split(":")
+ if itemdata[0] == "NodeMeta.CATEGORY":
+ metadata["CATEGORY"] = itemdata[1].strip()
+ #self.ui.txtMetaCategory.setText(itemdata[1])
+ if itemdata[0] == "NodeMeta.KEYWORDS":
+ metadata["KEYWORDS"] = itemdata[1].strip()
+ #self.ui.txtMetaKeywords.setText(itemdata[1])
+ if itemdata[0] == "NodeMeta.CACHE_ENABLED":
+ metadata["CACHE_ENABLED"] = itemdata[1].strip()
+ #self.ui.chkMetaCacheEnabled.setChecked(itemdata[1])
+
+
+ self.functiondict[defname]["MetaData"] = metadata
+ implementdata2.append(implementdata[:curlbracketstart-6])
+ implementdata2.append(implementdata[curlbracketstart-6:curlbracketend - 1] + "})")
+
+ #Definition Item
+ defs = {}
+ pos = 1
+ while pos <= len(defdata)-1:
+ if defdata[pos] == "=":
+ pos2 = pos
+ while pos2 != -1 and defdata[pos2] != ",":
+ pos2 -= 1
+ variable = defdata[pos2 + 1:pos].strip()
+ pos3 = pos + 1
+ while defdata[pos3] != "=" and pos3 != len(defdata) - 1:
+ pos3 += 1
+ if pos3 != len(defdata) - 1:
+ pos4 = pos3
+ while defdata[pos4] != ",":
+ pos4 -= 1
+ settings = defdata[pos + 1:pos4].strip()
+ else:
+ settings = defdata[pos + 1:len(defdata) - 1].strip()
+ pos4 = len(defdata) - 1
+ defs[variable] = settings.strip()
+ pos = pos4
+ pos += 1
+
+ #Output Pin
+
+ outputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ rowcount = 0
+ pinOutCounter = 0
+ pindefs = {}
+ pindefs["Inputs"] = {}
+ pindefs["Outputs"] = {}
+ pindata = {}
+ data = idata["returns"]
+
+ if data != "None":
+ pinOutCounter += 1
+ curlbracketstart = data.find("{") + 1
+ curlbracketend = curlbracketstart
+ bracketcount = 1
+ if curlbracketstart != 0:
+ while curlbracketend < len(data) and bracketcount != 0:
+ if data[curlbracketend:curlbracketend + 1] == "{":
+ bracketcount += 1
+ if data[curlbracketend:curlbracketend + 1] == "}":
+ bracketcount -= 1
+ curlbracketend += 1
+
+ curlystuff3 = data[curlbracketstart:curlbracketend - 1]
+ #data = data.replace(curlystuff3, "curlystuff")
+ pindata = {}
+ for y in curlystuff3.split(","):
+ itemdata = y.strip().split(":")
+ if itemdata[0] == "PinSpecifiers.SUPPORTED_DATA_TYPES":
+ pindata["SUPPORTED_DATA_TYPES"] = itemdata[1][1:].strip()
+ if itemdata[0] == "PinSpecifiers.CONSTRAINT":
+ pindata["CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.STRUCT_CONSTRAINT":
+ pindata["STRUCT_CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.ENABLED_OPTIONS":
+ pindata["ENABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DISABLED_OPTIONS":
+ pindata["DISABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.INPUT_WIDGET_VARIANT":
+ pindata["INPUT_WIDGET_VARIANT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DESCRIPTION":
+ pindata["DESCRIPTION"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_LIST":
+ pindata["VALUE_LIST"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_RANGE":
+ pindata["VALUE_RANGE"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DRAGGER_STEPS":
+ pindata["DRAGGER_STEPS"] = itemdata[1].strip()
+
+ listdata = data.split(",")
+ pindata["Name"] = "out"
+ pindata["Direction"] = "Out"
+ pindata["Order"] = pinOutCounter
+ pindata["DataType"] = listdata[0][1:].strip().replace("\"", "").replace("\'", "")
+ pindata["DefaultValue"] = listdata[1].strip().replace("))","")
+ pindefs["Outputs"]["out"] = pindata
+
+ #InputPin
+
+ rowcount2 = 0
+
+ pindata = {}
+ curlystuff3 = None
+ PinCounter = 0
+
+ for variable, data in defs.items():
+ #print(variable, data)
+ PinCounter += 1
+ if data.find("REF") == -1:
+ curlbracketstart = data.find("{") + 1
+ curlbracketend = curlbracketstart
+ bracketcount = 1
+ if curlbracketstart != 0:
+ while curlbracketend < len(data) and bracketcount != 0:
+ if data[curlbracketend:curlbracketend + 1] == "{":
+ bracketcount += 1
+ if data[curlbracketend:curlbracketend + 1] == "}":
+ bracketcount -= 1
+ curlbracketend += 1
+
+ curlystuff3 = data[curlbracketstart:curlbracketend - 1]
+
+ if curlystuff3 != None: data = data.replace(curlystuff3, "curlystuff")
+ listdata = data.split(",")
+ pindata = {}
+ pindata["Name"] = variable
+ pindata["Direction"] = "In"
+ pindata["Order"] = PinCounter
+ pindata["DataType"] = listdata[0][1:].strip().replace("\"", "").replace("\'", "")
+ if len(listdata) >= 2:
+ pindata["DefaultValue"] = listdata[1].strip()
+
+ if curlystuff3 is not None:
+ for y in curlystuff3.split(","):
+ itemdata = y.strip().split(":")
+ if itemdata[0] == "PinSpecifiers.SUPPORTED_DATA_TYPES":
+ pindata["SUPPORTED_DATA_TYPES"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.CONSTRAINT":
+ pindata["CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.STRUCT_CONSTRAINT":
+ pindata["STRUCT_CONSTRAINT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.ENABLED_OPTIONS":
+ pindata["ENABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DISABLED_OPTIONS":
+ pindata["DISABLED_OPTIONS"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.INPUT_WIDGET_VARIANT":
+ pindata["INPUT_WIDGET_VARIANT"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DESCRIPTION":
+ pindata["DESCRIPTION"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_LIST":
+ pindata["VALUE_LIST"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.VALUE_RANGE":
+ pindata["VALUE_RANGE"] = itemdata[1].strip()
+ if itemdata[0] == "PinSpecifiers.DRAGGER_STEPS":
+ pindata["DRAGGER_STEPS"] = itemdata[1].strip()
+
+ pindefs["Inputs"][variable] = pindata
+
+ rowcount2 += 1
+ else:
+ pinOutCounter += 1
+ startvalue = data.find("\"")
+ if startvalue == -1:
+ startvalue = data.find("\'")
+
+ endvalue = data.find(")")
+ pindata = {}
+ pindata["Name"] = variable
+ pindata["Direction"] = "Out"
+ pindata["Order"] = pinOutCounter
+
+ listdata = data[startvalue:endvalue].split(",")
+ pindata["DataType"] = listdata[0].strip().replace("\"", "").replace("\'", "")
+
+ if len(listdata) >= 2: pindata["DefaultValue"] = listdata[1].strip()
+
+ pindefs["Outputs"][variable] = pindata
+
+ rowcount += 1
+
+ self.functiondict[defname]["PinDefs"] = pindefs
+
+ self.functiondict[defname]["Implement"] = implementdata2
+
+ for variable, data in defs.items():
+ defdata2.append(variable + "=" + data)
+
+ self.functiondict[defname]["Definition"] = defdata2
+
+ def loadPinTable(self, defname):
+ # InputPin
+ pindatatypemodel = QtGui.QStandardItemModel(0, 2)
+ for index, key in enumerate(self.pinDict):
+ pindatatypemodel.setItem(index, 0, QtGui.QStandardItem(str(index)))
+ pindatatypemodel.setItem(index, 1, QtGui.QStandardItem(key))
+
+ if "PinDefs" in self.functiondict[defname]:
+ inputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ inputPinList = []
+ if "Inputs" in self.functiondict[defname]["PinDefs"]:
+ for pindata in self.functiondict[defname]["PinDefs"]["Inputs"]:
+ row = int(self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["Order"]) - 1
+ inputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+ DataTypeValue = ""
+ if "DataType" in self.functiondict[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["DataType"]))
+ DataTypeValue = self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["DataType"]
+ inputPinList.append(DataTypeValue)
+ if "DefaultValue" in self.functiondict[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Inputs"][pindata]["DefaultValue"]))
+
+ inputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ outputPinList = []
+ if "Outputs" in self.functiondict[defname]["PinDefs"]:
+ outputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ for rowcount2, pindata in enumerate(self.functiondict[defname]["PinDefs"]["Outputs"]):
+ row = int(self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["Order"]) - 1
+ DataTypeValue = ""
+ if rowcount2 == 0:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem("out"))
+ else:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+
+ if "DataType" in self.functiondict[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["DataType"]))
+ DataTypeValue = self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["DataType"]
+ outputPinList.append(DataTypeValue)
+
+ if "DefaultValue" in self.functiondict[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(self.functiondict[defname]["PinDefs"]["Outputs"][pindata]["DefaultValue"]))
+
+ outputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ self.ui.tblFInputPins.setModel(inputpinlistmodel)
+ self.ui.tblFOutputPins.setModel(outputpinlistmodel)
+
+ if "Inputs" in self.functiondict[defname]["PinDefs"]:
+ for row, data in enumerate(self.functiondict[defname]["PinDefs"]["Inputs"]):
+ self.ui.tblFInputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+ c.setValue(self.functiondict[defname]["PinDefs"]["Inputs"][data]["DataType"], 1)
+ i = self.ui.tblFInputPins.model().index(row, 1)
+ #c.currentIndexChanged2[dict].connect(self.on_lstPinSettings_cmbTableChanged)
+ self.ui.tblFInputPins.setIndexWidget(i, c)
+
+ if "Outputs" in self.functiondict[defname]["PinDefs"]:
+ for row, data in enumerate(self.functiondict[defname]["PinDefs"]["Outputs"]):
+ self.ui.tblFOutputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+ c.setValue(self.functiondict[defname]["PinDefs"]["Outputs"][data]["DataType"], 1)
+ i = self.ui.tblFOutputPins.model().index(row, 1)
+ #c.currentIndexChanged2[dict].connect(self.on_lstTableSettings_cmbTableChanged)
+ self.ui.tblFOutputPins.setIndexWidget(i, c)
+
+ #self.ui.tblFInputPins.resizeColumnsToContents()
+ #self.ui.tblFOutputPins.resizeColumnsToContents()
+
+ #self.ui.tblFInputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+ #self.ui.tblFOutputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+
+ self.initializePinData()
+
+ '''for z in curlystuff2.split(","):
+ itemdata = z.strip().split(":")
+ #print(itemdata[0], itemdata[1].strip())'''
+
+ def on_lstPinSettings_cmbTableChanged(self, int):
+ print(int)
+
+ def initializeform(self):
+
+ self.ui.txtFunctionFileName.setText("")
+ self.ui.txtFunctionName.setText("")
+ #self.ui.tblFInputPins
+ #self.ui.tblFOutputPins
+ self.ui.txtFImplement.setText("")
+ self.ui.txtFDef.setText("")
+ self.ui.txtCodeDescription.setText("")
+ self.ui.txtCode.setText("")
+
+ self.ui.txtMetaCategory.setText("")
+ self.ui.txtMetaKeywords.setText("")
+ self.ui.chkMetaCacheEnabled.setChecked(False)
+
+ def blockPinSignals(self):
+ self.ui.chkPSSupportedDataTypes.blockSignals(True)
+ self.ui.chkPSSupportedDataTypes.blockSignals(True)
+ self.ui.txtPSSupportedDataTypes.blockSignals(True)
+ self.ui.chkPSConstraint.blockSignals(True)
+ self.ui.txtPSConstraint.blockSignals(True)
+ self.ui.chkPSStructConstraint.blockSignals(True)
+ self.ui.txtPSStructConstraint.blockSignals(True)
+ #self.ui.chkPSEnableOptions.blockSignals(True)
+ #self.ui.txtPSEnableOptions.blockSignals(True)
+ self.ui.chkPSDisableOptions.blockSignals(True)
+ self.ui.txtPSDisableOptions.blockSignals(True)
+ self.ui.chkPSInputWidget.blockSignals(True)
+ self.ui.txtPSInputWidget.blockSignals(True)
+ self.ui.chkPSDescription.blockSignals(True)
+ self.ui.txtPSDescription.blockSignals(True)
+ self.ui.chkPSValueList.blockSignals(True)
+ self.ui.txtPSValueList.blockSignals(True)
+ self.ui.chkPSValueRange.blockSignals(True)
+ self.ui.txtPSValueRange.blockSignals(True)
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.blockSignals(True)
+
+ self.ui.chkArraySupported.blockSignals(True)
+ self.ui.chkDictionarySupported.blockSignals(True)
+ self.ui.chkSupportOnlyArrays.blockSignals(True)
+ self.ui.chkAllowMultipleConnections.blockSignals(True)
+ self.ui.chkChangeTypeOnConnection.blockSignals(True)
+ self.ui.chkRenamingEnabled.blockSignals(True)
+ self.ui.chkDynamic.blockSignals(True)
+ self.ui.chkAlwaysPushDirty.blockSignals(True)
+ self.ui.chkStorable.blockSignals(True)
+ self.ui.chkAllowAny.blockSignals(True)
+ self.ui.chkDictionaryElementSupported.blockSignals(True)
+
+ def unblockPinSignals(self):
+ self.ui.chkPSSupportedDataTypes.blockSignals(False)
+ self.ui.txtPSSupportedDataTypes.blockSignals(False)
+ self.ui.chkPSConstraint.blockSignals(False)
+ self.ui.txtPSConstraint.blockSignals(False)
+ self.ui.chkPSStructConstraint.blockSignals(False)
+ self.ui.txtPSStructConstraint.blockSignals(False)
+ #self.ui.chkPSEnableOptions.blockSignals(False)
+ #self.ui.txtPSEnableOptions.blockSignals(False)
+ self.ui.chkPSDisableOptions.blockSignals(False)
+ self.ui.txtPSDisableOptions.blockSignals(False)
+ self.ui.chkPSInputWidget.blockSignals(False)
+ self.ui.txtPSInputWidget.blockSignals(False)
+ self.ui.chkPSDescription.blockSignals(False)
+ self.ui.txtPSDescription.blockSignals(False)
+ self.ui.chkPSValueList.blockSignals(False)
+ self.ui.txtPSValueList.blockSignals(False)
+ self.ui.chkPSValueRange.blockSignals(False)
+ self.ui.txtPSValueRange.blockSignals(False)
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.blockSignals(False)
+
+ self.ui.chkArraySupported.blockSignals(False)
+ self.ui.chkDictionarySupported.blockSignals(False)
+ self.ui.chkSupportOnlyArrays.blockSignals(False)
+ self.ui.chkAllowMultipleConnections.blockSignals(False)
+ self.ui.chkChangeTypeOnConnection.blockSignals(False)
+ self.ui.chkRenamingEnabled.blockSignals(False)
+ self.ui.chkDynamic.blockSignals(False)
+ self.ui.chkAlwaysPushDirty.blockSignals(False)
+ self.ui.chkStorable.blockSignals(False)
+ self.ui.chkAllowAny.blockSignals(False)
+ self.ui.chkDictionaryElementSupported.blockSignals(False)
+
+ def initializePinData(self):
+ self.blockPinSignals()
+ self.ui.chkPSSupportedDataTypes.setChecked(False)
+ self.ui.txtPSSupportedDataTypes.setText("")
+ self.ui.chkPSConstraint.setChecked(False)
+ self.ui.txtPSConstraint.setText("")
+ self.ui.chkPSStructConstraint.setChecked(False)
+ self.ui.txtPSStructConstraint.setText("")
+ #self.ui.chkPSEnableOptions.setChecked(False)
+ #self.ui.txtPSEnableOptions.setText("")
+ self.ui.chkPSDisableOptions.setChecked(False)
+ self.ui.txtPSDisableOptions.setText("")
+ self.ui.chkPSInputWidget.setChecked(False)
+ self.ui.txtPSInputWidget.setText("")
+ self.ui.chkPSDescription.setChecked(False)
+ self.ui.txtPSDescription.setText("")
+ self.ui.chkPSValueList.setChecked(False)
+ self.ui.txtPSValueList.setText("")
+ self.ui.chkPSValueRange.setChecked(False)
+ self.ui.txtPSValueRange.setText("")
+ self.ui.chkPSDraggerSteps.setChecked(False)
+ self.ui.txtPSDraggerSteps.setText("")
+
+ self.ui.chkArraySupported.setChecked(False)
+ self.ui.chkDictionarySupported.setChecked(False)
+ self.ui.chkSupportOnlyArrays.setChecked(False)
+ self.ui.chkAllowMultipleConnections.setChecked(False)
+ self.ui.chkChangeTypeOnConnection.setChecked(False)
+ self.ui.chkRenamingEnabled.setChecked(False)
+ self.ui.chkDynamic.setChecked(False)
+ self.ui.chkAlwaysPushDirty.setChecked(False)
+ self.ui.chkStorable.setChecked(False)
+ self.ui.chkAllowAny.setChecked(False)
+ self.ui.chkDictionaryElementSupported.setChecked(False)
+ self.unblockPinSignals()
+
+ def writepindata(self):
+ self.functiondict = {}
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSSupportedDataTypes_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["SUPPORTED_DATA_TYPES"] = self.ui.txtPSSupportedDataTypes.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSSupportedDataTypes_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["SUPPORTED_DATA_TYPES"] = self.ui.txtPSSupportedDataTypes.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSConstraint_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["CONSTRAINT"] = self.ui.txtPSConstraint.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSConstraint_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["CONSTRAINT"] = self.ui.txtPSConstraint.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSStructConstraint_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["STRUCT_CONSTRAINT"] = self.ui.txtPSStructConstraint.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSStructConstraint_stateChanged(self):
+ a = self.ui.txtPSStructConstraint.text()
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["STRUCT_CONSTRAINT"] = self.ui.txtPSStructConstraint.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSDisableOptions_stateChanged(self):
+ a = self.ui.chkPSDisableOptions.isChecked()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.setFunctionDirty()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSDisableOptions_textChanged(self):
+ b = self.ui.txtPSDisableOptions.text()
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSInputWidget_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["INPUT_WIDGET_VARIANT"] = self.ui.txtPSInputWidget.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSInputWidget_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["INPUT_WIDGET_VARIANT"] = self.ui.txtPSInputWidget.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSDescription_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DESCRIPTION"] = self.ui.txtPSDescription.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSDescription_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DESCRIPTION"] = self.ui.txtPSDescription.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSValueList_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_LIST"] = self.ui.txtPSValueList.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSValueList_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_LIST"] = self.ui.txtPSValueList.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSValueRange_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_RANGE"] = self.ui.txtPSValueRange.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSValueRange_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["VALUE_RANGE"] = self.ui.txtPSValueRange.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkPSDraggerSteps_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DRAGGER_STEPS"] = self.ui.txtPSDraggerSteps.text()
+
+ #@QtCore.pyqtSlot(str)
+ def on_txtPSDraggerSteps_textChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DRAGGER_STEPS"] = self.ui.txtPSDraggerSteps.text()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkArraySupported_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["ArraySupported"] = self.ui.chkArraySupported.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkDictionarySupported_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DictSupported"] = self.ui.chkDictionarySupported.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkSupportOnlyArrays_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["SupportsOnlyArrays"] = self.ui.chkSupportOnlyArrays.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkAllowMultipleConnections_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["AllowMultipleConnections"] = self.ui.chkAllowMultipleConnections.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkChangeTypeOnConnection_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["ChangeTypeOnConnection"] = self.ui.chkChangeTypeOnConnection.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkRenamingEnabled_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["RenamingEnabled"] = self.ui.chkRenamingEnabled.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkDynamic_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["Dynamic"] = self.ui.chkDynamic.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkAlwaysPushDirty_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["AlwaysPushDirty"] = self.ui.chkAlwaysPushDirty.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkStorable_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["Storable"] = self.ui.chkStorable.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkAllowAny_stateChanged(self, value):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["AllowAny"] = self.ui.chkAllowAny.isChecked()
+
+ #@QtCore.pyqtSlot(int)
+ def on_chkDictionaryElementSupported_stateChanged(self):
+ self.setFunctionDirty()
+ self.pinDictCheck(self.selectedPinDir, self.selectedPinName)
+ self.pindefs[self.selectedPinDir][self.selectedPinName]["DictElementSupported"] = self.ui.chkDictionaryElementSupported.isChecked()
+
+ def pinDictCheck(self, Direction, PinName):
+ if Direction not in self.pindefs:
+ self.pindefs[Direction] = {}
+
+ if PinName not in self.pindefs["Inputs"] and PinName not in self.pindefs["Outputs"]:
+ self.pindefs[Direction][PinName] = {}
+ return(True)
+ else:
+ return(False)
+
+ #@QtCore.pyqtSlot()
+ def on_cmdUpOrderFInputPin_clicked(self):
+ order = self.pindefs["Inputs"][self.selectedPinName]["Order"]
+
+ if order > 1:
+ for key, data in self.pindefs["Inputs"].items():
+ if data["Order"] == order - 1:
+ data["Order"] += 1
+ self.pindefs["Inputs"][self.selectedPinName]["Order"] -= 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdDownOrderFInputPin_clicked(self):
+ order = self.pindefs["Inputs"][self.selectedPinName]["Order"]
+
+ if order < len(self.pindefs["Inputs"]):
+ for key, data in self.pindefs["Inputs"].items():
+ if data["Order"] == order + 1:
+ data["Order"] -= 1
+ self.pindefs["Inputs"][self.selectedPinName]["Order"] += 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdAddFInputPin_clicked(self):
+ self.setFunctionDirty()
+ if self.ui.txtFunctionName.text() != "":
+
+ newPinName = "NewPinName"
+ pinnum = ""
+ counter = 0
+ while not self.pinDictCheck("Inputs", newPinName + pinnum):
+ counter += 1
+ pinnum = "_" + str(counter)
+
+ newPinName = newPinName + pinnum
+
+ self.pindefs["Inputs"][newPinName]["DataType"] = 'AnyPin'
+ self.pindefs["Inputs"][newPinName]["DefaultValue"] = 'None'
+ self.pindefs["Inputs"][newPinName]["Order"] = len(self.pindefs["Inputs"])
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdRemoveFInputPin_clicked(self):
+ self.setFunctionDirty()
+ order = self.pindefs["Inputs"][self.selectedPinName]["Order"]
+ self.pindefs["Inputs"].pop(self.selectedPinName)
+ for key, data in self.pindefs["Inputs"].items():
+ if data["Order"] > order:
+ data["Order"] -= 1
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdUpOrderFOutputPin_clicked(self):
+ order = self.pindefs["Outputs"][self.selectedPinName]["Order"]
+
+ if order > 1:
+ for key, data in self.pindefs["Outputs"].items():
+ if data["Order"] == order - 1:
+ data["Order"] += 1
+ self.pindefs["Outputs"][self.selectedPinName]["Order"] -= 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdDownOrderFOutputPin_clicked(self):
+ order = self.pindefs["Outputs"][self.selectedPinName]["Order"]
+
+ if order < len(self.pindefs["Outputs"]):
+ for key, data in self.pindefs["Outputs"].items():
+ if data["Order"] == order + 1:
+ data["Order"] -= 1
+ self.pindefs["Outputs"][self.selectedPinName]["Order"] += 1
+ break
+
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdAddFOutputPin_clicked(self):
+ self.setFunctionDirty()
+ if self.ui.txtFunctionName.text() != "":
+ newPinName = "NewPinName"
+ pinnum = ""
+ counter = 0
+ while not self.pinDictCheck("Outputs", newPinName + pinnum):
+ counter += 1
+ pinnum = "_" + str(counter)
+
+ newPinName = newPinName + pinnum
+
+ self.pinDictCheck("Outputs", "NewPinName")
+ self.pindefs["Outputs"][newPinName]["DataType"] = 'AnyPin'
+ self.pindefs["Outputs"][newPinName]["DefaultValue"] = 'None'
+ self.pindefs["Outputs"][newPinName]["Order"] = len(self.pindefs["Outputs"])
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+ #@QtCore.pyqtSlot()
+ def on_cmdRemoveFOutputPin_clicked(self):
+ self.setFunctionDirty()
+ order = self.pindefs["Outputs"][self.selectedPinName]["Order"]
+ self.pindefs["Outputs"].pop(self.selectedPinName)
+ for key, data in self.pindefs["Outputs"].items():
+ if data["Order"] > order:
+ data["Order"] -= 1
+ self.loadPinTable(self.ui.txtFunctionName.text())
+
+
+ def setFunctionDirty(self):
+ self.ui.cmdSaveFunction.setEnabled(True)
+ self.ui.cmdSaveFunction.setVisible(True)
+ self.ui.cmdSaveFunction.setStyleSheet("background-color: red")
+
+ #@QtCore.pyqtSlot()
+ def on_cmdSaveFunction_clicked(self):
+ self.ui.cmdSaveFunction.setEnabled(False)
+ self.ui.cmdSaveFunction.setVisible(False)
+ self.writeFunction()
+ self.writeFile()
+
+ #@QtCore.pyqtSlot()
+ def on_cmdCreateNewFunction_clicked(self):
+ functionName = self.ui.txtFunctionName.text()
+ if functionName not in self.functiondict:
+
+ self.setFunctionDirty()
+ self.functiondict[functionName] = {}
+
+ self.functiondict[functionName]["PinDefs"] = {}
+ self.functiondict[functionName]["Implement"] = {}
+ self.functiondict[functionName]["Definition"] = {}
+ self.functiondict[functionName]["MetaData"] = {}
+ self.functiondict[functionName]["Code"] = ["Pass"]
+
+ #self.initializeform
+ self.initializePinData
+
+ #self.loadAllFunctions()
+ #self.loadPinTable(deft)
+ else:
+ print("Function Name Taken")
+
+ def renamePin(self, mydict, old_key, new_key):
+ mydict[new_key] = mydict.pop(old_key)
+
+ def loadAllFunctions(self):
+ try:
+ f = open(self.workingFile, "r")
+ for lineitem in f:
+ if lineitem.find("def ") != -1:
+ if lineitem[8] != "_":
+ classnamestart = 7
+ classnameend = lineitem.find("(")
+ if self.workingFile.find(lineitem[classnamestart+1:classnameend]) == -1:
+ functionname = lineitem[classnamestart+1:classnameend]
+ self.functiondict[functionname] = {}
+ self.loadFunctionProperties(self.workingFile, functionname)
+ except:
+ pass
+
+ def writeFunction(self):
+
+ defname = self.ui.txtFunctionName.text()
+ implementdata = []
+ defindata = []
+ defoutdata = []
+
+ print("Pin Def", self.pindefs)
+ idata = "@IMPLEMENT_NODE(returns=("
+ # @IMPLEMENT_NODE(returns=('AnyPin', None, {
+ outCounter = 0
+ if "out" in self.pindefs["Outputs"]:
+ data = self.pindefs["Outputs"]["out"]
+ idata += "\'" + data["DataType"].strip() + "\'"
+ if data["DefaultValue"] is not None:
+ idata += ", " + data["DefaultValue"]
+ else:
+ idata += ", None"
+
+ pinspecifiers = ""
+ if "SUPPORTED_DATA_TYPES" in data:
+ pinspecifiers += "PinSpecifiers.SUPPORTED_DATA_TYPES: " + data["SUPPORTED_DATA_TYPES"] + ", "
+ if "CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.CONSTRAINT: " + data["CONSTRAINT"] + ", "
+ if "STRUCT_CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.STRUCT_CONSTRAINT: " + data["STRUCT_CONSTRAINT"] + ", "
+ if "ENABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.ENABLED_OPTIONS: " + data["ENABLED_OPTIONS"] + ", "
+ if "DISABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.DISABLED_OPTIONS: " + data["DISABLED_OPTIONS"] + ", "
+ if "INPUT_WIDGET_VARIANT" in data:
+ pinspecifiers += "PinSpecifiers.INPUT_WIDGET_VARIANT: " + data["INPUT_WIDGET_VARIANT"] + ", "
+ if "DESCRIPTION" in data:
+ pinspecifiers += "PinSpecifiers.DESCRIPTION: \'" + data["DESCRIPTION"] + "\', "
+ if "VALUE_LIST" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_LIST: " + data["VALUE_LIST"] + ", "
+ if "VALUE_RANGE" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_RANGE: " + data["VALUE_RANGE"] + ", "
+ if "DRAGGER_STEPS" in data:
+ pinspecifiers += "PinSpecifiers.DRAGGER_STEPS: " + data["DRAGGER_STEPS"] + ", "
+ if pinspecifiers != "":
+ idata += ", {" + pinspecifiers[:-2] + "})"
+
+ idata += ", "
+
+ implementdata.append(idata)
+ mdata = ""
+ if "CATEGORY" in self.functiondict[defname]["MetaData"]:
+ mdata += "NodeMeta.CATEGORY: " + self.functiondict[defname]["MetaData"]["CATEGORY"] + ", "
+ if "KEYWORDS" in self.functiondict[defname]["MetaData"]:
+ mdata += "NodeMeta.KEYWORDS: " + self.functiondict[defname]["MetaData"]["KEYWORDS"] + ", "
+ if "CACHE_ENABLED" in self.functiondict[defname]["MetaData"]:
+ mdata += "NodeMeta.CACHE_ENABLED: " + self.functiondict[defname]["MetaData"]["CACHE_ENABLED"] + ", "
+
+ implementdata.append("meta={" + mdata[:-2] + "})")
+
+ for pin, data in self.pindefs["Inputs"].items():
+
+ didata = pin + "=("
+ didata += "\'" + data["DataType"].strip() + "\'"
+ if data["DefaultValue"] is not None:
+ didata += ", " + data["DefaultValue"]
+ else:
+ didata += ", None"
+
+ pinspecifiers = ""
+ if "SUPPORTED_DATA_TYPES" in data:
+ pinspecifiers += "PinSpecifiers.SUPPORTED_DATA_TYPES: " + data["SUPPORTED_DATA_TYPES"] + ", "
+ if "CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.CONSTRAINT: " + data["CONSTRAINT"] + ", "
+ if "STRUCT_CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.STRUCT_CONSTRAINT: " + data["STRUCT_CONSTRAINT"] + ", "
+ if "ENABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.ENABLED_OPTIONS: " + data["ENABLED_OPTIONS"] + ", "
+ if "DISABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.DISABLED_OPTIONS: " + data["DISABLED_OPTIONS"] + ", "
+ if "INPUT_WIDGET_VARIANT" in data:
+ pinspecifiers += "PinSpecifiers.INPUT_WIDGET_VARIANT: " + data["INPUT_WIDGET_VARIANT"] + ", "
+ if "DESCRIPTION" in data:
+ pinspecifiers += "PinSpecifiers.DESCRIPTION: " + data["DESCRIPTION"] + ", "
+ if "VALUE_LIST" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_LIST: " + data["VALUE_LIST"] + ", "
+ if "VALUE_RANGE" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_RANGE: " + data["VALUE_RANGE"] + ", "
+ if "DRAGGER_STEPS" in data:
+ pinspecifiers += "PinSpecifiers.DRAGGER_STEPS: " + data["DRAGGER_STEPS"] + ", "
+ if pinspecifiers != "":
+ didata += ", {" + pinspecifiers[:-2] + "}"
+
+ didata += ")"
+ defindata.append(didata)
+
+ outCounter = -1
+ for pin, data in self.pindefs["Outputs"].items():
+ ddata = ""
+ outCounter += 1
+ if outCounter != 0:
+ ddata += pin + "=("
+ ddata += "REF, ("
+
+ ddata += "\'" + data["DataType"].strip() + "\'"
+ if data["DefaultValue"] is not None:
+ ddata += ", " + data["DefaultValue"]
+ else:
+ ddata += ", None"
+
+ pinspecifiers = ""
+ if "SUPPORTED_DATA_TYPES" in data:
+ pinspecifiers += "PinSpecifiers.SUPPORTED_DATA_TYPES: " + data["SUPPORTED_DATA_TYPES"] + ", "
+ if "CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.CONSTRAINT: " + data["CONSTRAINT"] + ", "
+ if "STRUCT_CONSTRAINT" in data:
+ pinspecifiers += "PinSpecifiers.STRUCT_CONSTRAINT: " + data["STRUCT_CONSTRAINT"] + ", "
+ if "ENABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.ENABLED_OPTIONS: " + data["ENABLED_OPTIONS"] + ", "
+ if "DISABLED_OPTIONS" in data:
+ pinspecifiers += "PinSpecifiers.DISABLED_OPTIONS: " + data["DISABLED_OPTIONS"] + ", "
+ if "INPUT_WIDGET_VARIANT" in data:
+ pinspecifiers += "PinSpecifiers.INPUT_WIDGET_VARIANT: " + data["INPUT_WIDGET_VARIANT"] + ", "
+ if "DESCRIPTION" in data:
+ pinspecifiers += "PinSpecifiers.DESCRIPTION: " + "\'" + data["DESCRIPTION"] + "\', "
+ if "VALUE_LIST" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_LIST: " + data["VALUE_LIST"] + ", "
+ if "VALUE_RANGE" in data:
+ pinspecifiers += "PinSpecifiers.VALUE_RANGE: " + data["VALUE_RANGE"] + ", "
+ if "DRAGGER_STEPS" in data:
+ pinspecifiers += "PinSpecifiers.DRAGGER_STEPS: " + data["DRAGGER_STEPS"] + ", "
+ if pinspecifiers != "":
+ ddata += ", {" + pinspecifiers[:-2] + "}"
+
+ if outCounter == len(self.pindefs["Outputs"]) - 1:
+ ddata = ddata + ")))"
+ else:
+ ddata += "))"
+
+ defoutdata.append(ddata)
+
+ self.functiondict[defname]["Implement"] = implementdata
+ self.functiondict[defname]["Definition"] = defindata + defoutdata
+
+ def writeFile(self):
+ # Write Code
+ filename = self.ui.txtFunctionFileName.text()
+ # Write File
+ '''wizardfile = Wizards.__path__[0] + "\\"
+ filefullpath = os.path.join(wizardfile, "FunctionHeader.txt")
+ codestart = ""
+
+ f = open(filefullpath, "r")
+ for lineitem in f:
+ codestart += lineitem'''
+
+ fname = filename
+ filefullpath = Packages.__path__[0] + "\\"
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ filefullpath = os.path.join(filefullpath, selectedpackage)
+ filefullpath = os.path.join(filefullpath, "FunctionLibraries")
+ filefullpath = os.path.join(filefullpath, fname)
+
+ Filename = filename.split(".")[0]
+ classline = "class %s(FunctionLibraryBase):\n" % (Filename)
+ classline += "#doc string\n\n"
+ classline += " def __init__(self, packageName):\n"
+ classline += " super(%s, self).__init__(packageName)\n" % (Filename)
+
+ with open(filefullpath, 'w') as f:
+ f.write(self.intro)
+ f.write(classline)
+ for variable, code in self.functiondict.items():
+ if code != {}:
+ f.write(" @staticmethod\n")
+ for iitems in code["Implement"]:
+
+ if "meta" in iitems:
+ if len(iitems["meta"]):
+ f.write(" " + iitems + "\n")
+ else:
+ f.write(" " + iitems + "\n")
+ if code["Definition"] == []:
+ f.write(" " + "def " + variable + "():\n")
+ else:
+ for row, ditems in enumerate(code["Definition"]):
+ if row == len(code["Definition"])-1:
+ defend = ":"
+ else:
+ defend = ", "
+
+ if row == 0:
+ f.write(" " + "def " + variable + "("+ ditems + defend + "\n")
+ else:
+ f.write(" " + ditems + defend + "\n")
+
+ #f.write(code["CodeDescription"])
+ for codeline in code["Code"]:
+ f.write(codeline)
+
+ print("Done")
+
+
+ def on_tblFInputPins_Changed(self, index):
+ print("changed")
+ row = self.ui.tblFInputPins.selectionModel().currentIndex().row()
+ column = self.ui.tblFInputPins.selectionModel().currentIndex().column()
+ value = self.ui.tblFInputPins.selectionModel().currentIndex().data(0)
+ print("IP On Change", row, column, value)
+
+ def on_tblFInputPins_clicked(self, index):
+ '''print(index)
+ print("Click", self.ui.tblFInputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.ui.tblFInputPins.model().data(index, QtCore.Qt.UserRole))'''
+ #self.ReferenceNumber_id = self.ui.tblFInputPins.model().index(index.row(), 0).data()
+
+ self.selectedPinDir = "Inputs"
+ self.selectedPinName = self.ui.tblFInputPins.model().index(index.row(), 0).data()
+
+ if self.selectedPinName in self.pindefs[self.selectedPinDir]:
+ self.selectedPinData = self.pindefs[self.selectedPinDir][self.selectedPinName]
+ self.loadPinData(self.selectedPinData)
+
+ def on_tblFInputPins_doubleclicked(self, index):
+ print("Double Click", self.ui.tblFInputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.ui.tblFInputPins.model().data(index, QtCore.Qt.UserRole))
+
+ def on_tblFOutputPins_Changed(self, index):
+ row = self.ui.tblFOutputPins.selectionModel().currentIndex().row()
+ column = self.ui.tblFOutputPins.selectionModel().currentIndex().column()
+ value = self.ui.tblFOutputPins.selectionModel().currentIndex().data(0)
+ print("IP On Change", row, column, value)
+
+ def on_tblFOutputPins_clicked(self, index):
+ print("Click", self.ui.tblFOutputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.ui.tblFOutputPins.model().data(index, QtCore.Qt.UserRole))
+ self.ReferenceNumber_id = self.ui.tblFOutputPins.model().index(index.row(), 0).data()
+ #self.loadProjectdata()
+
+ self.selectedPinDir = "Outputs"
+ self.selectedPinName = self.ui.tblFOutputPins.model().index(index.row(), 0).data()
+
+ if self.selectedPinName in self.pindefs[self.selectedPinDir]:
+ self.selectedPinData = self.pindefs[self.selectedPinDir][self.selectedPinName]
+ self.loadPinData(self.selectedPinData)
+
+ def on_tblFOutputPins_doubleclicked(self, index):
+ print("Double Click", self.tblFOutputPins.model().index(index.row(), 0).data())
+ print("Row %d and Column %d was clicked" % (index.row(), index.column()))
+ print(self.tblFOutputPins.model().data(index, QtCore.Qt.UserRole))
+
+ def loadPinData(self, data):
+ self.initializePinData()
+ self.blockPinSignals()
+ if "SUPPORTED_DATA_TYPES" in data:
+ self.ui.chkPSSupportedDataTypes.setChecked(True)
+ self.ui.txtPSSupportedDataTypes.setText(data["SUPPORTED_DATA_TYPES"])
+ if "CONSTRAINT" in data:
+ self.ui.chkPSConstraint.setChecked(True)
+ self.ui.txtPSConstraint.setText(data["CONSTRAINT"])
+ if "STRUCT_CONSTRAINT" in data:
+ self.ui.chkPSStructConstraint.setChecked(True)
+ self.ui.txtPSStructConstraint.setText(data["STRUCT_CONSTRAINT"])
+ if "ENABLED_OPTIONS" in data:
+ options = data["ENABLED_OPTIONS"].split("|")
+ for option in options:
+ if "ArraySupported" in option: #: Pin can hold array data structure
+ self.ui.chkArraySupported.setChecked(True)
+ if "DictSupported" in option: #: Pin can hold dict data structure
+ self.ui.chkDictionarySupported.setChecked(True)
+ if "SupportsOnlyArrays" in option: #: Pin will only support other pins with array data structure
+ self.ui.chkSupportOnlyArrays.setChecked(True)
+ if "AllowMultipleConnections" in option: #: This enables pin to allow more that one input connection. See :func:`~PyFlow.Core.Common.connectPins`
+ self.ui.chkAllowMultipleConnections.setChecked(True)
+ if "ChangeTypeOnConnection" in option: #: Used by :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` to determine if it can change its data type on new connection.
+ self.ui.chkChangeTypeOnConnection.setChecked(True)
+ if "RenamingEnabled" in option: #: Determines if pin can be renamed
+ self.ui.chkRenamingEnabled.setChecked(True)
+ if "Dynamic" in option: #: Specifies if pin was created dynamically (during program runtime)
+ self.ui.chkDynamic.setChecked(True)
+ if "AlwaysPushDirty" in option: #: Pin will always be seen as dirty (computation needed)
+ self.ui.chkAlwaysPushDirty.setChecked(True)
+ if "Storable" in option: #: Determines if pin data can be stored when pin serialized
+ self.ui.chkStorable.setChecked(True)
+ if "AllowAny" in option: #: Special flag that allow a pin to be :class:`~PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin`, which means non typed without been marked as error. By default a :py:class:`PyFlow.Packages.PyFlowBase.Pins.AnyPin.AnyPin` need to be initialized with some data type, other defined pin. This flag overrides that. Used in lists and non typed nodes
+ self.ui.chkAllowAny.setChecked(True)
+ if "DictElementSupported" in option: #: Dicts are constructed with :class:`DictElement` objects. So dict pins will only allow other dicts until this flag enabled. Used in :class:`~PyFlow.Packages.PyFlowBase.Nodes.makeDict` node
+ self.ui.chkDictionaryElementSupported.setChecked(True)
+
+ if "DISABLED_OPTIONS" in data:
+ self.ui.chkPSDisableOptions.setChecked(True)
+ self.ui.txtPSDisableOptions.setText(data["DISABLED_OPTIONS"])
+ if "INPUT_WIDGET_VARIANT" in data:
+ self.ui.chkPSInputWidget.setChecked(True)
+ self.ui.txtPSInputWidget.setText(data["INPUT_WIDGET_VARIANT"])
+ if "DESCRIPTION" in data:
+ self.ui.chkPSDescription.setChecked(True)
+ self.ui.txtPSDescription.setText(data["DESCRIPTION"])
+ if "VALUE_LIST" in data:
+ self.ui.chkPSValueList.setChecked(True)
+ self.ui.txtPSValueList.setText(data["VALUE_LIST"])
+ if "VALUE_RANGE" in data:
+ self.ui.chkPSValueRange.setChecked(True)
+ self.ui.txtPSValueRange.setText(data["VALUE_RANGE"])
+ if "DRAGGER_STEPS" in data:
+ self.ui.chkPSDraggerSteps.setChecked(True)
+ self.ui.txtPSDraggerSteps.setText(data["DRAGGER_STEPS"])
+ self.unblockPinSignals()
+
+
+ def loadNodeProperties(self, filefullpath):
+ preamble = ""
+ precode = ""
+ readingimplementdata = -1
+ readingdefdata = 0
+ defdata = ""
+ codedata = []
+ eof = 0
+ defname = ""
+ code = ""
+ self.selectedNodeData = {}
+ self.selectedNodeData[self.selectedNodeDataName] = {}
+
+ addDecorator = ""
+
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ # Reading the parts of the code (Implement, Def, Code)
+ if lineitem.find("def ") != -1 or index == filesize:
+ if lineitem.find("_init_") != -1:
+ defname = "init"
+
+ else:
+ defnamestart = 7
+ defnameend = lineitem.find("(")
+
+ if "@" in codedata[-1]:
+ addDecorator = codedata[-1].replace(" ", "")
+ codedata.pop()
+ self.selectedNodeData[self.selectedNodeDataName][defname] = codedata
+ #Starts the Next Group of code
+ codedata = [addDecorator]
+ else:
+ addDecorator = ""
+ self.selectedNodeData[self.selectedNodeDataName][defname] = codedata
+ codedata = []
+
+ defname = lineitem[defnamestart + 1:defnameend]
+
+ if lineitem.find("class ") != -1:
+ preamble = precode
+ base = ""
+ codedata = []
+ else:
+ codedata.append(lineitem.replace(" ","").replace("\n",""))
+ except:
+ pass
+
+
+ def loadTableProperties(self, filefullpath):
+ className = None
+ tableName = None
+ baseName = None
+
+ fieldName = None
+ fieldDataType = None
+ fieldSize = None
+ fieldOptions = None
+
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ if lineitem.find("__tablename__") != -1:
+ print(lineitem)
+ if lineitem.find("class ") != -1:
+ print(lineitem)
+ codedata = []
+ if className:
+ pass
+ else:
+ codedata.append(lineitem.replace(" ","").replace("\n",""))
+
+ if className:
+ fieldName = None
+ fieldDataType = None
+ fieldSize = None
+ fieldOptions = None
+ except:
+ pass
+
+ def parseNodePins(self):
+ defname = self.selectedNodeDataName
+ self.selectedNodeData[defname]["PinDefs"] = {}
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"] = {}
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"] = {}
+ inputPinOrder = 0
+ outputPinOrder = 0
+ try:
+ for item in self.selectedNodeData[defname]["init"]:
+ if "=" in item:
+ phrase = item.split("=")
+ pinName = phrase[0].replace("self.", "")
+
+ pinOptionsStart = phrase[1].find("(")
+ pinOptionEnd = phrase[1].find(")")
+ if pinOptionsStart != -1:
+ pinOptions = phrase[1][pinOptionsStart:pinOptionEnd]
+
+ '''def createInputPin(self, pinName, dataType, defaultValue=None, callback=None,
+ structure=StructureType.Single, constraint=None, structConstraint=None, supportedPinDataTypes=[], group=""):'''
+
+ '''createOutputPin(self, pinName, dataType, defaultValue=None, structure=StructureType.Single, constraint=None,
+ structConstraint=None, supportedPinDataTypes=[], group=""):'''
+ pinOptionList = pinOptions.split(",")
+
+ pinData = {}
+
+ pinName = pinOptionList[0].replace("\"","").replace("\'","").replace("(","").replace(")","").replace(" ","").strip()
+ pinData["DataType"] = pinOptionList[1][2:-1].replace("\"","").replace("\'","").strip()
+
+ for row, options in enumerate(pinOptionList):
+ if row > 2:
+ if "=" not in options:
+ if row == 2:
+ pinData["DefaultValue"] = options.replace("\"","").replace("\'","").strip()
+ if row == 3:
+ pinData["callback"] = options.replace("\"","").replace("\'","").strip()
+ else:
+ moreoptions = options.split("=")
+ pinData[moreoptions[0]] = moreoptions[0]
+ else:
+ if "headerColor" in phrase[0]:
+ self.selectedNodeData[defname]["HeaderColor"] = phrase[1].strip()
+ self.ui.txtNodeHeaderColor.setText(self.selectedNodeData[defname]["HeaderColor"])
+
+ if 'createOutputPin' in phrase[1]:
+ outputPinOrder += 1
+ self.selectedNodeData[defname]
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pinName] = {}
+ pinData["Order"] = outputPinOrder
+
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pinName] = pinData
+ if 'createInputPin' in phrase[1]:
+ inputPinOrder += 1
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pinName] = {}
+ pinData["Order"] = inputPinOrder
+
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pinName] = pinData
+
+ except:
+ print(self.selectedNodeDataName, "Fail")
+
+ print(self.selectedNodeData[defname])
+ value = ""
+ if "description" in self.selectedNodeData[defname]:
+ for items in self.selectedNodeData[defname]["description"]:
+ if "return" in items or len(value):
+ value += items.replace("return","").replace("\'","").strip()
+
+ self.ui.txtNodeDescription.setText(value)
+
+ if "category" in self.selectedNodeData[defname]:
+ for items in self.selectedNodeData[defname]["category"]:
+ if "return" in items:
+ value = items.replace("return","").replace("\'","").strip()
+ self.ui.txtNodeCategory.setText(value)
+
+ if "keywords" in self.selectedNodeData[defname]:
+ for items in self.selectedNodeData[defname]["keywords"]:
+ if "return" in items:
+ value = items.replace("return","").replace("\'","").replace("[","").replace("]","").strip()
+ self.ui.txtNodeKeyWords.setText(value)
+
+ self.loadNodePinTable()
+
+ def loadNodePinTable(self):
+ # InputPin
+ defname = self.selectedNodeDataName
+ pindatatypemodel = QtGui.QStandardItemModel(0, 2)
+
+ for index, key in enumerate(self.pinDict):
+ pindatatypemodel.setItem(index, 0, QtGui.QStandardItem(str(index)))
+ pindatatypemodel.setItem(index, 1, QtGui.QStandardItem(key))
+ pindatatypemodel.setItem(index+1, 0, QtGui.QStandardItem(str(index+1)))
+ pindatatypemodel.setItem(index+1, 1, QtGui.QStandardItem('ExecPin'))
+
+ if "PinDefs" in self.selectedNodeData[defname]:
+ inputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ inputPinList = []
+ if "Inputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for rowcount1, pindata in enumerate(self.selectedNodeData[defname]["PinDefs"]["Inputs"]):
+ row = int(self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["Order"]) - 1
+ inputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+ DataTypeValue = ""
+ if "DataType" in self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["DataType"]))
+ DataTypeValue = self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["DataType"]
+ inputPinList.append(DataTypeValue)
+ if "DefaultValue" in self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]:
+ inputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Inputs"][pindata]["DefaultValue"]))
+
+ inputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ inputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ outputPinList = []
+ outputpinlistmodel = QtGui.QStandardItemModel(0, 2)
+ if "Outputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for rowcount2, pindata in enumerate(self.selectedNodeData[defname]["PinDefs"]["Outputs"]):
+ row = int(self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["Order"]) - 1
+ DataTypeValue = ""
+ if rowcount2 == 0:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem("out"))
+ else:
+ outputpinlistmodel.setItem(row, 0, QtGui.QStandardItem(pindata))
+
+ if "DataType" in self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 1, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["DataType"]))
+ DataTypeValue = self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["DataType"]
+ outputPinList.append(DataTypeValue)
+
+ if "DefaultValue" in self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]:
+ outputpinlistmodel.setItem(row, 2, QtGui.QStandardItem(
+ self.selectedNodeData[defname]["PinDefs"]["Outputs"][pindata]["DefaultValue"]))
+
+ outputpinlistmodel.setHeaderData(0, QtCore.Qt.Horizontal, 'Name', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(1, QtCore.Qt.Horizontal, 'Data Type', role=QtCore.Qt.DisplayRole)
+ outputpinlistmodel.setHeaderData(2, QtCore.Qt.Horizontal, 'Default Value', role=QtCore.Qt.DisplayRole)
+
+ self.ui.tblNInputPins.setModel(inputpinlistmodel)
+ self.ui.tblNOutputPins.setModel(outputpinlistmodel)
+
+ if "Inputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for row, data in enumerate(self.selectedNodeData[defname]["PinDefs"]["Inputs"]):
+ self.ui.tblNInputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+
+ c.setValue(self.selectedNodeData[defname]["PinDefs"]["Inputs"][data]["DataType"], 1)
+ i = self.ui.tblNInputPins.model().index(row, 1)
+ # c.currentIndexChanged2[dict].connect(self.on_lstPinSettings_cmbTableChanged)
+ self.ui.tblNInputPins.setIndexWidget(i, c)
+
+ if "Outputs" in self.selectedNodeData[defname]["PinDefs"]:
+ for row, data in enumerate(self.selectedNodeData[defname]["PinDefs"]["Outputs"]):
+ self.ui.tblNOutputPins.openPersistentEditor(pindatatypemodel.index(row, 1))
+ c = TableComboModel(self, dataModel=pindatatypemodel, id=row, row=row, column=1)
+ c.setValue(self.selectedNodeData[defname]["PinDefs"]["Outputs"][data]["DataType"], 1)
+ i = self.ui.tblNOutputPins.model().index(row, 1)
+ # c.currentIndexChanged2[dict].connect(self.on_lstTableSettings_cmbTableChanged)
+ self.ui.tblNOutputPins.setIndexWidget(i, c)
+
+ # self.ui.tblNInputPins.resizeColumnsToContents()
+ # self.ui.tblNOutputPins.resizeColumnsToContents()
+
+ # self.ui.tblNInputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+ # self.ui.tblNOutputPins.setItemDelegateForColumn(1, ComboDelegate(self, inputpinlistmodel))
+
+ #self.initializePinData()
+
+ '''for z in curlystuff2.split(","):
+ itemdata = z.strip().split(":")
+ #print(itemdata[0], itemdata[1].strip())'''
+
+
+ def loadPinProperties(self, filefullpath):
+
+ previousitem = ""
+ implementdata = ""
+ readingimplementdata = -1
+ readingdefdata = 0
+ defdata = ""
+ codedata = []
+ eof = 0
+ defname = ""
+ code = ""
+ codedescription = ""
+ NestDict = {}
+ try:
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ if lineitem.find("class") != -1:
+ self.intro = code
+ precode = code
+ code += lineitem
+ codedata.append(lineitem)
+ #print(lineitem)
+ if lineitem.find("super") != -1:
+ code = ""
+ codedata = []
+
+ if lineitem.find("@staticmethod") != -1 or index == filesize-1:
+ readingdefdata = 0
+ if precode.find("@staticmethod") != -1:
+ NestDict = {}
+ implement2 = implementdata
+ NestDict["Implement"] = implement2.replace("@staticmethod", "")
+ NestDict["Definition"] = defdata
+ NestDict["CodeDescription"] = codedescription
+ NestDict["Code"] = codedata[:-1]
+ self.pindict[defname] = NestDict
+
+ #self.parseFunctionFile(defname)
+ break
+ else:
+ implementdata = ""
+
+ if lineitem.find("def ") != -1 and lineitem.find(" " + defname) != -1:
+ defnamestart = 7
+ defnameend = lineitem.find("(")
+ defname = lineitem[defnamestart + 1:defnameend]
+ readingdefdata = 1
+
+ if readingdefdata == 1:
+ if lineitem.find("def ") != -1:
+ lineitem = lineitem[defnameend+1:]
+ readingimplementdata = 0
+ defdata += lineitem.strip()
+ if defdata[-1] == ":":
+ readingdefdata = 0
+ codedata = []
+ except:
+ pass
+
+
+ def loadPinData(self, filefullpath):
+ codenotes = 0
+ importlist = []
+ importstart = 0
+ classstart = 0
+ super = 0
+ staticmethod = []
+ definition = []
+ codedata = []
+
+ filesize = len(open(filefullpath).readlines())
+ f = open(filefullpath, "r")
+
+ for index, lineitem in enumerate(f):
+ #Reading the parts of the code (Implement, Def, Code)
+ codedata.append(lineitem)
+
+ if lineitem.find("import") != -1:
+ importlist.append(index)
+
+ if lineitem.find("class") != -1:
+ classstart = index
+
+ if lineitem.find("def") != -1:
+ definition.append(index)
+
+ defCount = len(definition)
+ for count, defitem in enumerate(definition):
+ line = codedata[defitem]
+ if count == defCount-1:
+ endCodeBlock = len(codedata)
+ else:
+ endCodeBlock = definition[count+1]-1
+
+ if codedata[defitem - 1].find("@staticmethod") != -1:
+ staticmethod = True
+ else:
+ staticmethod = False
+
+ if codedata[defitem].find("__init__") != -1:
+ if codedata[defitem].find("super") != -1:
+ pass
+
+ if codedata[defitem].find("toolTip") != -1:
+ for row in range(defitem,endCodeBlock):
+ line2 = codedata[row]
+ if codedata[row].find("return") != -1:
+ tooltip = codedata[row][15:]
+ self.ui.txtCommandToolTip.setText(tooltip)
+
+ if codedata[defitem].find("getIcon") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ getIcon = codedata[row][15:]
+ self.ui.txtCommandgetIcon.setText(getIcon)
+
+ if codedata[defitem].find("name") != -1:
+ for row in range(defitem, endCodeBlock):
+ if codedata[row].find("return") != -1:
+ name = codedata[row][15:]
+ self.ui.txtCommandName.setText(name)
+
+ if codedata[defitem].find("do") != -1:
+ code = ""
+ for codeline in codedata[defitem:endCodeBlock]:
+ code += codeline
+ self.ui.txtCommandCode.setText(code)
+
+ #@QtCore.pyqtSlot()
+ def on_cmdUpdateInit_clicked(self):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filefullpath = os.path.join(packagepath, "__init__.py")
+
+ fileList = []
+ for file in os.listdir(packagepath):
+ fileList.append(file)
+
+ with open(filefullpath, 'w') as f:
+ self.addIntro(f)
+
+ self.defineFiles(f, "Pins")
+ self.defineFiles(f, "FunctionLibraries")
+ self.defineFiles(f, "Nodes")
+ self.defineFiles(f, "Tools")
+ self.defineFiles(f, "Exporters")
+
+
+ self.defineFiles2(f, "def ", "Factories")
+ self.defineFiles2(f, "class ", "PrefsWidgets")
+
+ self.defineFoo(f, "FunctionLibraries", "_FOO_LIBS")
+ self.defineDict(f, "Nodes", "_NODES")
+ self.defineDict(f, "Pins", "_PINS")
+
+ self.defineOrderedDict(f, "Tools", "_TOOLS")
+ self.defineOrderedDict(f, "Exporters", "_EXPORTERS")
+ self.defineOrderedDict2(f, "PrefsWidgets", "_PREFS_WIDGETS")
+
+ self.createBasePackage(f)
+
+ def loadInit(self, f):
+ '''["Class Imports", "Pins", "Functions", "Nodes", "Factories", "Prefs widgets",
+ "Foo Dict", "Node Dict", "Pin Dict", "Toolbar Dict", "Export", "Preferred Widgets",
+ "Base Package"]'''
+
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ #selectedpackage = self.ui.lstPackages.model().index(self.ui.listPackages.row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ initfile = "__init__.py"
+ filename = os.path.join(packagepath, initfile)
+
+ category = "Introduction"
+ initDict = {}
+ initDict[category] = []
+ try:
+ f = open(filename, "r")
+
+ for index, lineitem in enumerate(f):
+ if "# [" in lineitem:
+ start = lineitem.find("[")
+ stop = lineitem.find("]")
+ category = lineitem[start+1:stop]
+ initDict[category] = [lineitem.replace("\n", "")]
+ else:
+ initDict[category].append(lineitem.replace("\n", ""))
+ except:
+ pass
+
+ for row, item in enumerate(initDict):
+ print(item)
+ for line in initDict[item]:
+ print(" ", line)
+
+ def addIntro(self,f):
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+
+ f.write("\'\'\'%s\n" % (selectedpackage))
+ f.write("\'\'\'\n")
+ f.write("PACKAGE_NAME = \'%s\'\n" % (selectedpackage))
+
+ f.write("# [Class Imports]\n")
+ f.write("from collections import OrderedDict\n")
+ f.write("from PyFlow.UI.UIInterfaces import IPackage\n")
+
+ f.write("\n")
+
+ def defineFiles(self, f, foldername):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ #f.write("%s = {" % (category))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ #from PyFlow.Packages.PyFlowBase.Pins.AnyPin import AnyPin
+ f.write("from PyFlow.Packages.%s.%s.%s import %s\n"% (selectedpackage, foldername, file2, file2))
+ f.write("\n")
+
+ def defineFiles2(self, f, search, foldername):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+
+ f2 = open(os.path.join(filepath, file), "r")
+ for index, lineitem in enumerate(f2):
+
+ if lineitem[:len(search)] == search:
+ classnamestart = len(search)
+ classnameend = lineitem.find("(")
+ if self.workingFile.find(lineitem[classnamestart + 1:classnameend]) == -1:
+ functionname = lineitem[classnamestart :classnameend]
+ f.write("from PyFlow.Packages.%s.%s.%s import %s\n" % (selectedpackage, foldername, file2, functionname))
+ break
+ #from PyFlow.Packages.PyFlowBase.Pins.AnyPin import AnyPin
+
+ f.write("\n")
+
+ def defineFoo(self, f, foldername, category):
+ "_FOO_LIBS, _NODES, _PINS"
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = {\n" % (category))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f.write(" %s.__name__: %s(PACKAGE_NAME),\n"% (file2, file2))
+ f.write("}\n")
+ f.write("\n")
+
+ def defineDict(self, f, foldername, category):
+ "_FOO_LIBS, _NODES, _PINS"
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = {\n" % (category))
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f.write(" %s.__name__: %s,\n"% (file2, file2))
+ f.write("}\n")
+ f.write("\n")
+
+ def defineOrderedDict(self, f, foldername, category):
+ "_TOOLS, _EXPORTERS, _PREFS_WIDGETS"
+ '''_TOOLS = OrderedDict()
+ _TOOLS[CompileTool.__name__] = CompileTool'''
+
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = OrderedDict()\n" % category)
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f.write("%s[%s.__name__] = %s\n"% (category, file2, file2))
+ f.write("\n")
+
+ def defineOrderedDict2(self, f, foldername, category):
+ packageRoot = Packages.__path__[0]
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+ packagepath = os.path.join(packageRoot, selectedpackage)
+ filepath = os.path.join(packagepath, foldername)
+ search = "class "
+ f.write("# [%s]\n" % (foldername))
+ f.write("%s = OrderedDict()\n" % category)
+ for file in os.listdir(filepath):
+ if file[1] != "_":
+ file2 = file.replace(".py","")
+ f2 = open(os.path.join(filepath, file), "r")
+ for index, lineitem in enumerate(f2):
+
+ if lineitem[:len(search)] == search:
+ classnamestart = len(search)
+ classnameend = lineitem.find("(")
+ if self.workingFile.find(lineitem[classnamestart + 1:classnameend]) == -1:
+ functionname = lineitem[classnamestart:classnameend]
+
+ f.write("%s[\"%s\"] = %s\n"% (category, file2, functionname))
+ break
+ f.write("\n")
+
+ def createBasePackage(self, f):
+ selectedpackage = self.ui.lstPackages.model().index(self.ui.lstPackages.currentIndex().row(), 0).data()
+
+ f.write("# [%s Package]\n" % (selectedpackage))
+ f.write("class %s(IPackage):\n" % (selectedpackage))
+ f.write(" \"\"\"%s package\n"% (selectedpackage))
+ f.write(" \"\"\"\n\n")
+
+ f.write(" def __init__(self):\n")
+ f.write(" super(%s, self).__init__()\n\n" % (selectedpackage))
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetExporters():\n")
+ f.write(" return _EXPORTERS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetFunctionLibraries():\n")
+ f.write(" return _FOO_LIBS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetNodeClasses():\n")
+ f.write(" return _NODES\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetPinClasses():\n")
+ f.write(" return _PINS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def GetToolClasses():\n")
+ f.write(" return _TOOLS\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def UIPinsFactory():\n")
+ f.write(" return createUIPin\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def UINodesFactory():\n")
+ f.write(" return createUINode\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def PinsInputWidgetFactory():\n")
+ f.write(" return getInputWidget\n\n")
+
+ f.write(" @staticmethod\n")
+ f.write(" def PrefsWidgets():\n")
+ f.write(" return _PREFS_WIDGETS")
+
+ def onDone(self):
+ # if we are here, everything is correct
+ self.accept()
+
+ @staticmethod
+ def run():
+ instance = PackageBuilder(None, None)
+ #instance.show()
+ instance.exec()
+
+if __name__ == "__main__":
+ import sys
+ app = QApplication(sys.argv)
+
+ w = PackageBuilder()
+ w.show()
+
+ sys.exit(app.exec_())
\ No newline at end of file
diff --git a/PyFlow/Wizards/PackageWizard.py b/PyFlow/Wizards/PackageWizard.py
index 17f82c46d..780e9e0c0 100644
--- a/PyFlow/Wizards/PackageWizard.py
+++ b/PyFlow/Wizards/PackageWizard.py
@@ -16,9 +16,9 @@
import os
import shutil
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import *
from PyFlow import Wizards
from PyFlow.Wizards.WizardDialogueBase import WizardDialogueBase
@@ -28,32 +28,47 @@
class PackageWizard(WizardDialogueBase):
"""docstring for PackageWizard."""
+
def __init__(self, parent=None):
super(PackageWizard, self).__init__(parent)
def addFinalPage(self):
- self.addPageWidget(QWidget(), "Everything is ready! Click done to generate the package!" +
- "\n\n**Note**: When job will be done, you will need to restart the editor in order to see your new stuff!" +
- "\nGood luck!")
+ self.addPageWidget(
+ QWidget(),
+ "Everything is ready! Click done to generate the package!"
+ + "\n\n**Note**: When job will be done, you will need to restart the editor in order to see your new stuff!"
+ + "\nGood luck!",
+ )
def onDone(self):
# if we are here, everything is correct
packageName = self.lePkgName.text()
- includeUINodeFactory = self.cbIncludeUINodeFactory.checkState() == QtCore.Qt.Checked and self.cbIncludeUINodeFactory.isEnabled()
- IncludeUIPinFactory = self.cbUIPinFactory.checkState() == QtCore.Qt.Checked and self.cbUIPinFactory.isEnabled()
- IncludePinInputWidgetFactory = self.cbPinInputWidgetFactory.checkState() == QtCore.Qt.Checked and self.cbPinInputWidgetFactory.isEnabled()
+ includeUINodeFactory = (
+ self.cbIncludeUINodeFactory.checkState() == QtCore.Qt.Checked
+ and self.cbIncludeUINodeFactory.isEnabled()
+ )
+ IncludeUIPinFactory = (
+ self.cbUIPinFactory.checkState() == QtCore.Qt.Checked
+ and self.cbUIPinFactory.isEnabled()
+ )
+ IncludePinInputWidgetFactory = (
+ self.cbPinInputWidgetFactory.checkState() == QtCore.Qt.Checked
+ and self.cbPinInputWidgetFactory.isEnabled()
+ )
IncludePrefsWidget = self.cbPrefsWidget.checkState() == QtCore.Qt.Checked
- generatePackage(packageName,
- self.pbOutPathSelect.text(),
- bIncludeClassNode=self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked,
- bIncludeFooLib=self.cbIncludeFooLib.checkState() == QtCore.Qt.Checked,
- bIncludeUINodeFactory=includeUINodeFactory,
- bIncludePin=self.cbIncludePin.checkState() == QtCore.Qt.Checked,
- bIncludeUIPinFactory=IncludeUIPinFactory,
- bIncludeTool=self.cbIncludeTool.checkState() == QtCore.Qt.Checked,
- bIncludeExporter=self.cbIncludeExporter.checkState() == QtCore.Qt.Checked,
- bIncludePinInputWidgetFactory=IncludePinInputWidgetFactory,
- bIncludePrefsWindget=IncludePrefsWidget)
+ generatePackage(
+ packageName,
+ self.pbOutPathSelect.text(),
+ bIncludeClassNode=self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked,
+ bIncludeFooLib=self.cbIncludeFooLib.checkState() == QtCore.Qt.Checked,
+ bIncludeUINodeFactory=includeUINodeFactory,
+ bIncludePin=self.cbIncludePin.checkState() == QtCore.Qt.Checked,
+ bIncludeUIPinFactory=IncludeUIPinFactory,
+ bIncludeTool=self.cbIncludeTool.checkState() == QtCore.Qt.Checked,
+ bIncludeExporter=self.cbIncludeExporter.checkState() == QtCore.Qt.Checked,
+ bIncludePinInputWidgetFactory=IncludePinInputWidgetFactory,
+ bIncludePrefsWindget=IncludePrefsWidget,
+ )
self.accept()
def populate(self):
@@ -62,7 +77,7 @@ def populate(self):
self.p1Layout = QHBoxLayout(self.p1)
self.lePkgName = QLineEdit("DemoPackage")
# allow only letters without spaces
- self.lePkgName.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp("^[A-Za-z]+$")))
+ self.lePkgName.setValidator(QtGui.QRegularExpressionValidator(QtCore.QRegularExpression("^[A-Za-z]+$")))
self.lePkgName.setAlignment(QtCore.Qt.AlignCenter)
self.p1Layout.addWidget(self.lePkgName)
@@ -121,9 +136,12 @@ def populate(self):
self.p2Layout.addWidget(self.cbIncludeExporter)
self.p2Layout.addWidget(self.cbPrefsWidget)
- self.addPageWidget(self.p2, "What components should be included?",
- "Please select at least one component to include to package!",
- self.isPackaeModuleSelected)
+ self.addPageWidget(
+ self.p2,
+ "What components should be included?",
+ "Please select at least one component to include to package!",
+ self.isPackaeModuleSelected,
+ )
# third page
self.p3 = QWidget()
@@ -131,10 +149,12 @@ def populate(self):
self.pbOutPathSelect = QPushButton("...")
self.p3Layout.addWidget(self.pbOutPathSelect)
self.pbOutPathSelect.clicked.connect(self.onSelectPackageDirectory)
- self.addPageWidget(self.p3,
- "Select output directory for your new package!" +
- "\n\n**Note**: Output directory should be writable.",
- pageEnterCallback=self.onSelectPackageRootEntered)
+ self.addPageWidget(
+ self.p3,
+ "Select output directory for your new package!"
+ + "\n\n**Note**: Output directory should be writable.",
+ pageEnterCallback=self.onSelectPackageRootEntered,
+ )
def checkUIPinFactories(self, state):
checked = self.cbIncludePin.checkState() == QtCore.Qt.Checked
@@ -143,7 +163,9 @@ def checkUIPinFactories(self, state):
def checkIncludeUINodeFactory(self, state):
# ui node factories can be created now only for class nodes
- self.cbIncludeUINodeFactory.setEnabled(self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked)
+ self.cbIncludeUINodeFactory.setEnabled(
+ self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked
+ )
def onGotoComponentsDocs(self):
print("Open components docs page")
@@ -152,15 +174,24 @@ def onSelectPackageRootEntered(self):
self.pbOutPathSelect.setText(Packages.__path__[0])
def isPackaeModuleSelected(self):
- return any([self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked,
- self.cbIncludeFooLib.checkState() == QtCore.Qt.Checked,
- self.cbIncludePin.checkState() == QtCore.Qt.Checked,
- self.cbIncludeTool.checkState() == QtCore.Qt.Checked,
- self.cbIncludeExporter.checkState() == QtCore.Qt.Checked])
+ return any(
+ [
+ self.cbIncludeClassNode.checkState() == QtCore.Qt.Checked,
+ self.cbIncludeFooLib.checkState() == QtCore.Qt.Checked,
+ self.cbIncludePin.checkState() == QtCore.Qt.Checked,
+ self.cbIncludeTool.checkState() == QtCore.Qt.Checked,
+ self.cbIncludeExporter.checkState() == QtCore.Qt.Checked,
+ ]
+ )
def onSelectPackageDirectory(self, *args):
packageName = self.lePkgName.text()
- packageRoot = QFileDialog.getExistingDirectory(self, "Choose folder", "Choose folder", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
+ packageRoot = QFileDialog.getExistingDirectory(
+ self,
+ "Choose folder",
+ "Choose folder",
+ QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks,
+ )
newPackagePath = os.path.join(packageRoot, packageName)
newPackagePath = os.path.normpath(newPackagePath)
self.pbOutPathSelect.setText(packageRoot)
diff --git a/PyFlow/Wizards/PkgGen.py b/PyFlow/Wizards/PkgGen.py
index 1cbfa6a29..73af1e6b9 100644
--- a/PyFlow/Wizards/PkgGen.py
+++ b/PyFlow/Wizards/PkgGen.py
@@ -21,54 +21,76 @@
from PyFlow import Wizards
-def generatePackageInit(packageName,
- bIncludeClassNode=True,
- bIncludeFooLib=True,
- bIncludeUINodeFactory=True,
- bIncludePin=True,
- bIncludeUIPinFactory=True,
- bIncludeTool=True,
- bIncludeExporter=True,
- bIncludePinInputWidgetFactory=True,
- bIncludePrefsWindget=False):
+def generatePackageInit(
+ packageName,
+ bIncludeClassNode=True,
+ bIncludeFooLib=True,
+ bIncludeUINodeFactory=True,
+ bIncludePin=True,
+ bIncludeUIPinFactory=True,
+ bIncludeTool=True,
+ bIncludeExporter=True,
+ bIncludePinInputWidgetFactory=True,
+ bIncludePrefsWindget=False,
+):
result = "PACKAGE_NAME = '{0}'\n\n".format(packageName)
result += "from collections import OrderedDict\n"
result += "from PyFlow.UI.UIInterfaces import IPackage\n\n"
if bIncludePin:
result += "# Pins\n"
- result += "from PyFlow.Packages.{0}.Pins.DemoPin import DemoPin\n\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.Pins.DemoPin import DemoPin\n\n".format(
+ packageName
+ )
if bIncludeFooLib:
result += "# Function based nodes\n"
- result += "from PyFlow.Packages.{0}.FunctionLibraries.DemoLib import DemoLib\n\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.FunctionLibraries.DemoLib import DemoLib\n\n".format(
+ packageName
+ )
if bIncludeClassNode:
result += "# Class based nodes\n"
- result += "from PyFlow.Packages.{0}.Nodes.DemoNode import DemoNode\n\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.Nodes.DemoNode import DemoNode\n\n".format(
+ packageName
+ )
if bIncludeTool:
result += "# Tools\n"
- result += "from PyFlow.Packages.{0}.Tools.DemoShelfTool import DemoShelfTool\n".format(packageName)
- result += "from PyFlow.Packages.{0}.Tools.DemoDockTool import DemoDockTool\n\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.Tools.DemoShelfTool import DemoShelfTool\n".format(
+ packageName
+ )
+ result += "from PyFlow.Packages.{0}.Tools.DemoDockTool import DemoDockTool\n\n".format(
+ packageName
+ )
if bIncludeExporter:
result += "# Exporters\n"
- result += "from PyFlow.Packages.{0}.Exporters.DemoExporter import DemoExporter\n\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.Exporters.DemoExporter import DemoExporter\n\n".format(
+ packageName
+ )
result += "# Factories\n"
if bIncludeUIPinFactory:
- result += "from PyFlow.Packages.{0}.Factories.UIPinFactory import createUIPin\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.Factories.UIPinFactory import createUIPin\n".format(
+ packageName
+ )
if bIncludeUINodeFactory:
- result += "from PyFlow.Packages.{0}.Factories.UINodeFactory import createUINode\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.Factories.UINodeFactory import createUINode\n".format(
+ packageName
+ )
if bIncludePinInputWidgetFactory:
- result += "from PyFlow.Packages.{0}.Factories.PinInputWidgetFactory import getInputWidget\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.Factories.PinInputWidgetFactory import getInputWidget\n".format(
+ packageName
+ )
if bIncludePrefsWindget:
result += "# Prefs widgets\n"
- result += "from PyFlow.Packages.{0}.PrefsWidgets.DemoPrefs import DemoPrefs\n".format(packageName)
+ result += "from PyFlow.Packages.{0}.PrefsWidgets.DemoPrefs import DemoPrefs\n".format(
+ packageName
+ )
result += "\n"
@@ -99,39 +121,51 @@ def generatePackageInit(packageName,
if bIncludePrefsWindget:
result += """_PREFS_WIDGETS["Demo"] = DemoPrefs\n\n"""
- result += "\nclass {0}(IPackage):\n\tdef __init__(self):\n\t\tsuper({0}, self).__init__()\n\n".format(packageName)
+ result += "\nclass {0}(IPackage):\n\tdef __init__(self):\n\t\tsuper({0}, self).__init__()\n\n".format(
+ packageName
+ )
result += """\t@staticmethod\n\tdef GetExporters():\n\t\treturn _EXPORTERS\n\n"""
- result += """\t@staticmethod\n\tdef GetFunctionLibraries():\n\t\treturn _FOO_LIBS\n\n"""
+ result += (
+ """\t@staticmethod\n\tdef GetFunctionLibraries():\n\t\treturn _FOO_LIBS\n\n"""
+ )
result += """\t@staticmethod\n\tdef GetNodeClasses():\n\t\treturn _NODES\n\n"""
result += """\t@staticmethod\n\tdef GetPinClasses():\n\t\treturn _PINS\n\n"""
result += """\t@staticmethod\n\tdef GetToolClasses():\n\t\treturn _TOOLS\n\n"""
if bIncludeUIPinFactory:
- result += """\t@staticmethod\n\tdef UIPinsFactory():\n\t\treturn createUIPin\n\n"""
+ result += (
+ """\t@staticmethod\n\tdef UIPinsFactory():\n\t\treturn createUIPin\n\n"""
+ )
if bIncludeUINodeFactory:
- result += """\t@staticmethod\n\tdef UINodesFactory():\n\t\treturn createUINode\n\n"""
+ result += (
+ """\t@staticmethod\n\tdef UINodesFactory():\n\t\treturn createUINode\n\n"""
+ )
if bIncludePinInputWidgetFactory:
result += """\t@staticmethod\n\tdef PinsInputWidgetFactory():\n\t\treturn getInputWidget\n\n"""
if bIncludePrefsWindget:
- result += """\t@staticmethod\n\tdef PrefsWidgets():\n\t\treturn _PREFS_WIDGETS\n\n"""
+ result += (
+ """\t@staticmethod\n\tdef PrefsWidgets():\n\t\treturn _PREFS_WIDGETS\n\n"""
+ )
return result
-def generatePackage(packageName,
- newPackageRoot,
- bIncludeClassNode=True,
- bIncludeFooLib=True,
- bIncludeUINodeFactory=True,
- bIncludePin=True,
- bIncludeUIPinFactory=True,
- bIncludeTool=True,
- bIncludeExporter=True,
- bIncludePinInputWidgetFactory=True,
- bIncludePrefsWindget=False):
+def generatePackage(
+ packageName,
+ newPackageRoot,
+ bIncludeClassNode=True,
+ bIncludeFooLib=True,
+ bIncludeUINodeFactory=True,
+ bIncludePin=True,
+ bIncludeUIPinFactory=True,
+ bIncludeTool=True,
+ bIncludeExporter=True,
+ bIncludePinInputWidgetFactory=True,
+ bIncludePrefsWindget=False,
+):
wizardsRoot = Wizards.__path__[0]
templatesRoot = os.path.join(wizardsRoot, "Templates")
packageTemplateDirPath = os.path.join(templatesRoot, "PackageTemplate")
@@ -149,22 +183,29 @@ def generatePackage(packageName,
with open(txtFilePath, "r") as f:
txtContent = f.read()
pyContent = txtContent.replace("@PACKAGE_NAME", packageName)
- pyContent = pyContent.replace("@RAND", "".join([choice(ascii_uppercase) for i in range(5)]))
+ pyContent = pyContent.replace(
+ "@RAND", "".join([choice(ascii_uppercase) for i in range(5)])
+ )
with open(pyFilePath, "w") as pf:
pf.write(pyContent)
os.remove(txtFilePath)
moduleInitFilePath = os.path.join(newPackagePath, "__init__.py")
with open(moduleInitFilePath, "w") as f:
- f.write(generatePackageInit(packageName, bIncludeClassNode=bIncludeClassNode,
- bIncludeFooLib=bIncludeFooLib,
- bIncludeUINodeFactory=bIncludeUINodeFactory,
- bIncludePin=bIncludePin,
- bIncludeUIPinFactory=bIncludeUIPinFactory,
- bIncludeTool=bIncludeTool,
- bIncludeExporter=bIncludeExporter,
- bIncludePinInputWidgetFactory=bIncludePinInputWidgetFactory,
- bIncludePrefsWindget=bIncludePrefsWindget))
+ f.write(
+ generatePackageInit(
+ packageName,
+ bIncludeClassNode=bIncludeClassNode,
+ bIncludeFooLib=bIncludeFooLib,
+ bIncludeUINodeFactory=bIncludeUINodeFactory,
+ bIncludePin=bIncludePin,
+ bIncludeUIPinFactory=bIncludeUIPinFactory,
+ bIncludeTool=bIncludeTool,
+ bIncludeExporter=bIncludeExporter,
+ bIncludePinInputWidgetFactory=bIncludePinInputWidgetFactory,
+ bIncludePrefsWindget=bIncludePrefsWindget,
+ )
+ )
# remove unneeded directories
for path, dirs, files in os.walk(newPackagePath):
@@ -182,19 +223,19 @@ def generatePackage(packageName,
if dirName == "PrefsWidgets" and not bIncludePrefsWindget:
shutil.rmtree(path)
if dirName == "Factories":
- removedFactoresCount = 0
+ removedFactoriesCount = 0
if not bIncludeUINodeFactory:
os.remove(os.path.join(path, "UINodeFactory.py"))
- removedFactoresCount += 1
+ removedFactoriesCount += 1
if not bIncludeUIPinFactory:
os.remove(os.path.join(path, "UIPinFactory.py"))
- removedFactoresCount += 1
+ removedFactoriesCount += 1
if not bIncludePinInputWidgetFactory:
os.remove(os.path.join(path, "PinInputWidgetFactory.py"))
- removedFactoresCount += 1
+ removedFactoriesCount += 1
- if removedFactoresCount == 3:
+ if removedFactoriesCount == 3:
shutil.rmtree(path)
if dirName == "UI":
diff --git a/PyFlow/Wizards/Templates/PackageTemplate/Factories/PinInputWidgetFactory.txt b/PyFlow/Wizards/Templates/PackageTemplate/Factories/PinInputWidgetFactory.txt
index 941cd8410..f69c07f65 100644
--- a/PyFlow/Wizards/Templates/PackageTemplate/Factories/PinInputWidgetFactory.txt
+++ b/PyFlow/Wizards/Templates/PackageTemplate/Factories/PinInputWidgetFactory.txt
@@ -1,6 +1,6 @@
from PyFlow.Core.Common import *
-from Qt import QtCore
-from Qt.QtWidgets import QCheckBox
+from qtpy import QtCore
+from qtpy.QtWidgets import QCheckBox
from PyFlow.UI.Widgets.InputWidgets import *
diff --git a/PyFlow/Wizards/Templates/PackageTemplate/PrefsWidgets/DemoPrefs.txt b/PyFlow/Wizards/Templates/PackageTemplate/PrefsWidgets/DemoPrefs.txt
index e2cc8056f..bc0e7b4b9 100644
--- a/PyFlow/Wizards/Templates/PackageTemplate/PrefsWidgets/DemoPrefs.txt
+++ b/PyFlow/Wizards/Templates/PackageTemplate/PrefsWidgets/DemoPrefs.txt
@@ -1,4 +1,4 @@
-from Qt.QtWidgets import *
+from qtpy.QtWidgets import *
from PyFlow.UI.Widgets.PropertiesFramework import CollapsibleFormWidget
from PyFlow.UI.Widgets.PreferencesWindow import CategoryWidgetBase
diff --git a/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoDockTool.txt b/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoDockTool.txt
index 515271f37..306d6af1d 100644
--- a/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoDockTool.txt
+++ b/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoDockTool.txt
@@ -1,5 +1,4 @@
-from nine import str
-from Qt import QtGui
+from qtpy import QtGui
from PyFlow.UI.Tool.Tool import DockTool
@@ -19,4 +18,4 @@ class DemoDockTool(DockTool):
@staticmethod
def name():
- return str("DemoDockTool")
+ return "DemoDockTool"
diff --git a/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoShelfTool.txt b/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoShelfTool.txt
index 81b1cfc2b..437af63ab 100644
--- a/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoShelfTool.txt
+++ b/PyFlow/Wizards/Templates/PackageTemplate/Tools/DemoShelfTool.txt
@@ -1,8 +1,7 @@
-from nine import str
from PyFlow.UI.Tool.Tool import ShelfTool
from PyFlow.Core.Common import Direction
-from Qt import QtGui
+from qtpy import QtGui
class DemoShelfTool(ShelfTool):
@@ -20,7 +19,7 @@ class DemoShelfTool(ShelfTool):
@staticmethod
def name():
- return str("DemoShelfTool")
+ return "DemoShelfTool"
def do(self):
print("Greet!")
diff --git a/PyFlow/Wizards/WizardDialogueBase.py b/PyFlow/Wizards/WizardDialogueBase.py
index 8054ce380..0000ac54d 100644
--- a/PyFlow/Wizards/WizardDialogueBase.py
+++ b/PyFlow/Wizards/WizardDialogueBase.py
@@ -13,9 +13,9 @@
## limitations under the License.
-from Qt import QtCore
-from Qt import QtGui
-from Qt.QtWidgets import *
+from qtpy import QtCore
+from qtpy import QtGui
+from qtpy.QtWidgets import *
from docutils import core
@@ -27,6 +27,7 @@ def rst2html(rst):
class WizardDialogueBase(QDialog):
"""docstring for WizardDialogueBase."""
+
def __init__(self, parent=None):
super(WizardDialogueBase, self).__init__(parent)
self.setWindowTitle("Package wizard")
@@ -131,7 +132,9 @@ def onDone(self):
def onGoForward(self):
futureIndex = self.stackWidget.currentIndex() + 1
- isCurrentPageValid = self.pageValidationHooks[self.stackWidget.currentWidget()]()
+ isCurrentPageValid = self.pageValidationHooks[
+ self.stackWidget.currentWidget()
+ ]()
if isCurrentPageValid:
self.stackWidget.setCurrentIndex(futureIndex)
self.updateNavigationVisibility()
@@ -142,7 +145,14 @@ def onGoForward(self):
else:
self.setMessageRst(self.errorMessages[self.stackWidget.currentWidget()])
- def addPageWidget(self, widget, messageRst, errorMessageRst="Something is wrong!", validationHook=lambda: True, pageEnterCallback=lambda: None):
+ def addPageWidget(
+ self,
+ widget,
+ messageRst,
+ errorMessageRst="Something is wrong!",
+ validationHook=lambda: True,
+ pageEnterCallback=lambda: None,
+ ):
self.stackWidget.addWidget(widget)
self.messages[widget] = messageRst
self.pageValidationHooks[widget] = validationHook
@@ -163,6 +173,7 @@ def run():
if __name__ == "__main__":
import sys
+
app = QApplication(sys.argv)
w = WizardDialogueBase()
diff --git a/PyFlow/__init__.py b/PyFlow/__init__.py
index 7dc98bcb6..6942c7511 100644
--- a/PyFlow/__init__.py
+++ b/PyFlow/__init__.py
@@ -17,11 +17,11 @@
"""
# this line adds extension-packages not installed inside the PyFlow directory
-__path__ = __import__('pkgutil').extend_path(__path__, __name__)
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
import importlib
import pkgutil
-import collections.abc as collections
+import collections.abc
from copy import copy
import os
import json
@@ -31,8 +31,9 @@
__all__ = [
"INITIALIZE",
- "GET_PACKAGES",
"GET_PACKAGE_CHECKED",
+ "GET_PACKAGE_PATH",
+ "GET_PACKAGES",
"CreateRawPin",
"getPinDefaultValueByType",
"findPinClassByType",
@@ -88,7 +89,7 @@ def getHashableDataTypes():
for pin in getAllPinClasses():
t = pin.internalDataStructure()
if t is not type(None) and t is not None:
- if isinstance(pin.pinDataTypeHint()[1], collections.Hashable):
+ if isinstance(pin.pinDataTypeHint()[1], collections.abc.Hashable):
__HASHABLE_TYPES.append(pin.__name__)
return copy(__HASHABLE_TYPES)
@@ -110,15 +111,16 @@ def CreateRawPin(name, owningNode, dataType, direction, **kwds):
def getRawNodeInstance(nodeClassName, packageName=None, libName=None, **kwargs):
from PyFlow.Core.NodeBase import NodeBase
+
package = GET_PACKAGE_CHECKED(packageName)
- # try find function first
+ # try to find function first
if libName is not None:
for key, lib in package.GetFunctionLibraries().items():
foos = lib.getFunctions()
if libName == key and nodeClassName in foos:
return NodeBase.initializeFromFunction(foos[nodeClassName])
- # try find node class
+ # try to find node class
nodes = package.GetNodeClasses()
if nodeClassName in nodes:
return nodes[nodeClassName](nodeClassName, **kwargs)
@@ -144,7 +146,7 @@ def getRawNodeInstance(nodeClassName, packageName=None, libName=None, **kwargs):
for compoundNodeFileName in files:
compoundNodeName, _ = os.path.splitext(compoundNodeFileName)
compoundNodeFullPath = os.path.join(path, compoundNodeFileName)
- with open(compoundNodeFullPath, 'r') as f:
+ with open(compoundNodeFullPath, "r") as f:
compoundData = json.load(f)
if compoundData["name"] == nodeClassName:
compoundNode = getRawNodeInstance("compound", "PyFlowBase")
@@ -155,13 +157,15 @@ def getRawNodeInstance(nodeClassName, packageName=None, libName=None, **kwargs):
return compoundNode
-def INITIALIZE(additionalPackageLocations=[], software=""):
+def INITIALIZE(additionalPackageLocations=None, software=""):
+ if additionalPackageLocations is None:
+ additionalPackageLocations = []
from PyFlow.UI.Tool import REGISTER_TOOL
from PyFlow.UI.Widgets.InputWidgets import REGISTER_UI_INPUT_WIDGET_PIN_FACTORY
from PyFlow.UI.Canvas.UINodeBase import REGISTER_UI_NODE_FACTORY
from PyFlow.UI.Canvas.UIPinBase import REGISTER_UI_PIN_FACTORY
from PyFlow import ConfigManager
- from Qt.QtWidgets import QMessageBox
+ from qtpy.QtWidgets import QMessageBox
packagePaths = Packages.__path__
@@ -188,7 +192,7 @@ def recursePackagePaths(inPath):
# check for additional package locations
if "PYFLOW_PACKAGES_PATHS" in os.environ:
- delim = ';'
+ delim = ";"
pathsString = os.environ["PYFLOW_PACKAGES_PATHS"]
# remove delimiters from right
pathsString = pathsString.rstrip(delim)
@@ -207,12 +211,14 @@ def recursePackagePaths(inPath):
for importer, modname, ispkg in pkgutil.iter_modules(packagePaths):
try:
if ispkg:
- mod = importer.find_module(modname).load_module(modname)
+ mod = importer.find_spec(modname).loader.load_module()
package = getattr(mod, modname)()
__PACKAGES[modname] = package
__PACKAGE_PATHS[modname] = os.path.normpath(mod.__path__[0])
except Exception as e:
- QMessageBox.critical(None, str("Fatal error"), "Error On Module %s :\n%s" % (modname, str(e)))
+ QMessageBox.critical(
+ None, "Fatal error", "Error On Module %s :\n%s" % (modname, str(e))
+ )
continue
registeredInternalPinDataTypes = set()
@@ -227,7 +233,11 @@ def recursePackagePaths(inPath):
if pin.IsValuePin():
internalType = pin.internalDataStructure()
if internalType in registeredInternalPinDataTypes:
- raise Exception("Pin with {0} internal data type already been registered".format(internalType))
+ raise Exception(
+ "Pin with {0} internal data type already been registered".format(
+ internalType
+ )
+ )
registeredInternalPinDataTypes.add(internalType)
uiPinsFactory = package.UIPinsFactory()
diff --git a/PyFlow/graphUiParser.py b/PyFlow/graphUiParser.py
index d06b9a98f..eb93beaee 100644
--- a/PyFlow/graphUiParser.py
+++ b/PyFlow/graphUiParser.py
@@ -13,21 +13,17 @@
## limitations under the License.
import os
-import sys
import json
import threading
-import time
-from Qt.QtWidgets import *
-from Qt import QtGui
-from Qt import QtCore
+from qtpy.QtWidgets import *
+from qtpy import QtGui
from PyFlow import INITIALIZE
from PyFlow.Core.Common import *
from PyFlow.Core.GraphManager import GraphManagerSingleton
from PyFlow.UI.Canvas.UINodeBase import getUINodeInstance
from PyFlow.UI.Utils.stylesheet import editableStyleSheet
from PyFlow.UI.Widgets.PropertiesFramework import CollapsibleFormWidget
-import PyFlow.UI.resources
def run(filePath):
@@ -40,7 +36,7 @@ def run(filePath):
msg.setIcon(QMessageBox.Critical)
if os.path.exists(filePath):
- with open(filePath, 'r') as f:
+ with open(filePath, "r") as f:
data = json.load(f)
# Window to display inputs
@@ -62,8 +58,8 @@ def run(filePath):
uiNodeJsonTemplate["wrapper"] = inp.wrapperJsonData
uiNode.postCreate(uiNodeJsonTemplate)
cat = CollapsibleFormWidget(headName=inp.name)
- prop.layout().addWidget(cat)
- cat = uiNode.createOutputWidgets(cat)
+ prop.layout().addWidget(cat)
+ uiNode.createOutputWidgets(cat)
nodes = grph.getNodesList()
if len(nodes) > 0:
@@ -73,24 +69,29 @@ def run(filePath):
uiNodeJsonTemplate["wrapper"] = node.wrapperJsonData
uiNode.postCreate(uiNodeJsonTemplate)
if uiNode.bExposeInputsToCompound:
- cat = CollapsibleFormWidget(headName="{} inputs".format(node.name))
- prop.layout().addWidget(cat)
+ cat = CollapsibleFormWidget(
+ headName="{} inputs".format(node.name)
+ )
+ prop.layout().addWidget(cat)
uiNode.createInputWidgets(cat, pins=False)
prop.show()
+
def programLoop():
while True:
man.Tick(deltaTime=0.02)
time.sleep(0.02)
if man.terminationRequested:
break
+
t = threading.Thread(target=programLoop)
t.start()
def quitEvent():
man.terminationRequested = True
t.join()
+
app.aboutToQuit.connect(quitEvent)
- # If no GraphInput Nodes Exit propgram
+ # If no GraphInput Nodes Exit program
else:
msg.setInformativeText(filePath)
msg.setDetailedText("The file doesn't contain graphInputs nodes")
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 0c6cc3b30..ac2800f23 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -12,13 +12,15 @@
#
import os
import sys
-sys.path.insert(0, os.path.abspath('../../'))
+
+sys.path.insert(0, os.path.abspath("../../"))
from PyFlow.Core.version import currentVersion
+
# -- Project information -----------------------------------------------------
-project = 'PyFlow'
-copyright = '2019, Ilgar Lunin, Pedro Cabrera'
-author = 'Ilgar Lunin, Pedro Cabrera'
+project = "PyFlow"
+copyright = "2019, Ilgar Lunin, Pedro Cabrera"
+author = "Ilgar Lunin, Pedro Cabrera"
# The full version, including alpha/beta/rc tags
release = currentVersion().__str__()
@@ -28,23 +30,17 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
-extensions = [
- 'sphinx.ext.todo',
- 'sphinx.ext.autodoc',
- 'sphinx.ext.intersphinx'
-]
-
-source_suffix = {
- '.rst': 'restructuredtext',
-}
+extensions = ["sphinx.ext.todo", "sphinx.ext.autodoc", "sphinx.ext.intersphinx"]
+
+source_suffix = {".rst": "restructuredtext"}
intersphinx_mapping = {
- 'python': ('https://docs.python.org/3', None),
- 'blinker': ('https://pythonhosted.org/blinker', None)
+ "python": ("https://docs.python.org/3", None),
+ "blinker": ("https://pythonhosted.org/blinker", None),
}
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@@ -58,23 +54,20 @@
# a list of builtin themes.
#
import sphinx_rtd_theme
-html_theme = 'sphinx_rtd_theme'
+
+html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
-html_context = {
- 'css_files': [
- '_static/theme_overrides.css'
- ]
-}
+html_context = {"css_files": ["_static/theme_overrides.css"]}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
autodoc_default_options = {
- 'member-order': 'bysource',
- 'special-members': '__init__',
- 'exclude-members': '__weakref__'
+ "member-order": "bysource",
+ "special-members": "__init__",
+ "exclude-members": "__weakref__",
}
-master_doc = 'index'
\ No newline at end of file
+master_doc = "index"
diff --git a/integrations/3dsmax/3ds_launcher.py b/integrations/3dsmax/3ds_launcher.py
index 8fd74bcc8..de4b6740e 100644
--- a/integrations/3dsmax/3ds_launcher.py
+++ b/integrations/3dsmax/3ds_launcher.py
@@ -4,7 +4,7 @@
from PySide2 import QtWidgets
from PySide2 import QtCore
-ptvsd.enable_attach(address=('0.0.0.0', 3000), redirect_output=True)
+ptvsd.enable_attach(address=("0.0.0.0", 3000), redirect_output=True)
mainWindow = QtWidgets.QWidget.find(pymxs.runtime.windows.getMAXHWND())
diff --git a/integrations/maya/maya_launcher.py b/integrations/maya/maya_launcher.py
index c882deb4e..9bf77c982 100644
--- a/integrations/maya/maya_launcher.py
+++ b/integrations/maya/maya_launcher.py
@@ -9,7 +9,7 @@
except NameError:
long = int # Python 3
-ptvsd.enable_attach(address=('0.0.0.0', 3000), redirect_output=True)
+ptvsd.enable_attach(address=("0.0.0.0", 3000), redirect_output=True)
mayaMainWindowPtr = omui.MQtUtil.mainWindow()
mayaMainWindow = wrapInstance(long(mayaMainWindowPtr), QWidget)
diff --git a/pyflow-mdi.py b/pyflow-mdi.py
new file mode 100644
index 000000000..813e0967f
--- /dev/null
+++ b/pyflow-mdi.py
@@ -0,0 +1,36 @@
+## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
+
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+
+## http://www.apache.org/licenses/LICENSE-2.0
+
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+
+import sys
+from PyFlow.AppMDI import MDIMain
+from qtpy.QtWidgets import QApplication
+from qtpy.QtGui import QGuiApplication
+
+
+def main():
+ app = QApplication(sys.argv)
+
+ instance = MDIMain()
+ if instance is not None:
+ instance.show()
+ instance.activateWindow()
+ try:
+ sys.exit(app.exec_())
+ except Exception as e:
+ print(e)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/pyflow.py b/pyflow.py
index 5210e9e55..267520daa 100644
--- a/pyflow.py
+++ b/pyflow.py
@@ -15,16 +15,37 @@
import sys
from PyFlow.App import PyFlow
-from Qt.QtWidgets import QApplication
+from qtpy.QtWidgets import QApplication
+import argparse
+import os
+import json
-def main():
+def main():
app = QApplication(sys.argv)
instance = PyFlow.instance(software="standalone")
if instance is not None:
- app.setActiveWindow(instance)
+ instance.activateWindow()
instance.show()
+
+ parser = argparse.ArgumentParser(description="PyFlow CLI")
+ parser.add_argument("-f", "--filePath", type=str, default="Untitled.pygraph")
+ parsedArguments, unknown = parser.parse_known_args(sys.argv[1:])
+ filePath = parsedArguments.filePath
+ if not filePath.endswith(".pygraph"):
+ filePath += ".pygraph"
+ if os.path.exists(filePath):
+ with open(filePath, 'r') as f:
+ data = json.load(f)
+ instance.loadFromData(data)
+ instance.currentFileName = filePath
+
+ try:
+ sys.exit(app.exec_())
+ except Exception as e:
+ print(e)
+
try:
sys.exit(app.exec_())
@@ -32,5 +53,5 @@ def main():
print(e)
-if __name__ == '__main__':
+if __name__ == "__main__":
main()
diff --git a/pyflow_run.py b/pyflow_run.py
index 549de8249..41645e39f 100644
--- a/pyflow_run.py
+++ b/pyflow_run.py
@@ -16,5 +16,5 @@
import sys
from PyFlow import graphUiParser
-if __name__ == '__main__':
+if __name__ == "__main__":
graphUiParser.run(sys.argv[1])
diff --git a/requirements/requirements-3dsmax.txt b/requirements/requirements-3dsmax.txt
index ee5b3a8e2..e50bf2e00 100644
--- a/requirements/requirements-3dsmax.txt
+++ b/requirements/requirements-3dsmax.txt
@@ -1,6 +1,4 @@
-aenum; python_version < '3.4'
Qt.py
blinker
-nine
docutils
ptvsd
\ No newline at end of file
diff --git a/requirements/requirements-maya.txt b/requirements/requirements-maya.txt
index ee5b3a8e2..e50bf2e00 100644
--- a/requirements/requirements-maya.txt
+++ b/requirements/requirements-maya.txt
@@ -1,6 +1,4 @@
-aenum; python_version < '3.4'
Qt.py
blinker
-nine
docutils
ptvsd
\ No newline at end of file
diff --git a/requirements/requirements-standalone.txt b/requirements/requirements-standalone.txt
index 2605a6a7b..8672b4b59 100644
--- a/requirements/requirements-standalone.txt
+++ b/requirements/requirements-standalone.txt
@@ -1,6 +1,6 @@
-aenum; python_version < '3.4'
-PySide2
-Qt.py
+PySide6
+qtpy
blinker
-nine
-docutils
\ No newline at end of file
+docutils
+SQLAlchemy
+requests
\ No newline at end of file
diff --git a/setup.py b/setup.py
index be927ffbe..c0591a37d 100644
--- a/setup.py
+++ b/setup.py
@@ -2,30 +2,34 @@
import sys
import os
-sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), "PyFlow", "Core"))
+sys.path.append(
+ os.path.join(os.path.abspath(os.path.dirname(__file__)), "PyFlow", "Core")
+)
from version import currentVersion
setup(
name="PyFlow",
version=str(currentVersion()),
packages=find_packages(),
- entry_points={
- 'console_scripts': ['pyflow = PyFlow.Scripts:main']
- },
+ entry_points={"console_scripts": ["pyflow = PyFlow.Scripts:main"]},
include_package_data=True,
author="Ilgar Lunin, Pedro Cabrera",
author_email="wonderworks.software@gmail.com",
description="A general purpose runtime extendable python qt node editor.",
keywords="visual programming framework",
- url="https://wonderworks-software.github.io/PyFlow", # project home page
+ url="https://wonderworks-software.github.io/PyFlow", # project home page
project_urls={
"Bug Tracker": "https://github.com/wonderworks-software/PyFlow/issues",
"Documentation": "https://pyflow.readthedocs.io",
"Source Code": "https://github.com/wonderworks-software/PyFlow",
},
- classifiers=[
- 'License :: Appache-2.0'
+ classifiers=["License :: Appache-2.0"],
+ install_requires=[
+ "qtpy",
+ "blinker",
+ "docutils",
+ "SQLAlchemy",
+ "requests",
],
- install_requires=["enum ; python_version<'3.4'", "Qt.py", "blinker", "nine", "docutils"],
- extra_requires=["PySide2"]
+ extra_requires=["PySide6"],
)