Example #1
0
class DialogUpdateApplication(DialogBase):
    """Update application dialog."""

    WIDTH = 460

    def __init__(self, version, config=CONF, startup=False, qa_testing=False):
        """
        Update application dialog.

        Parameter
        ---------
        version: str
            New version of update available.
        """
        super(DialogUpdateApplication, self).__init__()
        self.tracker = GATracker()

        self.label = QLabel(
            "There's a new version of Anaconda Navigator available. "
            "We strongly recommend you to update. <br><br>"
            "If you click yes, Anaconda Navigator will close and then the "
            "Anaconda Navigator Updater will start.<br><br><br>"
            "Do you wish to update to <b>Anaconda Navigator {0}</b> now?"
            "<br><br>".format(version))
        self.button_yes = ButtonPrimary('Yes')
        self.button_no = ButtonNormal('No, remind me later')
        self.button_no_show = ButtonNormal("No, don't show again")
        self.config = config

        if not startup:
            self.button_no_show.setVisible(False)
            self.button_no.setText('No')

        # Widgets setup
        self.label.setWordWrap(True)
        self.setMinimumWidth(self.WIDTH)
        self.setMaximumWidth(self.WIDTH)
        self.setWindowTitle('Update Application')

        # On QA testing addicon continuumcrew channel allows to test that
        # the update checking mechanism is working with a dummy package
        # version 1000.0.0, this disallows any installation when using that
        # check
        if qa_testing:
            self.button_yes.setDisabled(True)
            self.button_no.setDisabled(True)
            self.button_no_show.setDisabled(True)

        # Layouts
        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_no_show)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_no)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_yes)

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout_buttons.addWidget(SpacerVertical())
        layout_buttons.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        # Signals
        self.button_yes.clicked.connect(self.accept)
        self.button_no.clicked.connect(self.reject)
        self.button_no_show.clicked.connect(self.no_show)

        self.button_yes.setFocus()

    def no_show(self):
        """Handle not showing updates on startup."""
        self.config.set('main', 'hide_update_dialog', True)
        self.reject()
Example #2
0
class DialogChannels(DialogBase):
    """Dialog to add delete and select active conda package channels."""

    sig_channels_updated = Signal(object, object)  # added, removed
    sig_setup_ready = Signal()
    sig_check_ready = Signal()
    WIDTH = 550

    def __init__(self, parent=None):
        """Dialog to add delete and select active conda pacakge channels ."""
        super(DialogChannels, self).__init__(parent)
        self._parent = parent
        self._conda_url = 'https://conda.anaconda.org'
        self.api = AnacondaAPI()
        self.initial_sources = None
        self.config_sources = None
        self.style_sheet = None
        self._setup_ready = False
        self._conda_url_setup_ready = False

        # Widgets
        self.list = ListWidgetChannels(parent=self, api=self.api)
        self.label_info = LabelBase(
            'Manage channels you want Navigator to include.')
        self.label_status = LabelBase('Collecting sources...')
        self.progress_bar = QProgressBar(self)
        self.button_add = ButtonNormal('Add...')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_ok = ButtonPrimary('Update channels')

        # Widget setup
        self.frame_title_bar.setVisible(False)
        self.list.setFrameStyle(QFrame.NoFrame)
        self.list.setFrameShape(QFrame.NoFrame)
        self.setWindowFlags(self.windowFlags() | Qt.Popup)
        self.setWindowOpacity(0.96)
        self.setMinimumHeight(300)
        self.setMinimumWidth(self.WIDTH)
        self.setModal(True)

        # Layout
        layout_button = QHBoxLayout()
        layout_button.addWidget(self.label_info)
        layout_button.addStretch()
        layout_button.addWidget(self.button_add)

        layout_ok = QHBoxLayout()
        layout_ok.addWidget(self.label_status)
        layout_ok.addWidget(SpacerHorizontal())
        layout_ok.addWidget(self.progress_bar)
        layout_ok.addWidget(SpacerHorizontal())
        layout_ok.addStretch()
        layout_ok.addWidget(self.button_cancel)
        layout_ok.addWidget(SpacerHorizontal())
        layout_ok.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addLayout(layout_button)
        layout.addWidget(SpacerVertical())
        layout.addWidget(self.list)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_ok)
        self.setLayout(layout)

        # Signals
        self.button_add.clicked.connect(self.add_channel)
        self.button_ok.clicked.connect(self.update_channels)
        self.button_cancel.clicked.connect(self.reject)
        self.list.sig_status_updated.connect(self.update_status)
        self.list.sig_channel_added.connect(
            lambda v=None: self.set_tab_order())
        self.list.sig_channel_added.connect(
            lambda v=None: self.button_ok.setFocus())
        self.list.sig_channel_removed.connect(
            lambda v=None: self.set_tab_order())
        self.list.sig_channel_removed.connect(
            lambda v=None: self.button_ok.setFocus())
        self.list.sig_channel_checked.connect(self.sig_check_ready)
        self.list.sig_channel_status.connect(self.refresh)

        self.button_add.setDisabled(True)
        self.button_ok.setDisabled(True)
        self.button_cancel.setDisabled(True)
        self.update_status(action='Collecting sources...',
                           value=0,
                           max_value=0)

    @staticmethod
    def _group_sources_and_channels(sources):
        """
        Flatten sources and channels dictionary to list of tuples.

        [(source, channel), (source, channel)...]
        """
        grouped = []
        for source, channels in sources.items():
            for channel in channels:
                grouped.append((source, channel))
        return grouped

    def keyPressEvent(self, event):
        """Override Qt method."""
        key = event.key()
        if key in [Qt.Key_Escape]:
            if self.list.is_editing:
                self.refresh()
                self.list.is_editing = False
            else:
                self.reject()

    # --- Public API
    # -------------------------------------------------------------------------
    def update_style_sheet(self, style_sheet=None):
        """Update custom css style sheets."""
        if style_sheet is None:
            self.style_sheet = load_style_sheet()
        else:
            self.style_sheet = style_sheet

        self.setStyleSheet(self.style_sheet)
        self.setMinimumWidth(SASS_VARIABLES.WIDGET_CHANNEL_DIALOG_WIDTH)

        try:
            self.list.update_style_sheet(style_sheet)
        except Exception:
            pass

    def update_api(self, worker, api_info, error):
        """Update api info."""
        self._conda_url = api_info.get('conda_url',
                                       'https://conda.anaconda.org')
        self._conda_url_setup_ready = True

        if self._setup_ready:
            self.sig_setup_ready.emit()

    def setup(self, worker, conda_config_data, error):
        """Setup the channels widget."""
        self.config_sources = conda_config_data.get('config_sources')
        self.button_add.setDisabled(False)

        for source, data in self.config_sources.items():
            channels = data.get('channels', [])
            for channel in channels:
                item = ListWidgetItemChannel(channel=channel, location=source)
                item.set_editable(False)
                self.list.addItem(item)

        self.set_tab_order()
        self.button_add.setFocus()
        self.button_ok.setDefault(True)
        self.button_cancel.setEnabled(True)

        self.initial_sources = self.list.sources.copy()
        self.update_status()
        self._setup_ready = True

        if self._conda_url_setup_ready:
            self.sig_setup_ready.emit()

    def set_tab_order(self):
        """Fix the tab ordering in the list."""
        if self.list._items:
            self.setTabOrder(self.button_add,
                             self.list._items[0].button_remove)
            self.setTabOrder(self.list._items[-1].button_remove,
                             self.button_cancel)

        self.setTabOrder(self.button_cancel, self.button_ok)
        self.refresh()

    def add_channel(self):
        """Add new conda channel."""
        user_rc_path = self.api._conda_api.user_rc_path
        item = ListWidgetItemChannel(channel='', location=user_rc_path)
        self.list.addItem(item)
        self.refresh(False)

    def update_channels(self):
        """Update channels list and status."""
        sources = self.list.sources

        original = self._group_sources_and_channels(self.initial_sources)
        updated = self._group_sources_and_channels(sources)

        if sorted(original) != sorted(updated):
            self.sig_channels_updated.emit(*self.sources)
            self.accept()
        else:
            self.reject()

    def refresh(self, channel_status=True):
        """Update enable/disable status based on item count."""
        self.button_add.setEnabled(channel_status and bool(self.list.count))
        self.button_ok.setEnabled(channel_status)
        self.button_cancel.setEnabled(True)

        if self.list.count() == 0:
            self.button_add.setEnabled(True)
            self.button_ok.setEnabled(False)

    def update_status(self, action='', message='', value=None, max_value=None):
        """Update the status and progress bar of the widget."""
        visible = bool(action)
        self.label_status.setText(action)
        self.label_status.setVisible(visible)
        if value is not None and max_value is not None:
            self.progress_bar.setVisible(True)
            self.progress_bar.setRange(0, max_value)
            self.progress_bar.setValue(value)
        else:
            self.progress_bar.setVisible(False)

    @property
    def sources(self):
        """Return sources to add and remove from config."""
        original = self._group_sources_and_channels(self.initial_sources)
        updated = self._group_sources_and_channels(self.list.sources)

        original = set(original)
        updated = set(updated)

        add = updated - original
        remove = original - updated

        return add, remove
Example #3
0
class DialogOfflineMode(DialogBase):
    """Offline mode dialog."""

    WIDTH = 460
    _MESSAGE_BASE = (
        "Some of the functionality of Anaconda Navigator will be limited. "
        "Conda environment creation will be subject to the packages "
        "currently available on your package cache."
        "<br><br>")
    _MESSAGE_LOC = (
        "<b>Offline mode</b> is indicated to the left of the login/logout "
        "button on the top right corner of the main application window."
        "<br><br>")
    _MESSAGE_ENABLE = (
        "Offline mode will be disabled automatically when internet "
        "connectivity is restored."
        "<br><br>")
    _MESSAGE_FORCE = (
        "You can also manually force <b>Offline mode</b> by enabling "
        "the setting on the application preferences."
        "<br>")
    _MESSAGE_EXTRA_PREF = (
        "By checking this option you will force <b>Offline mode</b>."
        "<br>")

    MESSAGE_TOOL = _MESSAGE_BASE + _MESSAGE_ENABLE + _MESSAGE_FORCE
    MESSAGE_PREFERENCES = (_MESSAGE_BASE + _MESSAGE_LOC + _MESSAGE_ENABLE +
                           _MESSAGE_EXTRA_PREF)
    MESSAGE_DIALOG = (_MESSAGE_BASE + _MESSAGE_LOC + _MESSAGE_ENABLE +
                      _MESSAGE_FORCE)

    def __init__(
        self,
        parent=None,
        config=CONF,
    ):
        """Offline mode dialog."""
        super(DialogOfflineMode, self).__init__(parent=parent)
        self.tracker = GATracker()

        self.label = QLabel(self.MESSAGE_DIALOG)
        self.button_ok = ButtonPrimary('Ok')
        self.checkbox_hide = QCheckBox("Don't show again")
        self.config = config

        # Widgets setup
        self.label.setWordWrap(True)
        self.setMinimumWidth(self.WIDTH)
        self.setMaximumWidth(self.WIDTH)
        self.setWindowTitle('Offline Mode')

        # Layouts
        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.checkbox_hide)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout_buttons.addWidget(SpacerVertical())
        layout_buttons.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.handle_accept)

        # Setup
        self.button_ok.setFocus()
        self.setup()

    def setup(self):
        """Setup widget content."""
        hide_dialog = self.config.get('main', 'hide_offline_dialog')
        self.checkbox_hide.setChecked(hide_dialog)

    def handle_accept(self):
        """Handle not showing updates on startup."""
        value = bool(self.checkbox_hide.checkState())
        self.config.set('main', 'hide_offline_dialog', value)
        self.accept()
Example #4
0
class LicenseManagerDialog(DialogBase):
    """License Manager main dialog."""

    CONTACT_LINK = 'https://support.continuum.io/'  # TODO: Centralize this?

    # Url, Sender
    sig_url_clicked = Signal(object, object)

    def __init__(self, parent=None):
        """License Manager main dialog."""
        super(LicenseManagerDialog, self).__init__(parent=parent)

        self.api = AnacondaAPI()

        # Widgets
        self.message_box = None  # For testing
        self.button_add = ButtonPrimary('Add license')
        self.button_ok = ButtonNormal('Close')
        self.button_remove = ButtonNormal('Remove license')
        self.button_contact = ButtonLink('Please contact us.')
        self.label_info = LabelBase('Manage your Continuum Analytics '
                                    'license keys.')
        self.label_contact = LabelBase('Got a problem with your license? ')
        self.proxy_model = QSortFilterProxyModel(parent=self)
        self.model = LicenseModel(parent=self)
        self.table = LicenseTableView(parent=self)
        self.delegate = BackgroundDelegate(self.table)

        # Widget setup
        self.proxy_model.setSourceModel(self.model)
        self.table.setItemDelegate(self.delegate)
        self.table.setModel(self.proxy_model)
        self.setWindowTitle('License Manager')

        # Layouts
        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.label_info)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_add)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_remove)

        layout_buttons_bottom = QHBoxLayout()
        layout_buttons_bottom.addWidget(self.label_contact)
        layout_buttons_bottom.addWidget(self.button_contact)
        layout_buttons_bottom.addStretch()
        layout_buttons_bottom.addWidget(self.button_ok)

        layout = QVBoxLayout()
        layout.addLayout(layout_buttons)
        layout.addWidget(SpacerVertical())
        layout.addWidget(self.table)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons_bottom)
        self.setLayout(layout)

        # Signals
        self.button_add.clicked.connect(lambda: self.add_license())
        self.button_remove.clicked.connect(self.remove_license)
        self.button_ok.clicked.connect(self.accept)
        self.button_contact.clicked.connect(
            lambda v=None: self.sig_url_clicked.emit(self.CONTACT_LINK,
                                                     'License Manager'))
        self.table.sig_dropped.connect(self.handle_drop)

        # Setup
        self.button_add.setFocus()
        self.load_licenses()

    def handle_drop(self, links):
        """Handle a drag and drop event."""
        self.api.add_license(links)
        self.load_licenses()

    def _hide_columns(self):
        """Hide columns."""
        for key, val in COL_MAP.items():
            if val in HIDDEN_COLUMNS:
                self.table.setColumnHidden(key, True)

    def add_license(self, v=None, path=None):
        """Add license file."""
        if path is None:
            filename, selected_filter = getopenfilename(
                self,
                'Select license file',
                filters='License files (*.txt)',
                basedir=get_home_dir(),
            )

            if filename:
                paths = [filename]
            else:
                paths = []
        else:
            paths = [path]

        valid_licenses, invalid_licenses = self.api.add_license(paths)

        for path in invalid_licenses:
            text = ('File: <b>"{0}"</b>'
                    '<br>is not a valid license file.').format(path)
            self.message_box = MessageBoxInformation(
                text=text, title="Invalid license file")
            self.message_box.exec_()

        if valid_licenses:
            self.load_licenses()

    def remove_license(self, row=None):
        """Remove license from file."""
        if row is None:
            index = self.table.currentIndex()
        else:
            index = self.proxy_model.index(row, 0)

        model_index = self.proxy_model.mapToSource(index)
        row_data = self.model.row(model_index.row())

        if row_data:
            text = ('Do you want to remove license for product:<br><br>'
                    '<b>{product}</b> ({issued} - {end_date})')
            text = text.format(product=row_data.get('product'),
                               end_date=row_data.get('end_date'),
                               issued=row_data.get('issued'))
            self.message_box = MessageBoxRemove(title='Remove license',
                                                text=text)
            if self.message_box.exec_():
                self.api.remove_license(row_data)
                self.load_licenses()

    def load_licenses(self):
        """Load license files."""
        res = self.api.load_licenses()
        self.model.load_licenses(res)
        self.proxy_model.setSourceModel(self.model)
        self.table.resizeColumnsToContents()
        self._hide_columns()
        self.update_status()

    def count(self):
        """Return the number of items in the table."""
        return self.table.model().rowCount()

    def update_status(self):
        """Update visible and enabled status for widgets based on actions."""
        self.button_remove.setEnabled(bool(self.count()))