def __init__(self, mod_fs, *args, **kwargs): """ :param skymodman.utils.archivefs.ArchiveFS mod_fs: An instance of an ArchiveFS pseudo-filesystem. :param args: passed to base class constructors :param kwargs: passed to base class constructors :return: """ super().__init__(*args, **kwargs) self.LOGGER << "init manual install dlg" self.setupUi(self) # self.resize(1200, self.height()) self.structure = mod_fs self.num_to_copy = 0 # create undo framework ( self.undostack, self.action_undo, self.action_redo # , self.undoview ) = self.__setup_undo() # Create button overlay # self.main_overlay, self.tree_overlay, self.button_overlay = self.__setup_overlay() self.main_overlay = self.__setup_overlay() # initialize tree model from structure, give undostack ref self.modfsmodel = ModArchiveTreeModel(self) self.mod_structure_view.setModel(self.modfsmodel) self.mod_structure_view.customContextMenuRequested.connect(self.custom_context_menu) # set default for active_view self._active_view = self.mod_structure_view # self.mod_structure_column_view.setModel(self.modfsmodel) # self.mod_structure_column_view.owner = self # self.mod_structure_column_view.setResizeGripsVisible(False) # create custom context menu self.rclickmenu = self.__setup_context_menu() self.rclicked_inode = None ## connect actions self.__setup_actions() ## connect some more signals # noinspection PyUnresolvedReferences self.modfsmodel.rowsMoved.connect(self.check_top_level) # noinspection PyUnresolvedReferences self.modfsmodel.rowsInserted.connect(self.check_top_level) self.modfsmodel.folder_structure_changed.connect(self.check_top_level) self.mod_structure_view.setToolTip(_tree_tooltip) # self.mod_structure_column_view.setToolTip(_tree_tooltip) ## Hide the Trash folder self.mod_structure_view.setRowHidden( self.modfsmodel.row4path(self.modfsmodel.trash), self.mod_structure_view.rootIndex(), # should still be "/" at this point True, ) self._icon_ok = icons.get("status-ok") self._icon_notok = icons.get("status-bad") self.check_top_level()
def __init__(self, mod_fs, *args, **kwargs): """ :param skymodman.utils.archivefs.ArchiveFS mod_fs: An instance of an ArchiveFS pseudo-filesystem. :param args: passed to base class constructors :param kwargs: passed to base class constructors :return: """ super().__init__(*args, **kwargs) self.LOGGER << "init manual install dlg" self.setupUi(self) # self.resize(1200, self.height()) self.structure = mod_fs self.num_to_copy = 0 # create undo framework (self.undostack , self.action_undo , self.action_redo # , self.undoview ) = self.__setup_undo() # Create button overlay # self.main_overlay, self.tree_overlay, self.button_overlay = self.__setup_overlay() self.main_overlay = self.__setup_overlay() # initialize tree model from structure, give undostack ref self.modfsmodel = ModArchiveTreeModel(self) self.mod_structure_view.setModel(self.modfsmodel) self.mod_structure_view.customContextMenuRequested.connect( self.custom_context_menu) # set default for active_view self._active_view = self.mod_structure_view # self.mod_structure_column_view.setModel(self.modfsmodel) # self.mod_structure_column_view.owner = self # self.mod_structure_column_view.setResizeGripsVisible(False) # create custom context menu self.rclickmenu = self.__setup_context_menu() self.rclicked_inode = None ## connect actions self.__setup_actions() ## connect some more signals # noinspection PyUnresolvedReferences self.modfsmodel.rowsMoved.connect(self.check_top_level) # noinspection PyUnresolvedReferences self.modfsmodel.rowsInserted.connect(self.check_top_level) self.modfsmodel.folder_structure_changed.connect(self.check_top_level) self.mod_structure_view.setToolTip(_tree_tooltip) # self.mod_structure_column_view.setToolTip(_tree_tooltip) ## Hide the Trash folder self.mod_structure_view.setRowHidden( self.modfsmodel.row4path(self.modfsmodel.trash), self.mod_structure_view.rootIndex(), # should still be "/" at this point True) self._icon_ok = icons.get("status-ok") self._icon_notok = icons.get("status-bad") self.check_top_level()
class ManualInstallDialog(QDialog, Ui_mod_structure_dialog): def __init__(self, mod_fs, *args, **kwargs): """ :param skymodman.utils.archivefs.ArchiveFS mod_fs: An instance of an ArchiveFS pseudo-filesystem. :param args: passed to base class constructors :param kwargs: passed to base class constructors :return: """ super().__init__(*args, **kwargs) self.LOGGER << "init manual install dlg" self.setupUi(self) # self.resize(1200, self.height()) self.structure = mod_fs self.num_to_copy = 0 # create undo framework ( self.undostack, self.action_undo, self.action_redo # , self.undoview ) = self.__setup_undo() # Create button overlay # self.main_overlay, self.tree_overlay, self.button_overlay = self.__setup_overlay() self.main_overlay = self.__setup_overlay() # initialize tree model from structure, give undostack ref self.modfsmodel = ModArchiveTreeModel(self) self.mod_structure_view.setModel(self.modfsmodel) self.mod_structure_view.customContextMenuRequested.connect(self.custom_context_menu) # set default for active_view self._active_view = self.mod_structure_view # self.mod_structure_column_view.setModel(self.modfsmodel) # self.mod_structure_column_view.owner = self # self.mod_structure_column_view.setResizeGripsVisible(False) # create custom context menu self.rclickmenu = self.__setup_context_menu() self.rclicked_inode = None ## connect actions self.__setup_actions() ## connect some more signals # noinspection PyUnresolvedReferences self.modfsmodel.rowsMoved.connect(self.check_top_level) # noinspection PyUnresolvedReferences self.modfsmodel.rowsInserted.connect(self.check_top_level) self.modfsmodel.folder_structure_changed.connect(self.check_top_level) self.mod_structure_view.setToolTip(_tree_tooltip) # self.mod_structure_column_view.setToolTip(_tree_tooltip) ## Hide the Trash folder self.mod_structure_view.setRowHidden( self.modfsmodel.row4path(self.modfsmodel.trash), self.mod_structure_view.rootIndex(), # should still be "/" at this point True, ) self._icon_ok = icons.get("status-ok") self._icon_notok = icons.get("status-bad") self.check_top_level() # self.mod_structure_column_view.setSelectionModel(self.mod_structure_view.selectionModel()) # show colview by default while we're testing it # self.btn_colview.click() def __setup_undo(self): undostack = QtWidgets.QUndoStack() undoaction = undostack.createUndoAction(self, "Undo") undoaction.pyqtConfigure( shortcut=QKeySequence.Undo, icon=icons.get("undo", scale_factor=0.85, offset=(0, 0.1)), # icon=QIcon.fromTheme("edit-undo"), triggered=self.undo, ) redoaction = undostack.createRedoAction(self, "Redo") redoaction.pyqtConfigure( shortcut=QKeySequence.Redo, icon=icons.get("redo", scale_factor=0.85, offset=(0, 0.1)), # icon=QIcon.fromTheme("edit-redo"), triggered=self.redo, ) # undoview = QtWidgets.QUndoView(undostack) # undoview.show() # undoview.setAttribute(Qt.WA_QuitOnClose, False) return undostack, undoaction, redoaction # , None# undoview def __setup_overlay(self): tree_overlay = Overlay() tree_overlay.addWidget(self.view_switcher) ## set up stylesheet for buttons ## pal = QPalette() # get main text and highlight colors from palette txtcol = pal.color(QPalette.WindowText) txtcol.setAlphaF(0.5) # normal border is main text color @ 50% opacity border_color = "rgba{}".format(str(txtcol.getRgb())) # hovered border is palette highlight color # hover_border_color = "rgba{}".format(str(hlcol.getRgb())) # hovered btn-bg is highlight color @ 30% opacity hlcol = pal.color(QPalette.Highlight) hlcol.setAlphaF(0.3) hover_bg = "rgba{}".format(str(hlcol.getRgb())) btn_stylesheet = """QGroupBox { background: transparent; padding: 6px; } QToolButton { background: transparent; border: 1px solid %s; border-radius: 2px; } QToolButton:checked { background: palette(midlight); } QToolButton:hover { background: %s; border: 1px solid palette(highlight); } """ % ( border_color, hover_bg, ) ## create overlay for undo/redo buttons ## undo_overlay = Overlay("top", "right", btn_stylesheet) undo_overlay.addWidget(self.undo_btngroup) self.btn_undo.setDefaultAction(self.action_undo) self.btn_redo.setDefaultAction(self.action_redo) ## View switching buttons ## # chview_btngroup = QtWidgets.QButtonGroup(self.view_switcher) self.change_view_btngroup.setVisible(False) # chview_btngroup.addButton(self.btn_treeview) # chview_btngroup.setId(self.btn_treeview, 0) # # chview_btngroup.addButton(self.btn_colview) # chview_btngroup.setId(self.btn_colview, 1) # self.btn_treeview.setIcon(icons.get("view-tree")) # self.btn_colview.setIcon(icons.get("view-column")) # chview_btngroup.buttonClicked[int].connect(self.change_view) # and the overlay # chview_overlay = Overlay("bottom", "right", btn_stylesheet) # chview_overlay.addWidget(self.change_view_btngroup) ## create and populate main overlay ## main_overlay = OverlayCenter(self.fsview) main_overlay.addLayout(tree_overlay) main_overlay.addLayout(undo_overlay) # main_overlay.addLayout(chview_overlay) return main_overlay # , tree_overlay#, btn_overlay def __setup_context_menu(self): # here's the custom menu (actions will be made in/visible as required) rclickmenu = QMenu(self.mod_structure_view) rclickmenu.addActions( [ self.action_unset_toplevel, self.action_set_toplevel, self.action_rename, self.action_delete, self.action_create_directory, ] ) return rclickmenu def __setup_actions(self): self.action_set_toplevel.triggered.connect(self.set_toplevel) self.action_unset_toplevel.triggered.connect(self.unset_toplevel) self.action_rename.triggered.connect(self.rename) self.action_create_directory.triggered.connect(self.create_dir) self.action_delete.triggered.connect(self.delete_file) def get_rclickmenu(self, for_view): # here's the custom menu (actions will be made in/visible # as required) rclickmenu = QMenu(for_view) rclickmenu.addActions( [ self.action_unset_toplevel, self.action_set_toplevel, self.action_rename, self.action_delete, self.action_create_directory, ] ) return rclickmenu @property def fsroot(self) -> QModelIndex: """ Return the index of the current visible root """ return self.mod_structure_view.rootIndex() @fsroot.setter def fsroot(self, index: QModelIndex): """ Set the visible root to the path pointed to by `index` """ self.mod_structure_view.setRootIndex(index) # self.mod_structure_column_view.setRootIndex(index) self.modfsmodel.root = index self.check_top_level() def set_toplevel(self, *args): """ Set the visible root to the target of the last context-menu-event """ self.LOGGER << "set_toplevel()" self.fsroot = self.modfsmodel.index4inode(self.rclicked_inode) def unset_toplevel(self, *args): """ Reset the visible root to the default root for the fs. :param args: """ self.LOGGER << "unset_toplevel()" self.fsroot = QModelIndex() def rename(self, *args): # self.LOGGER << "rename()" self.mod_structure_view.edit(self.modfsmodel.index4inode(self.rclicked_inode)) def create_dir(self, *args): """ Create a new directory as a sibling (if the clicked item was a file) or child (if it was a folder) of the target of the last context-menu event, and open its name-editor. :param args: """ # self.LOGGER << "create_dir()" fsmod = self.modfsmodel if fsmod._isdir(self.rclicked_inode): parent = fsmod.inode2path(self.rclicked_inode) else: parent = fsmod.inode2path(self.rclicked_inode).parent startname = "New Folder" # make sure it's unique parent_ls = parent.ls(conv=str.lower) suffix = 0 while startname.lower() in parent_ls: suffix += 1 startname = "New Folder %d" % suffix # noinspection PyArgumentList,PyTypeChecker new_name = QInputDialog.getText(self, "New Folder", "Create new folder in:\n{}".format(parent), text=startname)[ 0 ] if new_name: fsmod.create_new_dir(parent, new_name) def show_context_menu(self, view, index, global_pos): topidx = self.fsroot # current root node if index.isValid(): self.rclicked_inode = index.internalId() else: self.rclicked_inode = topidx.internalId() user_set_root, clicked_isdir, non_root = ( topidx.isValid(), self.modfsmodel._isdir(self.rclicked_inode), index.isValid(), ) # adjust visible options # # ---------------------- # # show unset option if user has set custom root self.action_unset_toplevel.setVisible(user_set_root) # show set option if user clicked on directory (that is not the root) self.action_set_toplevel.setVisible(clicked_isdir and non_root) # show rename/delete options if user clicked on anything but root self.action_rename.setVisible(non_root) self.action_delete.setVisible(non_root) # always show create-dir option. # self.action_create_directory self.get_rclickmenu(view).exec_(global_pos) def custom_context_menu(self, position): clicked_index = self.mod_structure_view.indexAt(position) topidx = self.fsroot # current root node if clicked_index.isValid(): self.rclicked_inode = clicked_index.internalId() else: self.rclicked_inode = topidx.internalId() user_set_root, clicked_isdir, non_root = ( topidx.isValid(), self.modfsmodel._isdir(self.rclicked_inode), clicked_index.isValid(), ) # adjust visible options # # ---------------------- # # show unset option if user has set custom root self.action_unset_toplevel.setVisible(user_set_root) # show set option if user clicked on directory (that is not the root) self.action_set_toplevel.setVisible(clicked_isdir and non_root) # show rename/delete options if user clicked on anything but root self.action_rename.setVisible(non_root) self.action_delete.setVisible(non_root) # always show create-dir option. # self.action_create_directory self.rclickmenu.exec_(self.mod_structure_view.mapToGlobal(position)) # def check_top_level(self, parent=None, first=-1, last=-1, # dest=None, dest_row=-1): def check_top_level(self, *args): isvalid = self.modfsmodel.validate_mod_structure(self.fsroot) if isvalid: self.lbl_structure_icon.setPixmap(self._icon_ok.pixmap(22, 22)) else: self.lbl_structure_icon.setPixmap(self._icon_notok.pixmap(22, 22)) # print(isvalid) def delete_file(self): # so, in what myriad ways might this fail? self.modfsmodel.delete(self.rclicked_inode) def undo(self): self.undostack.undo() def redo(self): self.undostack.redo() # @pyqtSlot(int) # def change_view(self, widget_index): # self.view_switcher.setCurrentIndex(widget_index) # self._active_view = (self.mod_structure_view, # self.mod_structure_column_view # )[widget_index] def is_expanded(self, index): return self._active_view.isExpanded(index)
class ManualInstallDialog(QDialog, Ui_mod_structure_dialog): def __init__(self, mod_fs, *args, **kwargs): """ :param skymodman.utils.archivefs.ArchiveFS mod_fs: An instance of an ArchiveFS pseudo-filesystem. :param args: passed to base class constructors :param kwargs: passed to base class constructors :return: """ super().__init__(*args, **kwargs) self.LOGGER << "init manual install dlg" self.setupUi(self) # self.resize(1200, self.height()) self.structure = mod_fs self.num_to_copy = 0 # create undo framework (self.undostack , self.action_undo , self.action_redo # , self.undoview ) = self.__setup_undo() # Create button overlay # self.main_overlay, self.tree_overlay, self.button_overlay = self.__setup_overlay() self.main_overlay = self.__setup_overlay() # initialize tree model from structure, give undostack ref self.modfsmodel = ModArchiveTreeModel(self) self.mod_structure_view.setModel(self.modfsmodel) self.mod_structure_view.customContextMenuRequested.connect( self.custom_context_menu) # set default for active_view self._active_view = self.mod_structure_view # self.mod_structure_column_view.setModel(self.modfsmodel) # self.mod_structure_column_view.owner = self # self.mod_structure_column_view.setResizeGripsVisible(False) # create custom context menu self.rclickmenu = self.__setup_context_menu() self.rclicked_inode = None ## connect actions self.__setup_actions() ## connect some more signals # noinspection PyUnresolvedReferences self.modfsmodel.rowsMoved.connect(self.check_top_level) # noinspection PyUnresolvedReferences self.modfsmodel.rowsInserted.connect(self.check_top_level) self.modfsmodel.folder_structure_changed.connect(self.check_top_level) self.mod_structure_view.setToolTip(_tree_tooltip) # self.mod_structure_column_view.setToolTip(_tree_tooltip) ## Hide the Trash folder self.mod_structure_view.setRowHidden( self.modfsmodel.row4path(self.modfsmodel.trash), self.mod_structure_view.rootIndex(), # should still be "/" at this point True) self._icon_ok = icons.get("status-ok") self._icon_notok = icons.get("status-bad") self.check_top_level() # self.mod_structure_column_view.setSelectionModel(self.mod_structure_view.selectionModel()) # show colview by default while we're testing it # self.btn_colview.click() def __setup_undo(self): undostack = QtWidgets.QUndoStack() undoaction = undostack.createUndoAction(self, "Undo") undoaction.pyqtConfigure(shortcut=QKeySequence.Undo, icon=icons.get( "undo", scale_factor=0.85, offset=(0, 0.1)), # icon=QIcon.fromTheme("edit-undo"), # triggered=self.undo ) redoaction = undostack.createRedoAction(self, "Redo") redoaction.pyqtConfigure(shortcut=QKeySequence.Redo, icon=icons.get( "redo", scale_factor=0.85, offset=(0, 0.1)), # icon=QIcon.fromTheme("edit-redo"), # triggered=self.redo ) # undoview = QtWidgets.QUndoView(undostack) # undoview.show() # undoview.setAttribute(Qt.WA_QuitOnClose, False) return undostack, undoaction, redoaction #, None# undoview def __setup_overlay(self): tree_overlay = Overlay() tree_overlay.addWidget(self.view_switcher) ## set up stylesheet for buttons ## pal = QPalette() # get main text and highlight colors from palette txtcol = pal.color(QPalette.WindowText) txtcol.setAlphaF(0.5) # normal border is main text color @ 50% opacity border_color = f"rgba{str(txtcol.getRgb())}" # hovered border is palette highlight color # hover_border_color = "rgba{}".format(str(hlcol.getRgb())) # hovered btn-bg is highlight color @ 30% opacity hlcol = pal.color(QPalette.Highlight) hlcol.setAlphaF(0.3) hover_bg = f"rgba{str(hlcol.getRgb())}" btn_stylesheet = f"""QGroupBox {{ background: transparent; padding: 6px; }} QToolButton {{ background: transparent; border: 1px solid {border_color}; border-radius: 2px; }} QToolButton:checked {{ background: palette(midlight); }} QToolButton:hover {{ background: {hover_bg}; border: 1px solid palette(highlight); }} """ ## create overlay for undo/redo buttons ## undo_overlay = Overlay("top", "right", btn_stylesheet) undo_overlay.addWidget(self.undo_btngroup) self.btn_undo.setDefaultAction(self.action_undo) self.btn_redo.setDefaultAction(self.action_redo) ## View switching buttons ## # chview_btngroup = QtWidgets.QButtonGroup(self.view_switcher) self.change_view_btngroup.setVisible(False) # chview_btngroup.addButton(self.btn_treeview) # chview_btngroup.setId(self.btn_treeview, 0) # # chview_btngroup.addButton(self.btn_colview) # chview_btngroup.setId(self.btn_colview, 1) # self.btn_treeview.setIcon(icons.get("view-tree")) # self.btn_colview.setIcon(icons.get("view-column")) # chview_btngroup.buttonClicked[int].connect(self.change_view) # and the overlay # chview_overlay = Overlay("bottom", "right", btn_stylesheet) # chview_overlay.addWidget(self.change_view_btngroup) ## create and populate main overlay ## main_overlay = OverlayCenter(self.fsview) main_overlay.addLayout(tree_overlay) main_overlay.addLayout(undo_overlay) # main_overlay.addLayout(chview_overlay) return main_overlay#, tree_overlay#, btn_overlay def __setup_context_menu(self): # here's the custom menu (actions will be made in/visible as required) rclickmenu = QMenu(self.mod_structure_view) rclickmenu.addActions( [self.action_unset_toplevel, self.action_set_toplevel, self.action_rename, self.action_delete, self.action_create_directory]) return rclickmenu def __setup_actions(self): self.action_set_toplevel.triggered.connect( self.set_toplevel) self.action_unset_toplevel.triggered.connect( self.unset_toplevel) self.action_rename.triggered.connect(self.rename) self.action_create_directory.triggered.connect(self.create_dir) self.action_delete.triggered.connect(self.delete_file) def get_rclickmenu(self, for_view): # here's the custom menu (actions will be made in/visible # as required) rclickmenu = QMenu(for_view) rclickmenu.addActions( [self.action_unset_toplevel, self.action_set_toplevel, self.action_rename, self.action_delete, self.action_create_directory]) return rclickmenu @property def fsroot(self) -> QModelIndex: """ Return the index of the current visible root """ return self.mod_structure_view.rootIndex() @fsroot.setter def fsroot(self, index: QModelIndex): """ Set the visible root to the path pointed to by `index` """ self.mod_structure_view.setRootIndex(index) # self.mod_structure_column_view.setRootIndex(index) self.modfsmodel.root = index self.check_top_level() def set_toplevel(self, *args): """ Set the visible root to the target of the last context-menu-event """ self.LOGGER << "set_toplevel()" self.fsroot = self.modfsmodel.index4inode(self.rclicked_inode) def unset_toplevel(self, *args): """ Reset the visible root to the default root for the fs. :param args: """ self.LOGGER << "unset_toplevel()" self.fsroot = QModelIndex() def rename(self, *args): # self.LOGGER << "rename()" self.mod_structure_view.edit( self.modfsmodel.index4inode(self.rclicked_inode)) def create_dir(self, *args): """ Create a new directory as a sibling (if the clicked item was a file) or child (if it was a folder) of the target of the last context-menu event, and open its name-editor. :param args: """ # self.LOGGER << "create_dir()" fsmod = self.modfsmodel if fsmod._isdir(self.rclicked_inode): parent = fsmod.inode2path(self.rclicked_inode) else: parent = fsmod.inode2path(self.rclicked_inode).parent startname = "New Folder" #make sure it's unique parent_ls = parent.ls(conv=str.lower) suffix = 0 while startname.lower() in parent_ls: suffix+=1 startname = "New Folder %d" % suffix # noinspection PyArgumentList,PyTypeChecker new_name = QInputDialog.getText(self, "New Folder", f"Create new folder in:\n{parent}", text=startname)[0] if new_name: fsmod.create_new_dir(parent, new_name) def show_context_menu(self, view, index, global_pos): topidx = self.fsroot # current root node if index.isValid(): self.rclicked_inode = index.internalId() else: self.rclicked_inode = topidx.internalId() user_set_root, clicked_isdir, non_root = (topidx.isValid(), self.modfsmodel._isdir( self.rclicked_inode), index.isValid() ) # adjust visible options # # ---------------------- # # show unset option if user has set custom root self.action_unset_toplevel.setVisible(user_set_root) # show set option if user clicked on directory (that is not the root) self.action_set_toplevel.setVisible(clicked_isdir and non_root) # show rename/delete options if user clicked on anything but root self.action_rename.setVisible(non_root) self.action_delete.setVisible(non_root) # always show create-dir option. # self.action_create_directory self.get_rclickmenu(view).exec_(global_pos) def custom_context_menu(self, position): clicked_index = self.mod_structure_view.indexAt(position) topidx = self.fsroot # current root node non_root = clicked_index.isValid() if non_root: self.rclicked_inode = clicked_index.internalId() else: self.rclicked_inode = topidx.internalId() user_set_root = topidx.isValid() clicked_isdir = self.modfsmodel._isdir(self.rclicked_inode) # user_set_root, clicked_isdir, non_root = (topidx.isValid(), # self.modfsmodel._isdir(self.rclicked_inode), # clicked_index.isValid() # ) # adjust visible options # # ---------------------- # # show unset option if user has set custom root self.action_unset_toplevel.setVisible(user_set_root) # show set option if user clicked on directory (that is not the root) self.action_set_toplevel.setVisible(clicked_isdir and non_root) # show rename/delete options if user clicked on anything but root self.action_rename.setVisible(non_root) self.action_delete.setVisible(non_root) # always show create-dir option. # self.action_create_directory self.rclickmenu.exec_(self.mod_structure_view.mapToGlobal(position)) # def check_top_level(self, parent=None, first=-1, last=-1, # dest=None, dest_row=-1): def check_top_level(self, *args): isvalid = self.modfsmodel.validate_mod_structure(self.fsroot) if isvalid: self.lbl_structure_icon.setPixmap(self._icon_ok.pixmap(22, 22)) else: self.lbl_structure_icon.setPixmap( self._icon_notok.pixmap(22, 22)) # print(isvalid) def delete_file(self): # so, in what myriad ways might this fail? self.modfsmodel.delete(self.rclicked_inode) # def undo(self): # self.undostack.undo() # def redo(self): # self.undostack.redo() # @pyqtSlot(int) # def change_view(self, widget_index): # self.view_switcher.setCurrentIndex(widget_index) # self._active_view = (self.mod_structure_view, # self.mod_structure_column_view # )[widget_index] def is_expanded(self, index): return self._active_view.isExpanded(index)