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 + + + + + + + + + + + + + + 0 + 0 + 1225 + 22 + + + + + &File + + + + + Database + + + + + + + + + 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"], )