Esempio n. 1
0
class WorkflowableROI(ROI):
    # FIXME: do we still want this for our (e.g.) CorrelationStage process_actions???
    def __init__(self, *args, **kwargs):
        super(WorkflowableROI, self).__init__(*args, **kwargs)
        self.operation = ROIOperation(self)
        self._param = None

    def parameter(self) -> Parameter:
        raise NotImplementedError

    def getMenu(self):
        if self.menu is None:
            self.menu = QMenu()
            self.menu.setTitle("ROI")
            if self.removable:  # FIXME: if the removable attr is changed, the menu will not react and remAct won't show
                remAct = QAction("Remove ROI", self.menu)
                remAct.triggered.connect(self.removeClicked)
                self.menu.addAction(remAct)
                self.menu.remAct = remAct
            editAct = QAction("Edit ROI", self.menu)
            editAct.triggered.connect(self.edit_parameters)
            self.menu.addAction(editAct)
            self.menu.editAct = editAct
        self.menu.setEnabled(True)
        return self.menu

    def contextMenuEnabled(self):
        return True

    def edit_parameters(self):
        class DefocusParameterTree(QWidget):
            def __init__(self, *args, **kwargs):
                super(DefocusParameterTree, self).__init__(*args, **kwargs)
                self.setLayout(QVBoxLayout())
                self.parameter_tree = ParameterTree()
                self.layout().addWidget(self.parameter_tree)
                self.layout().setContentsMargins(0, 0, 0, 0)

            def setParameters(self, *args, **kwargs):
                self.parameter_tree.setParameters(*args, **kwargs)

        # self.parameter_tree = DefocusParameterTree()
        self.parameter_tree = DefocusParameterTree()
        self.parameter_tree.setParameters(self.parameter())
        self.parameter_tree.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup)
        # self.parameter_tree = QLabel('blah')
        self.parameter_tree.show()
        self.parameter_tree.activateWindow()
        self.parameter_tree.raise_()
        self.parameter_tree.move(QCursor().pos())
        self.parameter_tree.setFocus(Qt.PopupFocusReason)
        self.parameter_tree.resize(QSize(300, 400))
Esempio n. 2
0
File: apps.py Progetto: bopopescu/QC
class ListItemApplication(ListWidgetItemBase):
    """Item with custom widget for the applications list."""

    ICON_SIZE = 64

    def __init__(
        self,
        name=None,
        display_name=None,
        description=None,
        command=None,
        versions=None,
        image_path=None,
        prefix=None,
        needs_license=False,
        non_conda=False,
    ):
        """Item with custom widget for the applications list."""
        super(ListItemApplication, self).__init__()

        self.api = AnacondaAPI()
        self.prefix = prefix
        self.name = name
        self.display_name = display_name if display_name else name
        self.url = ''
        self.expired = False
        self.needs_license = needs_license
        self.description = description
        self.command = command
        self.versions = versions
        self.image_path = image_path if image_path else ANACONDA_ICON_256_PATH
        self.style_sheet = None
        self.timeout = 2000
        self.non_conda = non_conda
        self._vscode_version_value = None

        # Widgets
        self.button_install = ButtonApplicationInstall("Install")  # or Try!
        self.button_launch = ButtonApplicationLaunch("Launch")
        self.button_options = ButtonApplicationOptions()
        self.label_license = LabelApplicationLicense('')
        self.button_license = ButtonApplicationLicense('')
        self.label_icon = LabelApplicationIcon()
        self.label_name = LabelApplicationName(self.display_name)
        self.label_description = LabelApplicationDescription(self.description)
        self.button_version = ButtonApplicationVersion(
            to_text_string(self.version))
        self.menu_options = QMenu('Application options')
        self.menu_versions = QMenu('Install specific version')
        self.pixmap = QPixmap(self.image_path)
        self.timer = QTimer()
        self.widget = WidgetApplication()
        self.frame_spinner = FrameApplicationSpinner()
        self.spinner = NavigatorSpinner(self.widget, total_width=16)
        lay = QHBoxLayout()
        lay.addWidget(self.spinner)
        self.frame_spinner.setLayout(lay)

        # Widget setup
        self.button_version.setFocusPolicy(Qt.NoFocus)
        self.button_version.setEnabled(True)
        self.label_description.setAlignment(Qt.AlignCenter)
        self.timer.setInterval(self.timeout)
        self.timer.setSingleShot(True)
        self.label_icon.setPixmap(self.pixmap)
        self.label_icon.setScaledContents(True)  # important on High DPI!
        self.label_icon.setMaximumWidth(self.ICON_SIZE)
        self.label_icon.setMaximumHeight(self.ICON_SIZE)
        self.label_icon.setAlignment(Qt.AlignCenter)
        self.label_name.setAlignment(Qt.AlignCenter)
        self.label_name.setWordWrap(True)
        self.label_description.setWordWrap(True)
        self.label_description.setAlignment(Qt.AlignTop | Qt.AlignHCenter)
        self.frame_spinner.setVisible(False)

        # Layouts
        layout_spinner = QHBoxLayout()
        layout_spinner.addWidget(self.button_version, 0, Qt.AlignCenter)
        layout_spinner.addWidget(self.frame_spinner, 0, Qt.AlignCenter)

        layout_license = QHBoxLayout()
        layout_license.addStretch()
        layout_license.addWidget(self.label_license, 0, Qt.AlignCenter)
        layout_license.addWidget(self.button_license, 0, Qt.AlignCenter)
        layout_license.addStretch()

        layout_main = QVBoxLayout()
        layout_main.addWidget(self.button_options, 0, Qt.AlignRight)
        layout_main.addWidget(self.label_icon, 0, Qt.AlignCenter)
        layout_main.addWidget(self.label_name, 0, Qt.AlignCenter)
        layout_main.addLayout(layout_spinner)
        layout_main.addLayout(layout_license)
        layout_main.addWidget(self.label_description, 0, Qt.AlignCenter)
        layout_main.addWidget(self.button_launch, 0, Qt.AlignCenter)
        layout_main.addWidget(self.button_install, 0, Qt.AlignCenter)

        self.widget.setLayout(layout_main)
        self.widget.setStyleSheet(load_style_sheet())
        self.setSizeHint(self.widget_size())
        # This might help with visual quirks on the home screen
        self.widget.setMinimumSize(self.widget_size())

        # Signals
        self.button_install.clicked.connect(self.install_application)
        self.button_launch.clicked.connect(self.launch_application)
        self.button_options.clicked.connect(self.actions_menu_requested)
        self.button_license.clicked.connect(self.launch_url)
        self.timer.timeout.connect(self._application_launched)

        # Setup
        self.update_status()

    # --- Callbacks
    # -------------------------------------------------------------------------
    def _application_launched(self):
        self.button_launch.setDisabled(False)
        update_pointer()

    # --- Helpers
    # -------------------------------------------------------------------------
    def update_style_sheet(self, style_sheet=None):
        """Update custom CSS stylesheet."""
        if style_sheet:
            self.style_sheet = style_sheet
        else:
            self.style_sheet = load_style_sheet()

        self.menu_options.setStyleSheet(self.style_sheet)
        self.menu_versions.setStyleSheet(self.style_sheet)

    def ordered_widgets(self):
        """Return a list of the ordered widgets."""
        return [
            self.button_license, self.button_install, self.button_launch,
            self.button_options
        ]

    @staticmethod
    def widget_size():
        """Return the size defined in the SASS file."""
        return QSize(SASS_VARIABLES.WIDGET_APPLICATION_TOTAL_WIDTH,
                     SASS_VARIABLES.WIDGET_APPLICATION_TOTAL_HEIGHT)

    def launch_url(self):
        """Launch signal for url click."""
        self.widget.sig_url_clicked.emit(self.url)

    def actions_menu_requested(self):
        """Create and display menu for the currently selected application."""
        self.menu_options.clear()
        self.menu_versions.clear()

        # Add versions menu
        versions = self.versions if self.versions else []
        version_actions = []
        for version in reversed(versions):
            action = create_action(self.widget,
                                   version,
                                   triggered=lambda value, version=version:
                                   self.install_application(version=version))

            action.setCheckable(True)
            if self.version == version and self.installed:
                action.setChecked(True)
                action.setDisabled(True)

            version_actions.append(action)

        install_action = create_action(
            self.widget,
            'Install application',
            triggered=lambda: self.install_application())
        install_action.setEnabled(not self.installed)

        update_action = create_action(
            self.widget,
            'Update application',
            triggered=lambda: self.update_application())

        if versions and versions[-1] == self.version:
            update_action.setDisabled(True)
        else:
            update_action.setDisabled(False)

        if self.non_conda and self.name == GLOBAL_VSCODE_APP:
            update_action.setDisabled(True)

        remove_action = create_action(
            self.widget,
            'Remove application',
            triggered=lambda: self.remove_application())
        remove_action.setEnabled(self.installed)

        actions = [
            install_action, update_action, remove_action, None,
            self.menu_versions
        ]
        add_actions(self.menu_options, actions)
        add_actions(self.menu_versions, version_actions)
        offset = QPoint(self.button_options.width(), 0)
        position = self.button_options.mapToGlobal(QPoint(0, 0))
        self.menu_versions.setEnabled(len(versions) > 1)
        self.menu_options.move(position + offset)
        self.menu_options.exec_()

    def update_status(self):
        """Update status."""
        # License check
        license_label_text = ''
        license_url_text = ''
        self.url = ''
        self.expired = False
        button_label = 'Install'

        if self.needs_license:
            # TODO: Fix this method to use the api
            license_info = self.api.get_package_license(self.name)
            license_days = self.api.get_days_left(license_info)
            end_date = license_info.get('end_date', '')
            self.expired = license_days == 0
            plural = 's' if license_days != 1 else ''
            is_trial = license_info.get('type', '').lower() == 'trial'

            if self.installed and license_info:
                if is_trial and not self.expired:
                    license_label_text = ('Trial, {days} day{plural} '
                                          'remaining'.format(days=license_days,
                                                             plural=plural))
                    self.url = ''
                elif is_trial and self.expired:
                    license_label_text = 'Trial expired, '
                    license_url_text = 'contact us'
                    self.url = 'mailto:[email protected]'
                elif not is_trial and not self.expired:
                    license_label_text = 'License expires {}'.format(end_date)
                    self.url = ''
                elif not is_trial and self.expired:
                    license_url_text = 'Renew license'
                    self.url = 'mailto:[email protected]'
            elif self.installed and not bool(license_info):
                # Installed but no license found!
                license_url_text = 'No license found'
                self.url = 'mailto:[email protected]'
            else:
                if not self.expired:
                    button_label = 'Install'
                else:
                    button_label = 'Try'

        self.button_license.setText(license_url_text)
        self.button_license.setVisible(bool(self.url))
        self.label_license.setText(license_label_text)
        self.label_license.setVisible(bool(license_label_text))

        # Version and version updates
        if (self.versions and self.version != self.versions[-1]
                and self.installed):
            # The property is used with CSS to display updatable packages.
            self.button_version.setProperty('pressed', True)
            self.button_version.setToolTip('Version {0} available'.format(
                self.versions[-1]))
        else:
            self.button_version.setProperty('pressed', False)

        # For VScode app do not display if new updates are available
        # See: https://github.com/ContinuumIO/navigator/issues/1504
        if self.non_conda and self.name == GLOBAL_VSCODE_APP:
            self.button_version.setProperty('pressed', False)
            self.button_version.setToolTip('')

        if not self.needs_license:
            self.button_install.setText(button_label)
            self.button_install.setVisible(not self.installed)
            self.button_launch.setVisible(self.installed)
        else:
            self.button_install.setText('Try' if self.expired else 'Install')
            self.button_launch.setVisible(not self.expired)
            self.button_install.setVisible(self.expired)

        self.button_launch.setEnabled(True)

    def update_versions(self, version=None, versions=None):
        """Update button visibility depending on update availability."""
        logger.debug(str((self.name, self.dev_tool, self.installed)))

        if self.installed and version:
            self.button_options.setVisible(True)
            self.button_version.setText(version)
            self.button_version.setVisible(True)
        elif not self.installed and versions:
            self.button_install.setEnabled(True)
            self.button_version.setText(versions[-1])
            self.button_version.setVisible(True)

        self.versions = versions
        self.version = version
        self.update_status()

    def set_loading(self, value):
        """Set loading status."""
        self.button_install.setDisabled(value)
        self.button_options.setDisabled(value)
        self.button_launch.setDisabled(value)
        self.button_license.setDisabled(value)

        if value:
            self.spinner.start()
        else:
            self.spinner.stop()
            if self.version is None and self.versions is not None:
                version = self.versions[-1]
            else:
                version = self.version
            self.button_version.setText(version)
            self.button_launch.setDisabled(self.expired)

        self.frame_spinner.setVisible(value)
        self.button_version.setVisible(not value)

    # --- Helpers using api
    # -------------------------------------------------------------------------
    def _vscode_version(self):
        """Query the vscode version for the default installation path."""
        version = None
        if self._vscode_version_value is None:
            cmd = [self.api.vscode_executable(), '--version']
            # print(cmd)
            import subprocess
            try:
                output = subprocess.check_output(cmd)
                if PY3:
                    output = output.decode()
                output = [o for o in output.split('\n') if o and '.' in o]
                # print(output)
                if output:
                    version = output[0]
            except Exception:
                pass
                # print(e)

            self._vscode_version_value = version
        else:
            version = self._vscode_version_value

        return version

    @property
    def installed(self):
        """Return the installed status of the package."""
        version = None
        if self.non_conda and self.name == GLOBAL_VSCODE_APP:
            # TODO: Vscode program location, check existence
            version = self._vscode_version()
        elif self.prefix:
            version = self.api.conda_package_version(prefix=self.prefix,
                                                     pkg=self.name,
                                                     build=False)
        return bool(version)

    @property
    def version(self):
        """Return the current installed version or the highest version."""
        version = None
        if self.non_conda and self.name == GLOBAL_VSCODE_APP:
            version = self._vscode_version()
        elif self.prefix:
            version = self.api.conda_package_version(prefix=self.prefix,
                                                     pkg=self.name,
                                                     build=False)

        if not version:
            version = self.versions[-1]

        return version

    # --- Application actions
    # ------------------------------------------------------------------------
    def install_application(self, value=None, version=None, install=True):
        """
        Update the application on the defined prefix environment.

        This is used for both normal install and specific version install.
        """
        if not version:
            version = self.versions[-1]

        action = C.APPLICATION_INSTALL if install else C.APPLICATION_UPDATE
        self.widget.sig_conda_action_requested.emit(
            action,
            self.name,
            version,
            C.TAB_HOME,
            self.non_conda,
        )
        self.set_loading(True)

    def remove_application(self):
        """Remove the application from the defined prefix environment."""
        self.widget.sig_conda_action_requested.emit(
            C.APPLICATION_REMOVE,
            self.name,
            None,
            C.TAB_HOME,
            self.non_conda,
        )
        self.set_loading(True)

    def update_application(self):
        """Update the application on the defined prefix environment."""
        self.install_application(version=self.versions[-1], install=False)

    def launch_application(self):
        """Launch application installed in prefix environment."""
        leave_path_alone = False
        if self.command is not None:
            if self.non_conda and self.name == GLOBAL_VSCODE_APP:
                leave_path_alone = True
                args = [self.command]
            else:
                args = self.command.split(' ')
                leave_path_alone = True

            self.button_launch.setDisabled(True)
            self.timer.setInterval(self.timeout)
            self.timer.start()
            update_pointer(Qt.BusyCursor)
            self.widget.sig_launch_action_requested.emit(
                self.name,
                args,
                leave_path_alone,
                self.prefix,
                C.TAB_HOME,
                self.non_conda,
            )
Esempio n. 3
0
class LineEditChannel(LineEditBase):
    """
    Custom line edit that uses different validators for text and url.

    More info:
    http://conda.pydata.org/docs/config.html#channel-locations-channels

    Valid entries:
    - defaults  <- Special case
    - <some-channel-name>
    - https://conda.anaconda.org/<channel>/<package>
    - https://conda.anaconda.org/t/<token>/<package>
    - http://<some.custom.url>/<channel>
    - https://<some.custom.url>/<channel>
    - file:///<some-local-directory>
    """

    VALID_RE = QRegExp('^[A-Za-z][A-Za-z0-9_-]+$|'
                       '^https?://.*|'
                       '^file:///.*')

    sig_return_pressed = Signal()
    sig_escape_pressed = Signal()
    sig_copied = Signal()

    def __init__(self, *args, **kwargs):
        """Custom line edit that uses different validators for text and url."""
        super(LineEditChannel, self).__init__(*args, **kwargs)
        self._validator = QRegExpValidator(self.VALID_RE)
        self.menu = QMenu(parent=self)
        self.setValidator(self._validator)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    def event(self, event):
        """Override Qt method."""
        if (event.type() == QEvent.MouseButtonPress
                and event.buttons() & Qt.RightButton and not self.isEnabled()):
            self.show_menu(event.pos())
            return True
        else:
            return super(LineEditChannel, self).event(event)

    def keyPressEvent(self, event):
        """Override Qt method."""
        key = event.key()

        # Display a copy menu in case the widget is disabled.
        if event.matches(QKeySequence.Paste):
            clipboard = QApplication.clipboard()
            text = clipboard.text()
            if self.VALID_RE.exactMatch(text):
                self.setText(text)
                return
        else:
            if key in [Qt.Key_Return, Qt.Key_Enter]:
                self.sig_return_pressed.emit()
            elif key in [Qt.Key_Escape]:
                self.sig_escape_pressed.emit()
        super(LineEditChannel, self).keyPressEvent(event)

    def show_menu(self, pos):
        """Show copy menu for channel item."""
        self.menu.clear()
        copy = QAction("&Copy", self.menu)
        copy.triggered.connect(self.copy_text)
        self.menu.addAction(copy)
        self.menu.setEnabled(True)
        self.menu.exec_(self.mapToGlobal(pos))

    def copy_text(self):
        """Copy channel text to clipboard."""
        clipboard = QApplication.clipboard()
        clipboard.setText(self.text())
        self.sig_copied.emit()
Esempio n. 4
0
File: Menu.py Progetto: pixpil/gii
class MenuNode(object):
    _currentMenuContext = None
    """docstring for MenuNode"""
    def __init__(self, option, parent, menubar=None):
        if isinstance(option, str):
            blobs = option.split('|')
            _option = {'label': blobs[0]}
            l = len(blobs)
            if l > 1: _option['shortcut'] = blobs[1]
            if l > 2: _option['help'] = blobs[2]
            option = _option

        self.qtmenubar = menubar
        self.qtaction = None
        self.qtmenu = None

        # self.qtaction = None
        self.owner = None
        self.parent = parent
        self.groupName = None

        signal = option.get('signal', None)
        self.setSignal(signal)

        self.mgr = parent and parent.mgr
        self.owner = parent and parent.owner

        self.children = []
        self.actionGroups = {}

        self.label = option.get('label', 'UNNAMED')
        self.name = option.get('name',
                               self.label.replace('&', '').replace(' ', '_'))
        self.name = self.name.lower()

        self.shortcut = option.get('shortcut', False)
        self.help = option.get('help', '')
        self.priority = option.get('priority', 0)
        self.itemType = option.get('type', False)
        self.onClick = option.get('on_click', None)
        self.cmd = option.get('command', None)
        self.cmdArgs = option.get('command_args', None)
        self.link = None

        self.groupName = option.get('group', None)

        self.menuType = self.qtmenubar and 'menubar' or 'item'

        children = option.get('children', None)
        link = option.get('link', None)

        if children or self.itemType == 'menu':
            if self.menuType != 'menubar':
                self.menuType = 'menu'
                self.itemType = False

        elif link:
            self.link = link
            if self.menuType != 'menubar':
                self.menuType = 'link'

        elif parent and parent.menuType == 'menubar':
            self.menuType = 'menu'

        if self.menuType == 'menu':
            self.qtmenu = QMenu(self.label)

        if not parent or parent.menuType == 'root': return

        parent.addChildControl(self)
        if self.itemType == 'check':
            checked = option.get('checked', False)
            self.setValue(checked or False)
        if children:
            for data in children:
                self.addChild(data)
        # self.mgr.addNodeIndex(self)

    def getFullName(self):
        if parent:
            return parent.getFullName() + '/' + self.name
        return self.name

    def addChild(self, option, owner=None):
        if option == '----':
            if self.qtmenu:
                self.qtmenu.addSeparator()
        elif isinstance(option, list):
            output = []
            for data in option:
                n = self.addChild(data)
                if n:
                    output.append(n)
                    if owner: n.owner = owner
            return output
        else:
            node = MenuNode(option, self)
            if owner: node.owner = owner
            self.children.append(node)
            return node

    def affirmQtActionGroup(self, name):
        group = self.actionGroups.get(name, None)
        if not group:
            group = QtWidgets.QActionGroup(self.qtmenu)
            self.actionGroups[name] = group
        return group

    def addChildControl(self, child):
        childType = child.menuType
        selfType = self.menuType

        if selfType == 'menu':
            if childType == 'menu':
                child.qtaction = self.qtmenu.addMenu(child.qtmenu)

            elif child.link:
                qtmenu = child.link.qtmenu
                child.qtaction = self.qtmenu.addMenu(qtmenu)

            else:

                action = QtWidgets.QAction(child.label,
                                           None,
                                           shortcut=child.shortcut,
                                           statusTip=child.help,
                                           checkable=child.itemType == 'check',
                                           triggered=child.handleEvent)

                self.qtmenu.addAction(action)
                child.qtaction = action

                if child.groupName:
                    self.affirmQtActionGroup(child.groupName).addAction(action)

        elif selfType == 'menubar':
            if childType == 'menu':
                self.qtmenubar.addMenu(child.qtmenu)
                child.qtaction = child.qtmenu.menuAction()
            else:
                logging.warning('attempt to add menuitem/link to a menubar')
                return
        else:
            logging.warning('menuitem has no child')

    def setEnabled(self, enabled):
        #todo: set state of linked item
        selfType = self.menuType
        if selfType == 'menubar':
            self.qtmenubar.setEnable(enabled)
            return

        if self.qtmenu:
            self.qtmenu.setEnabled(enabled)
        else:
            self.qtaction.setEnabled(enabled)

    def remove(self):
        self.clear()
        self.parent.children.remove(self)
        selfType = self.menuType

        if not self.parent: return

        if selfType == 'menubar':
            return

        parentType = self.parent.menuType

        if parentType == 'menu':
            self.parent.qtmenu.removeAction(self.qtaction)
        elif parentType == 'menubar':
            self.parent.qtmenubar.removeAction(self.qtaction)
        logging.info('remove menunode:' + self.name)

    def clear(self):
        if self.menuType in ['menu', 'menubar']:
            for node in self.children[:]:
                node.remove()

    def findChild(self, name):
        name = name.lower()
        for c in self.children:
            if c.name == name: return c
        return None

    def getValue(self):
        if self.itemType in ('check', 'radio'):
            return self.qtaction.isChecked()
        return True

    def setValue(self, v):
        if self.itemType in ('check', 'radio'):
            self.qtaction.setChecked(v and True or False)

    def setSignal(self, signal):
        if isinstance(signal, str):
            signal = signals.get(signal)
        self.signal = signal

    def popUp(self, **option):
        if self.qtmenu:
            context = option.get('context', None)
            MenuNode._currentMenuContext = context
            self.qtmenu.exec_(QtGui.QCursor.pos())

    def getContext(self):
        return MenuNode._currentMenuContext

    def setOnClick(self, onClick):
        self.onClick = onClick

    def handleEvent(self):
        itemtype = self.itemType
        value = self.getValue()
        logging.debug('menu event:' + self.name)
        if self.owner:
            if hasattr(self.owner, 'onMenu'):
                self.owner.onMenu(self)

        if self.signal:
            self.signal(value)
        if self.onClick != None:
            self.onClick(value)
        if self.cmd:
            args = self.cmdArgs or {}
            app.doCommand(self.cmd, **args)
        MenuNode._currentMenuContext = None
Esempio n. 5
0
class LineEditEnvironment(LineEditBase):
    """
    Custom line edit to handle regex for naming an environment.
    """
    if WIN:
        VALID_RE = QRegExp('^[A-Za-z][A-Za-z0-9 _-]{0,30}$')
    else:
        VALID_RE = QRegExp('^[A-Za-z][A-Za-z0-9_ -]{0,30}$')

    sig_return_pressed = Signal()
    sig_escape_pressed = Signal()
    sig_copied = Signal()

    def __init__(self, *args, **kwargs):
        """Custom line edit for naming an environment."""
        super(LineEditEnvironment, self).__init__(*args, **kwargs)
        self._validator = QRegExpValidator(self.VALID_RE)
        self.menu = QMenu(parent=self)
        self.setValidator(self._validator)
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)

    def event(self, event):
        """Override Qt method."""
        if (event.type() == QEvent.MouseButtonPress
                and event.buttons() & Qt.RightButton and not self.isEnabled()):
            self.show_menu(event.pos())
            return True
        else:
            return super(LineEditEnvironment, self).event(event)

    def keyPressEvent(self, event):
        """Override Qt method."""
        key = event.key()
        # Display a copy menu in case the widget is disabled.
        if event.matches(QKeySequence.Paste):
            clipboard = QApplication.clipboard()
            text = clipboard.text()
            if self.VALID_RE.exactMatch(text):
                self.setText(text)
                return
        else:
            if key in [Qt.Key_Return, Qt.Key_Enter]:
                self.sig_return_pressed.emit()
            elif key in [Qt.Key_Escape]:
                self.sig_escape_pressed.emit()
        super(LineEditEnvironment, self).keyPressEvent(event)

    def show_menu(self, pos):
        """Show copy menu for channel item."""
        self.menu.clear()
        copy = QAction("&Copy", self.menu)
        copy.triggered.connect(self.copy_text)
        self.menu.addAction(copy)
        self.menu.setEnabled(True)
        self.menu.exec_(self.mapToGlobal(pos))

    def copy_text(self):
        """Copy environment text to clipboard."""
        clipboard = QApplication.clipboard()
        clipboard.setText(self.text())
        self.sig_copied.emit()
Esempio n. 6
0
    def context_menu_requested(self, event, right_click=False):
        """
        Custom context menu.
        """
        if self.proxy_model is None:
            return

        self._menu = QMenu(self)
        index = self.currentIndex()
        model_index = self.proxy_model.mapToSource(index)
        row_data = self.source_model.row(model_index.row())
        column = model_index.column()
        name = row_data[const.COL_NAME]
        # package_type = row_data[const.COL_PACKAGE_TYPE]
        versions = self.source_model.get_package_versions(name)
        current_version = self.source_model.get_package_version(name)

#        if column in [const.COL_ACTION, const.COL_VERSION, const.COL_NAME]:
        if column in [const.COL_ACTION] and not right_click:
            is_installable = self.source_model.is_installable(model_index)
            is_removable = self.source_model.is_removable(model_index)
            is_upgradable = self.source_model.is_upgradable(model_index)

            action_status = self.source_model.action_status(model_index)
            actions = []
            action_unmark = create_action(
                self,
                _('Unmark'),
                triggered=lambda: self.set_action_status(model_index,
                                                         const.ACTION_NONE,
                                                         current_version))
            action_install = create_action(
                self,
                _('Mark for installation'),
                triggered=lambda: self.set_action_status(model_index,
                                                         const.ACTION_INSTALL,
                                                         versions[-1]))
            action_upgrade = create_action(
                self,
                _('Mark for upgrade'),
                triggered=lambda: self.set_action_status(model_index,
                                                         const.ACTION_UPGRADE,
                                                         versions[-1]))
            action_remove = create_action(
                self,
                _('Mark for removal'),
                triggered=lambda: self.set_action_status(model_index,
                                                         const.ACTION_REMOVE,
                                                         current_version))

            version_actions = []
            for version in reversed(versions):
                def trigger(model_index=model_index,
                            action=const.ACTION_INSTALL,
                            version=version):
                    return lambda: self.set_action_status(model_index,
                                                          status=action,
                                                          version=version)
                if version == current_version:
                    version_action = create_action(
                        self,
                        version,
                        icon=QIcon(),
                        triggered=trigger(model_index,
                                          const.ACTION_INSTALL,
                                          version))
                    if not is_installable:
                        version_action.setCheckable(True)
                        version_action.setChecked(True)
                        version_action.setDisabled(True)
                elif version != current_version:
                    if ((version in versions and versions.index(version)) >
                            (current_version in versions and
                             versions.index(current_version))):
                        upgrade_or_downgrade_action = const.ACTION_UPGRADE
                    else:
                        upgrade_or_downgrade_action = const.ACTION_DOWNGRADE

                    if is_installable:
                        upgrade_or_downgrade_action = const.ACTION_INSTALL

                    version_action = create_action(
                        self,
                        version,
                        icon=QIcon(),
                        triggered=trigger(model_index,
                                          upgrade_or_downgrade_action,
                                          version))

                version_actions.append(version_action)

            install_versions_menu = QMenu('Mark for specific version '
                                          'installation', self)
            add_actions(install_versions_menu, version_actions)
            actions = [action_unmark, action_install, action_upgrade,
                       action_remove]
            actions += [None, install_versions_menu]
            install_versions_menu.setEnabled(len(version_actions) > 1)

            if action_status is const.ACTION_NONE:
                action_unmark.setDisabled(True)
                action_install.setDisabled(not is_installable)
                action_upgrade.setDisabled(not is_upgradable)
                action_remove.setDisabled(not is_removable)
                install_versions_menu.setDisabled(False)
            else:
                action_unmark.setDisabled(False)
                action_install.setDisabled(True)
                action_upgrade.setDisabled(True)
                action_remove.setDisabled(True)
                install_versions_menu.setDisabled(True)

        elif right_click:
            license_ = row_data[const.COL_LICENSE]

            metadata = self.metadata_links.get(name, {})
            pypi = metadata.get('pypi', '')
            home = metadata.get('home', '')
            dev = metadata.get('dev', '')
            docs = metadata.get('docs', '')

            q_pypi = QIcon(get_image_path('python.png'))
            q_home = QIcon(get_image_path('home.png'))
            q_docs = QIcon(get_image_path('conda_docs.png'))

            if 'git' in dev:
                q_dev = QIcon(get_image_path('conda_github.png'))
            elif 'bitbucket' in dev:
                q_dev = QIcon(get_image_path('conda_bitbucket.png'))
            else:
                q_dev = QIcon()

            if 'mit' in license_.lower():
                lic = 'http://opensource.org/licenses/MIT'
            elif 'bsd' == license_.lower():
                lic = 'http://opensource.org/licenses/BSD-3-Clause'
            else:
                lic = None

            actions = []

            if license_ != '':
                actions.append(create_action(self, _('License: ' + license_),
                                             icon=QIcon(), triggered=lambda:
                                             self.open_url(lic)))
                actions.append(None)

            if pypi != '':
                actions.append(create_action(self, _('Python Package Index'),
                                             icon=q_pypi, triggered=lambda:
                                             self.open_url(pypi)))
            if home != '':
                actions.append(create_action(self, _('Homepage'),
                                             icon=q_home, triggered=lambda:
                                             self.open_url(home)))
            if docs != '':
                actions.append(create_action(self, _('Documentation'),
                                             icon=q_docs, triggered=lambda:
                                             self.open_url(docs)))
            if dev != '':
                actions.append(create_action(self, _('Development'),
                                             icon=q_dev, triggered=lambda:
                                             self.open_url(dev)))
        if actions and len(actions) > 1:
            # self._menu = QMenu(self)
            add_actions(self._menu, actions)

            if event.type() == QEvent.KeyRelease:
                rect = self.visualRect(index)
                global_pos = self.viewport().mapToGlobal(rect.bottomRight())
            else:
                pos = QPoint(event.x(), event.y())
                global_pos = self.viewport().mapToGlobal(pos)

            self._menu.popup(global_pos)
Esempio n. 7
0
class ListItemApplication(QListWidgetItem):
    """
    Item with custom widget for the applications list.
    """
    ICON_SIZE = 48

    def __init__(self,
                 name=None,
                 description=None,
                 command=None,
                 pixmap=None,
                 version=None,
                 versions=None,
                 path=None,
                 dev_tool=True,
                 prefix=None,
                 is_conda_app=False,
                 packages_widget=None):
        super(ListItemApplication, self).__init__()

        self.api = AnacondaAPI()
        self.command = command
        self.dev_tool = dev_tool
        self.installed = False
        self.is_conda_app = is_conda_app
        self.name = name
        self.path = path
        self.pixmap = pixmap if pixmap else QPixmap(ANACONDA_ICON_64_PATH)
        self.prefix = prefix
        self.timeout = 10000  # In miliseconds
        self.version = version
        self.versions = versions
        self.packages_widget = packages_widget

        # Widgets
        self.button_install = ButtonApplicationInstall("Install")
        self.button_launch = ButtonApplicationLaunch("Launch")
        self.button_options = ButtonApplicationOptions()
        self.label_icon = LabelApplicationIcon()
        self.label_name = LabelApplicationName(name)
        self.label_description = LabelApplicationDescription(description)
        #        self.label_update = LabelApplicationUpdate()
        self.button_version = ButtonApplicationVersion(to_text_string(version))
        self.label_spinner = LabelApplicationSpinner()
        #        self.label_version = LabelApplicationVersion(to_text_string(version))
        self.menu_options = QMenu('Application options')
        self.menu_versions = QMenu('Install specific version')
        self.movie_spinner = QMovie(SPINNER_WHITE_16_PATH)
        self.timer = QTimer()
        self.widget = WidgetApplication()

        # Widget setup
        self.button_version.setFocusPolicy(Qt.NoFocus)
        self.label_name.setToolTip(description)
        self.label_description.setAlignment(Qt.AlignCenter)
        self.movie_spinner.start()
        self.timer.setInterval(self.timeout)
        self.timer.setSingleShot(True)
        self.label_icon.setToolTip(description)
        self.label_icon.setPixmap(
            self.pixmap.scaled(self.ICON_SIZE, self.ICON_SIZE,
                               Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self.label_icon.setAlignment(Qt.AlignCenter)
        self.label_name.setAlignment(Qt.AlignCenter)
        self.label_name.setWordWrap(True)
        self.label_description.setWordWrap(True)
        self.label_description.setAlignment(Qt.AlignTop | Qt.AlignHCenter)
        self.label_spinner.setVisible(False)
        self.label_spinner.setMinimumWidth(16)
        self.label_spinner.setMinimumHeight(16)

        # Layouts
        layout = QVBoxLayout()
        layout.addWidget(self.button_options, 0, Qt.AlignRight)
        layout.addWidget(self.label_icon, 0, Qt.AlignCenter)
        layout.addWidget(self.label_name, 0, Qt.AlignCenter)
        layout.addWidget(self.label_description, 0, Qt.AlignCenter)

        #        hlayout = QHBoxLayout()
        #        hlayout.addWidget(self.label_update)
        #        hlayout.addWidget(self.label_version)
        #        layout.addLayout(hlayout)
        #        layout.addWidget(self.label_version, 0, Qt.AlignCenter)
        layout.addWidget(self.button_version, 0, Qt.AlignCenter)
        layout.addWidget(self.label_spinner, 0, Qt.AlignCenter)
        layout.addWidget(self.button_launch, 0, Qt.AlignCenter)
        layout.addWidget(self.button_install, 0, Qt.AlignCenter)

        self.widget.setLayout(layout)
        self.widget.setStyleSheet(load_style_sheet())
        self.setSizeHint(self.widget.sizeHint())

        # Signals
        self.button_install.clicked.connect(self.install_application)
        self.button_launch.clicked.connect(self.launch_application)
        self.button_options.clicked.connect(self.actions_menu_requested)
        self.timer.timeout.connect(self._application_launched)

        # Setup
        self.update_status()

    # --- Callbacks
    # -------------------------------------------------------------------------
    def _application_launched(self):
        """
        """
        self.button_launch.setDisabled(False)
        update_pointer()

    def _application_installed(self, worker, output, error):
        """
        """
        self.handle_action_finished(worker, output, error)

    def _application_updated(self, worker, output, error):
        """
        """
        self.handle_action_finished(worker, output, error)

    def _application_removed(self, worker, output, error):
        """
        """
        self.handle_action_finished(worker, output, error)

    # --- Helpers
    # -------------------------------------------------------------------------
    def _partial_output_ready(self, worker, output, error):
        """
        """
        message = None
        progress = (0, 0)

        if isinstance(output, dict):
            progress = (output.get('progress',
                                   None), output.get('maxval', None))
            name = output.get('name', None)
            fetch = output.get('fetch', None)

            if fetch:
                message = "Downloading <b>{0}</b>...".format(fetch)

            if name:
                self._current_action_name = name
                message = "Linking <b>{0}</b>...".format(name)

        logger.debug(message)
        self.widget.sig_status_updated.emit(message)

    def update_style_sheet(self, style_sheet=None):
        if style_sheet is None:
            style_sheet = load_style_sheet()
        self.menu_options.setStyleSheet(style_sheet)
        self.menu_versions.setStyleSheet(style_sheet)

    def actions_menu_requested(self):
        """
        Create and display options menu for the currently selected application.
        """
        self.menu_options.clear()
        self.menu_versions.clear()

        # Add versions menu
        versions = self.versions if self.versions else []
        version_actions = []
        for version in reversed(versions):
            action = create_action(self.widget,
                                   version,
                                   triggered=lambda value, version=version:
                                   self.install_application(version=version))

            action.setCheckable(True)
            if self.version == version:
                action.setChecked(True)
                action.setDisabled(True)

            version_actions.append(action)

        update_action = create_action(
            self.widget,
            'Update application',
            triggered=lambda: self.update_application())

        if versions and versions[-1] == self.version:
            update_action.setDisabled(True)
        else:
            update_action.setDisabled(False)

        remove_action = create_action(
            self.widget,
            'Remove application',
            triggered=lambda: self.remove_application())
        remove_action.setEnabled(self.installed)

        actions = [update_action, remove_action, None, self.menu_versions]
        add_actions(self.menu_options, actions)
        add_actions(self.menu_versions, version_actions)
        offset = QPoint(self.button_options.width(), 0)
        position = self.button_options.mapToGlobal(QPoint(0, 0))
        self.menu_versions.setEnabled(bool(versions))
        self.menu_options.move(position + offset)
        self.menu_options.exec_()

    def update_status(self):
        if self.prefix:
            self.version = self.api.conda_package_version(self.prefix,
                                                          pkg=self.name)
        self.installed = bool(self.version)
        if (self.versions and self.version != self.versions[-1]
                and self.installed):
            self.button_version.setIcon(QIcon(CONDA_MANAGER_UPGRADE_ARROW))
            self.button_version.setStyleSheet(
                "ButtonApplicationVersion {color: #0071a0;}")
            self.button_version.setToolTip('Version {0} available'.format(
                self.versions[-1]))
        else:
            self.button_version.setIcon(QIcon())
            self.button_version.setStyleSheet(
                "ButtonApplicationVersion {color: black;}")

        self.button_install.setVisible(not self.installed)
        self.button_launch.setVisible(self.installed)

    def set_loading(self, value):
        self.button_launch.setDisabled(value)
        self.button_install.setDisabled(value)
        self.button_options.setDisabled(value)

        if value:
            self.label_spinner.setMovie(self.movie_spinner)
        else:
            self.label_spinner.setMovie(None)
            if self.version is None:
                version = self.versions[-1]
            else:
                version = self.version
            self.button_version.setText(version)

        self.label_spinner.setVisible(value)
        self.button_version.setVisible(not value)

    def handle_action_finished(self, worker, output, error):
        if not isinstance(output, dict):
            output = {}
        success = output.get('success', True)

        if error or not success:
            # Error might be harmless if no decoding was possible...
            # Success deserves some sort of messagebox
            logger.error(error)
        self.widget.sig_application_updated.emit(self.name, self.version)

        self.update_status()
        self.set_loading(False)

    def update_versions(self, version=None, versions=None):
        """
        Update button visibility depending on update availability.
        """
        update = versions[-1] != version
        logger.debug(str((self.name, self.dev_tool, self.installed)))

        if self.installed and version:
            self.button_options.setVisible(True)
            self.button_version.setText(version)
            self.button_version.setVisible(True)
        elif not self.installed and versions:
            self.button_install.setEnabled(True)
            self.button_version.setText(versions[-1])
            self.button_version.setVisible(True)

        self.versions = versions
        self.version = version
        self.update_status()

    # --- Public API
    # ------------------------------------------------------------------------
    def install_application(self, value=None, version=None):
        """
        Update the application on the defined prefix environment.

        This is used for both normal install and specific version install.
        """
        if version:
            self.version = version
        else:
            self.version = self.versions[-1]
            version = self.versions[-1]

        pkg = '{0}={1}'.format(self.name, version)
        pkgs = [pkg]
        logger.debug(str((pkg, self.dev_tool)))

        # Check if environment exists and then create or install
        #        is_installed = self.api.conda_package_version(prefix=self.prefix,
        #                                                      pkg=self.name)
        #        pkgs = [pkg] + self.BASIC_PACKAGES
        #        if is_installed:
        #            worker = self.api.conda_install(prefix=self.prefix, pkgs=pkgs)
        #        else:
        #            worker = self.api.conda_create(prefix=self.prefix, pkgs=pkgs)

        worker = self.api.conda_install(prefix=self.prefix, pkgs=pkgs)
        worker.sig_finished.connect(self._application_installed)
        worker.sig_partial.connect(self._partial_output_ready)
        self.set_loading(True)
        self.widget.sig_status_updated.emit('Installing application '
                                            '<b>{0}</b>'.format(self.name))

    def launch_application(self):
        """
        Launch application installed in prefix environment.
        """
        if self.command is not None:
            if self.command.startswith('open'):
                command = self.command.replace("${PREFIX}", self.prefix)
            elif self.prefix:
                command = os.sep.join([self.prefix, 'bin', self.command])
            else:
                command = self.command

            self.button_launch.setDisabled(True)
            self.timer.setInterval(self.timeout)
            self.timer.start()
            update_pointer(Qt.BusyCursor)
            launch(self.path, command)

    def remove_application(self):
        """
        Remove the application from the defined prefix environment.
        """
        pkg = self.name
        pkgs = [pkg]
        logger.debug(str((self.name, self.dev_tool)))
        worker = self.api.conda_remove(prefix=self.prefix, pkgs=pkgs)
        worker.sig_finished.connect(self._application_removed)
        worker.sig_partial.connect(self._partial_output_ready)
        self.set_loading(True)
        self.widget.sig_status_updated.emit('Removing application '
                                            '<b>{0}</b>'.format(self.name))

    def update_application(self):
        """
        Update the application on the defined prefix environment.
        """
        logger.debug(str((self.name, self.dev_tool, self.installed)))
        self.install_application(version=self.versions[-1])
        self.widget.sig_status_updated.emit('Updating application '
                                            '<b>{0}</b>'.format(self.name))
Esempio n. 8
0
class PMTableView(QTableView):
    """
    基类,用于显示数据。输入数据类型为列表。
    """
    INSERT_ROW = 0
    DELETE_ROW = 1
    INSERT_COLUMN = 2
    DELETE_COLUMN = 3

    signal_need_save = Signal(bool)

    def __init__(self, data=None):
        super().__init__()
        self.translator = create_translator(
            path=os.path.join(os.path.dirname(__file__), 'translations',
                              'qt_{0}.qm'.format(QLocale.system().name())))  # translator
        self.data = None
        self.menu = QMenu()
        self.action_insert_row = self.menu.addAction(QCoreApplication.translate('PMTableView','Insert Row'))
        self.action_insert_row.triggered.connect(lambda: self.on_change_row_col(self.INSERT_ROW))
        self.action_delete_row = self.menu.addAction(QCoreApplication.translate('PMTableView','Delete Row'))
        self.action_delete_row.triggered.connect(lambda: self.on_change_row_col(self.DELETE_ROW))
        self.action_insert_col = self.menu.addAction(QCoreApplication.translate('PMTableView','Insert Column'))
        self.action_insert_col.triggered.connect(lambda: self.on_change_row_col(self.INSERT_COLUMN))
        self.action_delete_col = self.menu.addAction(QCoreApplication.translate('PMTableView','Delete Column'))
        self.action_delete_col.triggered.connect(lambda: self.on_change_row_col(self.DELETE_COLUMN))
        # self.menu.addAction("aaaaaa")
        if data is not None:
            self.set_data(data)

    def on_change_row_col(self, operation: int):
        """
        The slot for editting row or columns
        Args:
            operation:

        Returns:

        """
        import pandas as pd
        import numpy as np
        pd_data: pd.DataFrame = self.model._data
        current_index = self.currentIndex()
        row, column = current_index.row(), current_index.column()
        if operation == self.INSERT_ROW:
            prev = pd_data.iloc[:row]
            lat = pd_data.iloc[row:]
            self.model._data = pd.concat([prev, pd.DataFrame([[]]), lat])
        elif operation == self.DELETE_ROW:
            self.model._data = pd_data.drop(index=[row], axis=1)
        elif operation == self.INSERT_COLUMN:
            col_name, _ = QInputDialog.getText(self, QCoreApplication.translate('PMTableView','Input Column Title'), QCoreApplication.translate('PMTableView','Title'))
            if _:
                pd_data.insert(column, col_name, np.nan)
        elif operation == self.DELETE_COLUMN:
            self.model._data = pd_data.drop(columns=[column], axis=0)
        else:
            raise NotImplementedError
        self.model.layoutChanged.emit()
        self.signal_need_save.emit(True)

    def set_data(self, data):
        self.data = data
        self.show_data(data)

    def get_data(self):
        return self.model._data

    def show_data(self, data):
        import pandas as pd
        import numpy as np
        if isinstance(data, pd.DataFrame):
            self.model = TableModelForPandasDataframe(data, self.data)
        elif isinstance(data, np.ndarray):
            self.model = TableModelForNumpyArray(data)
            self.menu.setEnabled(False)
        elif isinstance(data, list):
            self.model = TableModelForList(data)
            self.menu.setEnabled(True)
        else:
            raise Exception("data type %s is not supported in PMTableView.\
                            \n Supported Types are: numpy.array,list and pandas.DataFrame." % type(data))
        self.setModel(self.model)

    def get_default_slicing_statement(self):
        return self.model.default_slicing_statement

    def mouseDoubleClickEvent(self, event: 'QMouseEvent') -> None:
        """
        TODO:编辑功能无效,暂时需要屏蔽掉。
        Args:
            event:

        Returns:

        """
        super().mouseDoubleClickEvent(event)
        self.show_edit_dialog(self.currentIndex().row(), self.currentIndex().column())

    def keyPressEvent(self, event: QKeyEvent) -> None:
        super(PMTableView, self).keyPressEvent(event)
        if event.key() == Qt.Key_Return:
            self.show_edit_dialog(self.currentIndex().row(), self.currentIndex().column())

    def show_edit_dialog(self, row, col):
        import pandas as pd
        data = self.model._data
        if isinstance(data, pd.DataFrame):
            def on_edited(text):
                from pandas import Timestamp, Period, Interval
                try:
                    result = eval(text)
                    data.iloc[row, col] = result
                    self.signal_need_save.emit(True)
                except:
                    import traceback
                    QMessageBox.warning(self, QCoreApplication.translate('PMTableView','Warning'),
                                        traceback.format_exc())
                    return

            def on_move_current_cell(direction: int):
                target_row = row + direction
                if 0 <= target_row < self.model.rowCount(col):
                    self.setCurrentIndex(self.model.index(target_row, col))
                    self.show_edit_dialog(target_row, col)

            original_data = data.iloc[row, col]

            dlg = InputValueDialog(self)
            dlg.setWindowTitle(QCoreApplication.translate('PMTableView','Input New Value'))
            dlg.edit.setText(repr(original_data))
            dlg.signal_edit_finished.connect(on_edited)
            dlg.signal_move_cursor.connect(on_move_current_cell)
            global_pos = self.mapToGlobal(
                QPoint(self.columnViewportPosition(col) + 50, self.rowViewportPosition(row) + 50))
            dlg.setGeometry(global_pos.x(), global_pos.y(), dlg.width(), dlg.height())
            dlg.exec_()
            # QInputDialog.getText(self, QCoreApplication.translate('PMTableView','Input New Value'), '', QLineEdit.Normal,
            # text=repr(original_data))

    def contextMenuEvent(self, event: QContextMenuEvent):
        import pandas as pd
        if isinstance(self.model._data, pd.DataFrame):
            self.menu.exec_(event.globalPos())

    def on_goto_index(self, row: int, col: int = 0):
        import pandas as pd
        import numpy as np

        if isinstance(self.data, (pd.DataFrame, np.ndarray)):
            assert 0 <= row <= self.model.rowCount(None)
        self.setCurrentIndex(self.model.index(row, col))