def onShow(self):
     super(HistoryTool, self).onShow()
     if self.undoStackWidget.count() != EditorHistory().count():
         stack = EditorHistory().getStack()
         for state in stack:
             self.undoStackWidget.addEntry(state)
         self.undoStackWidget.selectEntry(EditorHistory().activeState)
示例#2
0
    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:
            EditorHistory().clear()
            historyTools = self.getRegisteredTools(
                classNameFilters=["HistoryTool"])
            for historyTools in historyTools:
                historyTools.onClear()
            self.newFile()
            EditorHistory().saveState("New file")
        if currentInputAction in actionSaveVariants:
            self.save()
        if currentInputAction in actionLoadVariants:
            EditorHistory().clear()
            self.load()
        if currentInputAction in actionSaveAsVariants:
            self.save(True)
 def __init__(self, parent=None):
     super(HistoryWidget, self).__init__(parent)
     self.currentRowChanged.connect(self.onRowChanged)
     EditorHistory().statePushed.connect(self.addEntry)
     EditorHistory().stateRemoved.connect(self.onRemoveState)
     EditorHistory().stateSelected.connect(self.selectEntry)
     self._data = {}
示例#4
0
    def mousePressEvent(self, event):
        super(NodeBoxTreeWidget, self).mousePressEvent(event)
        item_clicked = self.currentItem()
        if not item_clicked:
            event.ignore()
            return
        # check if clicked item is a category
        if item_clicked.bCategory:
            event.ignore()
            return
        # find top level parent
        rootItem = item_clicked
        while not rootItem.parent() is None:
            rootItem = rootItem.parent()
        packageName = rootItem.text(0)
        pressed_text = item_clicked.text(0)
        libName = item_clicked.libName

        if pressed_text in self.categoryPaths.keys():
            event.ignore()
            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

        # TODO: Rewrite self.bGripsEnabled. Node box can be floating window or a tool.
        # If node box is a tool it onbly can create nodes by dragging and dropping
        if self.canvas.pressedPin is not None and self.bGripsEnabled:
            a = self.canvas.mapToScene(self.canvas.mouseReleasePos)
            jsonTemplate["x"] = a.x()
            jsonTemplate["y"] = a.y()
            node = self.canvas.createNode(jsonTemplate)
            self.canvas.hideNodeBox()
            pressedPin = self.canvas.pressedPin
            if pressedPin.direction == PinDirection.Input:
                for pin in node.UIoutputs.values():
                    wire = self.canvas.connectPinsInternal(pressedPin, pin)
                    if wire is not None:
                        EditorHistory().saveState("Connect pins", modify=True)
                        break
            if pressedPin.direction == PinDirection.Output:
                for pin in node.UIinputs.values():
                    wire = self.canvas.connectPinsInternal(pin, pressedPin)
                    if wire is not None:
                        EditorHistory().saveState("Connect pins", modify=True)
                        break
        else:
            drag = QtGui.QDrag(self)
            mime_data = QtCore.QMimeData()

            pressed_text = json.dumps(jsonTemplate)
            mime_data.setText(pressed_text)
            drag.setMimeData(mime_data)
            drag.exec_()
示例#5
0
    def closeEvent(self, event):
        self.tick_timer.stop()
        self.tick_timer.timeout.disconnect()
        EditorHistory().shutdown()

        self.canvasWidget.shoutDown()
        # save editor config
        settings = ConfigManager().getSettings("APP_STATE")

        # clear file each time to capture opened dock tools
        settings.clear()
        settings.sync()

        settings.beginGroup('Editor')
        settings.setValue("geometry", self.saveGeometry())
        settings.setValue("state", self.saveState())
        settings.endGroup()

        # save tools state
        settings.beginGroup('Tools')
        for tool in self._tools:
            if isinstance(tool, ShelfTool):
                settings.beginGroup("ShelfTools")
                settings.beginGroup(tool.name())
                tool.saveState(settings)
                settings.endGroup()
                settings.endGroup()
            if isinstance(tool, DockTool):
                settings.beginGroup("DockTools")
                settings.beginGroup(tool.uniqueName())
                tool.saveState(settings)
                settings.endGroup()
                settings.endGroup()
            tool.onDestroy()
        settings.endGroup()
        settings.sync()

        # remove temp directory if exists
        if os.path.exists(self.currentTempDir):
            shutil.rmtree(self.currentTempDir)

        # TODO: Use "Borg" pattern to destroy all singletons at once
        EditorHistory.destroy()
        GraphManagerSingleton.destroy()
        ConfigManager.destroy()
        PreferencesWindow.destroy()

        PyFlow.appInstance = None

        QMainWindow.closeEvent(self, event)
示例#6
0
    def killVar(self, uiVariableWidget):
        variableGraph = uiVariableWidget._rawVariable.graph
        variableGraph.killVariable(uiVariableWidget._rawVariable)
        self.actualize()

        self.clearProperties()
        EditorHistory().saveState("Kill variable")
示例#7
0
    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()
示例#8
0
 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)))
示例#9
0
文件: App.py 项目: DerekRein/PyFlow
    def __init__(self, parent=None):
        super(PyFlow, self).__init__(parent=parent)
        self.edHistory = EditorHistory(self)
        self.setWindowTitle("PyFlow v{0}".format(currentVersion().__str__()))
        self.undoStack = QUndoStack(self)
        self.setContentsMargins(1, 1, 1, 1)
        self.graphManager = GraphManagerSingleton()
        self.canvasWidget = CanvasWidget(self.graphManager.get(), self)
        self.canvasWidget.setObjectName("canvasWidget")
        self.setCentralWidget(self.canvasWidget)
        self.setTabPosition(QtCore.Qt.AllDockWidgetAreas, QTabWidget.North)
        self.setDockOptions(QMainWindow.AnimatedDocks
                            | QMainWindow.AllowNestedDocks)

        self.menuBar = QMenuBar(self)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 863, 21))
        self.menuBar.setObjectName("menuBar")
        self.setMenuBar(self.menuBar)
        self.toolBar = QToolBar(self)
        self.toolBar.setObjectName("toolBar")
        self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)

        self.setWindowIcon(QtGui.QIcon(":/LogoBpApp.png"))
        self._tools = set()
        self.currentTempDir = ""

        self.preferencesWindow = PreferencesWindow(self)

        self.setMouseTracking(True)

        self._lastClock = 0.0
        self.fps = EDITOR_TARGET_FPS
        self.tick_timer = QtCore.QTimer()
        self._current_file_name = 'Untitled'
        self.populateMenu()
示例#10
0
 def onImport(self):
     openPath, selectedFilter = QFileDialog.getOpenFileName(filter="Python node data (*.pynode)")
     if openPath != "":
         with open(openPath, 'r') as f:
             dataString = f.read()
             self.tryApplyNodeData(dataString)
         EditorHistory().saveState("Import python node data", modify=True)
示例#11
0
 def createVariable(self,
                    dataType=str('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")
     return uiVariable
示例#12
0
    def closeEvent(self, event):

        shouldSave = self.shouldSave()
        if shouldSave == QMessageBox.Yes:
            if not self.save():
                event.ignore()
                return
        elif shouldSave == QMessageBox.Discard:
            event.ignore()
            return

        self.tick_timer.stop()
        self.tick_timer.timeout.disconnect()
        EditorHistory().shutdown()

        self.canvasWidget.shoutDown()
        # save editor config
        settings = ConfigManager().getSettings("APP_STATE")

        # clear file each time to capture opened dock tools
        settings.clear()
        settings.sync()

        settings.beginGroup('Editor')
        settings.setValue("geometry", self.saveGeometry())
        settings.setValue("state", self.saveState())
        settings.endGroup()

        # save tools state
        settings.beginGroup('Tools')
        for tool in self._tools:
            if isinstance(tool, ShelfTool):
                settings.beginGroup("ShelfTools")
                settings.beginGroup(tool.name())
                tool.saveState(settings)
                settings.endGroup()
                settings.endGroup()
            if isinstance(tool, DockTool):
                settings.beginGroup("DockTools")
                settings.beginGroup(tool.uniqueName())
                tool.saveState(settings)
                settings.endGroup()
                settings.endGroup()
            tool.onDestroy()
        settings.endGroup()
        settings.sync()

        # remove temp directory if exists
        if os.path.exists(self.currentTempDir):
            shutil.rmtree(self.currentTempDir)

        SingletonDecorator.destroyAll()

        PyFlow.appInstance = None

        QMainWindow.closeEvent(self, event)
示例#13
0
    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 assignData(self, data):
     data["isRoot"] = False
     data["parentGraphName"] = self._rawNode.rawGraph.parentGraph.name
     missedPackages = set()
     if validateGraphDataPackages(data, missedPackages):
         data["nodes"] = self.canvasRef().makeSerializedNodesUnique(
             data["nodes"])
         self._rawNode.rawGraph.populateFromJson(data)
         self.canvasRef().createWrappersForGraph(self._rawNode.rawGraph)
         EditorHistory().saveState("Import compound", modify=True)
     else:
         logger.error("Missing dependencies! {0}".format(
             ",".join(missedPackages)))
示例#15
0
文件: App.py 项目: DerekRein/PyFlow
    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:
            EditorHistory().clear()
            self.newFile()
            EditorHistory().saveState("New file")
        if currentInputAction in actionSaveVariants:
            self.save()
        if currentInputAction in actionLoadVariants:
            EditorHistory().clear()
            self.load()
        if currentInputAction in actionSaveAsVariants:
            self.save(True)
示例#16
0
 def onImport(self):
     openPath, selectedFilter = QFileDialog.getOpenFileName(filter="Subgraph data (*.json)")
     if openPath != "":
         with open(openPath, 'r') as f:
             data = json.load(f)
             data["isRoot"] = False
             data["parentGraphName"] = self._rawNode.rawGraph.parentGraph.name
             missedPackages = set()
             if validateGraphDataPackages(data, missedPackages):
                 data["nodes"] = self.canvasRef().makeSerializedNodesUnique(data["nodes"])
                 self._rawNode.rawGraph.populateFromJson(data)
                 self.canvasRef().createWrappersForGraph(self._rawNode.rawGraph)
                 EditorHistory().saveState("Import compound")
             else:
                 logger.error("Missing dependencies! {0}".format(",".join(missedPackages)))
示例#17
0
    def __init__(self, parent=None):
        super(PyFlow, self).__init__(parent=parent)
        self._modified = False
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.currentSoftware = ""
        self.edHistory = EditorHistory(self)
        self.edHistory.statePushed.connect(self.historyStatePushed)
        self.setWindowTitle(winTitle())
        self.undoStack = QUndoStack(self)
        self.setContentsMargins(1, 1, 1, 1)
        self.graphManager = GraphManagerSingleton()
        self.canvasWidget = BlueprintCanvasWidget(self.graphManager.get(),
                                                  self)
        self.canvasWidget.setObjectName("canvasWidget")
        self.setCentralWidget(self.canvasWidget)
        self.setTabPosition(QtCore.Qt.AllDockWidgetAreas, QTabWidget.North)
        self.setDockOptions(QMainWindow.AnimatedDocks
                            | QMainWindow.AllowNestedDocks)

        self.menuBar = QMenuBar(self)
        self.menuBar.setGeometry(QtCore.QRect(0, 0, 863, 21))
        self.menuBar.setObjectName("menuBar")
        self.setMenuBar(self.menuBar)
        self.toolBar = QToolBar(self)
        self.toolBar.setObjectName("toolBar")
        self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar)

        self.setWindowIcon(QtGui.QIcon(":/LogoBpApp.png"))
        self._tools = set()
        self.currentTempDir = ""

        self.preferencesWindow = PreferencesWindow(self)

        self.setMouseTracking(True)

        self._lastClock = 0.0
        self.fps = EDITOR_TARGET_FPS
        self.tick_timer = QtCore.QTimer()
        self._currentFileName = ''
        self.currentFileName = None
 def mouseReleaseEvent(self, event):
     super(HistoryWidget, self).mouseReleaseEvent(event)
     item = self.currentItem()
     if item is not None:
         EditorHistory().selectState(item.state)
 def onClear(self):
     EditorHistory().clear()
     self.undoStackWidget.clear()
示例#20
0
 def onStructureChanged(self, name):
     self._rawVariable.structure = PinStructure[name]
     self.variablesWidget.pyFlowInstance.onRequestFillProperties(
         self.createPropertiesWidget)
     EditorHistory().saveState("Change variable struct", modify=True)
示例#21
0
文件: App.py 项目: DerekRein/PyFlow
    def instance(parent=None):
        if PyFlow.appInstance is not None:
            return PyFlow.appInstance

        settings = ConfigManager().getSettings("APP_STATE")

        instance = PyFlow(parent)
        REGISTER_TOOL("PyFlowBase", LoggerTool)
        a = GET_TOOLS()["PyFlowBase"][0]()
        a.setAppInstance(instance)
        instance.registerToolInstance(a)
        instance.addDockWidget(a.defaultDockArea(), a)
        a.setAppInstance(instance)
        a.onShow()

        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)
        except Exception as e:
            QMessageBox.critical(None, "Fatal error", str(e))
            return

        instance.startMainLoop()

        # populate tools
        canvas = instance.getCanvas()
        toolbar = instance.getToolbar()

        geo = settings.value('Editor/geometry')
        if geo is not None:
            instance.restoreGeometry(geo)
        state = settings.value('Editor/state')
        if state is not None:
            instance.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
                    instance.registerToolInstance(ToolInstance)
                    ToolInstance.setAppInstance(instance)
                    action = QAction(instance)
                    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()
                    settings.endGroup()

                if issubclass(ToolClass, DockTool):
                    menus = instance.menuBar.findChildren(QMenu)
                    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)
                    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(
                        ): 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
                        ]:
                            continue
                        toolName = dockToolGroupName.split("::")[0]
                        instance.invokeDockToolByName(packageName, toolName,
                                                      settings)
                        settings.endGroup()
                    settings.endGroup()

        PyFlow.appInstance = instance
        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 instance
示例#22
0
 def setDataType(self, dataType):
     self.dataType = dataType
     self._rawVariable.dataType = dataType
     EditorHistory().saveState("Change variable data type", modify=True)
示例#23
0
 def accessLevelChanged(x):
     self._rawVariable.accessLevel = AccessLevel[x]
     EditorHistory().saveState("Change variable access level",
                               modify=True)
示例#24
0
文件: General.py 项目: kthulhu/PyFlow
 def setHistoryCapacity():
     EditorHistory().capacity = self.historyDepth.value()
示例#25
0
 def setDataType(self, dataType):
     self.dataType = dataType
     EditorHistory().saveState("Change variable data type")
示例#26
0
    def instance(parent=None, software=""):
        assert (
            software != ""
        ), "Invalid arguments. Please pass you software name as second argument!"

        instance = PyFlow(parent)
        settings = ConfigManager().getSettings("APP_STATE")
        instance.currentSoftware = software
        SessionDescriptor().software = instance.currentSoftware

        if software == "standalone":
            editableStyleSheet(instance)

        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

        instance.startMainLoop()

        # populate tools
        canvas = instance.getCanvas()
        toolbar = instance.getToolbar()

        # populate menus
        instance.populateMenu()

        geo = settings.value('Editor/geometry')
        if geo is not None:
            instance.restoreGeometry(geo)
        state = settings.value('Editor/state')
        if state is not None:
            instance.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
                    instance.registerToolInstance(ToolInstance)
                    ToolInstance.setAppInstance(instance)
                    action = QAction(instance)
                    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()
                    settings.endGroup()

                if issubclass(ToolClass, DockTool):
                    menus = instance.menuBar.findChildren(QMenu)
                    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)
                    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(
                        ): 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
                        ]:
                            settings.endGroup()
                            continue
                        toolName = dockToolGroupName.split("::")[0]
                        instance.invokeDockToolByName(packageName, toolName,
                                                      settings)
                        settings.endGroup()
                    settings.endGroup()

        PyFlow.appInstance = instance
        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")

        if ConfigManager().loadedDefaults:
            QMessageBox.information(
                None, "First-time tips",
                "Welcome to DepthAI GUI. To use this tool efficiently, please do the following:\n\n1. Maximize the window\n2. Enable NodeBox widget (Tools > PyFlowBase > NodeBox)\n3. Enable Properties widget (Tools > PyFlowBase > Properties)\n\nHave fun!"
            )
        return instance