コード例 #1
0
class ViewMenuButton(QPushButton):

    sig_action = None

    def __init__(self, btn_name: str, show_field, actions_map: list):
        """
        Creates a QPushButton with a dropdown menu on it.

        :param btn_name: Button name (always displayed)
        :type btn_name: str
        :param actions_map: list of actions to put in the dropdown menu [(action_name, action_key), (separator_name), ...]
        :type actions_map: list
        :param show_field: entry field to display on create operations
        :type show_field: function
        """
        QPushButton.__init__(self, btn_name)

        self.menu = QMenu(self)

        for a in actions_map:
            if a == 'sep':  # This is a separator
                self.menu.addSeparator()
            else:  # Regular action
                t, k = a
                if k.startswith(
                        'create_'
                ):  # If we have a create feature, we display the entry field
                    self.menu.addAction(t, lambda k=k: show_field(k))
                else:
                    self.menu.addAction(t, lambda k=k: self.sig_action.emit(k))

        self.setMenu(self.menu)
        self.menu.setStyleSheet(get_stylesheet("menu"))
コード例 #2
0
class ProjectItemButton(ProjectItemButtonBase):
    def __init__(self, toolbox, icon, item_type, parent=None):
        super().__init__(toolbox, icon, item_type, parent=parent)
        self.setToolTip(
            f"<p>Drag-and-drop this onto the Design View to create a new <b>{item_type}</b> item.</p>"
        )
        if not toolbox.supports_specification(item_type):
            self._list_view = None
            self._menu = None
            return
        self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        self._list_view = ProjectItemDragListView()
        self._list_view.doubleClicked.connect(toolbox.edit_specification)
        self._list_view.context_menu_requested.connect(
            toolbox.show_specification_context_menu)
        self._list_widget = _CreateNewSpecListWidget(item_type)
        self._list_widget.itemClicked.connect(
            lambda _, item_type=item_type: toolbox.show_specification_form(
                item_type))
        self._menu = QMenu(self)  # Drop-down menu
        widget_action = CustomWidgetAction(self._menu)
        widget_action.setDefaultWidget(self._list_view)
        self._menu.addAction(widget_action)
        widget_action = CustomWidgetAction(self._menu)
        widget_action.setDefaultWidget(self._list_widget)
        self._menu.addAction(widget_action)
        self.setMenu(self._menu)
        self.setPopupMode(QToolButton.MenuButtonPopup)
        self._resize()
        model = toolbox.filtered_spec_factory_models.get(self.item_type)
        self._list_view.setModel(model)
        model.rowsInserted.connect(lambda *args: self._resize())
        model.rowsRemoved.connect(lambda *args: self._resize())
        model.modelReset.connect(lambda *args: self._resize())
        self._list_view.drag_about_to_start.connect(self._menu.hide)

    def set_menu_color(self, color):
        if self._menu:
            self._menu.setStyleSheet(
                f"QMenu{{background: {make_icon_background(color)};}}")

    def setIconSize(self, size):
        super().setIconSize(size)
        if self._list_view:
            self._list_view.setIconSize(size)

    def _resize(self):
        self._list_view._set_preferred_height()
        self._list_widget._set_preferred_height()
        width = max(self._list_view._get_preferred_width(),
                    self._list_widget._get_preferred_width())
        self._list_view.setFixedWidth(width)
        self._list_widget.setFixedWidth(width)
        event = QResizeEvent(QSize(), self.menu().size())
        QApplication.sendEvent(self.menu(), event)

    def _make_mime_data_text(self):
        return ",".join([self.item_type, ""])
コード例 #3
0
    def __init__(self, root):
        self.root = root
        self.app = QApplication([])
        self.icons = {
            "timelapse": QIcon(resource_path('icons/timelapse.png')),
            "sync": QIcon(resource_path('icons/sync.png')),
            "sync_disabled": QIcon(resource_path('icons/sync_disabled.png')),
            "logo": QIcon(resource_path('icons/logo.png')),
            "settings": QIcon(resource_path('icons/settings.png')),
            "github": QIcon(resource_path('icons/github.png'))
        }

        self.main_window = MainWindow(self)
        self.offset_window = None
        self.settings_window = None
        self.messages = [[], []]
        self.section_labels = [
            self.main_window.ui.right_status,
            self.main_window.ui.right_status_2
        ]

        menu = QMenu('FS Time Sync')
        menu.setStyleSheet("""
        QMenu {
            background-color: #151515;
            color: #ffffff;
        }
        QMenu::item {
            padding: 5px 10px 5px 10px;
        }
        QMenu::item:selected {
            background-color: #ffffff;
            color: #151515;
        }
        """)
        self.tray_actions = {}
        self.tray_actions["sync_now"] = menu.addAction("Sync Now")
        self.tray_actions["sync_now"].triggered.connect(
            lambda: self.root.sync_sim(force=True))
        self.tray_actions["hide_show"] = menu.addAction("Hide")
        self.tray_actions["hide_show"].triggered.connect(self.hide)
        self.tray_actions["exit"] = menu.addAction("Exit")
        self.tray_actions["exit"].triggered.connect(self.exit)

        self.tray = QSystemTrayIcon()
        self.tray.setIcon(self.icons['logo'])
        self.tray.setToolTip("FS Time Sync")
        self.tray.setContextMenu(menu)
        self.tray.activated.connect(self.trayActivated)
        self.tray.show()
コード例 #4
0
    def contextMenuEvent(self, event):

        if not self.is_selected:
            self.select()

        # build context menu
        menu = QMenu(self)
        menu.setStyleSheet("background-color: rgb(68,68,68);")
        #  instance_displ_act = menu.addAction("Display Instances")
        #  bb_displ_act = menu.addAction("Display Bounding Box")
        #  bbs_displ_act = menu.addAction("Display Bounding Boxes")
        #  hide_act = menu.addAction("Hide Selected Setup(s)")
        #  menu.addSeparator()
        #  convert_particle_act = menu.addAction("Convert to Particles")
        #  convert_particle_act = menu.addAction("Write to Alembic")
        #  convert_particle_act = menu.addAction("Write to Katana Live Group")
        #  convert_particle_act = menu.addAction("Combine Selected Setups")
        menu.addSeparator()
        remove_act = menu.addAction("Delete")

        action = menu.exec_(self.mapToGlobal(event.pos()))
        self.context_requested.emit(self, action)
コード例 #5
0
 def __init__(self, toolbox, icon, item_type, supports_specs, parent=None):
     super().__init__(parent=parent)
     self._toolbox = toolbox
     self.item_type = item_type
     self.setIcon(icon)
     self.setMouseTracking(True)
     self.setToolTip(
         f"<p>Drag-and-drop this onto the Design View to create a new <b>{item_type}</b> item.</p>"
     )
     if not supports_specs:
         self._list_view = None
         return
     self._list_view = ProjectItemDragListView()
     self._list_view.doubleClicked.connect(self._toolbox.edit_specification)
     self._list_view.context_menu_requested.connect(
         self._toolbox.show_specification_context_menu)
     self._list_widget = CreateNewSpecListWidget(item_type)
     self._list_widget.itemClicked.connect(
         lambda _, item_type=item_type: self._toolbox.
         show_specification_form(item_type))
     menu = QMenu(self)
     widget_action = CustomWidgetAction(menu)
     widget_action.setDefaultWidget(self._list_view)
     menu.addAction(widget_action)
     widget_action = CustomWidgetAction(menu)
     widget_action.setDefaultWidget(self._list_widget)
     menu.addAction(widget_action)
     menu.setStyleSheet(f"QMenu{{background: {ICON_BACKGROUND};}}")
     self.setMenu(menu)
     self.setPopupMode(QToolButton.MenuButtonPopup)
     self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
     self._resize()
     model = self._toolbox.filtered_spec_factory_models.get(self.item_type)
     self._list_view.setModel(model)
     model.rowsInserted.connect(lambda *args: self._resize())
     model.rowsRemoved.connect(lambda *args: self._resize())
     self.drag_about_to_start.connect(self._handle_drag_about_to_start)
     self._list_view.drag_about_to_start.connect(menu.hide)
コード例 #6
0
    def _show_menu(self, colleague, pos):
        if not self._is_owner and not colleague.is_you or colleague.is_deleting:
            return

        menu = QMenu(self._ui.colleagues_list)
        menu.setStyleSheet("background-color: #EFEFF4; ")
        if colleague.is_you:
            if colleague.is_owner:
                action = menu.addAction(tr("Quit collaboration"))
                action.triggered.connect(self._on_quit_collaboration)
            else:
                action = menu.addAction(tr("Leave collaboration"))
                action.triggered.connect(self._on_leave_collaboration)
        else:
            rights_group = QActionGroup(menu)
            rights_group.setExclusive(True)

            menu.addSection(tr("Access rights"))
            action = menu.addAction(tr("Can view"))
            action.setCheckable(True)
            rights_action = rights_group.addAction(action)
            rights_action.setData(False)
            rights_action.setChecked(not colleague.can_edit)
            action = menu.addAction(tr("Can edit"))
            action.setCheckable(True)
            rights_action = rights_group.addAction(action)
            rights_action.setChecked(colleague.can_edit)
            rights_action.setData(True)
            rights_group.triggered.connect(
                lambda a: self._on_grant_edit(colleague, a))
            menu.addSeparator()

            action = menu.addAction(tr("Remove user"))
            action.triggered.connect(lambda: self._on_remove_user(colleague))

        pos_to_show = QPoint(pos.x(), pos.y() + 10)
        menu.exec_(pos_to_show)
コード例 #7
0
class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()

        # Config --------------------------------------
        self.objectList = {}
        self.systemIcon = {
            'folder':
            QIcon(QApplication.style().standardIcon(QStyle.SP_DirIcon)),
            'folderLink':
            QIcon(QApplication.style().standardIcon(QStyle.SP_DirLinkIcon)),
            'file':
            QIcon(QApplication.style().standardIcon(QStyle.SP_FileIcon)),
            'quit':
            QIcon(QApplication.style().standardIcon(
                QStyle.SP_DialogCloseButton))
        }
        self.filetypeIcon = Icons.icon_filetypes_flat(self)
        self.includeExclude = IncludeExclude()
        self.includeExclude.includePaths = ['Examples']
        self.includeExclude.includeExtensions = ['md', 'py', 'png']
        self.includeExclude.excludeFilenames = ['gitignore']
        self.includeExclude.excludePaths = ['git', '__pycache__']

        # --------------------------------------------
        self.create_menu()
        self.add_directory(
            '&Scripts',
            '/media/m/Data/Development/Phyton/TreeViewUltraMenu/Python-UltraMenu'
        )
        self.add_exit()

    def create_menu(self):
        self.menu = QMenu(self)
        # Enable theme, uncomment:
        self.menu.setStyleSheet(appStyle)
        self.menu.setCursor(QtCore.Qt.PointingHandCursor)
        tt1 = self.menu.addAction('Tooltip 1')
        tt1.setToolTip('t1')
        tt2 = self.menu.addAction('Tooltip 2')
        tt2.setToolTip('t2')
        tt3 = self.menu.addAction('Tooltip 3')
        tt3.setWhatsThis('setWhatsThis')
        tt3.setIconText('q')
        tt3.iconText()

        # custom style label
        text = QLabel("Label with custom text", self)
        text.setProperty('class', 'singlestyle')
        styleItem = QWidgetAction(self)
        styleItem.setDefaultWidget(text)
        styleItem.setProperty('class', 'singlestyle')
        tt4 = self.menu.addAction(styleItem)
        # tt4.setProperty('class', 'singlestyle')

        # tt4.Priority
        # tt4.setStyleSheet("QLabel { color: rgb(50, 50, 50); font-size: 11px; background-color: rgba(188, 188, 188, 50); border: 1px solid rgba(188, 188, 188, 250); }")
        self.menu.setToolTipsVisible(True)
        # self.menu.setIcon
        # self.menu.setwha(True)
        # self.menu.hovered.connect(lambda tt1= tt1, tt1())
        # self.menu.keyPressEvent(self.keyPressEvent)
        # pos = tt3.
        self.menu.installEventFilter(self)
        # widgetRect = self.menu.mapToGlobal()
        p = (0, 0)
        # print(self.menu.mapToGlobal(0,0))
        widgetRect = self.geometry()

        print(widgetRect)
        # tt2.hovered.connect(lambda tt2=self.tt2, tt2.tooltip())
        tt3.hovered.connect(lambda pos=[self], parent=self.menu, index=2: self.
                            show_toolTip(pos, parent, index))

    # Listen for All KeyPress/Mouse events
    def eventFilter(self, widget, event):
        if (event.type() == QtCore.QEvent.KeyRelease and widget is self.menu):
            self.menu.toolTip()
            print('all')

            # self.
            key = event.key()
            if key == QtCore.Qt.Key_Escape:
                print('escape')
            else:
                if key == QtCore.Qt.Key_Return:
                    print('escape')
                elif key == QtCore.Qt.Key_Enter:
                    print('escape')
                elif key == QtCore.Qt.Key_C:
                    print('Key - c')
                return True
        # return QtGui.QWidget.eventFilter(self, widget, event)

    def show_toolTip(self, pos, parent, index):
        '''
        **Parameters**
            pos:    list
                list of all parent widget up to the upmost

            parent: PySide.QtGui.QAction
                the parent QAction

            index:  int
                place within the QMenu, beginning with zero
        '''
        position_x = 0
        position_y = 0
        for widget in pos:
            position_x += widget.pos().x()
            position_y += widget.pos().y()

        point = QtCore.QPoint()
        point.setX(position_x)
        point.setY(position_y + index * 22)  # set y Position of QToolTip
        QToolTip.showText(point, parent.toolTip())

    def add_directory(self, label, dir):

        qtParentMenuItem = self.menu.addMenu(self.systemIcon['folder'], label)
        parentMenuClass = FolderItem(qtMenuItem=qtParentMenuItem,
                                     label=label,
                                     iconPath='',
                                     globalHotkey='',
                                     path=Path(dir))
        uid = parentMenuClass.getUid()
        self.objectList[uid] = parentMenuClass
        self.add_directory_submenu(parentMenuClass)

    def add_directory_submenu(self, parentMenuClass: FolderItem):

        qtParentMenuItem = parentMenuClass.getQtMenuItem()

        # Don't add the menu items multiple times, every time you hover a submenu.
        if parentMenuClass.isSubmenuItemsAdded == True:
            return None

        parentMenuClass.isSubmenuItemsAdded = True
        dirPath: str = parentMenuClass.getFullPath()
        osPaths = sorted(Path(dirPath).glob('*'))
        osPaths.sort(key=lambda x: x.is_file())
        filesAndFoldersArr = []

        for item in osPaths:
            # path = dirPath + '/' + item.name
            if item.is_dir(
            ) == True and self.includeExclude.folderIsNotExcluded(item):

                folderIcon = self.systemIcon['folder']

                if item.is_symlink():
                    folderIcon = self.systemIcon['folderLink']

                qtFolderItem = qtParentMenuItem.addMenu(
                    folderIcon, '&' + item.name)
                folderItemClass = FolderItem(qtMenuItem=qtFolderItem,
                                             label=item.name,
                                             iconPath='',
                                             globalHotkey='',
                                             path=item)
                uid = folderItemClass.getUid()
                self.objectList[uid] = folderItemClass
                filesAndFoldersArr.append(folderItemClass)
                qtFolderItem.aboutToShow.connect(
                    lambda folderItemClass=folderItemClass: self.
                    add_directory_submenu(folderItemClass))
                # newFolder.triggered.connect(self.action_directory(item.uid))

            elif item.is_file() and self.includeExclude.fileIsNotExcluded(
                    item):

                fileExt = item.suffix

                qtIcon = self.systemIcon['file']

                # If we have a icon for the filetype, use that instead
                if fileExt in self.filetypeIcon:
                    qtIcon = QIcon(str(self.filetypeIcon[fileExt]))

                qtFileItem = qtParentMenuItem.addAction(qtIcon, item.name)
                folderItemClass = FolderItem(qtMenuItem=qtFileItem,
                                             label=item.name,
                                             iconPath='',
                                             globalHotkey='',
                                             path=item)
                uid = folderItemClass.getUid()
                self.objectList[uid] = folderItemClass
                filesAndFoldersArr.append(folderItemClass)
                # filename
                # item.name
                # extension
                # item.suffix
                # newFile.triggered.connect(self.action_directory(item.uid))
                #         # newFile.hovered.connect(self.exit_app)
                #         # newFile.hovered.connect(lambda:  item.printUid())

                #         # func = self.hover()
                #         # newFile.hovered.connect(lambda f=func,arg=newFile:f(arg))
            else:
                print("It is a special file (socket, FIFO, device file)")

    def action_directory(self, uid):
        print(uid)

    def add_exit(self):
        exit = self.menu.addAction(self.systemIcon['quit'], '&Quit')
        self.menu.insertSeparator(exit)
        exit.triggered.connect(self.exit_app)
        self.menu.exec_(QCursor.pos())

    def exit_app(self):
        self.close()
        sys.exit(0)
コード例 #8
0
class MainApp(QWidget):
    def ScreenChanged(self, screen):
        self.client.setMaximumSize(screen.size())

    def addServerAction(self, text, function, args=[], kwargs={}):
        action = QWidgetAction(self)
        aButton = QPushButton(text)
        aButton.clicked.connect(lambda: function(*args, **kwargs))

        action.setDefaultWidget(aButton)
        self.ServerActions.addAction(action)

    def setupTopBar(self):
        tb = QWidget()
        tbl = QHBoxLayout()
        tb.setLayout(tbl)
        tb.setStyleSheet("border: 3px solid black; background-color: #7289da;")

        tbl.setAlignment(Qt.AlignLeft)

        homeBtn = QPushButton()
        homeBtn.setIcon(self.client.ico)

        saTB = QToolBar()
        self.ServerActions = QMenu()
        self.ServerActions.setTitle("⠀           Home             ⠀")

        saTB.addAction(self.ServerActions.menuAction())

        tbl.addWidget(homeBtn)
        tbl.addWidget(saTB)
        saTB.setStyleSheet("border: 1px solid black;")
        homeBtn.setStyleSheet("border: none;")
        self.ServerActions.setStyleSheet("border: none;")

        return tb

    def __init__(self, parent):
        super().__init__()

        self.client = parent
        parent.maw = self
        parent.l.addWidget(self)

        l = QVBoxLayout()
        self.setLayout(l)
        l.setAlignment(Qt.AlignTop)

        tb = self.setupTopBar()

        l.addWidget(tb)

        parent.setMinimumSize(parent.size())
        parent.setMaximumSize(parent.screen().size())

        parent.screenChanged.connect(self.ScreenChanged)

        parent.hide()
        parent.setWindowFlag(Qt.CustomizeWindowHint)
        parent.setWindowFlag(Qt.WindowMaximizeButtonHint)
        parent.show()

        self.show()
コード例 #9
0
class FileNode(Node):
    
    def __init__(self):
        super(FileNode, self).__init__()
        self.menu = QMenu()
        self.menu.setStyleSheet("background-color: #3E3E42; color: white;")
        self.eventManager = FileEventManager()
        self.proxy: FileProxy = None
        self.saveAction = QAction(QIcon(main.resource_path("resources/save_file.png")), "Save file")
        self.renameAction = QAction(QIcon(main.resource_path("resources/rename_file.png")), "Rename file")
        self.deleteAction = QAction(QIcon(main.resource_path("resources/delete_file.png")), "Delete file")
        self.showInfilesAction = QAction(QIcon(main.resource_path("resources/open_folder.png")), "Show in files")
        self.menu.addAction(self.showInfilesAction)
        self.menu.addAction(self.saveAction)
        self.menu.addAction(self.renameAction)
        self.menu.addAction(self.deleteAction)

        self.connectActions()

    def renameFile(self):
        if not os.path.exists(self.proxy.getFilePath()):
            self.eventManager.invalidFile.emit(self)
            return
        type = self.path.split(".")[1]
        name, entered = QInputDialog.getText(None, "Rename file", "Enter new file name: ", QLineEdit.Normal, self.path.split(".")[0])
        if entered:
            name += "."+type
            parentDir = os.path.abspath(os.path.join(self.proxy.getFilePath(), os.pardir))
            newPath = os.path.join(parentDir, name)
            regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
            if " " in name or regex.search(name):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("File name cannot contain whitespace or special characters.")
                msg.setWindowTitle("File rename error")
                msg.exec_()
                return
            if os.path.exists(newPath):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("File with the same name already exists.")
                msg.setWindowTitle("File rename error")
                msg.exec_()
                return
            os.rename(self.proxy.getFilePath(), newPath)
            oldPath = self.path
            self.proxy.path = self.path = os.path.basename(newPath)
            self.setText(0, self.path)
            self.eventManager.fileRename.emit(oldPath, self.proxy)


    def getContextMenu(self) -> QMenu:
        return self.menu

    def connectActions(self):
        self.deleteAction.triggered.connect(self.deleteActionTriggered)
        self.saveAction.triggered.connect(self.saveFile)
        self.renameAction.triggered.connect(self.renameFile)
        self.showInfilesAction.triggered.connect(self.openInFileExplorer)

    def openInFileExplorer(self):
        path = self.getFilePath()
        if platform.system() == "Windows":
            os.startfile(path)
        elif platform.system() == "Darwin":
            subprocess.Popen(["open", path])
        else:
            subprocess.Popen(["nautilus", "--select", path])

    def deleteActionTriggered(self):
        if not os.path.exists(self.proxy.getFilePath()):
            self.eventManager.invalidFile.emit(self)
            return
        self.eventManager.fileRemoveRequsted.emit(self)

    def saveFile(self):
        if self.proxy.hasUnsavedChanges:
            try:
                self.proxy.saveFile()
                self.eventManager.fileSave.emit(self.proxy)
            except:
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("The following file could not be saved: {}".format(self.proxy.path))
                msg.setWindowTitle("File save error")
                msg.exec_()

    def getFilePath(self):
        return os.path.join(self.proxy.parent.parent.path, self.proxy.parent.path, self.proxy.path)
コード例 #10
0
class ProjectNode(Node):
    def __init__(self):
        super(ProjectNode, self).__init__()
        self.eventManager = ProjectEventManager()
        self.menu = QMenu()
        self.menu.setStyleSheet("background-color: #3E3E42; color: white;")
        self.proxy = ProjectProxy()
        self.saveAction = QAction(
            QIcon(main.resource_path("resources/save_folder.png")),
            "Save project")
        self.deleteAction = QAction(
            QIcon(main.resource_path("resources/remove_folder.png")),
            "Remove project")
        self.eraseAction = QAction(
            QIcon(main.resource_path("resources/delete_folder.png")),
            "Delete project from disk")
        self.renameAction = QAction(
            QIcon(main.resource_path("resources/rename_folder.png")),
            "Rename project")
        self.compileAction = QAction(
            QIcon(main.resource_path("resources/compile.png")),
            "Compile project")
        self.runAction = QAction(
            QIcon(main.resource_path("resources/run.png")), "Run project")
        self.debugAction = QAction(
            QIcon(main.resource_path("resources/debug.png")), "Debug project")
        self.newFileAction = QAction(
            QIcon(main.resource_path("resources/new_file.png")), "New file")
        self.importFileAction = QAction(
            QIcon(main.resource_path("resources/import_file.png")),
            "Import file")
        self.compilerOptionsAction = QAction(
            QIcon(main.resource_path("resources/compiler_options.png")),
            "Compiler options")
        self.showInFiles = QAction(
            QIcon(main.resource_path("resources/open_folder.png")),
            "Show in files")
        self.menu.addAction(self.saveAction)
        self.menu.addAction(self.compilerOptionsAction)
        self.menu.addAction(self.compileAction)
        self.menu.addAction(self.debugAction)
        self.menu.addAction(self.runAction)
        self.menu.addAction(self.showInFiles)
        self.menu.addSeparator()
        self.menu.addAction(self.newFileAction)
        self.menu.addAction(self.importFileAction)
        self.menu.addSeparator()
        self.menu.addAction(self.renameAction)
        self.menu.addAction(self.deleteAction)
        self.menu.addAction(self.eraseAction)

        self.connectActions()

    def getContextMenu(self) -> QMenu:
        return self.menu

    def __str__(self):
        return self.proxy.path

    def connectFileEventHandlers(self, file: FileNode):
        file.eventManager.fileRemoveRequsted.connect(self.removeFile)
        file.eventManager.fileRename.connect(
            lambda oldPath, fileProxy: self.eventManager.fileRename.emit(
                oldPath, fileProxy))
        file.eventManager.fileSave.connect(
            lambda fileProxy: self.eventManager.fileSave.emit(fileProxy))
        file.eventManager.invalidFile.connect(self.detachFile)

    def connectActions(self):
        self.saveAction.triggered.connect(self.saveProject)
        self.newFileAction.triggered.connect(self.createNewFile)
        self.importFileAction.triggered.connect(self.importFile)
        self.deleteAction.triggered.connect(self.deleteProject)
        self.renameAction.triggered.connect(self.renameProject)
        self.compilerOptionsAction.triggered.connect(self.editCompilerOptions)
        self.eraseAction.triggered.connect(self.eraseActionTriggered)
        self.compileAction.triggered.connect(self.compileActionTriggered)
        self.debugAction.triggered.connect(self.debugActionTriggered)
        self.runAction.triggered.connect(self.runActionTriggered)
        self.showInFiles.triggered.connect(self.openInFileExplorer)

    def openInFileExplorer(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        path = self.proxy.getProjectPath()
        if platform.system() == "Windows":
            os.startfile(path)
        elif platform.system() == "Darwin":
            subprocess.Popen(["open", path])
        else:
            subprocess.Popen(["xdg-open", path])

    def saveProject(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        self.eventManager.projectSave.emit(self.proxy)

    def detachFile(self, fileNode: FileNode):
        msg = QMessageBox()
        msg.setStyleSheet("background-color: #2D2D30; color: white;")
        msg.setModal(True)
        msg.setIcon(QMessageBox.Critical)
        msg.setText("File '{}' has been deleted from the disk.".format(
            fileNode.proxy.path))
        msg.setWindowTitle("Invalid file")
        msg.exec_()
        self.proxy.files.remove(fileNode.proxy)
        self.removeChild(fileNode)
        self.parent().saveWorkspace()
        self.eventManager.fileRemove.emit(fileNode.proxy)

    def eraseActionTriggered(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        self.eventManager.projectDeleteFromDiskRequested.emit(self)

    def compileActionTriggered(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        self.eventManager.projectCompileRequested.emit(self.proxy)

    def debugActionTriggered(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        self.eventManager.projectDebugRequested.emit(self.proxy)

    def runActionTriggered(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        self.eventManager.projectRunRequested.emit(self.proxy)

    def editCompilerOptions(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        editor = CompilerOptionsEditor(self.proxy)
        if editor.exec_():
            self.parent().saveWorkspace()

    def renameProject(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        name, entered = QInputDialog.getText(None, "Rename project",
                                             "Enter new workspace name: ",
                                             QLineEdit.Normal, self.path)
        if entered:
            parentDir = os.path.abspath(
                os.path.join(self.proxy.getProjectPath(), os.pardir))
            newPath = os.path.join(parentDir, name)
            # print(newPath)
            regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
            if " " in name or regex.search(name):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText(
                    "Project name cannot contain whitespace or special characters."
                )
                msg.setWindowTitle("Project rename error")
                msg.exec_()
                return
            if os.path.exists(newPath):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Folder with the same name already exists.")
                msg.setWindowTitle("Project rename error")
                msg.exec_()
                return
            os.rename(self.path, newPath)
            oldPath = self.path
            self.proxy.path = self.path = os.path.basename(newPath)
            self.setText(0, self.path)
            self.parent().saveWorkspace()
            self.eventManager.projectRename.emit(oldPath, self)

    def removeFile(self, file: FileNode):
        answer = QMessageBox.question(
            None, "Delete file",
            "Are you sure you want to delete file {} from the disk?".format(
                file.proxy.path), QMessageBox.Yes | QMessageBox.No)
        if not answer == QMessageBox.Yes:
            return
        self.proxy.files.remove(file.proxy)
        os.remove(file.proxy.getFilePath())
        self.eventManager.fileRemove.emit(file.proxy)
        self.removeChild(file)
        self.parent().saveWorkspace()

    def importFile(self, path=None):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        if not path:
            name, entered = QFileDialog.getOpenFileName(
                None, "Select file to import", ".",
                "Assembly files (*.S);;C files (*.c)")
        else:
            name = path
        if name:
            fileName = os.path.basename(name)
            filePath = os.path.join(self.proxy.getProjectPath(), fileName)
            regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
            if " " in filePath or regex.search(fileName):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText(
                    "File name cannot contain whitespace or special characters."
                )
                msg.setWindowTitle("File import error")
                msg.exec_()
                return
            alreadyInProject = False
            inSameDir = False
            if os.path.exists(filePath):
                for proxy in self.proxy.files:
                    if proxy.getFilePath() == name:
                        alreadyInProject = True
                inSameDir = os.path.join(self.proxy.getProjectPath(),
                                         os.path.basename(filePath)) == name
                if alreadyInProject:
                    if path:
                        return name
                    msg = QMessageBox()
                    msg.setStyleSheet(
                        "background-color: #2D2D30; color: white;")
                    msg.setModal(True)
                    msg.setIcon(QMessageBox.Critical)
                    msg.setText("File is already a part of the project.")
                    msg.setWindowTitle("File import error")
                    msg.exec_()
                    return
                if not inSameDir:
                    if path:
                        return name
                    msg = QMessageBox()
                    msg.setStyleSheet(
                        "background-color: #2D2D30; color: white;")
                    msg.setModal(True)
                    msg.setIcon(QMessageBox.Critical)
                    msg.setText(
                        "File with the same name ('{}') already exists.".
                        format(fileName))
                    msg.setWindowTitle("File import error")
                    msg.exec_()
                    return
            node = None
            if fileName[-1] == "S":
                node = AssemblyFileNode()
                node.setIcon(0, QIcon(main.resource_path("resources/s.png")))
            else:
                node = CFileNode()
                node.setIcon(0, QIcon(main.resource_path("resources/c.png")))
            node.setText(0, fileName)
            node.path = fileName
            if isinstance(node, AssemblyFileNode):
                node.proxy = AssemblyFileProxy()
            elif isinstance(node, CFileNode):
                node.proxy = CFileProxy()
            node.proxy.path = fileName
            node.proxy.parent = self.proxy
            self.addChild(node)
            # if not inSameDir:
            #     os.mknod(filePath)
            self.proxy.addFile(node.proxy)
            self.connectFileEventHandlers(node)
            if not inSameDir:
                shutil.copy2(name, filePath)
            self.setExpanded(True)
            self.parent().saveWorkspace()
            self.eventManager.newFile.emit(node.proxy)
            # with open(filePath, 'w') as file:
            #     with open(name, 'r') as inputFile:
            #         file.write(inputFile.read())

    def createQuickAssemblyFile(self):
        fileName = self.path + ".S"
        node = AssemblyFileNode()
        node.setIcon(0, QIcon(main.resource_path("resources/s.png")))
        node.setText(0, fileName)
        node.path = fileName
        node.proxy = AssemblyFileProxy()
        node.proxy.path = fileName
        node.proxy.parent = self.proxy
        self.addChild(node)
        os.mknod(os.path.join(self.proxy.getProjectPath(), fileName))
        self.proxy.addFile(node.proxy)
        self.connectFileEventHandlers(node)
        self.setExpanded(True)
        return node

    def createNewFile(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        dialog = NewFileDialog()
        dialog.exec_()
        if dialog.result:
            rootPath = os.path.join(self.parent().path, self.path,
                                    dialog.result)
            regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
            if " " in dialog.result or regex.search(dialog.result):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText(
                    "File name cannot contain whitespace or special characters."
                )
                msg.setWindowTitle("File creation error")
                msg.exec_()
                return
            if os.path.exists(rootPath):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("File with the same name already exists.")
                msg.setWindowTitle("File creation error")
                msg.exec_()
                return
            node = None
            if dialog.result[-1] == "S":
                node = AssemblyFileNode()
                node.setIcon(0, QIcon(main.resource_path("resources/s.png")))
            else:
                node = CFileNode()
                node.setIcon(0, QIcon(main.resource_path("resources/c.png")))
            node.setText(0, dialog.result)
            node.path = dialog.result
            if isinstance(node, AssemblyFileNode):
                node.proxy = AssemblyFileProxy()
            elif isinstance(node, CFileNode):
                node.proxy = CFileProxy()
            node.proxy.path = dialog.result
            node.proxy.parent = self.proxy
            self.addChild(node)
            os.mknod(rootPath)
            self.proxy.addFile(node.proxy)
            self.connectFileEventHandlers(node)
            self.setExpanded(True)
            self.eventManager.newFile.emit(node.proxy)

    def deleteProject(self):
        if not os.path.exists(self.proxy.getProjectPath()):
            self.eventManager.invalidProject.emit(self)
            return
        self.eventManager.projectRemoveRequested.emit(self)

    def loadProjectBackup(self):
        for proxy in self.proxy.files:
            node = None
            if isinstance(proxy, AssemblyFileProxy):
                node = AssemblyFileNode()
                node.setIcon(0, QIcon(main.resource_path("resources/s.png")))
            elif isinstance(proxy, CFileProxy):
                node = CFileNode()
                node.setIcon(0, QIcon(main.resource_path("resources/c.png")))
            if node:
                proxy.parent = self.proxy
                node.setText(0, proxy.path)
                node.path = proxy.path
                node.proxy = proxy
                try:
                    if proxy.text:
                        with open(proxy.getFilePath(), 'w') as file:
                            file.write(proxy.text)
                            proxy.hasUnsavedChanges = False
                except:
                    print("Could not write to file {}".format(
                        proxy.getFilePath()))

                self.addChild(node)
                self.connectFileEventHandlers(node)

    def loadProject(self, sourcePath=None):
        toBeDeleted = []
        for proxy in self.proxy.files:
            file = None
            if os.path.exists(os.path.join(proxy.getFilePath())):
                if isinstance(proxy, AssemblyFileProxy):
                    file = AssemblyFileNode()
                    file.setIcon(0,
                                 QIcon(main.resource_path("resources/s.png")))
                elif isinstance(proxy, CFileProxy):
                    file = CFileNode()
                    file.setIcon(0,
                                 QIcon(main.resource_path("resources/c.png")))
                if file:
                    proxy.parent = self.proxy
                    file.setText(0, proxy.path)
                    file.path = proxy.path
                    file.proxy = proxy
                    self.addChild(file)
                    self.connectFileEventHandlers(file)
            else:
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText(
                    "Failed to import file '{}' because it is deleted from the disk."
                    .format(proxy.path))
                msg.setWindowTitle("Failed to load a file.")
                msg.exec_()
                toBeDeleted.append(proxy)
        for proxy in toBeDeleted:
            self.proxy.files.remove(proxy)
        projectPath = self.proxy.getProjectPath(
        ) if not sourcePath else sourcePath
        for filePath in os.listdir(projectPath):
            if filePath not in [file.path for file in self.proxy.files]:
                node = None
                proxy = None
                if filePath.lower().endswith(".s"):
                    node = AssemblyFileNode()
                    node.setIcon(0,
                                 QIcon(main.resource_path("resources/s.png")))
                    proxy = AssemblyFileProxy()
                elif filePath.lower().endswith(".c"):
                    node = CFileNode()
                    node.setIcon(0,
                                 QIcon(main.resource_path("resources/c.png")))
                    proxy = CFileProxy()
                if node:
                    proxy.path = filePath
                    node.setText(0, filePath)
                    node.path = filePath
                    node.proxy = proxy
                    node.proxy.parent = self.proxy
                    self.addChild(node)
                    self.proxy.files.append(node.proxy)
                    self.connectFileEventHandlers(node)
                    if sourcePath:
                        shutil.copy2(
                            os.path.join(sourcePath, filePath),
                            os.path.join(self.proxy.getProjectPath(),
                                         filePath))
コード例 #11
0
class WorkspaceNode(Node):
    
    def __init__(self):
        super(WorkspaceNode, self).__init__()
        self.deleted = False
        self.eventManager = WorkspaceEventManager()
        self.menu = QMenu()
        self.menu.setStyleSheet("background-color: #3E3E42; color: white;")
        self.proxy = WorkspaceProxy()
        self.newProjectAction = QAction(QIcon(main.resource_path("resources/new_folder.png")), "New project")
        self.quickAssemblyProjectAction = QAction(QIcon(main.resource_path("resources/new_s.png")), "Quick assembly project")
        self.openFileAsAssemblyProjectAction = QAction(QIcon(main.resource_path("resources/open_s.png")), "Open file as assembly project")
        self.importProjectAction = QAction(QIcon(main.resource_path("resources/open_folder.png")), "Import project")
        self.saveAction = QAction(QIcon(main.resource_path("resources/save_folder.png")), "Save workspace")
        self.renameAction = QAction(QIcon(main.resource_path("resources/rename_folder.png")), "Rename workspace")
        self.switchAction = QAction(QIcon(main.resource_path("resources/switch_folder.png")), "Switch workspace")
        self.updateAction = QAction(QIcon(main.resource_path("resources/update_folder.png")), "Update workspace")
        self.showInfilesAction = QAction(QIcon(main.resource_path("resources/open_folder.png")), "Show in files")
        self.menu.addAction(self.newProjectAction)
        self.menu.addAction(self.importProjectAction)
        self.menu.addAction(self.showInfilesAction)
        self.menu.addSeparator()
        self.menu.addAction(self.quickAssemblyProjectAction)
        self.menu.addAction(self.openFileAsAssemblyProjectAction)
        self.menu.addSeparator()
        self.menu.addAction(self.saveAction)
        self.menu.addAction(self.switchAction)
        self.menu.addAction(self.renameAction)
        self.menu.addAction(self.updateAction)

        self.connectActions()

    def getContextMenu(self) -> QMenu:
        return self.menu

    def connectActions(self):
        self.renameAction.triggered.connect(self.renameWorkspace)
        self.newProjectAction.triggered.connect(self.createNewProject)
        self.quickAssemblyProjectAction.triggered.connect(self.createQuickAssemblyProject)
        self.openFileAsAssemblyProjectAction.triggered.connect(self.openFileAsAssemblyProject)
        self.importProjectAction.triggered.connect(self.importProject)
        self.saveAction.triggered.connect(self.saveWorkspace)
        self.updateAction.triggered.connect(self.reloadWorkspace)
        self.showInfilesAction.triggered.connect(self.openInFileExplorer)

    def openInFileExplorer(self):
        if not os.path.exists(self.path):
            self.eventManager.invalidWorkspace.emit(self)
            return
        path = self.path
        if platform.system() == "Windows":
            os.startfile(path)
        elif platform.system() == "Darwin":
            subprocess.Popen(["open", path])
        else:
            subprocess.Popen(["xdg-open", path])

    def reloadWorkspace(self):
        if not os.path.exists(self.path):
            self.eventManager.invalidWorkspace.emit(self)
            return
        self.eventManager.workspaceReload.emit(self.proxy)

    def renameWorkspace(self):
        if not os.path.exists(self.path):
            self.eventManager.invalidWorkspace.emit(self)
            return
        name, entered = QInputDialog.getText(None, "Rename workspace", "Enter new workspace name: ", QLineEdit.Normal, os.path.basename(self.path))
        if entered:
            parentDir = os.path.abspath(os.path.join(self.path, os.pardir))
            newPath = os.path.join(parentDir, name)
            regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
            if " " in name or regex.search(name):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Workspace name cannot contain whitespace or special characters.")
                msg.setWindowTitle("Workspace rename error")
                msg.exec_()
                return
            if os.path.exists(newPath):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Folder with the same name already exists.")
                msg.setWindowTitle("Workspace rename error")
                msg.exec_()
                return
            os.rename(self.path, newPath)
            oldPath = self.path
            self.proxy.path = self.path = newPath
            self.setText(0, os.path.basename(self.path))
            self.saveWorkspace()
            self.eventManager.workspaceRename.emit(oldPath, self.proxy)

    def importProject(self, path=None):
        if not os.path.exists(self.path):
            self.eventManager.invalidWorkspace.emit(self)
            return
        if not path:
            name = QFileDialog.getExistingDirectory(None, "Import project", ".", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)
        else:
            name = path
        if name:
            projectName = os.path.basename(name)
            regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
            if " " in projectName or regex.search(projectName):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Project name cannot contain whitespace or special characters.")
                msg.setWindowTitle("Project import error")
                msg.exec_()
                return
            if os.path.exists(os.path.join(self.path, projectName)) and os.path.join(self.path, projectName) != name:
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Folder with the same name already exists.")
                msg.setWindowTitle("Project import error")
                msg.exec_()
                return
            for projectProxy in self.proxy.projects:
                if projectProxy.getProjectPath() == name:
                    msg = QMessageBox()
                    msg.setStyleSheet("background-color: #2D2D30; color: white;")
                    msg.setModal(True)
                    msg.setIcon(QMessageBox.Critical)
                    msg.setText("Project is already a part of the workspace.")
                    msg.setWindowTitle("Project import error")
                    msg.exec_()
                    return
            project = ProjectNode()
            project.path = projectName
            project.proxy.path = projectName
            project.proxy.parent = self.proxy
            project.setIcon(0, QIcon(main.resource_path("resources/project.png")))
            project.setText(0, projectName)
            self.addChild(project)
            newPath = os.path.join(self.path, projectName)
            if os.path.join(self.path, projectName) != name:
                os.mkdir(newPath)
            self.proxy.addProject(project.proxy)
            sourcePath = name if os.path.join(self.path, projectName) != name else None
            project.loadProject(sourcePath)
            self.saveWorkspace()
            self.connectProjectEventHandlers(project)
            self.eventManager.projectAdded.emit(project)

    def createQuickAssemblyProject(self):
        project = self.createNewProject(quickAssembly=True)
        if not project:
            return
        fileNode = project.createQuickAssemblyFile()
        self.saveWorkspace()
        self.eventManager.quickAssemblyFile.emit(fileNode.proxy)

    def openFileAsAssemblyProject(self):
        if not os.path.exists(self.path):
            self.eventManager.invalidWorkspace.emit(self)
            return
        name, entered = QFileDialog.getOpenFileName(None, "Select assembly file to open", ".","Assembly files (*.S)")
        if not name:
            return
        regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
        fileName = os.path.basename(name)
        if " " in fileName or regex.search(fileName):
            msg = QMessageBox()
            msg.setStyleSheet("background-color: #2D2D30; color: white;")
            msg.setModal(True)
            msg.setIcon(QMessageBox.Critical)
            msg.setText("File name cannot contain whitespace or special characters.")
            msg.setWindowTitle("File import error")
            msg.exec_()
            return
        fileNode = AssemblyFileNode()
        fileNode.setIcon(0, QIcon(main.resource_path("resources/s.png")))
        fileNode.setText(0, fileName)
        fileNode.path = fileName
        fileNode.proxy = AssemblyFileProxy()
        fileNode.proxy.path = fileName
        projectName = fileName.strip(".S")
        projectPath = os.path.join(self.path, projectName)
        while os.path.exists(projectPath):
            projectName += "_1"
            projectPath = os.path.join(self.path, projectName)
        os.mkdir(projectPath)
        project = ProjectNode()
        project.path = projectName
        project.proxy.path = projectName
        project.proxy.parent = self.proxy
        fileNode.proxy.parent = project.proxy
        project.setIcon(0, QIcon(main.resource_path("resources/project.png")))
        project.setText(0, projectName)
        self.addChild(project)
        project.addChild(fileNode)
        project.proxy.addFile(fileNode.proxy)
        self.proxy.addProject(project.proxy)
        filePath = os.path.join(projectPath, fileName)
        shutil.copy2(name, filePath)
        project.connectFileEventHandlers(fileNode)
        self.saveWorkspace()
        self.connectProjectEventHandlers(project)
        project.setExpanded(True)
        self.eventManager.projectAdded.emit(project)
        self.eventManager.quickAssemblyFile.emit(fileNode.proxy)

    def createNewProject(self, quickAssembly=False, path=None):
        if not os.path.exists(self.path):
            self.eventManager.invalidWorkspace.emit(self)
            return
        dialogText = "New assembly project" if quickAssembly else "New project"
        if not path:
            name, entered = QInputDialog.getText(None, dialogText, "Enter project name: ", QLineEdit.Normal, "New project")
        else:
            name = os.path.basename(path)
            entered = True
        if entered:
            regex = re.compile('[@!#$%^&*()<>?/\|}{~:]')
            if " " in name or regex.search(name):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Project name cannot contain whitespace or special characters.")
                msg.setWindowTitle("Project creation error")
                msg.exec_()
                return
            if os.path.exists(os.path.join(self.path, name)):
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Folder with the same name already exists.")
                msg.setWindowTitle("Project creation error")
                msg.exec_()
                return
            project = ProjectNode()
            project.path = name
            project.proxy.path = name
            project.proxy.parent = self.proxy
            project.setIcon(0, QIcon(main.resource_path("resources/project.png")))
            project.setText(0, name)
            self.addChild(project)
            newPath = os.path.join(self.path, name)
            os.mkdir(newPath)
            self.proxy.addProject(project.proxy)
            self.saveWorkspace()
            self.connectProjectEventHandlers(project)
            self.eventManager.projectAdded.emit(project)
            return project

    def saveBackup(self, ws_path=None):
        try:
            previous_state = self.proxy.closedNormally
        except:
            self.proxy.closedNormally = True
            previous_state = self.proxy.closedNormally
        self.proxy.closedNormally = True
        if ws_path:
            save_path = ws_path
        else:
            save_path = self.proxy.path
        try:
            with open(os.path.join(save_path, '.backup'), 'wb') as backup:
                pickle.dump(self.proxy, backup, protocol=pickle.HIGHEST_PROTOCOL)
        except:
            return
        self.proxy.closedNormally = previous_state

    def saveWorkspace(self, ws_path=None):
        if not os.path.exists(self.path):
            self.eventManager.invalidWorkspace.emit(self)
            return
        try:
           test = self.proxy.closedNormally
        except:
            self.proxy.closedNormally = True
        if ws_path:
            save_path = ws_path
        else:
            save_path = self.proxy.path
        try:
            with open(os.path.join(save_path, '.metadata'), 'wb') as metadata:
                pickle.dump(self.proxy, metadata, protocol=pickle.HIGHEST_PROTOCOL)
        except:
            return
        self.saveBackup(save_path)


    def connectProjectEventHandlers(self, project: ProjectNode):
        project.eventManager.projectCompileRequested.connect(lambda proxy: self.eventManager.projectCompile.emit(proxy))
        project.eventManager.projectDebugRequested.connect(lambda proxy: self.eventManager.projectDebug.emit(proxy))
        project.eventManager.projectRunRequested.connect(lambda proxy: self.eventManager.projectRun.emit(proxy))
        project.eventManager.projectRemoveRequested.connect(self.removeProject)
        project.eventManager.projectDeleteFromDiskRequested.connect(self.deleteProjectFromDisk)
        project.eventManager.projectRename.connect(lambda oldPath, project: self.eventManager.projectRename.emit(oldPath, project))
        project.eventManager.fileRemove.connect(lambda fileProxy: self.eventManager.fileRemove.emit(fileProxy))
        project.eventManager.fileRename.connect(lambda oldPath, fileProxy: self.renameFile(oldPath, fileProxy))
        project.eventManager.fileSave.connect(lambda fileProxy: self.eventManager.fileSave.emit(fileProxy))
        project.eventManager.invalidProject.connect(lambda projectNode: self.removeProject(projectNode, ask=False))
        project.eventManager.projectSave.connect(lambda projectProxy: self.eventManager.projectSave.emit(projectProxy))
        project.eventManager.newFile.connect(lambda fileProxy: self.eventManager.newFile.emit(fileProxy))

    def renameFile(self, oldPath, fileProxy):
        self.saveWorkspace()
        self.eventManager.fileRename.emit(oldPath, fileProxy)

    def removeProject(self, project: ProjectNode, ask=True):
        if ask:
            answer = QMessageBox.question(None, "Delete project",
                                          "Are you sure you want to remove project {} from the workspace?".format(project.proxy.path),
                                          QMessageBox.Yes | QMessageBox.No)
            if not answer == QMessageBox.Yes:
                return
        else:
            msg = QMessageBox()
            msg.setStyleSheet("background-color: #2D2D30; color: white;")
            msg.setModal(True)
            msg.setIcon(QMessageBox.Critical)
            msg.setText("Project '{}' has been deleted from the disk.".format(project.proxy.path))
            msg.setWindowTitle("Invalid project")
            msg.exec_()
        self.proxy.projects.remove(project.proxy)
        self.eventManager.projectRemove.emit(project)
        self.removeChild(project)
        self.saveWorkspace()

    def deleteProjectFromDisk(self, project: ProjectNode):
        answer = QMessageBox.question(None, "Delete project from disk",
                                      "Are you sure you want to delete project {} and all its content from the disk?".format(
                                          project.proxy.path),
                                      QMessageBox.Yes | QMessageBox.No)
        if not answer == QMessageBox.Yes:
            return
        self.proxy.projects.remove(project.proxy)
        shutil.rmtree(project.proxy.getProjectPath())
        self.eventManager.projectRemove.emit(project)
        self.removeChild(project)
        self.saveWorkspace()

    def loadBackupWorkspace(self, ws_path=None):
        for projectProxy in self.proxy.projects:
            projectProxy.parent = self.proxy
            project = ProjectNode()
            project.setIcon(0, QIcon(main.resource_path("resources/project.png")))
            project.setText(0, projectProxy.path)
            project.path = projectProxy.path
            project.proxy = projectProxy
            project.loadProjectBackup()
            self.addChild(project)
            self.connectProjectEventHandlers(project)
        return True

    def loadWorkspace(self):
        toBeDeleted = []

        for projectProxy in self.proxy.projects:
            if os.path.exists(projectProxy.getProjectPath()):
                projectProxy.parent = self.proxy
                project = ProjectNode()
                project.setIcon(0, QIcon(main.resource_path("resources/project.png")))
                project.setText(0, projectProxy.path)
                project.path = projectProxy.path
                project.proxy = projectProxy
                project.loadProject()
                self.addChild(project)
                self.connectProjectEventHandlers(project)
            else:
                msg = QMessageBox()
                msg.setStyleSheet("background-color: #2D2D30; color: white;")
                msg.setModal(True)
                msg.setIcon(QMessageBox.Critical)
                msg.setText("Failed to import project '{}' because it is deleted from the disk.".format(projectProxy.path))
                msg.setWindowTitle("Failed to load a project.")
                msg.exec_()
                toBeDeleted.append(projectProxy)
        for proxy in toBeDeleted:
            self.proxy.projects.remove(proxy)
        try:
            self.saveWorkspace()
            return True
        except:
            return False
コード例 #12
0
    def _show_menu(self, index, pos):
        node_id, \
        node_name, \
        is_online, \
        is_itself, \
        is_wiped = self._model.get_node_id_online_itself(index)
        if not node_id:
            return

        license_free = self._license_type == FREE_LICENSE

        menu = QMenu(self._view)
        menu.setStyleSheet("background-color: #EFEFF4; ")
        menu.setToolTipsVisible(license_free)
        if license_free:
            menu.setStyleSheet(
                'QToolTip {{background-color: #222222; color: white;}}')
            menu.hovered.connect(lambda a: self._on_menu_hovered(a, menu))

        def add_menu_item(caption,
                          index=None,
                          action_name=None,
                          action_type="",
                          start_transfers=False,
                          disabled=False,
                          tooltip=""):
            action = menu.addAction(caption)
            action.setEnabled(not disabled)
            action.tooltip = tooltip if tooltip else ""
            if not start_transfers:
                action.triggered.connect(lambda: self._on_menu_clicked(
                    index, action_name, action_type))
            else:
                action.triggered.connect(self.start_transfers.emit)

        tooltip = tr("Not available for free license") \
            if license_free and not is_itself else ""
        if not is_online:
            action_in_progress = ("hideNode", "") in \
                                 self._nodes_actions.get(node_id, set())
            item_text = tr("Remove node") if not action_in_progress \
                else tr("Remove node in progress...")
            add_menu_item(item_text,
                          index,
                          "hideNode",
                          disabled=action_in_progress)
        elif is_itself:
            add_menu_item(tr("Transfers..."), start_transfers=True)
        if not is_wiped:
            wipe_in_progress = ("execute_remote_action", "wipe") in \
                                 self._nodes_actions.get(node_id, set())
            if not wipe_in_progress:
                action_in_progress = ("execute_remote_action", "logout") in \
                                     self._nodes_actions.get(node_id, set())
                item_text = tr("Log out") if not action_in_progress \
                    else tr("Log out in progress...")
                add_menu_item(item_text,
                              index,
                              "execute_remote_action",
                              "logout",
                              disabled=action_in_progress
                              or license_free and not is_itself,
                              tooltip=tooltip)
            item_text = tr("Log out && wipe") if not wipe_in_progress \
                else tr("Wipe in progress...")
            add_menu_item(item_text,
                          index,
                          "execute_remote_action",
                          "wipe",
                          disabled=wipe_in_progress
                          or license_free and not is_itself,
                          tooltip=tooltip)

        pos_to_show = QPoint(pos.x(), pos.y() + 20)
        menu.exec_(self._view.mapToGlobal(pos_to_show))
コード例 #13
0
class QPushColorButton(QPushButton):
    colorSelected = Signal(QColor)
    def __init__(self, parent=None, columns=int(0), rows=int(0), palette=list):
        super(QPushColorButton, self).__init__(parent, "")
        self.setMaximumWidth(25)
        self.setMaximumHeight(25)
        self.currentColor = QColor(255,255,255)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.palette = palette
        self._columns = columns
        self._rows = rows

    def paintEvent(self,event):
        super(QPushColorButton, self).paintEvent(event)
        size = 13
        height = (self.height() - size)/2
        width = (self.width() - size)/2
        qp = QPainter(self)
        # qp.begin(self)
        qp.setPen(Qt.NoPen)
        qp.setBrush(self.currentColor)
        qp.drawRect(width, height, size, size)

    def color_menu(self, QPos=list):
        '''
        Show color menu.

        Parameters
        ----------
        QPos: (list)
            list of x and y location.
        '''
        self.mainMenu = QMenu()
        self.mainMenu.setStyleSheet("QMenu {background-color: #222222;}")
        colorAction = ColorAction(self.mainMenu, self._columns, self._rows, self.palette)
        colorAction.colorSelected.connect(self.handleColorSelected)
        self.mainMenu.addAction(colorAction)

        pos = self.mapToGlobal(QPoint(0,0))
        self.mainMenu.move(pos + QPos)
        self.mainMenu.show()

    def get_palette(self):
        return self.palette
    def set_palette(self, value=list):
        self.palette = value
    Palette= property(get_palette, set_palette)

    def get_columns(self):
        return self._columns
    def set_columns(self, value=int):
        self._columns = value
    Columns = property(get_columns,set_columns)

    def get_rows(self):
        return self._rows
    def set_rows(self, value=int):
        self._rows = value
    Rows = property(get_rows,set_rows)

    def handleColorSelected(self, color=QColor):
        self.currentColor = color
        self.colorSelected.emit(color)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.color_menu(event.pos())
        super(QPushColorButton, self).mousePressEvent(event)

    def get_current_color(self):
        return self.currentColor
    def set_current_color(self, color=QColor):
        self.currentColor = color
        self.update()
    CurrentColor = property(get_current_color,set_current_color)
コード例 #14
0
ファイル: browser.py プロジェクト: libreblog/cells
 def contextMenuEvent(self, event):
     menu = QMenu()
     menu.setStyleSheet(Theme.contextMenu.style)
     menu.setFont(Theme.contextMenu.font)
     menu.addActions(self.actions())
     menu.exec_(event.globalPos())