Exemplo n.º 1
0
    def send(self):
        """Send error report to github and create an issue with a template."""
        import webbrowser
        from anaconda_navigator.utils.analytics import GATracker
        base = "https://github.com/ContinuumIO/anaconda-issues/issues/new?{0}"
        template = '''
## Main error
{text}
## Traceback
```
{trace}
```
## System information
```
{info}
```
'''
        info = GATracker().info
        info = '\n'.join('{}: {}'.format(k, v) for k, v in info.items())
        query = parse.urlencode(
            {
                'title': "Navigator Error",
                'labels': "tag:navigator",
                'body': template.format(
                    text=self.text, trace=self.error, info=info
                )
            }
        )
        url = base.format(query)
        webbrowser.open_new_tab(url)
Exemplo n.º 2
0
class AboutDialog(DialogBase):
    GITHUB_URL = 'https://github.com/ContinuumIO/anaconda-issues/issues'

    def __init__(self, *args, **kwargs):
        super(AboutDialog, self).__init__(*args, **kwargs)
        self.tracker = GATracker()
        text = """<b>Anaconda Navigator {version}</b><br>
            <br>Copyright &copy; 2016 Continuum Analytics
            <p>Created by Continuum Analytics
            <br>
            <p>For bug reports and feature requests, please visit our
            """.format(version=__version__)
        self.label_icon = QLabel()
        self.label_about = QLabel(text)
        self.button_link = ButtonLink('Issue Tracker')
        self.button_label = ButtonLabel('on GitHub.')
        self.button_ok = QPushButton('Ok')

        # Widget setup
        self.button_ok.setMinimumWidth(70)
        self.label_about.setOpenExternalLinks(True)
        self.label_icon.setPixmap(QPixmap(images.ANACONDA_ICON_64_PATH))
        self.setWindowTitle("About Anaconda Navigator")

        # Layouts
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.label_icon, 0, Qt.AlignTop)
        h_layout.addSpacing(10)

        content_layout = QVBoxLayout()
        content_layout.addWidget(self.label_about, 0, Qt.AlignBottom)
        content_layout.setContentsMargins(0, 0, 0, 0)
        h_content_layout = QHBoxLayout()
        h_content_layout.addWidget(self.button_link, 0, Qt.AlignLeft)
        h_content_layout.addWidget(self.button_label, 0, Qt.AlignLeft)
        h_content_layout.addStretch(0)
        h_content_layout.setContentsMargins(0, 0, 0, 0)

        content_layout.addLayout(h_content_layout)
        h_layout.addLayout(content_layout)

        buttons_layout = QHBoxLayout()
        buttons_layout.addStretch()
        buttons_layout.addWidget(self.button_ok)

        main_layout = QVBoxLayout()
        main_layout.addLayout(h_layout)
        main_layout.addSpacing(24)
        main_layout.addLayout(buttons_layout)
        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_link.clicked.connect(
            lambda: self.open_url(self.GITHUB_URL))

    def open_url(self, url):
        self.tracker.track_event('content', 'click', url)
        QDesktopServices.openUrl(QUrl(url))
Exemplo n.º 3
0
class ListWidgetContent(QListWidget):
    """
    List Widget holding available videos in the learning tab.
    """
    sig_view_video = Signal(str, str)

    def __init__(self, *args, **kwargs):
        self._main = kwargs.pop('main', None)
        super(ListWidgetContent, self).__init__(*args, **kwargs)

        self.tracker = GATracker()
        self._items = []
        self.setObjectName('VideoListWidget')
        self.setResizeMode(QListWidget.Adjust)
        self.setMovement(QListWidget.Static)
        self.setFrameStyle(QListWidget.Plain)
        self.setSelectionMode(QAbstractItemView.NoSelection)
        self.setViewMode(QListWidget.IconMode)
        self.setFocusPolicy(Qt.NoFocus)
        self.setUniformItemSizes(True)

    def addItem(self, item):
        """
        Add a content item to the list.
        """
        super(ListWidgetContent, self).addItem(item)
        self._items.append(item)
        self.setItemWidget(item, item.widget)
        uri = item.uri
        title = item.title
        item.button_view.clicked.connect(lambda: self.launch(uri, title))

    def launch(self, uri, title):
        """
        Emit signal with youtube video identifier string.
        """
        qurl = QUrl(uri)
        QDesktopServices.openUrl(qurl)
        self.tracker.track_event('content', 'click', uri)
        self.sig_view_video.emit(uri, title)

    def update_style_sheet(self, style_sheet=None):
        """
        """
        if style_sheet is None:
            style_sheet = load_style_sheet()

        for item in self._items:
            try:
                item_widget = self.itemWidget(item)
                item_widget.setStyleSheet(style_sheet)
                item.setSizeHint(item_widget.sizeHint())
            except Exception:
                # This error is just in case the C++ object has been
                # deleted and it is not crucial to log.
                pass
        self.update()
        self.repaint()
Exemplo n.º 4
0
    def post_visible_setup(self):
        if self.splash:
            self.splash.hide()

        CONF.set('main', 'first_run', False)

        # Start the tracker only after post_visible_setup
        self.tracker = GATracker()
        self._track_tab(0)  # Start tracking home
        self.fix_tab_ordering()
        self.show_welcome_screen()
Exemplo n.º 5
0
class ListWidgetContent(ListWidgetBase):
    """List Widget holding available videos in the learning tab."""

    sig_view_video = Signal(str, str)

    def __init__(self, *args, **kwargs):
        """List Widget holding available videos in the learning tab."""
        self._main = kwargs.pop('main', None)  # FIXME:
        super(ListWidgetContent, self).__init__(*args, **kwargs)
        self.tracker = GATracker()
        self.setViewMode(QListWidget.IconMode)

    def ordered_widgets(self):
        """Return a list of the ordered widgets."""
        ordered_widgets = []
        for item in self.items():
            ordered_widgets += item.ordered_widgets()
        return ordered_widgets

    def setup_item(self, item):
        """Override base method."""
        max_width = (
            SASS_VARIABLES.WIDGET_CONTENT_TOTAL_WIDTH - 2 *
            SASS_VARIABLES.WIDGET_CONTENT_PADDING - 2 *
            SASS_VARIABLES.WIDGET_CONTENT_MARGIN
        )
        uri = item.uri
        title = item.title

        item.button_text.clicked.connect(lambda: self.launch(uri, title))
        item.button_text.sig_entered.connect(
            lambda: item.frame_hover.fade_in()
        )
        item.button_text.sig_entered.connect(lambda: self.scroll_to_item(item))
        item.button_text.sig_left.connect(lambda: item.frame_hover.fade_out())
        item.frame_hover.sig_clicked.connect(lambda: self.launch(uri, title))
        item.frame_hover.sig_clicked.connect(
            lambda: item.button_text.setFocus()
        )
        item.widget.setStyleSheet(self.style_sheet)
        item.label_text.setText(
            '\n' + split_text(title, item.label_text, max_width)
        )

    def launch(self, uri, title):
        """Emit signal with youtube video identifier string."""
        qurl = QUrl(uri)
        QDesktopServices.openUrl(qurl)
        self.tracker.track_event('content', 'click', uri)
        self.sig_view_video.emit(uri, title)
Exemplo n.º 6
0
    def __init__(self, *args, **kwargs):
        self._main = kwargs.pop('main', None)
        super(ListWidgetContent, self).__init__(*args, **kwargs)

        self.tracker = GATracker()
        self._items = []
        self.setObjectName('VideoListWidget')
        self.setResizeMode(QListWidget.Adjust)
        self.setMovement(QListWidget.Static)
        self.setFrameStyle(QListWidget.Plain)
        self.setSelectionMode(QAbstractItemView.NoSelection)
        self.setViewMode(QListWidget.IconMode)
        self.setFocusPolicy(Qt.NoFocus)
        self.setUniformItemSizes(True)
Exemplo n.º 7
0
    def __init__(self, version):
        super(DialogUpdateApplication, self).__init__()
        self.tracker = GATracker()

        self.label = QLabel('Version "{0}" of Anaconda Navigator is available.'
                            '\n\n'
                            'Do you want to install the new '
                            'update?\n\n'.format(version))
        self.button_yes = QPushButton('Yes')
        self.button_no = ButtonCancel('No')
        self.bbox = QDialogButtonBox(Qt.Horizontal)

        # Widgets setup
        self.bbox.addButton(self.button_no, QDialogButtonBox.RejectRole)
        self.bbox.addButton(self.button_yes, QDialogButtonBox.AcceptRole)
        self.setMinimumWidth(260)
        self.setWindowTitle('Update Application')

        # Layouts
        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.bbox)
        self.setLayout(layout)

        # Signals
        self.button_yes.clicked.connect(self.accept)
        self.button_no.clicked.connect(self.reject)
Exemplo n.º 8
0
    def __init__(self, version, config=CONF, startup=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, you Anaconda Navigator will close and 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')

        # 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()
Exemplo n.º 9
0
    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()
Exemplo n.º 10
0
    def __init__(self, api, parent=None):
        """Login dialog."""
        super(AuthenticationDialog, self).__init__(parent)

        self._parent = parent
        self.config = CONF
        self.api = api
        self.token = None
        self.error = None
        self.tracker = GATracker()
        self.forgot_username_url = None
        self.forgot_password_url = None

        # Widgets
        self.label_username = QLabel('Username:'******'Password:'******'<hr><br><b>Already a member? '
                                        'Sign in!</b><br>')
        # For styling purposes the label next to a ButtonLink is also a button
        # so they align adequately
        self.button_register_text = ButtonLabel('You can register by '
                                                'visiting the ')
        self.button_register = ButtonLink('Anaconda Cloud')
        self.button_register_after_text = ButtonLabel('website.')
        self.label_information = QLabel('''
            <strong>Anaconda Cloud</strong> is where packages, notebooks,
            and <br> environments are shared. It provides powerful <br>
            collaboration and package management for open <br>
            source and private projects.<br>
            ''')
        self.label_message = QLabel('')
        self.button_forgot_username = ButtonLink('I forgot my username')
        self.button_forgot_password = ButtonLink('I forgot my password')
        self.button_login = ButtonPrimary('Login')
        self.button_cancel = ButtonNormal('Cancel')

        # Widgets setup
        self.button_login.setDefault(True)
        username_validator = QRegExpValidator(self.USER_RE)
        self.text_username.setValidator(username_validator)

        self.setMinimumWidth(260)
        self.setWindowTitle('Sign in')

        # This allows to completely style the dialog with css using the frame
        self.text_password.setEchoMode(QLineEdit.Password)
        self.label_message.setVisible(False)

        # Layout
        grid_layout = QVBoxLayout()
        grid_layout.addWidget(self.label_username)
        # grid_layout.addWidget(SpacerVertical())
        grid_layout.addWidget(self.text_username)
        grid_layout.addWidget(SpacerVertical())
        grid_layout.addWidget(self.label_password)
        # grid_layout.addWidget(SpacerVertical())
        grid_layout.addWidget(self.text_password)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.label_information)

        register_layout = QHBoxLayout()
        register_layout.addWidget(self.button_register_text)
        register_layout.addWidget(self.button_register)
        register_layout.addWidget(self.button_register_after_text)
        register_layout.addStretch()
        main_layout.addLayout(register_layout)
        main_layout.addWidget(self.label_signin_text)
        main_layout.addLayout(grid_layout)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(self.label_message)
        main_layout.addWidget(self.button_forgot_username, 0, Qt.AlignRight)
        main_layout.addWidget(self.button_forgot_password, 0, Qt.AlignRight)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_login)

        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(layout_buttons)

        self.setLayout(main_layout)

        # Signals
        self.text_username.textEdited.connect(self.check_text)
        self.text_password.textEdited.connect(self.check_text)
        self.button_login.clicked.connect(self.login)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.check_text()
        self.update_style_sheet()
        self.text_username.setFocus()
        self.setup()
Exemplo n.º 11
0
class AuthenticationDialog(DialogBase):
    """Login dialog."""

    # See https://github.com/Anaconda-Platform/anaconda-server settings
    USER_RE = QRegExp('^[A-Za-z0-9_][A-Za-z0-9_-]+$')
    FORGOT_USERNAME_URL = 'account/forgot_username'
    FORGOT_PASSWORD_URL = 'account/forgot_password'

    sig_authentication_succeeded = Signal()
    sig_authentication_failed = Signal()
    sig_url_clicked = Signal(object)

    def __init__(self, api, parent=None):
        """Login dialog."""
        super(AuthenticationDialog, self).__init__(parent)

        self._parent = parent
        self.config = CONF
        self.api = api
        self.token = None
        self.error = None
        self.tracker = GATracker()
        self.forgot_username_url = None
        self.forgot_password_url = None

        # Widgets
        self.label_username = QLabel('Username:'******'Password:'******'<hr><br><b>Already a member? '
                                        'Sign in!</b><br>')
        # For styling purposes the label next to a ButtonLink is also a button
        # so they align adequately
        self.button_register_text = ButtonLabel('You can register by '
                                                'visiting the ')
        self.button_register = ButtonLink('Anaconda Cloud')
        self.button_register_after_text = ButtonLabel('website.')
        self.label_information = QLabel('''
            <strong>Anaconda Cloud</strong> is where packages, notebooks,
            and <br> environments are shared. It provides powerful <br>
            collaboration and package management for open <br>
            source and private projects.<br>
            ''')
        self.label_message = QLabel('')
        self.button_forgot_username = ButtonLink('I forgot my username')
        self.button_forgot_password = ButtonLink('I forgot my password')
        self.button_login = ButtonPrimary('Login')
        self.button_cancel = ButtonNormal('Cancel')

        # Widgets setup
        self.button_login.setDefault(True)
        username_validator = QRegExpValidator(self.USER_RE)
        self.text_username.setValidator(username_validator)

        self.setMinimumWidth(260)
        self.setWindowTitle('Sign in')

        # This allows to completely style the dialog with css using the frame
        self.text_password.setEchoMode(QLineEdit.Password)
        self.label_message.setVisible(False)

        # Layout
        grid_layout = QVBoxLayout()
        grid_layout.addWidget(self.label_username)
        # grid_layout.addWidget(SpacerVertical())
        grid_layout.addWidget(self.text_username)
        grid_layout.addWidget(SpacerVertical())
        grid_layout.addWidget(self.label_password)
        # grid_layout.addWidget(SpacerVertical())
        grid_layout.addWidget(self.text_password)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.label_information)

        register_layout = QHBoxLayout()
        register_layout.addWidget(self.button_register_text)
        register_layout.addWidget(self.button_register)
        register_layout.addWidget(self.button_register_after_text)
        register_layout.addStretch()
        main_layout.addLayout(register_layout)
        main_layout.addWidget(self.label_signin_text)
        main_layout.addLayout(grid_layout)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(self.label_message)
        main_layout.addWidget(self.button_forgot_username, 0, Qt.AlignRight)
        main_layout.addWidget(self.button_forgot_password, 0, Qt.AlignRight)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_login)

        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(layout_buttons)

        self.setLayout(main_layout)

        # Signals
        self.text_username.textEdited.connect(self.check_text)
        self.text_password.textEdited.connect(self.check_text)
        self.button_login.clicked.connect(self.login)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.check_text()
        self.update_style_sheet()
        self.text_username.setFocus()
        self.setup()

    def setup(self):
        """Setup login dialog."""
        self.update_links()

    def update_links(self):
        """Update links."""
        for button in [
                self.button_forgot_username,
                self.button_forgot_password,
                self.button_register,
        ]:
            try:
                button.disconnect()
            except TypeError:  # pragma: no cover
                pass

        # TODO, get this from anaconda client directly?
        # from binstar_client.utils import get_config, set_config
        # config = get_config()
        anaconda_api_url = self.config.get('main', 'anaconda_api_url', None)
        if anaconda_api_url:
            # Remove api if using a subdomain
            base_url = anaconda_api_url.lower().replace('//api.', '//')
            self.base_url = base_url

            # Remove api if not using a subdomain
            parts = base_url.lower().split('/')
            if parts[-1] == 'api':
                base_url = '/'.join(parts[:-1])

            self.forgot_username_url = (base_url + '/' +
                                        self.FORGOT_USERNAME_URL)
            self.forgot_password_url = (base_url + '/' +
                                        self.FORGOT_PASSWORD_URL)

            self.button_register.clicked.connect(
                lambda: self.open_url(base_url))
            self.button_forgot_username.clicked.connect(
                lambda: self.open_url(self.forgot_username_url))
            self.button_forgot_password.clicked.connect(
                lambda: self.open_url(self.forgot_password_url))

    @property
    def username(self):
        """Return the logged username."""
        return self.text_username.text().lower()

    def update_style_sheet(self, style_sheet=None):
        """Update custom css style sheet."""
        if style_sheet is None:
            style_sheet = load_style_sheet()
        self.setStyleSheet(style_sheet)

    def check_text(self):
        """Check that `username` and `password` are valid.

        If not empty and disable/enable buttons accordingly.
        """
        username = self.text_username.text()
        password = self.text_password.text()

        if len(username) == 0 or len(password) == 0:
            self.button_login.setDisabled(True)
        else:
            self.button_login.setDisabled(False)

    def login(self):
        """Try to log the user in the specified anaconda api endpoint."""
        self.button_login.setEnabled(False)
        self.text_username.setText(self.text_username.text().lower())
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.label_message.setText('')
        worker = self.api.login(self.text_username.text().lower(),
                                self.text_password.text())
        worker.sig_finished.connect(self._finished)

    def _finished(self, worker, output, error):
        """Callback for the login procedure after worker has finished."""
        token = output
        if token:
            self.token = token
            self.sig_authentication_succeeded.emit()
            self.accept()
        elif error:
            username = self.text_username.text().lower()
            bold_username = '******'.format(username)

            # The error might come in (error_message, http_error) format
            try:
                error_message = ast.literal_eval(str(error))[0]
            except Exception:  # pragma: no cover
                error_message = str(error)

            error_message = error_message.lower().capitalize()
            error_message = error_message.split(', ')[0]
            error_text = '<i>{0}</i>'.format(error_message)
            error_text = error_text.replace(username, bold_username)
            self.label_message.setText(error_text)
            self.label_message.setVisible(True)

            if error_message:
                domain = self.api.client_domain()
                label = '{0}/{1}: {2}'.format(domain, username,
                                              error_message.lower())
                self.tracker.track_event(
                    'authenticate',
                    'login failed',
                    label=label,
                )
                self.text_password.setFocus()
                self.text_password.selectAll()
            self.sig_authentication_failed.emit()

        self.button_login.setDisabled(False)
        self.check_text()
        QApplication.restoreOverrideCursor()

    def open_url(self, url):
        """Open given url in the default browser and log the action."""
        self.tracker.track_event('content', 'click', url)
        self.sig_url_clicked.emit(url)
        QDesktopServices.openUrl(QUrl(url))
Exemplo n.º 12
0
    def __init__(self, api, parent=None):
        super(AuthenticationDialog, self).__init__(parent)

        self.api = api
        self._parent = parent
        self.token = None
        self.error = None
        self.tracker = GATracker()

        # Widgets
        self.label_username = QLabel('Username:'******'Password:'******'You can register ')
        self.label_signin_text = QLabel('<hr><br><b>Already a member? '
                                        'Sign in!</b><br>')
        # For styling purposes the label next to a ButtonLink is also a button
        # so they align adequately
        self.button_register_text = ButtonLabel('You can register by '
                                                'visiting the')
        self.button_register = ButtonLink('Anaconda Cloud')
        self.button_register_after_text = ButtonLabel('website.')
        self.label_information = QLabel('''
            <strong>Anaconda Cloud</strong> is where packages, notebooks,
            and <br> environments are shared. It provides powerful <br>
            collaboration and package management for open <br>
            source and private projects.<br>
            ''')
        self.label_message = QLabel('')
        self.button_forgot_username = ButtonLink('I forgot my username')
        self.button_forgot_password = ButtonLink('I forgot my password')
        self.button_login = QPushButton('Login')
        self.button_cancel = ButtonCancel('Cancel')
        self.bbox = QDialogButtonBox(Qt.Horizontal)

        # Widgets setup
        self.bbox.addButton(self.button_cancel, QDialogButtonBox.RejectRole)
        self.bbox.addButton(self.button_login, QDialogButtonBox.AcceptRole)
        self.text_username.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.text_password.setAttribute(Qt.WA_MacShowFocusRect, False)

        self.setMinimumWidth(260)
        self.setWindowTitle('Sign in')

        # This allows to completely style the dialog with css using the frame
        self.text_password.setEchoMode(QLineEdit.Password)
        self.label_message.setVisible(False)

        # Layout
        grid_layout = QGridLayout()
        grid_layout.addWidget(self.label_username, 0, 0)
        grid_layout.addWidget(self.text_username, 0, 1)
        grid_layout.addWidget(self.label_password, 1, 0)
        grid_layout.addWidget(self.text_password, 1, 1)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.label_information)

        register_layout = QHBoxLayout()
        register_layout.addWidget(self.button_register_text, 0)
        register_layout.addWidget(self.button_register, 0, Qt.AlignLeft)
        register_layout.addWidget(self.button_register_after_text, 0,
                                  Qt.AlignLeft)
        register_layout.addStretch()
        register_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.addLayout(register_layout)
        main_layout.addWidget(self.label_signin_text)
        main_layout.addLayout(grid_layout)
        main_layout.addSpacing(5)
        main_layout.addWidget(self.label_message)
        main_layout.addWidget(self.button_forgot_username, 0, Qt.AlignRight)
        main_layout.addWidget(self.button_forgot_password, 0, Qt.AlignRight)

        main_layout.addSpacing(15)
        main_layout.addWidget(self.bbox)

        self.setLayout(main_layout)

        # Signals
        self.button_forgot_username.clicked.connect(
            lambda: self.open_url(self.FORGOT_USERNAME_URL))
        self.button_forgot_password.clicked.connect(
            lambda: self.open_url(self.FORGOT_PASWORD_URL))
        self.button_register.clicked.connect(
            lambda: self.open_url(self.REGISTER_URL))
        self.text_username.textEdited.connect(self.check_text)
        self.text_password.textEdited.connect(self.check_text)
        self.button_login.clicked.connect(self.login)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.check_text()
        self.update_style_sheet()
        self.text_username.setFocus()
Exemplo n.º 13
0
class EnvironmentsTab(WidgetBase):
    """
    This tab holds the list of named and application environments in the local
    machine.

    Available options include, `create`, `clone` and `remove` and package
    management.
    """
    BLACKLIST = ['anaconda-navigator']  # Do not show in package manager.

    sig_status_updated = Signal(object, object, object, object)

    def __init__(self, parent=None):
        super(EnvironmentsTab, self).__init__(parent)

        self.api = AnacondaAPI()
        self.last_env_prefix = None
        self.last_env_name = None
        self.previous_environments = None
        self.tracker = GATracker()
        self.metadata = {}

        active_channels = CONF.get('main',  'conda_active_channels', tuple())
        channels = CONF.get('main',  'conda_channels', tuple())
        conda_url = CONF.get('main',  'conda_url',
                             'https:/conda.anaconda.org')
        conda_api_url = CONF.get('main',  'anaconda_api_url',
                                 'https://api.anaconda.org')

        # Widgets
        self.button_clone = ButtonEnvironmentPrimary("Clone")
        self.button_create = ButtonEnvironmentPrimary("Create")
        self.button_remove = ButtonEnvironmentCancel("Remove")
        self.frame_environments = FrameEnvironments(self)
        self.frame_environments_list = FrameEnvironmentsList(self)
        self.frame_environments_list_buttons = FrameEnvironmentsListButtons(self)
        self.frame_environments_packages = FrameEnvironmentsPackages(self)
        self.list_environments = ListWidgetEnvironment()
        self.packages_widget = CondaPackagesWidget(
            self,
            setup=False,
            active_channels=active_channels,
            channels=channels,
            data_directory=CHANNELS_PATH,
            conda_api_url=conda_api_url,
            conda_url=conda_url)
        self.menu_list = QMenu()
        self.text_search = LineEditSearch()
        self.timer_environments = QTimer()

        # Widgets setup
        self.list_environments.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.list_environments.setContextMenuPolicy(Qt.CustomContextMenu)
        self.packages_widget.textbox_search.setAttribute(
            Qt.WA_MacShowFocusRect, False)
        self.packages_widget.textbox_search.set_icon_visibility(False)
        self.text_search.setPlaceholderText("Search Environments")
        self.text_search.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.timer_environments.setInterval(5000)

        # Layouts
        environments_layout = QVBoxLayout()
        environments_layout.addWidget(self.text_search)

        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(self.button_create)
        buttons_layout.addWidget(self.button_clone)
        buttons_layout.addWidget(self.button_remove)
        buttons_layout.setContentsMargins(0, 0, 0, 0)

        list_buttons_layout = QVBoxLayout()
        list_buttons_layout.addWidget(self.list_environments)
        list_buttons_layout.addLayout(buttons_layout)
        self.frame_environments_list_buttons.setLayout(list_buttons_layout)
        list_buttons_layout.setContentsMargins(0, 0, 0, 0)
        environments_layout.addWidget(self.frame_environments_list_buttons)

        self.frame_environments_list.setLayout(environments_layout)

        packages_layout = QHBoxLayout()
        packages_layout.addWidget(self.packages_widget)
        packages_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_environments_packages.setLayout(packages_layout)

        main_layout = QHBoxLayout()
        main_layout.addWidget(self.frame_environments_list, 1)
        main_layout.addWidget(self.frame_environments_packages, 3)
        main_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_environments.setLayout(main_layout)

        layout = QHBoxLayout()
        layout.addWidget(self.frame_environments)
        self.setLayout(layout)

        # Signals
        self.button_clone.clicked.connect(self.clone_environment)
        self.button_create.clicked.connect(self.create_environment)
        self.button_remove.clicked.connect(self.remove_environment)
        self.list_environments.sig_item_selected.connect(
            self.load_environment)
        self.packages_widget.sig_packages_ready.connect(self.refresh)
        self.packages_widget.sig_channels_updated.connect(self.update_channels)
#        self.packages_widget.sig_environment_cloned.connect(
#            self._environment_created)
#        self.packages_widget.sig_environment_created.connect(
#            self._environment_created)
#        self.packages_widget.sig_environment_removed.connect(
#            self._environment_removed)
        self.text_search.textChanged.connect(self.filter_environments)
        self.timer_environments.timeout.connect(self.refresh_environments)
        self.packages_widget.sig_process_cancelled.connect(
            lambda: self.update_visibility(True))

    # --- Helpers
    # -------------------------------------------------------------------------
    def update_visibility(self, enabled=True):
        self.button_create.setDisabled(not enabled)
        self.button_remove.setDisabled(not enabled)
        self.button_clone.setDisabled(not enabled)
        self.list_environments.setDisabled(not enabled)
        update_pointer()

    def update_style_sheet(self, style_sheet=None):
        if style_sheet is None:
            style_sheet = load_style_sheet()

        self.setStyleSheet(style_sheet)
        self.menu_list.setStyleSheet(style_sheet)
        self.list_environments.setFrameStyle(QFrame.NoFrame)
        self.list_environments.setFrameShape(QFrame.NoFrame)
        self.packages_widget.table.setFrameStyle(QFrame.NoFrame)
        self.packages_widget.table.setFrameShape(QFrame.NoFrame)
        self.packages_widget.layout().setContentsMargins(0, 0, 0, 0)

        size = QSize(16, 16)

        palette = {
            'icon.action.not_installed': QIcon(images.CONDA_MANAGER_NOT_INSTALLED).pixmap(size),
            'icon.action.installed': QIcon(images.CONDA_MANAGER_INSTALLED).pixmap(size),
            'icon.action.remove': QIcon(images.CONDA_MANAGER_REMOVE).pixmap(size),
            'icon.action.add': QIcon(images.CONDA_MANAGER_ADD).pixmap(size),
            'icon.action.upgrade': QIcon(images.CONDA_MANAGER_UPGRADE).pixmap(size),
            'icon.action.downgrade': QIcon(images.CONDA_MANAGER_DOWNGRADE).pixmap(size),
            'icon.upgrade.arrow': QIcon(images.CONDA_MANAGER_UPGRADE_ARROW).pixmap(size),
            'background.remove': QColor(0, 0, 0, 0),
            'background.install': QColor(0, 0, 0, 0),
            'background.upgrade': QColor(0, 0, 0, 0),
            'background.downgrade': QColor(0, 0, 0, 0),
            'foreground.not.installed': QColor("#666"),
            'foreground.upgrade': QColor("#0071a0"),
            }

        self.packages_widget.update_style_sheet(
            style_sheet=style_sheet,
            extra_dialogs={'cancel_dialog': ClosePackageManagerDialog,
                           'apply_actions_dialog': ActionsDialog,
                           'message_box_error': MessageBoxError,
                           },
            palette=palette,
            )

    def get_environments(self):
        """
        Return an ordered dictionary of all existing named environments as
        keys and the prefix as items.

        The dictionary includes the root environment as the first entry.
        """
        environments = OrderedDict()
        environments_prefix = sorted(self.api.conda_get_envs())
        environments['root'] = self.api.ROOT_PREFIX

        for prefix in environments_prefix:
            name = os.path.basename(prefix)
            environments[name] = prefix

        return environments

    def refresh_environments(self):
        """
        Check every `timer_refresh_envs` amount of miliseconds for newly
        created environments and update the list if new ones are found.
        """
        environments = self.get_environments()
        if self.previous_environments is None:
            self.previous_environments = environments.copy()

        if self.previous_environments != environments:
            self.previous_environments = environments.copy()
            self.setup_tab()

    def open_environment_in(self, which):
        environment_prefix = self.list_environments.currentItem().prefix()
        environment_name = self.list_environments.currentItem().text()
        logger.debug("%s, %s", which, environment_prefix)

        if environment_name == 'root':
            environment_prefix = None

        if which == 'terminal':
            launch.console(environment_prefix)
        else:
            launch.py_in_console(environment_prefix, which)

    def set_last_active_prefix(self):
        current_item = self.list_environments.currentItem()
        if current_item:
            self.last_env_prefix = getattr(current_item, '_prefix')
        else:
            self.last_env_prefix = self.api.ROOT_PREFIX
        CONF.set('main', 'last_active_prefix', self.last_env_prefix)

    def setup_tab(self, metadata={}, load_environment=True):
        if metadata:
            self.metadata = metadata

        # show_apps = CONF.get('main', 'show_application_environments')
        envs = self.get_environments()
        self.timer_environments.start()
        self.menu_list.clear()
        menu_item = self.menu_list.addAction('Open Terminal')
        menu_item.triggered.connect(
            lambda: self.open_environment_in('terminal'))

        for word in ['Python', 'IPython', 'Jupyter Notebook']:
            menu_item = self.menu_list.addAction("Open with " + word)
            menu_item.triggered.connect(
                lambda x, w=word: self.open_environment_in(w.lower()))

        def select(value=None, position=None):
            current_item = self.list_environments.currentItem()
            prefix = current_item.prefix()

            if isinstance(position, bool) or position is None:
                width = current_item.button_options.width()
                position = QPoint(width, 0)

#            parent_position = self.list_environments.mapToGlobal(QPoint(0, 0))
            point = QPoint(0, 0)
            parent_position = current_item.button_options.mapToGlobal(point)
            self.menu_list.move(parent_position + position)
            self.menu_list.actions()[2].setEnabled(
                launch.check_prog('ipython', prefix))
            self.menu_list.actions()[3].setEnabled(
                launch.check_prog('notebook', prefix))
            self.menu_list.exec_()

        self.set_last_active_prefix()
        self.list_environments.clear()

#        if show_apps:
#            separator_item = ListItemSeparator('My environments:')
#            self.list_environments.addItem(separator_item)

        for env in envs:
            prefix = envs[env]
            item = ListItemEnvironment(env, prefix=prefix)
            item.button_options.clicked.connect(select)
            self.list_environments.addItem(item)

#        if show_apps:
#            application_envs = self.api.get_application_environments()
#            separator_item = ListItemSeparator('Application environments:')
#            self.list_environments.addItem(separator_item)
#            for app in application_envs:
#                env_prefix = application_envs[app]
#                item = ListItemEnvironment(name=app, prefix=env_prefix)
#                item.button_options.clicked.connect(select)
#                self.list_environments.addItem(item)

        if load_environment:
            self.load_environment()
        else:
            return

        # Adjust Tab Order
        self.setTabOrder(self.text_search,
                         self.list_environments._items[0].widget)
        for i in range(len(self.list_environments._items) - 1):
            self.setTabOrder(self.list_environments._items[i].widget,
                             self.list_environments._items[i+1].widget)
        self.setTabOrder(self.list_environments._items[-1].button_name,
                         self.button_create)
        self.setTabOrder(self.button_create, self.button_clone)
        self.setTabOrder(self.button_clone, self.button_remove)
        self.setTabOrder(self.button_remove,
                         self.packages_widget.combobox_filter)
        self.setTabOrder(self.packages_widget.combobox_filter,
                         self.packages_widget.button_channels)
        self.setTabOrder(self.packages_widget.button_channels,
                         self.packages_widget.button_update)
        self.setTabOrder(self.packages_widget.button_update,
                         self.packages_widget.textbox_search)
        self.setTabOrder(self.packages_widget.textbox_search,
                         self.packages_widget.table_first_row)
        self.setTabOrder(self.packages_widget.table_last_row,
                         self.packages_widget.button_apply)
        self.setTabOrder(self.packages_widget.button_apply,
                         self.packages_widget.button_clear)
        self.setTabOrder(self.packages_widget.button_clear,
                         self.packages_widget.button_cancel)

    def filter_environments(self):
        """
        Filter displayed environments by matching search text.
        """
        text = self.text_search.text().lower()

        for i in range(self.list_environments.count()):
            item = self.list_environments.item(i)
            item.setHidden(text not in item.text().lower())

            if not item.widget.isVisible():
                item.widget.repaint()

    def load_environment(self, item=None):
        self.update_visibility(False)
        if item is None:
            item = self.list_environments.currentItem()

        if item is None or not isinstance(item, ListItemEnvironment):
            prefix = self.api.ROOT_PREFIX
            index = 0
        elif item and isinstance(item, ListItemEnvironment):
            prefix = item.prefix()
        else:
            prefix = self.last_env_prefix if self.last_env_prefix else None

        index = [i for i, it in enumerate(self.list_environments._items)
                 if prefix in it.prefix()]
        index = index[0] if len(index) else 0

        self.list_environments.setCurrentRow(index)
        self.packages_widget.set_environment(prefix=prefix)
        self.packages_widget.setup(check_updates=False,
                                   blacklist=self.BLACKLIST,
                                   metadata=self.metadata)
        self.list_environments.setDisabled(True)
        self.update_visibility(False)
        self.set_last_active_prefix()
#        update_pointer(Qt.BusyCursor)

    def refresh(self):
        self.update_visibility(True)
        self.list_environments.setDisabled(False)
        item = self.list_environments.currentItem()

        try:
            item.set_loading(False)
        except RuntimeError:
            pass
            # C/C++ object not found

        is_root = item.text() == 'root'

        self.button_remove.setDisabled(is_root)
        self.button_clone.setDisabled(is_root)

    def update_channels(self, channels, active_channels):
        """
        Save updated channels to the CONF.
        """
        CONF.set('main', 'conda_active_channels', active_channels)
        CONF.set('main', 'conda_channels', channels)

    # --- Callbacks
    # -------------------------------------------------------------------------
    def _environment_created(self, worker, output, error):
        if error:
            logger.error(str(error))

        self.update_visibility(False)
        for row, environment in enumerate(self.get_environments()):
            if worker.name == environment:
                break

        self.last_env_prefix = self.api.conda_get_prefix_envname(environment)
        self.setup_tab(load_environment=False)
        self.list_environments.setCurrentRow(row)
        self.load_environment()
        self.refresh()
        self.update_visibility(True)
        update_pointer()

    def _environment_removed(self, worker, output, error):
        self.update_visibility(True)
        if error:
            logger.error(str(error))

        self.setup_tab()
        self.list_environments.setCurrentRow(0)

    # --- Public API
    # -------------------------------------------------------------------------
    def update_domains(self, anaconda_api_url, conda_url):
        self.packages_widget.update_domains(
            anaconda_api_url=anaconda_api_url,
            conda_url=conda_url,
            )

    def create_environment(self):
        """
        Create new basic environment with selectable python version.

        Actually makes new env on disc, in directory within the project
        whose name depends on the env name. New project state is saved.
        Should also sync to spec file.
        """
        dlg = CreateEnvironmentDialog(parent=self,
                                      environments=self.get_environments())
        self.tracker.track_page('/environments/create',
                                pagetitle='Create new environment dialog')

        if dlg.exec_():
            name = dlg.text_name.text().strip()
            pyver = dlg.combo_version.currentText()

            if name:
                logger.debug(str('{0}, {1}'.format(name, pyver)))

                self.update_visibility(False)
                update_pointer(Qt.BusyCursor)

                if pyver:
                    pkgs = ['python=' + pyver, 'jupyter']
                else:
                    pkgs = ['jupyter']

                channels = self.packages_widget._active_channels
                logger.debug(str((name, pkgs, channels)))
                self.update_visibility(False)
                worker = self.packages_widget.create_environment(name=name, 
                                                                 packages=pkgs)
#                worker = self.api.conda_create(name=name, pkgs=pkgs,
#                                               channels=channels)
                worker.name = name
                worker.sig_finished.connect(self._environment_created)
        self.tracker.track_page('/environments')

    def remove_environment(self):
        """
        Clone currently selected environment.
        """
        current_item = self.list_environments.currentItem()
        if current_item is not None:
            name = current_item.text()

            if name == 'root':
                return

            dlg = RemoveEnvironmentDialog(environment=name)
            self.tracker.track_page('/environments/remove',
                                    pagetitle='Remove environment dialog')
            if dlg.exec_():
                logger.debug(str(name))
                self.update_visibility(False)
                update_pointer(Qt.BusyCursor)
                worker = self.packages_widget.remove_environment(name=name)
#                worker = self.api.conda_remove(name=name, all_=True)
                worker.sig_finished.connect(self._environment_removed)
#                self.sig_status_updated.emit('Deleting environment '
#                                             '"{0}"'.format(name),
#                                             0, -1, -1)
            self.tracker.track_page('/environments')

    def clone_environment(self):
        """
        Clone currently selected environment.
        """
        current_item = self.list_environments.currentItem()
        if current_item is not None:
            current_name = current_item.text()
            dlg = CloneEnvironmentDialog(parent=self,
                                         environments=self.get_environments())
            self.tracker.track_page('/environments/clone',
                                    pagetitle='Clone environment dialog')

            if dlg.exec_():
                name = dlg.text_name.text().strip()

                if name and current_name:
                    logger.debug(str("{0}, {1}".format(current_name, name)))

                    self.update_visibility(False)
                    update_pointer(Qt.BusyCursor)
                    worker = self.packages_widget.clone_environment(clone=current_name,
                                                                    name=name)
#                    worker = self.api.conda_clone(current_name, name=name)
                    worker.name = name
                    worker.sig_finished.connect(self._environment_created)
            self.tracker.track_page('/environments')

    def import_environment(self):
        """
Exemplo n.º 14
0
 def _link_activated(url):
     QDesktopServices.openUrl(QUrl(url))
     from anaconda_navigator.utils.analytics import GATracker
     tracker = GATracker()
     tracker.track_event('content', 'link', url)
Exemplo n.º 15
0
 def __init__(self, *args, **kwargs):
     """List Widget holding available videos in the learning tab."""
     self._main = kwargs.pop('main', None)  # FIXME:
     super(ListWidgetContent, self).__init__(*args, **kwargs)
     self.tracker = GATracker()
     self.setViewMode(QListWidget.IconMode)
Exemplo n.º 16
0
    def __init__(
        self,
        version,
        config=CONF,
        startup=False,
        qa_testing=False,
        is_root_writable=False,
    ):
        """
        Update application dialog.

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

        extra_text = ''
        if not is_root_writable and WIN:
            extra_text = '<br>(You will be prompted to elevate privileges)<br>'

        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>{1}<br>".format(version, extra_text))
        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()
Exemplo n.º 17
0
class AuthenticationDialog(DialogBase):
    FORGOT_USERNAME_URL = 'https://anaconda.org/account/forgot_username'
    FORGOT_PASWORD_URL = 'https://anaconda.org/account/forgot_password'
    REGISTER_URL = 'https://anaconda.org'

    def __init__(self, api, parent=None):
        super(AuthenticationDialog, self).__init__(parent)

        self.api = api
        self._parent = parent
        self.token = None
        self.error = None
        self.tracker = GATracker()

        # Widgets
        self.label_username = QLabel('Username:'******'Password:'******'You can register ')
        self.label_signin_text = QLabel('<hr><br><b>Already a member? '
                                        'Sign in!</b><br>')
        # For styling purposes the label next to a ButtonLink is also a button
        # so they align adequately
        self.button_register_text = ButtonLabel('You can register by '
                                                'visiting the')
        self.button_register = ButtonLink('Anaconda Cloud')
        self.button_register_after_text = ButtonLabel('website.')
        self.label_information = QLabel('''
            <strong>Anaconda Cloud</strong> is where packages, notebooks,
            and <br> environments are shared. It provides powerful <br>
            collaboration and package management for open <br>
            source and private projects.<br>
            ''')
        self.label_message = QLabel('')
        self.button_forgot_username = ButtonLink('I forgot my username')
        self.button_forgot_password = ButtonLink('I forgot my password')
        self.button_login = QPushButton('Login')
        self.button_cancel = ButtonCancel('Cancel')
        self.bbox = QDialogButtonBox(Qt.Horizontal)

        # Widgets setup
        self.bbox.addButton(self.button_cancel, QDialogButtonBox.RejectRole)
        self.bbox.addButton(self.button_login, QDialogButtonBox.AcceptRole)
        self.text_username.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.text_password.setAttribute(Qt.WA_MacShowFocusRect, False)

        self.setMinimumWidth(260)
        self.setWindowTitle('Sign in')

        # This allows to completely style the dialog with css using the frame
        self.text_password.setEchoMode(QLineEdit.Password)
        self.label_message.setVisible(False)

        # Layout
        grid_layout = QGridLayout()
        grid_layout.addWidget(self.label_username, 0, 0)
        grid_layout.addWidget(self.text_username, 0, 1)
        grid_layout.addWidget(self.label_password, 1, 0)
        grid_layout.addWidget(self.text_password, 1, 1)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.label_information)

        register_layout = QHBoxLayout()
        register_layout.addWidget(self.button_register_text, 0)
        register_layout.addWidget(self.button_register, 0, Qt.AlignLeft)
        register_layout.addWidget(self.button_register_after_text, 0,
                                  Qt.AlignLeft)
        register_layout.addStretch()
        register_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.addLayout(register_layout)
        main_layout.addWidget(self.label_signin_text)
        main_layout.addLayout(grid_layout)
        main_layout.addSpacing(5)
        main_layout.addWidget(self.label_message)
        main_layout.addWidget(self.button_forgot_username, 0, Qt.AlignRight)
        main_layout.addWidget(self.button_forgot_password, 0, Qt.AlignRight)

        main_layout.addSpacing(15)
        main_layout.addWidget(self.bbox)

        self.setLayout(main_layout)

        # Signals
        self.button_forgot_username.clicked.connect(
            lambda: self.open_url(self.FORGOT_USERNAME_URL))
        self.button_forgot_password.clicked.connect(
            lambda: self.open_url(self.FORGOT_PASWORD_URL))
        self.button_register.clicked.connect(
            lambda: self.open_url(self.REGISTER_URL))
        self.text_username.textEdited.connect(self.check_text)
        self.text_password.textEdited.connect(self.check_text)
        self.button_login.clicked.connect(self.login)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.check_text()
        self.update_style_sheet()
        self.text_username.setFocus()

    @property
    def username(self):
        return self.text_username.text()

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

    def check_text(self):
        """
        Check that `username` and `password` are not empty and disabel/enable
        buttons accordingly.
        """
        username = self.text_username.text()
        password = self.text_password.text()

        if len(username) == 0 or len(password) == 0:
            self.button_login.setDisabled(True)
        else:
            self.button_login.setDisabled(False)

    def login(self):
        self.button_login.setEnabled(False)
        QApplication.setOverrideCursor(Qt.WaitCursor)
        self.label_message.setText('')
        worker = self.api.client_login(self.text_username.text(),
                                       self.text_password.text(),
                                       'Anaconda Navigator',
                                       '')
        worker.sig_finished.connect(self._finished)

    def _finished(self, worker, output, error):
        """
        Method called when the anaconda-client Api has finished a process
        that runs in a separate worker thread.
        """
        token = output

        if token:
            self.token = token
            self.accept()
        elif error:
            username = self.text_username.text()
            bold_username = '******'.format(username)

            # The error might come in (error_message, http_error) format
            try:
                error_message = eval(str(error))[0]
            except Exception:
                error_message = str(error)

            error_message = error_message.lower().capitalize()
            error_message = error_message.split(', ')[0]
            error_text = '<i>{0}</i>'.format(error_message)
            error_text = error_text.replace(username, bold_username)
            self.label_message.setText(error_text)
            self.label_message.setVisible(True)

            if error_message:
                domain = self.api.client_domain()
                label = '{0}/{1}: {2}'.format(domain, username,
                                              error_message.lower())
                self.tracker.track_event('authenticate', 'login failed',
                                         label=label)
                self.text_password.setFocus()
                self.text_password.selectAll()

        self.button_login.setDisabled(False)
        self.check_text()
        QApplication.restoreOverrideCursor()

    def open_url(self, url):
        self.tracker.track_event('content', 'click', url)
        QDesktopServices.openUrl(QUrl(url))
Exemplo n.º 18
0
    def __init__(self, parent=None):
        super(EnvironmentsTab, self).__init__(parent)

        self.api = AnacondaAPI()
        self.last_env_prefix = None
        self.last_env_name = None
        self.previous_environments = None
        self.tracker = GATracker()
        self.metadata = {}

        active_channels = CONF.get('main',  'conda_active_channels', tuple())
        channels = CONF.get('main',  'conda_channels', tuple())
        conda_url = CONF.get('main',  'conda_url',
                             'https:/conda.anaconda.org')
        conda_api_url = CONF.get('main',  'anaconda_api_url',
                                 'https://api.anaconda.org')

        # Widgets
        self.button_clone = ButtonEnvironmentPrimary("Clone")
        self.button_create = ButtonEnvironmentPrimary("Create")
        self.button_remove = ButtonEnvironmentCancel("Remove")
        self.frame_environments = FrameEnvironments(self)
        self.frame_environments_list = FrameEnvironmentsList(self)
        self.frame_environments_list_buttons = FrameEnvironmentsListButtons(self)
        self.frame_environments_packages = FrameEnvironmentsPackages(self)
        self.list_environments = ListWidgetEnvironment()
        self.packages_widget = CondaPackagesWidget(
            self,
            setup=False,
            active_channels=active_channels,
            channels=channels,
            data_directory=CHANNELS_PATH,
            conda_api_url=conda_api_url,
            conda_url=conda_url)
        self.menu_list = QMenu()
        self.text_search = LineEditSearch()
        self.timer_environments = QTimer()

        # Widgets setup
        self.list_environments.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.list_environments.setContextMenuPolicy(Qt.CustomContextMenu)
        self.packages_widget.textbox_search.setAttribute(
            Qt.WA_MacShowFocusRect, False)
        self.packages_widget.textbox_search.set_icon_visibility(False)
        self.text_search.setPlaceholderText("Search Environments")
        self.text_search.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.timer_environments.setInterval(5000)

        # Layouts
        environments_layout = QVBoxLayout()
        environments_layout.addWidget(self.text_search)

        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(self.button_create)
        buttons_layout.addWidget(self.button_clone)
        buttons_layout.addWidget(self.button_remove)
        buttons_layout.setContentsMargins(0, 0, 0, 0)

        list_buttons_layout = QVBoxLayout()
        list_buttons_layout.addWidget(self.list_environments)
        list_buttons_layout.addLayout(buttons_layout)
        self.frame_environments_list_buttons.setLayout(list_buttons_layout)
        list_buttons_layout.setContentsMargins(0, 0, 0, 0)
        environments_layout.addWidget(self.frame_environments_list_buttons)

        self.frame_environments_list.setLayout(environments_layout)

        packages_layout = QHBoxLayout()
        packages_layout.addWidget(self.packages_widget)
        packages_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_environments_packages.setLayout(packages_layout)

        main_layout = QHBoxLayout()
        main_layout.addWidget(self.frame_environments_list, 1)
        main_layout.addWidget(self.frame_environments_packages, 3)
        main_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_environments.setLayout(main_layout)

        layout = QHBoxLayout()
        layout.addWidget(self.frame_environments)
        self.setLayout(layout)

        # Signals
        self.button_clone.clicked.connect(self.clone_environment)
        self.button_create.clicked.connect(self.create_environment)
        self.button_remove.clicked.connect(self.remove_environment)
        self.list_environments.sig_item_selected.connect(
            self.load_environment)
        self.packages_widget.sig_packages_ready.connect(self.refresh)
        self.packages_widget.sig_channels_updated.connect(self.update_channels)
#        self.packages_widget.sig_environment_cloned.connect(
#            self._environment_created)
#        self.packages_widget.sig_environment_created.connect(
#            self._environment_created)
#        self.packages_widget.sig_environment_removed.connect(
#            self._environment_removed)
        self.text_search.textChanged.connect(self.filter_environments)
        self.timer_environments.timeout.connect(self.refresh_environments)
        self.packages_widget.sig_process_cancelled.connect(
            lambda: self.update_visibility(True))
Exemplo n.º 19
0
 def _link_activated(self, url):
     QDesktopServices.openUrl(QUrl(url))
     tracker = GATracker()
     tracker.track_event('content', 'link', url)
Exemplo n.º 20
0
class MessageBox(DialogBase):
    """Base message box dialog."""

    QUESTION_BOX = 100
    INFORMATION_BOX = 101
    ERROR_BOX = 102
    REMOVE_BOX = 103

    sig_url_clicked = Signal(object)

    def __init__(self, type_, error='', title='', text='', learn_more=None):
        """Base message box dialog."""
        super(MessageBox, self).__init__()
        from anaconda_navigator.utils.analytics import GATracker

        self.tracker = GATracker()
        self.label_text = QLabel(to_text_string(text))
        self.textbox_error = QTextEdit()
        self.button_ok = ButtonPrimary('Ok')
        self.button_yes = ButtonPrimary('Yes')
        self.button_no = ButtonNormal('No')
        self.button_copy = ButtonNormal('Copy text')
        self.button_learn = ButtonNormal('Learn more')
        self.button_remove = ButtonDanger('Remove')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_send = ButtonNormal('Report Issue', parent=self)

        self.label_text.setOpenExternalLinks(False)
        self.label_text.setWordWrap(True)
        self.label_text.linkActivated.connect(self.url_clicked)
        self.textbox_error.setReadOnly(True)
        self.textbox_error.setFrameStyle(QTextEdit.Plain)
        self.textbox_error.setFrameShape(QTextEdit.NoFrame)
        self.setMinimumWidth(260)
        self.textbox_error.verticalScrollBar().show()
        self.setWindowTitle(to_text_string(title))

        error = to_text_string(error).split('\n')
        error = '<br>'.join(error)
        self.textbox_error.setText(error)

        # Layouts
        layout = QVBoxLayout()
        layout.addWidget(self.label_text)
        layout.addWidget(SpacerVertical())
        if error:
            layout.addWidget(self.textbox_error)
            layout.addWidget(SpacerVertical())
            layout.addWidget(self.button_copy)
            layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()

        layout.addLayout(layout_buttons)

        self.layout = layout
        self.setLayout(layout)

        # Signals
        self.button_copy.clicked.connect(self.copy_text)
        self.button_ok.clicked.connect(self.accept)
        self.button_yes.clicked.connect(self.accept)
        self.button_no.clicked.connect(self.reject)
        self.button_remove.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_send.clicked.connect(self.send)

        # Setup
        self.button_learn.setVisible(bool(learn_more))
        if bool(learn_more):
            layout_buttons.addWidget(self.button_learn)
            layout_buttons.addWidget(SpacerHorizontal())
            self.button_learn.clicked.connect(
                lambda: self.show_url(learn_more)
            )

        if type_ == self.ERROR_BOX:
            layout_buttons.addWidget(self.button_send)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.INFORMATION_BOX:
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.textbox_error.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.QUESTION_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_no)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_yes)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.REMOVE_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_cancel)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_remove)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)

        self.button_send.setVisible(False)
        self.layout_buttons = layout_buttons

    def url_clicked(self, url):
        """Emit url interaction."""
        self.sig_url_clicked.emit(url)

    def copy_text(self):
        """Copy all the content of the displayed error message."""
        self.textbox_error.selectAll()
        self.textbox_error.copy()

    def show_url(self, url=None):
        """Open url in default browser."""
        if url:
            qurl = QUrl(url)
            QDesktopServices.openUrl(qurl)
            self.tracker.track_event('help', 'documentation', url)

    def send(self):
        """Send error report to github and create an issue with a template."""
        import webbrowser
        from anaconda_navigator.utils.analytics import GATracker
        base = "https://github.com/ContinuumIO/anaconda-issues/issues/new?{0}"
        template = '''
## Main error
{text}
## Traceback
```
{trace}
```
## System information
```
{info}
```
'''
        info = GATracker().info
        info = '\n'.join('{}: {}'.format(k, v) for k, v in info.items())
        query = parse.urlencode(
            {
                'title': "Navigator Error",
                'labels': "tag:navigator",
                'body': template.format(
                    text=self.text, trace=self.error, info=info
                )
            }
        )
        url = base.format(query)
        webbrowser.open_new_tab(url)
Exemplo n.º 21
0
class MainWindow(QMainWindow):
    sig_logged_in = Signal()
    sig_logged_out = Signal()

    DOCS_URL = 'https://docs.continuum.io/anaconda/navigator'
    VIDEOS_URL = "http://content.continuum.io/api/videos"
    EVENTS_URL = "http://content.continuum.io/api/events"
    WEBINARS_URL = "http://content.continuum.io/api/webinars"

    def __init__(self, splash=None):
        super(MainWindow, self).__init__()
        self.tracker = None
        self.splash = splash

        # Anaconda API
        self.api = AnacondaAPI()
        self.busy = False
        self.logged = False
        self.username = ''
        self._login_text = 'Sign in to Anaconda Cloud'
        self.first_run = CONF.get('main', 'first_run')
        self.application_update_version = None

        # Widgets
        self.frame_header = FrameHeader(self)
        self.frame_body = FrameBody(self)
        self.label_logo = LabelHeaderLogo('ANACONDA NAVIGATOR')
        self.button_logged_text = ButtonLabelLogin('')
        self.button_logged_username = ButtonLinkLogin('')
        self.label_update_available = LabelHeaderUpdate('Update available!')
        self.button_update_available = ButtonHeaderUpdate('Update')
        self.button_login = ButtonLogin(self._login_text)
        self.central_widget = QWidget()
        self.statusbar = self.statusBar()
        self.progressbar = QProgressBar()

        self.stack = TabWidgetBody(self)
        self.home_tab = HomeTab(parent=self)
        self.environments_tab = EnvironmentsTab(parent=self)
        self.learning_tab = CommunityTab(
            parent=self,
            tags=['webinar', 'documentation', 'video', 'training'],
            content_urls=[self.VIDEOS_URL, self.WEBINARS_URL])
        self.community_tab = CommunityTab(parent=self,
                                          tags=['event', 'forum', 'social'],
                                          content_urls=[self.EVENTS_URL])

        #        self.projects_tab = ProjectsTab(parent=self)

        # Note: Icons are set in CSS
        self.stack.addTab(self.home_tab, text='Home')
        self.stack.addTab(self.environments_tab, text='Environments')
        self.stack.addTab(self.learning_tab, text='Learning')
        self.stack.addTab(self.community_tab, text='Community')
        #        self.stack.addTab(self.projects_tab, 'Projects')

        # Widget setup
        self.button_login.setDefault(True)
        self.label_logo.setPixmap(QPixmap(images.ANACONDA_NAVIGATOR_LOGO))
        self.setWindowTitle("Anaconda Navigator")
        self.statusbar.addPermanentWidget(self.progressbar)
        self.progressbar.setVisible(False)

        # Layout
        header_layout = QHBoxLayout()
        header_layout.addWidget(self.label_logo)
        header_layout.addSpacing(18)
        header_layout.addWidget(self.label_update_available, 0, Qt.AlignCenter)
        header_layout.addWidget(self.button_update_available, 0,
                                Qt.AlignCenter)
        header_layout.addStretch()
        header_layout.addWidget(self.button_logged_text, 0, Qt.AlignTrailing)
        header_layout.addWidget(self.button_logged_username, 0,
                                Qt.AlignTrailing)
        header_layout.addWidget(self.button_login, 0, Qt.AlignTrailing)
        header_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_header.setLayout(header_layout)

        body_layout = QHBoxLayout()
        body_layout.addWidget(self.stack)
        body_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_body.setLayout(body_layout)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.frame_header)
        main_layout.addWidget(self.frame_body)
        main_layout.setContentsMargins(0, 0, 0, 0)
        main_layout.setSpacing(0)
        self.central_widget.setLayout(main_layout)
        self.setContentsMargins(0, 0, 0, 0)
        self.setCentralWidget(self.central_widget)

        # Signals
        self.button_login.clicked.connect(self.login)
        self.button_logged_username.clicked.connect(self.open_login_page)
        self.button_update_available.clicked.connect(self.update_application)
        self.stack.currentChanged.connect(self._track_tab)

        # This needs to be reworked!
        #        self.projects_tab.sig_apps_updated.connect(
        #            self.home_tab.set_applications)
        #        self.projects_tab.sig_apps_changed.connect(
        #            self.home_tab.set_applications)
        #        self.projects_tab.sig_project_updated.connect(
        #            self.home_tab.set_applications)
        #        self.projects_tab.sig_status_updated.connect(self.update_status_bar)

        # Setup
        self.api.set_data_directory(CHANNELS_PATH)
        self.update_style_sheet()

    # Helpers
    # -------------------------------------------------------------------------
    def _track_tab(self, index=None):
        """
        Tracks the active tab by index, or set `Home` when no index has been
        provided.
        """
        if index is None:
            index = self.stack.currentIndex()

        text = self.stack.currentText().lower()

        if self.tracker:
            page = '/{0}'.format(text)
            self.tracker.track_page(page)

    def _metadata_updated(self, worker, path, error):
        self.set_splash('Updating repodata...')
        if error:
            logger.error(str(error))

        if path and os.path.isfile(path):
            with open(path, 'r') as f:
                data = f.read()
        try:
            self._metadata = json.loads(data)
        except Exception:
            self._metadata = {}

        channels = CONF.get('main', 'conda_channels', default=tuple())
        if not channels:
            channels = self.api.conda_get_condarc_channels()
            CONF.set('main', 'conda_channels', channels)
            CONF.set('main', 'conda_active_channels', channels)

        self.api.update_repodata(channels=channels)
        self.api.sig_repodata_updated.connect(self._repodata_updated)

    def _repodata_updated(self, paths):
        self.set_splash('Loading repodata...')
        self.api.sig_repodata_updated.disconnect(self._repodata_updated)

        if self.first_run:
            self.set_splash('Initial configuration...')
            self.api.create_default_project()

        worker = self.api.client_load_repodata(paths, self._metadata)
        worker.sig_finished.connect(self.create_application_projects)

    # --- Public API
    # -------------------------------------------------------------------------
    def setup(self):
        """
        Perform initial setup and configuration.
        """
        self.set_splash('Updating metadata...')
        user = self.api.client_set_domain()
        self.update_login_status(user)
        self.setup_toolbars()
        self.set_application_icon()
        worker = self.api.update_metadata()
        worker.sig_finished.connect(self._metadata_updated)
        statusbar = self.statusBar()
        statusbar.setVisible(False)
        statusbar.setMaximumHeight(0)
        statusbar.hide()

    def create_application_projects(self, worker, output, error):
        if error:
            logger.error(str(error))
        packages, apps = output
        self.api.create_application_projects(
            apps,
            add_project=self.first_run,
        )

        self.post_setup(apps)
        self.check_for_updates(packages)

    def post_setup(self, apps):
        CONF.set('main', 'first_run', False)
        self.set_splash('Loading applications...')
        self.home_tab.setup_tab(apps)
        #        self.set_splash('Loading projects...')
        #        self.projects_tab.setup_tab()
        self.set_splash('Loading environments...')
        self.environments_tab.setup_tab(metadata=self._metadata)
        self.set_splash('Loading content...')
        self.community_tab.setup_tab()
        self.set_splash('Loading content...')
        self.learning_tab.setup_tab()
        self.update_style_sheet()

        self.showMaximized()
        self.post_visible_setup()

    def set_application_icon(self):
        """
        """
        app = QCoreApplication.instance()
        app_icon = QIcon()
        app_icon.addFile(images.ANACONDA_ICON_16_PATH, QSize(16, 16))
        app_icon.addFile(images.ANACONDA_ICON_24_PATH, QSize(24, 24))
        app_icon.addFile(images.ANACONDA_ICON_32_PATH, QSize(32, 32))
        app_icon.addFile(images.ANACONDA_ICON_48_PATH, QSize(48, 48))
        app_icon.addFile(images.ANACONDA_ICON_256_PATH, QSize(256, 256))
        app.setWindowIcon(app_icon)

    def setup_toolbars(self):
        menubar = self.menuBar()

        file_menu = menubar.addMenu('&File')
        file_menu.addAction(
            create_action(self,
                          "&Preferences",
                          triggered=self.show_preferences,
                          shortcut="Ctrl+P"))
        file_menu.addAction(
            create_action(self,
                          "&Quit",
                          triggered=self.close,
                          shortcut="Ctrl+Q"))

        helpmenu = menubar.addMenu('&Help')
        helpmenu.addAction(
            create_action(self,
                          "&Online Documentation",
                          triggered=lambda: self.open_url(self.DOCS_URL)))
        helpmenu.addAction(
            create_action(self,
                          "&Logs viewer",
                          triggered=self.show_log_viewer,
                          shortcut="F6"))
        helpmenu.addSeparator()
        helpmenu.addAction(
            create_action(self, "&About", triggered=self.show_about))

    def post_visible_setup(self):
        if self.splash:
            self.splash.hide()

        CONF.set('main', 'first_run', False)

        # Start the tracker only after post_visible_setup
        self.tracker = GATracker()
        self._track_tab(0)  # Start tracking home
        self.fix_tab_ordering()
        self.show_welcome_screen()

    def check_for_updates(self, packages=None):
        # Check if there is an update for navigator!
        version = self.api.conda_package_version(name='root',
                                                 pkg='anaconda-navigator')
        # Temporal mock test
        # mock_versions = [version, '1.1.0']
        # packages['anaconda-navigator'] = {'versions': mock_versions}
        self.button_update_available.setVisible(False)
        self.label_update_available.setVisible(False)

        text = ''
        if packages:
            package_data = packages.get('anaconda-navigator')
            if package_data:
                versions = package_data.get('versions')
                if versions and version != versions[-1]:
                    self.application_update_version = versions[-1]
                    self.label_update_available.setText(text)
                    self.label_update_available.setVisible(True)
                    self.button_update_available.setVisible(True)

    def fix_tab_ordering(self):
        return
        for tab in [self.community_tab, self.learning_tab]:
            self.setTabOrder(self.stack.tabbar.buttons[-1],
                             tab.filter_widgets[0])
            for i in range(len(tab.filter_widgets) - 1):
                self.setTabOrder(tab.filter_widgets[i],
                                 tab.filter_widgets[i + 1])
            self.setTabOrder(tab.filter_widgets[-1], tab.text_filter)
            self.setTabOrder(tab.text_filter.button_icon, tab.list)
            self.setTabOrder(tab.list, self.button_login)


#        self.button_login.setFocus()

        self.setTabOrder(self.stack.tabbar.buttons[-1],
                         self.environments_tab.text_search)

        self.environments_tab.packages_widget.table_last_row.add_focus_widget(
            self.button_login)
        self.setTabOrder(self.environments_tab.packages_widget.table_last_row,
                         self.button_login)

    def update_style_sheet(self):
        style_sheet = load_style_sheet()
        #        self.home_tab.update_style_sheet(style_sheet)
        self.environments_tab.update_style_sheet(style_sheet)
        #        self.community_tab.update_style_sheet(style_sheet)
        #        self.learning_tab.update_style_sheet(style_sheet)
        self.setStyleSheet(style_sheet)

    def set_splash(self, message):
        """
        Set splash message.
        """
        if self.splash:
            self.splash.show_message(message)
        QApplication.processEvents()

    # --- Login
    # -------------------------------------------------------------------------
    def update_login_status(self, user_data=None):
        """
        Update login button and information.
        """
        if user_data:
            self.username = user_data.get('login', '')
            self.logged = True

        if self.logged:
            username = self.username
            anaconda_api_url = CONF.get('main', 'anaconda_api_url')
            token = self.api.client_load_token(anaconda_api_url)
            self.button_logged_text.setText('Signed in as')
            self.button_logged_username.setText(username)
            url = "{0}/{1}".format(CONF.get('main', 'conda_url'), username)
            self.button_logged_username.setToolTip(url)
            self.button_login.setText('Sign out')
            self.environments_tab.packages_widget.set_token(token)
        else:
            self.button_logged_text.setText('')
            self.button_logged_username.setText('')
            self.button_login.setText(self._login_text)
        QApplication.restoreOverrideCursor()

    def login(self):
        """
        Open up login dialog or log out depending on logged status.
        """
        if self.logged:
            QApplication.setOverrideCursor(Qt.WaitCursor)
            self.api.client_logout()
            self.api.client_remove_token()
            self.logged = False
            self.sig_logged_out.emit()
            self.tracker.track_event('authenticate',
                                     'logout',
                                     label=self.username)
        else:
            dlg = AuthenticationDialog(self.api, parent=self)

            if self.tracker:
                self.tracker.track_page('/login', pagetitle='Login dialog')

            if dlg.exec_():
                self.api.client_store_token(dlg.token)
                self.username = dlg.username
                self.logged = True
                self.sig_logged_in.emit()

                if self.tracker:
                    self.tracker.track_event('authenticate',
                                             'login',
                                             label=self.username)
            self._track_tab()

        self.update_login_status()
        logger.debug(str((self.logged, self.username)))

    # --- Dialogs
    # -------------------------------------------------------------------------
    def show_preferences(self):
        """
        Display the preferences dialog and apply the needed actions.
        """
        dlg = PreferencesDialog(self)
        self.tracker.track_page('/preferences', pagetitle='Preferences dialog')
        set_domains = self.environments_tab.packages_widget.update_domains
        set_domains = self.environments_tab.packages_widget.update_domains

        dlg.sig_urls_updated.connect(set_domains)
        dlg.sig_urls_updated.connect(lambda au, cu: self.login())
        dlg.exec_()
        self._track_tab()

    def show_about(self):
        """
        Display the `About` dialog with information on the project.
        """
        dlg = AboutDialog(self)
        self.tracker.track_page('/about', pagetitle='About dialog')
        dlg.exec_()
        self._track_tab()

    def show_log_viewer(self):
        """
        Display the logs viewer to the user
        """
        dlg = LogViewerDialog()
        self.tracker.track_page('/logs', pagetitle='Log Viewer Dialog')
        dlg.exec_()
        self._track_tab()

    def show_welcome_screen(self):
        if getattr(self, 'showme', True) and CONF.get('main', 'show_startup',
                                                      True):
            from anaconda_navigator.widgets.splash import FirstSplash

            self.showme = False
            self.splash.hide()
            dlg = FirstSplash()
            dlg.raise_()
            dlg.exec_()

    # --- Update Navigator
    # -------------------------------------------------------------------------
    def _update_application(self, worker, output, error):
        self.button_update_available.setDisabled(False)
        if error:
            text = 'Anaconda Navigator Update error:'
            dlg = MessageBoxError(text=text,
                                  error=error,
                                  title='Application Update Error')
            self.tracker.track_page('/update/error',
                                    pagetitle='Update Application Error '
                                    'Message Box')
            dlg.exec_()
        else:
            text = ('Anaconda Navigator Updated succefully.\n\n'
                    'Please restart the application')
            dlg = MessageBoxInformation(text=text, title='Application Update')
            self.tracker.track_page('/update/successful',
                                    pagetitle='Application Update Succesful '
                                    'Message Box')
            dlg.exec_()
        self._track_tab()

    def update_application(self):
        version = self.application_update_version
        if version:
            dlg = DialogUpdateApplication(version=version)
            self.tracker.track_page('/update',
                                    pagetitle='Update Application Dialog')
            reply = dlg.exec_()
            if reply:
                self.tracker.track_event('application', 'updated', version)
                self.busy = True
                pkg = 'anaconda-navigator={}'.format(version)
                worker = self.api.conda_install(name='root', pkgs=[pkg])
                worker.sig_finished.connect(self._update_application)
                self.button_update_available.setDisabled(True)
            self._track_tab()

    def update_status_bar(self, message='', timeout=0, val=-1, max_val=-1):
        """ """
        statusbar = self.statusBar()
        if val != -1 and max_val != -1:
            self.progressbar.setVisible(True)
            self.progressbar.setValue(val)
            self.progressbar.setMaximum(max_val)
        else:
            self.progressbar.setVisible(False)

        if message:
            statusbar.showMessage(message, timeout)
        else:
            statusbar.clearMessage()
        statusbar.setVisible(False)
        statusbar.setMaximumHeight(0)
        statusbar.hide()

    # --- Url handling
    # -------------------------------------------------------------------------
    def open_url(self, url):
        qurl = QUrl(url)
        QDesktopServices.openUrl(qurl)
        self.tracker.track_event('help', 'documentation', url)

    def open_login_page(self):
        """
        """
        conda_url = CONF.get('main', 'conda_url')
        url = "{0}/{1}".format(conda_url, self.username)
        qurl = QUrl(url)
        QDesktopServices.openUrl(qurl)
        self.tracker.track_event('content', 'clicked', url)

    # --- Qt methods
    # -------------------------------------------------------------------------
    def closeEvent(self, event):
        """
        Catch close event.
        """
        # TODO: check if an update is not in progress or things might break!!
        #        if self.busy:
        show_dialog = not CONF.get('main', 'hide_quit_dialog')
        if show_dialog:
            if self.tracker:
                self.tracker.track_page('/quit', pagetitle='Quit dialog')
            dlg = QuitApplicationDialog()
            reply = dlg.exec_()

            if not reply:
                event.ignore()
                self._track_tab()

    def keyPressEvent(self, event):
        """
        Qt override.
        """
        #        if event.key() in [Qt.Key_F5]:
        #            self.update_style_sheet()
        super(MainWindow, self).keyPressEvent(event)

    def paintEvent(self, event):
        """
        Qt override.

        Draw lower left border of the main Stacked Widget.
        """
        super(MainWindow, self).paintEvent(event)
        tab = self.stack.tabbar
        tab_pos = self.mapTo(self, tab.pos())
        pane_pos = self.mapTo(self, self.stack.pos())

        stack_height = self.stack.height()
        menu_height = self.menuBar().height()
        header_height = 49  # From css
        padding = 20  # From css
        left = 1  # From css
        extra = 8  # Still wondering where this extra Y delta is
        deltay = menu_height + header_height + tab.height() + padding + extra
        x0 = tab_pos.x() + tab.width() + padding - left
        y0 = tab_pos.y() + deltay
        y1 = pane_pos.y() + stack_height + deltay - tab.height() - padding

        painter = QPainter(self)
        painter.setPen(QPen(QColor('#006f43'), 1, Qt.SolidLine, Qt.RoundCap))
        painter.drawLine(x0, y0, x0, y1)
Exemplo n.º 22
0
    def __init__(self, type_, error='', title='', text='', learn_more=None):
        """Base message box dialog."""
        super(MessageBox, self).__init__()
        from anaconda_navigator.utils.analytics import GATracker

        self.tracker = GATracker()
        self.label_text = QLabel(to_text_string(text))
        self.textbox_error = QTextEdit()
        self.button_ok = ButtonPrimary('Ok')
        self.button_yes = ButtonPrimary('Yes')
        self.button_no = ButtonNormal('No')
        self.button_copy = ButtonNormal('Copy text')
        self.button_learn = ButtonNormal('Learn more')
        self.button_remove = ButtonDanger('Remove')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_send = ButtonNormal('Report Issue', parent=self)

        self.label_text.setOpenExternalLinks(False)
        self.label_text.setWordWrap(True)
        self.label_text.linkActivated.connect(self.url_clicked)
        self.textbox_error.setReadOnly(True)
        self.textbox_error.setFrameStyle(QTextEdit.Plain)
        self.textbox_error.setFrameShape(QTextEdit.NoFrame)
        self.setMinimumWidth(260)
        self.textbox_error.verticalScrollBar().show()
        self.setWindowTitle(to_text_string(title))

        error = to_text_string(error).split('\n')
        error = '<br>'.join(error)
        self.textbox_error.setText(error)

        # Layouts
        layout = QVBoxLayout()
        layout.addWidget(self.label_text)
        layout.addWidget(SpacerVertical())
        if error:
            layout.addWidget(self.textbox_error)
            layout.addWidget(SpacerVertical())
            layout.addWidget(self.button_copy)
            layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()

        layout.addLayout(layout_buttons)

        self.layout = layout
        self.setLayout(layout)

        # Signals
        self.button_copy.clicked.connect(self.copy_text)
        self.button_ok.clicked.connect(self.accept)
        self.button_yes.clicked.connect(self.accept)
        self.button_no.clicked.connect(self.reject)
        self.button_remove.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_send.clicked.connect(self.send)

        # Setup
        self.button_learn.setVisible(bool(learn_more))
        if bool(learn_more):
            layout_buttons.addWidget(self.button_learn)
            layout_buttons.addWidget(SpacerHorizontal())
            self.button_learn.clicked.connect(
                lambda: self.show_url(learn_more)
            )

        if type_ == self.ERROR_BOX:
            layout_buttons.addWidget(self.button_send)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.INFORMATION_BOX:
            layout_buttons.addWidget(self.button_ok)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.textbox_error.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.QUESTION_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_no)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_yes)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_remove.setVisible(False)
            self.button_cancel.setVisible(False)
        elif type_ == self.REMOVE_BOX:
            layout_buttons.addStretch()
            layout_buttons.addWidget(self.button_cancel)
            layout_buttons.addWidget(SpacerHorizontal())
            layout_buttons.addWidget(self.button_remove)
            layout_buttons.addWidget(SpacerHorizontal())
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)

        self.button_send.setVisible(False)
        self.layout_buttons = layout_buttons