Exemplo n.º 1
0
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,
        version=None,
        versions=None,
        image_path=None,
        prefix=None,
        needs_license=False,
        non_conda=False,
        installed=False,
        summary=None,
    ):
        """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 if description else summary
        self.command = command
        self.version = version
        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.installed = installed

        # 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)

        if self.non_conda:
            install_action.setDisabled(True)

        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 = [
            install_action, update_action, remove_action, None,
            self.menu_versions
        ]

        if self.non_conda:
            # we're not going to support messing with vscode/pycharm via navigator for now
            update_action.setDisabled(True)
            remove_action.setDisabled(True)
            install_action.setDisabled(True)
            versions = []
            self.menu_versions.setDisabled(True)

        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:
            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:
                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)

    # --- 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.
        """
        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."""
        # version = None is equivalent to saying "most recent version that is compatible with my env"
        self.install_application(version=None, 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:
                leave_path_alone = True
                # args = [self.command]
                args = external_apps[self.name](
                    config=self.api.config,
                    process_api=self.api._process_api,
                    conda_api=self.api._conda_api
                ).executable
            else:
                args = self.command
                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,
            )
Exemplo n.º 2
0
    def context_menu_requested(self, event, right_click=False):
        """Custom context menu."""
        if self.proxy_model is None:
            return

        self._menu = QMenu(self)
        left_click = not right_click
        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[C.COL_NAME]
        # package_type = row_data[C.COL_PACKAGE_TYPE]
        versions = self.source_model.get_package_versions(name)
        current_version = self.source_model.get_package_version(name)
        action_version = row_data[C.COL_ACTION_VERSION]
        package_status = row_data[C.COL_STATUS]
        package_type = row_data[C.COL_PACKAGE_TYPE]

        remove_actions = bool(self.source_model.count_remove_actions())
        install_actions = bool(self.source_model.count_install_actions())
        update_actions = bool(self.source_model.count_update_actions())

        if column in [C.COL_ACTION] and left_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, C.ACTION_NONE, current_version))
            action_install = create_action(
                self,
                _('Mark for installation'),
                toggled=lambda: self.set_action_status(model_index, C.
                                                       ACTION_INSTALL))
            action_update = create_action(
                self,
                _('Mark for update'),
                toggled=lambda: self.set_action_status(model_index, C.
                                                       ACTION_UPDATE, None))
            action_remove = create_action(
                self,
                _('Mark for removal'),
                toggled=lambda: self.set_action_status(
                    model_index, C.ACTION_REMOVE, current_version))
            version_actions = []
            for version in reversed(versions):

                def trigger(model_index=model_index,
                            action=C.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, C.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 = C.ACTION_UPGRADE
                    else:
                        upgrade_or_downgrade_action = C.ACTION_DOWNGRADE

                    if is_installable:
                        upgrade_or_downgrade_action = C.ACTION_INSTALL

                    version_action = create_action(
                        self,
                        version,
                        icon=QIcon(),
                        triggered=trigger(model_index,
                                          upgrade_or_downgrade_action,
                                          version))
                if action_version == version:
                    version_action.setCheckable(True)
                    version_action.setChecked(True)

                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_update, action_remove
            ]
            actions += [None, install_versions_menu]

            # Disable firing of signals, while setting the checked status
            for ac in actions + version_actions:
                if ac:
                    ac.blockSignals(True)

            if action_status == C.ACTION_NONE:
                action_unmark.setEnabled(False)
                action_install.setEnabled(is_installable)
                action_update.setEnabled(is_upgradable)
                action_remove.setEnabled(is_removable)

                if install_actions:
                    # Invalidate remove and update if install actions selected
                    action_update.setDisabled(True)
                    action_remove.setDisabled(True)
                elif remove_actions:
                    # Invalidate install/update if remove actions already
                    action_install.setDisabled(True)
                    action_update.setDisabled(True)
                elif update_actions:
                    # Invalidate install/update if remove actions already
                    action_install.setDisabled(True)
                    action_remove.setDisabled(True)

                install_versions_menu.setDisabled(False)
            elif action_status == C.ACTION_INSTALL:
                action_unmark.setEnabled(True)
                action_install.setEnabled(False)
                action_install.setChecked(True)
                action_update.setEnabled(False)
                action_remove.setEnabled(False)
            elif action_status == C.ACTION_REMOVE:
                action_unmark.setEnabled(True)
                action_install.setEnabled(False)
                action_update.setEnabled(False)
                action_remove.setEnabled(False)
                action_remove.setChecked(True)
            elif action_status == C.ACTION_UPDATE:
                action_unmark.setEnabled(True)
                action_install.setEnabled(False)
                action_update.setEnabled(False)
                action_update.setChecked(True)
                action_remove.setEnabled(False)
            elif action_status in [C.ACTION_UPGRADE, C.ACTION_DOWNGRADE]:
                action_unmark.setEnabled(True)
                action_install.setEnabled(False)
                action_update.setEnabled(False)
                action_update.setChecked(False)
                action_remove.setEnabled(False)
                install_versions_menu.setEnabled(False)

            if package_status == C.NOT_INSTALLED:
                action_remove.setEnabled(False)
                action_update.setEnabled(False)

            if package_type == C.PIP_PACKAGE:
                action_unmark.setEnabled(False)
                action_install.setEnabled(False)
                action_update.setEnabled(False)
                action_remove.setEnabled(False)

            # Enable firing of signals, while setting the checked status
            for ac in actions + version_actions:
                if ac:
                    ac.blockSignals(False)

                install_versions_menu.setDisabled(True)

            install_versions_menu.setEnabled(
                len(version_actions) > 1 and not remove_actions
                and not update_actions)
        elif right_click:
            license_ = row_data[C.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)
Exemplo n.º 3
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)