def add_transformation_at_index(
    component_model: ComponentTreeModel,
    component_tree_view: QTreeView,
    component_index: QModelIndex,
):
    """
    Adds a transformation to the component model at a given index.
    :param component_model: The component model.
    :param component_tree_view: The component tree view.
    :param component_index: The QModelIndex that is selected when the transformation is added.
    """
    component_tree_view.setCurrentIndex(component_index)
    component_model.add_transformation(component_index, TransformationType.TRANSLATION)
def add_link_at_index(
    component_model: ComponentTreeModel,
    component_tree_view: QTreeView,
    component_index: QModelIndex,
):
    """
    Adds a link to the component model at a given index.
    :param component_model: The component model.
    :param component_tree_view: The component tree view.
    :param component_index: The QModelIndex that is selected when the link is added.
    """
    component_tree_view.setCurrentIndex(component_index)
    component_model.add_link(component_index)
class Snippets(QDialog):

    def __init__(self, parent=None):
        super(Snippets, self).__init__(parent)
        # Create widgets
        self.setWindowModality(Qt.ApplicationModal)
        self.title = QLabel(self.tr("Snippet Editor"))
        self.saveButton = QPushButton(self.tr("Save"))
        self.closeButton = QPushButton(self.tr("Close"))
        self.clearHotkeyButton = QPushButton(self.tr("Clear Hotkey"))
        self.setWindowTitle(self.title.text())
        self.newFolderButton = QPushButton("New Folder")
        self.deleteSnippetButton = QPushButton("Delete")
        self.newSnippetButton = QPushButton("New Snippet")
        self.edit = QPlainTextEdit()
        self.edit.setPlaceholderText("python code")
        self.resetting = False
        self.columns = 3

        self.keySequenceEdit = QKeySequenceEdit(self)
        self.currentHotkey = QKeySequence()
        self.currentHotkeyLabel = QLabel("")
        self.currentFileLabel = QLabel()
        self.currentFile = ""
        self.snippetDescription = QLineEdit()
        self.snippetDescription.setPlaceholderText("optional description")

        #Set Editbox Size
        font = getMonospaceFont(self)
        self.edit.setFont(font)
        font = QFontMetrics(font)
        self.edit.setTabStopWidth(4 * font.width(' ')); #TODO, replace with settings API

        #Files
        self.files = QFileSystemModel()
        self.files.setRootPath(snippetPath)
        self.files.setNameFilters(["*.py"])

        #Tree
        self.tree = QTreeView()
        self.tree.setModel(self.files)
        self.tree.setSortingEnabled(True)
        self.tree.hideColumn(2)
        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.tree.setRootIndex(self.files.index(snippetPath))
        for x in range(self.columns):
            #self.tree.resizeColumnToContents(x)
            self.tree.header().setSectionResizeMode(x, QHeaderView.ResizeToContents) 
        treeLayout = QVBoxLayout()
        treeLayout.addWidget(self.tree)
        treeButtons = QHBoxLayout()
        treeButtons.addWidget(self.newFolderButton)
        treeButtons.addWidget(self.newSnippetButton)
        treeButtons.addWidget(self.deleteSnippetButton)
        treeLayout.addLayout(treeButtons)
        treeWidget = QWidget()
        treeWidget.setLayout(treeLayout)

        # Create layout and add widgets
        buttons = QHBoxLayout()
        buttons.addWidget(self.clearHotkeyButton)
        buttons.addWidget(self.keySequenceEdit)
        buttons.addWidget(self.currentHotkeyLabel)
        buttons.addWidget(self.closeButton)
        buttons.addWidget(self.saveButton)

        description = QHBoxLayout()
        description.addWidget(QLabel(self.tr("Description: ")))
        description.addWidget(self.snippetDescription)

        vlayoutWidget = QWidget()
        vlayout = QVBoxLayout()
        vlayout.addLayout(description)
        vlayout.addWidget(self.edit)
        vlayout.addLayout(buttons)
        vlayoutWidget.setLayout(vlayout)

        hsplitter = QSplitter()
        hsplitter.addWidget(treeWidget)
        hsplitter.addWidget(vlayoutWidget)

        hlayout = QHBoxLayout()
        hlayout.addWidget(hsplitter)

        self.showNormal() #Fixes bug that maximized windows are "stuck"
        self.settings = QSettings("Vector35", "Snippet Editor")
        if self.settings.contains("ui/snippeteditor/geometry"):
            self.restoreGeometry(self.settings.value("ui/snippeteditor/geometry"))
        else:
            self.edit.setMinimumWidth(80 * font.averageCharWidth())
            self.edit.setMinimumHeight(30 * font.lineSpacing())

        # Set dialog layout
        self.setLayout(hlayout)

        # Add signals
        self.saveButton.clicked.connect(self.save)
        self.closeButton.clicked.connect(self.close)
        self.clearHotkeyButton.clicked.connect(self.clearHotkey)
        self.tree.selectionModel().selectionChanged.connect(self.selectFile)
        self.newSnippetButton.clicked.connect(self.newFileDialog)
        self.deleteSnippetButton.clicked.connect(self.deleteSnippet)
        self.newFolderButton.clicked.connect(self.newFolder)

        if self.settings.contains("ui/snippeteditor/selected"):
            selectedName = self.settings.value("ui/snippeteditor/selected")
            self.tree.selectionModel().select(self.files.index(selectedName), QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
            if self.tree.selectionModel().hasSelection():
                self.selectFile(self.tree.selectionModel().selection(), None)
                self.edit.setFocus()
                cursor = self.edit.textCursor()
                cursor.setPosition(self.edit.document().characterCount()-1)
                self.edit.setTextCursor(cursor)
            else:
                self.readOnly(True)
        else:
            self.readOnly(True)


    @staticmethod
    def registerAllSnippets():
        for action in list(filter(lambda x: x.startswith("Snippets\\"), UIAction.getAllRegisteredActions())):
            if action == "Snippets\\Snippet Editor...":
                continue
            UIActionHandler.globalActions().unbindAction(action)
            Menu.mainMenu("Tools").removeAction(action)
            UIAction.unregisterAction(action)

        for snippet in includeWalk(snippetPath, ".py"):
            snippetKeys = None
            (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(snippet)
            if not snippetDescription:
                actionText = "Snippets\\" + os.path.basename(snippet).rstrip(".py")
            else:
                actionText = "Snippets\\" + snippetDescription
            if snippetCode:
                if snippetKeys == None:
                    UIAction.registerAction(actionText)
                else:
                    UIAction.registerAction(actionText, snippetKeys)
                UIActionHandler.globalActions().bindAction(actionText, UIAction(makeSnippetFunction(snippetCode)))
                Menu.mainMenu("Tools").addAction(actionText, actionText)

    def clearSelection(self):
        self.keySequenceEdit.clear()
        self.currentHotkey = QKeySequence()
        self.currentHotkeyLabel.setText("")
        self.currentFileLabel.setText("")
        self.snippetDescription.setText("")
        self.edit.setPlainText("")
        self.currentFile = ""

    def reject(self):
        self.settings.setValue("ui/snippeteditor/geometry", self.saveGeometry())

        if self.snippetChanged():
            question = QMessageBox.question(self, self.tr("Discard"), self.tr("You have unsaved changes, quit anyway?"))
            if question != QMessageBox.StandardButton.Yes:
                return
        self.accept()

    def newFolder(self):
        (folderName, ok) = QInputDialog.getText(self, self.tr("Folder Name"), self.tr("Folder Name: "))
        if ok and folderName:
            index = self.tree.selectionModel().currentIndex()
            selection = self.files.filePath(index)
            if QFileInfo(selection).isDir():
                QDir(selection).mkdir(folderName)
            else:
                QDir(snippetPath).mkdir(folderName)    

    def selectFile(self, new, old):
        if (self.resetting):
            self.resetting = False
            return
        newSelection = self.files.filePath(new.indexes()[0])
        self.settings.setValue("ui/snippeteditor/selected", newSelection)
        if QFileInfo(newSelection).isDir():
            self.readOnly(True)
            self.tree.clearSelection()
            self.currentFile = ""
            return

        if old and old.length() > 0:
            oldSelection = self.files.filePath(old.indexes()[0])
            if not QFileInfo(oldSelection).isDir() and self.snippetChanged():
                question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?"))
                if question != QMessageBox.StandardButton.Yes:
                    self.resetting = True
                    self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
                    return False

        self.currentFile = newSelection
        self.loadSnippet()

    def loadSnippet(self):
        self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName())
        (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile)
        self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("")
        self.keySequenceEdit.setKeySequence(snippetKeys) if snippetKeys else self.keySequenceEdit.setKeySequence(QKeySequence(""))
        self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("")
        self.readOnly(False)

    def newFileDialog(self):
        (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: "))
        if ok and snippetName:
            if not snippetName.endswith(".py"):
                snippetName += ".py"
            index = self.tree.selectionModel().currentIndex()
            selection = self.files.filePath(index)
            if QFileInfo(selection).isDir():
                path = os.path.join(selection, snippetName)
            else:
                path = os.path.join(snippetPath, snippetName)
                self.readOnly(False)
            open(path, "w").close()
            self.tree.setCurrentIndex(self.files.index(path))
            log_debug("Snippet %s created." % snippetName)

    def readOnly(self, flag):
        self.keySequenceEdit.setEnabled(not flag)
        self.snippetDescription.setReadOnly(flag)
        self.edit.setReadOnly(flag)
        if flag:
            self.snippetDescription.setDisabled(True)
            self.edit.setDisabled(True)
        else:
            self.snippetDescription.setEnabled(True)
            self.edit.setEnabled(True)

    def deleteSnippet(self):
        selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row
        snippetName = self.files.fileName(selection)
        question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName)
        if (question == QMessageBox.StandardButton.Yes):
            log_debug("Deleting snippet %s." % snippetName)
            self.clearSelection()
            self.files.remove(selection)
            self.registerAllSnippets()

    def snippetChanged(self):
        if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()):
            return False
        (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile)
        if snippetKeys == None and not self.keySequenceEdit.keySequence().isEmpty():
            return True
        if snippetKeys != None and snippetKeys != self.keySequenceEdit.keySequence().toString():
            return True
        return self.edit.toPlainText() != snippetCode or \
               self.snippetDescription.text() != snippetDescription

    def save(self):
        log_debug("Saving snippet %s" % self.currentFile)
        outputSnippet = open(self.currentFile, "w")
        outputSnippet.write("#" + self.snippetDescription.text() + "\n")
        outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n")
        outputSnippet.write(self.edit.toPlainText())
        outputSnippet.close()
        self.registerAllSnippets()

    def clearHotkey(self):
        self.keySequenceEdit.clear()
Exemple #4
0
class WTreeEdit(QWidget):
    """TreeEdit widget is to show and edit all of the pyleecan objects data."""

    # Signals
    dataChanged = Signal()

    def __init__(self, obj, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)

        self.class_dict = ClassInfo().get_dict()
        self.treeDict = None  # helper to track changes
        self.obj = obj  # the object
        self.is_save_needed = False

        self.model = TreeEditModel(obj)

        self.setupUi()

        # === Signals ===
        self.selectionModel.selectionChanged.connect(self.onSelectionChanged)
        self.treeView.collapsed.connect(self.onItemCollapse)
        self.treeView.expanded.connect(self.onItemExpand)
        self.treeView.customContextMenuRequested.connect(self.openContextMenu)
        self.model.dataChanged.connect(self.onDataChanged)
        self.dataChanged.connect(self.setSaveNeeded)

        # === Finalize ===
        # set 'root' the selected item and resize columns
        self.treeView.setCurrentIndex(self.treeView.model().index(0, 0))
        self.treeView.resizeColumnToContents(0)

    def setupUi(self):
        """Setup the UI"""
        # === Widgets ===
        # TreeView
        self.treeView = QTreeView()
        # self.treeView.rootNode = model.invisibleRootItem()
        self.treeView.setModel(self.model)
        self.treeView.setAlternatingRowColors(False)

        # self.treeView.setColumnWidth(0, 150)
        self.treeView.setMinimumWidth(100)

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.selectionModel = self.treeView.selectionModel()

        self.statusBar = QStatusBar()
        self.statusBar.setSizeGripEnabled(False)
        self.statusBar.setSizePolicy(QSizePolicy.Expanding,
                                     QSizePolicy.Maximum)
        self.statusBar.setStyleSheet(
            "QStatusBar {border: 1px solid rgb(200, 200, 200)}")
        self.saveLabel = QLabel("unsaved")
        self.saveLabel.setVisible(False)
        self.statusBar.addPermanentWidget(self.saveLabel)

        # Splitters
        self.leftSplitter = QSplitter()
        self.leftSplitter.setStretchFactor(0, 0)
        self.leftSplitter.setStretchFactor(1, 1)

        # === Layout ===
        # Horizontal Div.
        self.hLayout = QVBoxLayout()
        self.hLayout.setContentsMargins(0, 0, 0, 0)
        self.hLayout.setSpacing(0)

        # add widgets to layout
        self.hLayout.addWidget(self.leftSplitter)
        self.hLayout.addWidget(self.statusBar)

        # add widgets
        self.leftSplitter.addWidget(self.treeView)

        self.setLayout(self.hLayout)

    def update(self, obj):
        """Check if object has changed and update tree in case."""
        if not obj is self.obj:
            self.obj = obj
            self.model = TreeEditModel(obj)
            self.treeView.setModel(self.model)
            self.model.dataChanged.connect(self.onDataChanged)
            self.selectionModel = self.treeView.selectionModel()
            self.selectionModel.selectionChanged.connect(
                self.onSelectionChanged)
            self.treeView.setCurrentIndex(self.treeView.model().index(0, 0))
            self.setSaveNeeded(True)

    def setSaveNeeded(self, state=True):
        self.is_save_needed = state
        self.saveLabel.setVisible(state)

    def openContextMenu(self, point):
        """Generate and open context the menu at the given point position."""
        index = self.treeView.indexAt(point)
        pos = QtGui.QCursor.pos()

        if not index.isValid():
            return

        # get the data
        item = self.model.item(index)
        obj_info = self.model.get_obj_info(item)

        # init the menu
        menu = TreeEditContextMenu(obj_dict=obj_info, parent=self)
        menu.exec_(pos)

        self.onSelectionChanged(self.selectionModel.selection())

    def onItemCollapse(self, index):
        """Slot for item collapsed"""
        # dynamic resize
        for ii in range(3):
            self.treeView.resizeColumnToContents(ii)

    def onItemExpand(self, index):
        """Slot for item expand"""
        # dynamic resize
        for ii in range(3):
            self.treeView.resizeColumnToContents(ii)

    def onDataChanged(self, first=None, last=None):
        """Slot for changed data"""
        self.dataChanged.emit()
        self.onSelectionChanged(self.selectionModel.selection())

    def onSelectionChanged(self, itemSelection):
        """Slot for changed item selection"""
        # get the index
        if itemSelection.indexes():
            index = itemSelection.indexes()[0]
        else:
            index = self.treeView.model().index(0, 0)
            self.treeView.setCurrentIndex(index)
            return

        # get the data
        item = self.model.item(index)
        obj = item.object()
        typ = type(obj).__name__
        obj_info = self.model.get_obj_info(item)
        ref_typ = obj_info["ref_typ"] if obj_info else None

        # set statusbar information on class typ
        msg = f"{typ} (Ref: {ref_typ})" if ref_typ else f"{typ}"
        self.statusBar.showMessage(msg)

        # --- choose the respective widget by class type ---
        # numpy array -> table editor
        if typ == "ndarray":
            widget = WTableData(obj, editable=True)
            widget.dataChanged.connect(self.dataChanged.emit)

        elif typ == "MeshSolution":
            widget = WMeshSolution(obj)  # only a view (not editable)

        # list (no pyleecan type, non empty) -> table editor
        # TODO add another widget for lists of non 'primitive' types (e.g. DataND)
        elif isinstance(obj, list) and not self.isListType(ref_typ) and obj:
            widget = WTableData(obj, editable=True)
            widget.dataChanged.connect(self.dataChanged.emit)

        # generic editor
        else:
            # widget = SimpleInputWidget().generate(obj)
            widget = WTableParameterEdit(obj)
            widget.dataChanged.connect(self.dataChanged.emit)

        # show the widget
        if self.leftSplitter.widget(1) is None:
            self.leftSplitter.addWidget(widget)
        else:
            self.leftSplitter.replaceWidget(1, widget)
            widget.setParent(
                self.leftSplitter)  # workaround for PySide2 replace bug
            widget.show()
        pass

    def isListType(self, typ):
        if not typ:
            return False
        return typ[0] == "[" and typ[-1] == "]" and typ[1:-1] in self.class_dict

    def isDictType(self, typ):
        if not typ:
            return False
        return typ[0] == "{" and typ[-1] == "}" and typ[1:-1] in self.class_dict
Exemple #5
0
class BrowserWidget(QWidget):
    """
    This class puts together a TabCompletionLineEdit and a list view of teh FolderListModel in one single widget.
    """

    activated = Signal(str)  # emitted when the user selects a file

    def __init__(self, parent=None):
        super().__init__(parent)
        self._recursiveActivated = False
        self._model = FolderListModel(self)
        self._completer = QCompleter(self._model, self)
        self._completer.setCompletionColumn(3)
        self._lineedit = TabCompletionLineEdit(self._completer, self)
        self._view = QTreeView(self)
        self._view.setModel(self._model)
        self._model.folderChanged.connect(self._lineedit.setText)
        self._model.setFolder(".")
        self._view.header().setSectionResizeMode(0, QHeaderView.Interactive)
        self._view.header().setSectionResizeMode(1,
                                                 QHeaderView.ResizeToContents)
        self._view.header().setSectionResizeMode(2,
                                                 QHeaderView.ResizeToContents)
        self._view.header().resizeSection(0, 500)
        self._view.header().setSectionHidden(3, True)
        layout = QVBoxLayout(self)
        layout.addWidget(self._lineedit)
        layout.addWidget(self._view)
        self.setLayout(layout)
        self._view.activated.connect(self._activated)
        self.activated.connect(self._lineedit.setText)
        self._lineedit.returnPressed.connect(self._leActivated,
                                             Qt.QueuedConnection)
        self._lineedit.textEdited.connect(self._leTextEdited)

    def setActive(self, activeFile):
        """
        set the activated file

        :param activeFile: a string or Path instance
        :return:
        """
        activeFile = Path(activeFile)
        assert activeFile.is_file()
        self._model.setFolder(activeFile.parent)
        idx = self._model.fileToIndex(activeFile)
        self._view.setCurrentIndex(idx)
        self._view.scrollTo(idx)
        self.activated.emit(str(activeFile))

    def active(self):
        """
        The currently activated file

        :return: a string instance
        """
        cidx = self._view.currentIndex()
        c = self._model.data(cidx, Qt.UserRole)
        return str(c) if c is not None else None

    def current(self):
        """
        A synonym for active()

        :return: a string instance
        """
        return self.active()

    def setFilter(self, flt):
        """
        Set the name filter of the file browser

        :param flt: a string instance or a list of strings
        :return:
        """
        self._model.setFilter(flt)

    def scrollTo(self, item):
        """
        Scrolls to the given item.

        :param item: a string instance
        :return:
        """
        cidx = self._model.fileToIndex(item)
        self._view.scrollTo(cidx)

    def folder(self):
        """
        Returns the current folder

        :return: a Path instance
        """
        return self._model.folder()

    def setFolder(self, folder):
        """
        Sets the current folder

        :param folder: a string or a Path instance
        :return:
        """
        self._model.setFolder(folder)

    def _leActivated(self):
        idx = self._model.fileToIndex(self._lineedit.text())
        self._activated(idx)

    def _leTextEdited(self, text):
        p = Path(text)
        if p.is_dir() and len(text) > 0 and text[-1] in ["/", "\\"]:
            self.setFolder(p)

    def _activated(self, idx):
        c = self._model.data(idx, Qt.UserRole)
        if c is None:
            return
        logger.debug("activate %s", c)
        if c.is_file():
            self.activated.emit(str(c))
        else:
            self._model.setFolder(c)
Exemple #6
0
 def for_selecting_current_item_in_tree_view(self, tree_view: QTreeView):
     return ModelIndexNavigator(
         is_expanded_callback=lambda x: tree_view.isExpanded(x),
         expand_callback=lambda x: tree_view.expand(x),
         final_item_callback=lambda x: tree_view.setCurrentIndex(x))
Exemple #7
0
class FileSystemWidget(QWidget):
    def __init__(self, parent, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.currentRootPath = '/'
        self.currentPath = QDir.currentPath()

        self.mainWindow = parent

        self.chooseDirAction = QAction(IconFactory.getIcon('folder'),
                                       'Root directory',
                                       self,
                                       statusTip="Change root directory",
                                       triggered=self.chooseRootDir)
        self.showOFAction = QAction(IconFactory.getIcon('filter_alt'),
                                    'Show only FITS files',
                                    self,
                                    statusTip="Show only FITS/all files",
                                    triggered=self.showOFFiles)
        self.showOFAction.setCheckable(True)
        self.showOFAction.toggled.connect(self.showOFFiles)

        self.chooseDirBtn = QToolButton()
        self.chooseDirBtn.setDefaultAction(self.chooseDirAction)

        self.showOFBtn = QToolButton()
        self.showOFBtn.setDefaultAction(self.showOFAction)

        iconlayout = QHBoxLayout()
        iconlayout.setAlignment(Qt.AlignLeft)
        iconlayout.addWidget(self.chooseDirBtn)
        iconlayout.addWidget(self.showOFBtn)

        self.viewsSplitter = QSplitter(Qt.Horizontal)
        self.viewsSplitter.splitterMoved.connect(self.splitterMoved)

        self.dirsModel = QFileSystemModel(self)
        self.dirsModel.setOption(QFileSystemModel.DontWatchForChanges, True)
        self.dirsModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
        self.dirsModel.setNameFilterDisables(False)

        self.dirs = QTreeView()
        self.dirs.setModel(self.dirsModel)
        self.dirs.hideColumn(1)
        self.dirs.hideColumn(2)
        self.dirs.hideColumn(3)

        self.dirs.clicked.connect(self.onDirsClick)
        self.dirs.doubleClicked.connect(self.onDirsDoubleClick)

        self.filesModel = QFileSystemModel(self)
        self.filesModel.setOption(QFileSystemModel.DontWatchForChanges, True)
        self.filesModel.setFilter(QDir.NoDotAndDotDot | QDir.Files)
        self.filesModel.setNameFilterDisables(False)

        self.files = QListView()
        self.files.setModel(self.filesModel)
        self.files.doubleClicked.connect(self.onFilesDoubleClick)

        self.viewsSplitter.addWidget(self.dirs)
        self.viewsSplitter.addWidget(self.files)
        viewslayout = QHBoxLayout()
        viewslayout.addWidget(self.viewsSplitter)

        layout = QVBoxLayout()
        layout.addLayout(iconlayout)
        layout.addLayout(viewslayout)

        self.setLayout(layout)

        self.dirsModel.setRootPath(self.currentRootPath)
        self.dirs.setRootIndex(self.dirsModel.index(self.currentRootPath))

        index = self.dirsModel.index(self.currentPath)
        self.dirs.setCurrentIndex(index)
        self.dirs.setExpanded(index, True)

        self.filesModel.setRootPath(self.currentPath)
        self.files.setRootIndex(self.filesModel.index(self.currentPath))

    def splitterMoved(self, pos, index):
        if pos == 0:
            self.filesModel.setFilter(QDir.NoDot | QDir.AllEntries
                                      | QDir.DirsFirst | QDir.Type)
        elif pos == self.viewsSplitter.width(
        ) - self.viewsSplitter.handleWidth():
            self.dirsModel.setFilter(QDir.NoDotAndDotDot | QDir.AllEntries)
        else:
            self.dirsModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
            self.filesModel.setFilter(QDir.NoDotAndDotDot | QDir.Files)

    def onDirsClick(self, item):
        index = self.dirs.selectedIndexes()[0]
        info = self.dirsModel.fileInfo(index)
        if info.isDir():
            self.currentPath = info.filePath()
            self.files.setRootIndex(
                self.filesModel.setRootPath(info.filePath()))

    def onDirsDoubleClick(self, item):
        index = self.dirs.selectedIndexes()[0]
        info = self.dirsModel.fileInfo(index)
        if info.isDir():
            self.currentPath = info.filePath()
            self.files.setRootIndex(
                self.filesModel.setRootPath(info.filePath()))
        else:
            self.mainWindow.open_fits(info.filePath())

    def onFilesDoubleClick(self, item):
        index = self.files.selectedIndexes()[0]
        info = self.filesModel.fileInfo(index)
        if info.isDir():
            self.setPath(info.filePath())
        else:
            try:
                self.mainWindow.open_fits(info.filePath())
            except FileNotFoundError:
                self.setPath(self.currentPath)  # refesh maybe?

    def setPath(self, path):
        self.currentPath = path

        index = self.dirsModel.index(self.currentPath)
        self.dirs.setCurrentIndex(index)
        self.dirs.setExpanded(index, True)

        self.files.setRootIndex(self.filesModel.setRootPath(self.currentPath))

    def chooseRootDir(self):
        dir = QFileDialog.getExistingDirectory(self, 'Select directory')
        if dir:
            self.setRootPath(dir)

    def setRootPath(self, dir):
        self.currentRootPath = dir

        self.dirsModel.setRootPath(self.currentRootPath)
        self.dirs.setRootIndex(self.dirsModel.index(self.currentRootPath))

        self.setPath(self.currentRootPath)

    def showOFFiles(self):
        if self.showOFAction.isChecked():
            self.dirsModel.setNameFilters(["*.FITS", "*.fits"])
            self.filesModel.setNameFilters(["*.FITS", "*.fits"])
        else:
            self.dirsModel.setNameFilters(["*"])
            self.filesModel.setNameFilters(["*"])

    def writeSettings(self, settings):
        settings.beginGroup("fileWidget")
        settings.setValue('splitterGeometry',
                          self.viewsSplitter.saveGeometry())
        settings.setValue('splitterState', self.viewsSplitter.saveState())
        settings.setValue('rootPath', self.currentRootPath)
        settings.setValue('path', self.currentPath)
        settings.endGroup()

    def readSettings(self, settings):
        settings.beginGroup("fileWidget")
        self.viewsSplitter.restoreGeometry(settings.value("splitterGeometry"))
        self.viewsSplitter.restoreState(settings.value("splitterState"))
        rootPath = settings.value("rootPath")
        path = settings.value("path")
        settings.endGroup()

        if rootPath is None:
            rootPath = '/'
        self.setRootPath(rootPath)

        if path is None:
            path = QDir.currentPath()
        self.setPath(path)

        self.splitterMoved(self.viewsSplitter.handle(1).pos().x(), 0)
class ClassTreeWidget(QDialog):
    """
    TreeView widget to show pyleecan classes structured by their inheritance together
    with the selected class description.
    """
    def __init__(self, keys=None, expand=True):
        super(ClassTreeWidget, self).__init__()
        self.setupUi()
        self.expandAll = expand
        self.setMinimumHeight(600)

        # === default variables ===
        self.classDict = ClassInfo().get_dict()
        self.keys = keys or ClassInfo().get_base_classes()  # TODO all classes
        self.selectionModel = self.treeView.selectionModel()

        # === Signals ===
        self.selectionModel.selectionChanged.connect(self.onSelectionChanged)
        self.treeView.doubleClicked.connect(self.onClassSelected)
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)

        # === Generate content ===
        self.generate()

    def onClassSelected(self, index):
        """Method to accept the selection if a class was double clicked."""
        if index.isValid():
            self.accept()

    def onSelectionChanged(self, itSelection):
        """ """
        index = itSelection.indexes()[0]
        desc = index.model().itemFromIndex(index).data()
        self.text.setText(desc)

    def getSelectedClass(self):
        """Get the currently selected class by its name."""
        index = self.selectionModel.selectedIndexes()[0]
        return index.model().itemFromIndex(index).text()

    def setupUi(self):
        """Init. the UI."""
        self.setWindowIcon(QIcon(ICON))
        # === Widgets ===
        # TreeView
        model = QtGui.QStandardItemModel(0, 1)
        model.setHorizontalHeaderLabels(["Class"])

        self.treeView = QTreeView()
        self.treeView.rootNode = model.invisibleRootItem()
        self.treeView.setModel(model)
        self.treeView.setAlternatingRowColors(False)

        # size options
        # setting min. width in self.generate to fit content

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)

        # Hide Debug Columns
        # self.treeView.hideColumn(1)

        # text output
        self.text = QLabel()
        self.text.setAlignment(Qt.AlignTop)
        self.text.setWordWrap(True)
        self.text.setMinimumWidth(300)

        # Splitters
        self.splitter = QSplitter(self)
        self.splitter.setStretchFactor(0, 0)
        self.splitter.setStretchFactor(1, 1)
        self.splitter.addWidget(self.treeView)
        self.splitter.addWidget(self.text)

        # dialog buttons
        self.buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )  # window to create confirmation and cancellation buttons

        # === Layout ===
        # Horizontal Div.
        layout = QVBoxLayout()

        # layout.setContentsMargins(0, 0, 0, 0)
        # layout.setSpacing(0)

        layout.addWidget(self.splitter)
        layout.addWidget(self.buttons)

        self.setLayout(layout)

    def generate(self):
        """Method to (recursively) build the tree (view) of the data object."""
        self.treeDict = dict()
        for key in self.keys:
            self.genTreeDict(key, self.treeDict)

        self.genTreeView(self.treeDict)

        # set first row active & expand all branches
        index = self.treeView.model().index(0, 0)
        self.treeView.setCurrentIndex(index)
        self.treeView.expandAll()
        wHint = self.treeView.sizeHintForColumn(0)
        self.treeView.setMinimumWidth(wHint)
        self.treeView.setColumnWidth(0, wHint)
        if not self.expandAll:
            self.treeView.collapseAll()

    def genTreeDict(self, key, parent):
        """Generate a dict structure of the classes recursively on the parent dict."""
        parent[key] = dict()
        for typ in self.classDict[key]["daughters"]:
            if key == self.classDict[typ]["mother"]:
                self.genTreeDict(typ, parent[key])

    def genTreeView(self, tree, parent=None):
        """Generate the view item structure on the parent item."""
        # init if root
        if parent is None:
            parent = self.treeView.rootNode
            self.treeView.rootNode.removeRows(
                0, self.treeView.rootNode.rowCount())

        for key, item in tree.items():
            desc = (
                f"Class: {key} \nPackage: {self.classDict[key]['package']}" +
                f"\nDescription: {self.classDict[key]['desc']}")
            row = self.addRow(parent, key, desc)

            if item:
                self.genTreeView(item, parent=row)

    def addRow(self, parent, name="", desc=""):
        """Add a new row to the parent item."""
        item = QtGui.QStandardItem(name)
        item.setEditable(False)
        item.setData(desc)
        parent.appendRow([item])
        return item