示例#1
0
class CloneDialog(EnvironmentActionsDialog):
    """Clone environment dialog."""
    def __init__(self, parent=None, clone_from_name=None):
        """Clone environment dialog."""
        super(CloneDialog, self).__init__(parent=parent)

        # Widgets
        self.label_name = LabelBase("Name:")
        self.text_name = LineEditEnvironment()

        self.label_location = LabelBase("Location:")
        self.label_prefix = LabelBase()
        self.button_ok = ButtonPrimary('Clone')
        self.button_cancel = ButtonNormal('Cancel')

        # Widget setup
        self.align_labels([self.label_name, self.label_location])
        self.setMinimumWidth(self.BASE_DIALOG_WIDTH)
        self.setWindowTitle("Clone from environment: " + clone_from_name)
        self.text_name.setPlaceholderText("New environment name")
        self.label_prefix.setObjectName('environment-location')

        # Layouts
        grid = QGridLayout()
        grid.addWidget(self.label_name, 2, 0)
        grid.addWidget(SpacerHorizontal(), 2, 1)
        grid.addWidget(self.text_name, 2, 2)
        grid.addWidget(SpacerVertical(), 3, 0)
        grid.addWidget(self.label_location, 4, 0)
        grid.addWidget(self.label_prefix, 4, 2)

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

        layout = QVBoxLayout()
        layout.addLayout(grid)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

        # Signals
        self.text_name.textChanged.connect(self.refresh)
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.text_name.setFocus()
        self.refresh()

    def refresh(self, text=''):
        """Update status of buttons based on combobox selection."""
        name = self.name
        self.update_location()
        if self.environments:
            self.button_ok.setDisabled(not self.is_valid_env_name(name))
示例#2
0
    def __init__(self, *args, **kwargs):
        super(ProjectEditor, self).__init__(*args, **kwargs)

        # Widgets
        self.editor = EditorBase(self)
        self.button_save = ButtonPrimary('Save')
        self.button_problems = ButtonProjectProblems('Problems')
        self.button_suggestions = ButtonProjectSuggestions('Suggestions')
        self.original_text = None
        self.problems = None
        self.suggestions = None

        # Layouts
        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.button_save)
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_problems)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_suggestions)

        layout = QVBoxLayout()
        layout.addLayout(layout_buttons)
        layout.addWidget(self.editor)

        self.setLayout(layout)

        # Signals
        self.editor.textChanged.connect(self.text_changed)
        self.button_save.clicked.connect(self.save)
        self.button_problems.clicked.connect(self.show_problems)
        self.button_suggestions.clicked.connect(self.show_suggestions)
示例#3
0
    def __init__(self, parent=None, default=DEFAULT_PROJECTS_PATH):
        """Select project folder."""
        super(ProjectsPathDialog, self).__init__(parent=parent)

        # Widgets
        self.label_description = QLabel(
            "If no path is selected, the default"
            " one will be used."
        )
        self.label_name = QLabel("Select the projects folder to use")
        self.label_path = QLabel("Projects path")
        self.label_info = QLabel('')
        self.text_path = QLineEdit()
        self.button_path = ButtonNormal("")
        self.button_default = ButtonNormal('Use default')
        self.button_ok = ButtonPrimary('Select')

        # Widgets setup
        self.button_path.setObjectName('import')
        self.default = default
        self.text_path.setPlaceholderText("projects folder path")
        self.text_path.setReadOnly(True)
        self.setMinimumWidth(580)
        self.setWindowTitle("Select Projects Path")

        # Layouts
        layout_grid = QGridLayout()
        layout_grid.addWidget(self.label_path, 0, 0)
        layout_grid.addWidget(SpacerHorizontal(), 0, 1)
        layout_grid.addWidget(self.text_path, 0, 2)
        layout_grid.addWidget(SpacerHorizontal(), 0, 3)
        layout_grid.addWidget(self.button_path, 0, 4)
        layout_grid.addWidget(SpacerVertical(), 1, 0, 1, 4)
        layout_grid.addWidget(self.label_info, 2, 2, 1, 2)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_default)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

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

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_default.clicked.connect(self.use_default)
        self.button_path.clicked.connect(self.choose)

        # Setup
        self.refresh()
示例#4
0
    def __init__(self, *args, **kwargs):
        """About dialog."""
        super(PasswordDialog, self).__init__(*args, **kwargs)
        self.wm = WorkerManager()

        # Widgets
        self.label_text = LabelBase(
            'VSCode will be installed through your system <br> package '
            'manager.<br><br>'
            'This action requires elevated privileges. Please <br>provide a '
            'password to forward to sudo')
        self.lineedit = PasswordEdit()
        self.label_info = LabelBase()
        self.button_cancel = ButtonNormal('Cancel')
        self.button_ok = ButtonPrimary('Ok')
        self.worker = None
        self._valid = False
        self._timer = QTimer()
        self._timer.setInterval(3000)
        self._timer.timeout.connect(self.check)

        # Widgets setup
        self.button_ok.setMinimumWidth(70)
        self.button_ok.setDefault(True)
        self.setWindowTitle("Privilege Elevation Required")
        self.lineedit.setEchoMode(LineEditBase.Password)

        # Layouts
        layout_content = QVBoxLayout()
        layout_content.addWidget(self.label_text)
        layout_content.addWidget(SpacerVertical())
        layout_content.addWidget(self.lineedit, 0, Qt.AlignBottom)
        layout_content.addWidget(SpacerVertical())
        layout_content.addWidget(self.label_info, 0, Qt.AlignTop)
        layout_content.addWidget(SpacerVertical())

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

        layout_main = QVBoxLayout()
        layout_main.addLayout(layout_content)
        layout_main.addWidget(SpacerVertical())
        layout_main.addWidget(SpacerVertical())
        layout_main.addLayout(layout_buttons)
        self.setLayout(layout_main)

        # Signals
        self.button_ok.clicked.connect(self.accept2)
        self.button_cancel.clicked.connect(self.reject2)
        self.lineedit.textChanged.connect(self.refresh)

        # Setup
        self.lineedit.setFocus()
        self.refresh()
示例#5
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()
示例#6
0
    def __init__(self, parent=None, clone_from_name=None):
        """Clone environment dialog."""
        super(CloneDialog, self).__init__(parent=parent)

        # Widgets
        self.label_name = LabelBase("Name:")
        self.text_name = LineEditBase()

        self.label_location = LabelBase("Location:")
        self.label_prefix = LabelBase()
        self.button_ok = ButtonPrimary('Clone')
        self.button_cancel = ButtonNormal('Cancel')

        # Widget setup
        self.align_labels([self.label_name, self.label_location])
        self.setMinimumWidth(self.BASE_DIALOG_WIDTH)
        self.setWindowTitle("Clone from environment: " + clone_from_name)
        self.text_name.setPlaceholderText("New environment name")
        self.text_name.setValidator(self.get_regex_validator())
        self.label_prefix.setObjectName('environment-location')

        # Layouts
        grid = QGridLayout()
        grid.addWidget(self.label_name, 2, 0)
        grid.addWidget(SpacerHorizontal(), 2, 1)
        grid.addWidget(self.text_name, 2, 2)
        grid.addWidget(SpacerVertical(), 3, 0)
        grid.addWidget(self.label_location, 4, 0)
        grid.addWidget(self.label_prefix, 4, 2)

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

        layout = QVBoxLayout()
        layout.addLayout(grid)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

        # Signals
        self.text_name.textChanged.connect(self.refresh)
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.text_name.setFocus()
        self.refresh()
示例#7
0
    def __init__(self, config=CONF, **kwargs):
        """Application preferences dialog."""
        super(PreferencesDialog, self).__init__(**kwargs)

        self.api = AnacondaAPI()
        self.widgets_changed = set()
        self.widgets = []
        self.widgets_dic = {}
        self.config = config

        # Widgets
        self.button_ok = ButtonPrimary('Apply')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_reset = ButtonNormal('Reset to defaults')
        self.row = 0

        # Widget setup
        self.setWindowTitle("Preferences")

        # Layouts
        self.grid_layout = QGridLayout()

        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(self.button_reset)
        buttons_layout.addStretch()
        buttons_layout.addWidget(self.button_cancel)
        buttons_layout.addWidget(SpacerHorizontal())
        buttons_layout.addWidget(self.button_ok)

        main_layout = QVBoxLayout()
        main_layout.addLayout(self.grid_layout)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(buttons_layout)
        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_reset.clicked.connect(self.reset_to_defaults)
        self.button_reset.clicked.connect(
            lambda: self.button_ok.setEnabled(True)
        )

        # Setup
        self.grid_layout.setSpacing(0)
        self.setup()
        self.button_ok.setDisabled(True)
        self.widgets[0].setFocus()
        self.button_ok.setDefault(True)
        self.button_ok.setAutoDefault(True)
示例#8
0
    def __init__(self, text, packages=(), parent=None):
        """Accept actions for pacakge manager."""
        super(ActionsDialog, self).__init__(parent=parent)

        self.packages = packages

        self.label = QLabel(text)
        self.list = ListWidgetActionPackages(self)
        self.button_cancel = ButtonDanger('Cancel')
        self.button_accept = ButtonPrimary('Ok')

        self.setWindowTitle('Proceed with the following actions?')

        for item in packages:
            item = QListWidgetItem(item)
            self.list.addItem(item)

        # Layout
        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_cancel)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_accept)

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

        self.button_accept.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
示例#9
0
    def __init__(self, parent=None, projects=None):
        """Create new environment dialog."""
        super(CreateDialog, self).__init__(parent=parent)

        self.projects = projects

        # Widgets
        self.label_name = QLabel("Project name")
        self.text_name = QLineEdit()
        self.button_ok = ButtonPrimary('Create')
        self.button_cancel = ButtonNormal('Cancel')

        # Widgets setup
        self.text_name.setPlaceholderText("New project name")
        self.setMinimumWidth(380)
        self.setWindowTitle("Create new project")
        self.text_name.setValidator(get_regex_validator())

        # Layouts
        grid = QGridLayout()
        grid.addWidget(self.label_name, 0, 0)
        grid.addWidget(SpacerHorizontal(), 0, 1)
        grid.addWidget(self.text_name, 0, 2)
        grid.addWidget(SpacerVertical(), 1, 0)

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

        main_layout = QVBoxLayout()
        main_layout.addLayout(grid)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(layout_buttons)

        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.text_name.textChanged.connect(self.refresh)

        # Setup
        self.refresh()
示例#10
0
    def __init__(self, parent=None):
        """Startup splash to display the first time that Navigator runs."""
        super(FirstSplash, self).__init__(parent=parent)

        text = """
        Thanks for installing Anaconda!

        Anaconda Navigator helps you easily start important Python applications
        and manage the packages in your local Anaconda installation. It also
        connects you to online resources for learning and engaging with the
        Python, SciPy, and PyData community.

        To help us improve Anaconda Navigator, fix bugs, and make it even
        easier for everyone to use Python, we gather anonymized usage
        information, just like most web browsers and mobile apps.

        To opt out of this, please uncheck below (You can always change this
        setting in the Preferences menu).
        """
        # Variables
        self.config = CONF

        # Widgets
        self.button_ok = ButtonNormal('Ok')
        self.button_ok_dont_show = ButtonPrimary("Ok, and don't show again")
        self.checkbox_track = QCheckBox("Yes, I'd like to help improve "
                                        "Anaconda.")
        self.label_about = QLabel(text)
        self.widget_icon = QSvgWidget(ANACONDA_NAVIGATOR_LOGO)

        # Widget setup
        self.frame_title_bar.hide()
        self.widget_icon.setFixedSize(self.widget_icon.size_for_width(400))

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

        layout = QVBoxLayout()
        layout.addWidget(self.widget_icon, 0, Qt.AlignCenter)
        layout.addWidget(self.label_about)
        layout.addWidget(self.checkbox_track, 0, Qt.AlignCenter)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)
        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(lambda: self.accept(show_startup=True))
        self.button_ok_dont_show.clicked.connect(
            lambda: self.accept(show_startup=False))

        self.setup()
示例#11
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()
示例#12
0
    def __init__(self, *args, **kwargs):
        """Quit application confirmation dialog."""
        self.config = kwargs.pop('config', CONF)
        super(QuitApplicationDialog, self).__init__(*args, **kwargs)
        self.widget_icon = QSvgWidget(images.ANACONDA_LOGO)
        self.label_about = QLabel('Quit Anaconda Navigator?')
        self.button_ok = ButtonPrimary('Yes')
        self.button_cancel = ButtonNormal('No')
        self.buttonbox = QDialogButtonBox(Qt.Horizontal)
        self.checkbox = CheckBoxBase("Don't show again")

        # Widgets setup
        self.setWindowTitle("Quit application")
        self.widget_icon.setFixedSize(QSize(100, 100))

        # Layouts
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.widget_icon, 0, Qt.AlignTop)
        h_layout.addWidget(SpacerHorizontal())
        h_layout.addWidget(self.label_about)

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

        main_layout = QVBoxLayout()
        main_layout.addLayout(h_layout)
        main_layout.addWidget(self.checkbox, 0, Qt.AlignRight)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(layout_buttons)
        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)

        # Setup
        self.update_style_sheet()
        self.setup()
        self.button_cancel.setFocus()
示例#13
0
    def __init__(self, title='', text='', value=None, value_type=None):
        """Base message box dialog."""
        super(InputDialog, self).__init__()

        # Widgets
        self.label = LabelBase(text)
        self.text = LineEditBase()
        self.button_ok = ButtonPrimary('Ok')
        self.button_cancel = ButtonNormal('Cancel')

        # Widget setup
        self.setWindowTitle(to_text_string(title))
        if value:
            self.text.setText(str(value))

        # Layouts
        layout = QVBoxLayout()

        layout_text = QHBoxLayout()
        layout_text.addWidget(self.label)
        layout_text.addWidget(SpacerHorizontal())
        layout_text.addWidget(self.text)

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

        layout.addLayout(layout_text)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
示例#14
0
    def __init__(self, parent=None, problems=None):
        """Dialog to display anaconda project problems."""
        # Check arguments: active channels, must be within channels, otherwise
        # just remove that channel from active channels
        super(DialogProblems, self).__init__(parent=parent)
        self._parent = parent
        self._problems = problems
        self.style_sheet = None

        # Widgets
        self.list = ListWidgetProblems(parent=self)
        self.button_ok = ButtonPrimary('Ok')

        # Widget setup
        self.frame_title_bar.setVisible(False)
        self.list.setFrameStyle(QFrame.NoFrame)
        self.list.setFrameShape(QFrame.NoFrame)
        self.list.setWordWrap(True)
        self.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint)
        self.setWindowOpacity(0.96)
        self.setModal(False)

        # Layout
        layout_ok = QHBoxLayout()
        layout_ok.addStretch()
        layout_ok.addWidget(self.button_ok)

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

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.setup()
示例#15
0
    def __init__(self, parent=None, projects=None):
        """Import project from folder or environment files."""
        super(ImportDialog, self).__init__(parent=parent)

        self.projects = projects if projects else {}
        self.selected_file_filter = None
        self._path = None

        # Widgets
        self.label_info = LabelSpecInfo('', parent=self)
        self.label_name = QLabel("Project name")
        self.label_path = QLabel("Specification File")
        self.text_name = QLineEdit()
        self.text_path = QLineEdit()
        self.button_path = ButtonNormal("")
        self.radio_folder = QRadioButton('From folder')
        self.radio_spec = QRadioButton('From specification file')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_ok = ButtonPrimary('Import')

        # Widgets setup
        self.button_path.setObjectName('import')
        self.button_ok.setDefault(True)
        self.text_path.setPlaceholderText("File to import from")
        self.text_name.setPlaceholderText("New project name")
        self.setMinimumWidth(380)
        self.setWindowTitle("Import new project")
        self.text_name.setValidator(get_regex_validator())

        # Layouts
        layout_radio = QHBoxLayout()
        layout_radio.addWidget(self.radio_folder)
        layout_radio.addWidget(SpacerHorizontal())
        layout_radio.addWidget(self.radio_spec)

        layout_infile = QHBoxLayout()
        layout_infile.addWidget(self.text_path)
        layout_infile.addWidget(SpacerHorizontal())
        layout_infile.addWidget(self.button_path)

        layout_grid = QGridLayout()
        layout_grid.addWidget(self.label_name, 0, 0, 1, 2)
        layout_grid.addWidget(SpacerHorizontal(), 0, 2)
        layout_grid.addWidget(self.text_name, 0, 3)
        layout_grid.addWidget(SpacerVertical(), 1, 0)
        layout_grid.addWidget(self.label_path, 2, 0)
        layout_grid.addWidget(self.label_info, 2, 1)
        layout_grid.addWidget(SpacerHorizontal(), 2, 2)
        layout_grid.addLayout(layout_infile, 2, 3)

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

        layout = QVBoxLayout()
        layout.addLayout(layout_radio)
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_grid)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_path.clicked.connect(self.choose)
        self.text_path.textChanged.connect(self.refresh)
        self.text_name.textChanged.connect(self.refresh)
        self.radio_folder.toggled.connect(self.refresh)
        self.radio_spec.toggled.connect(self.refresh)

        # Setup
        self.radio_folder.toggle()
        self.refresh()
示例#16
0
    def __init__(self, *args, **kwargs):
        super(ProjectsWidget, self).__init__(*args, **kwargs)

        self.api = AnacondaAPI()
        self.timer = None
        self.timer_content_changed = QTimer()
        self.project_path = None
        self.original_content = None
        self.config = CONF
        self.timer = None

        # Widgets
        self.frame_projects_header = FrameProjectDetailsHeader()
        self.frame_projects_footer = FrameProjectDetailsFooter()
        self.button_upload = ButtonPrimary('Upload to Anaconda Cloud')
        self.button_cancel = ButtonDanger('Cancel')
        self.label_project_location = LabelProjectLocation(
            '<b>Project location</b>')
        self.label_status_message = LabelBase('')
        self.text_project_location = TextProjectLocation()
        self.tab_details = QTabWidget()
        self.file_explorer = ExplorerWidget()
        self.editor = ProjectEditor(parent=self)

        # Wigets setup
        tabbar = self.tab_details.tabBar()
        tabbar.setFocusPolicy(Qt.StrongFocus)
        self.tab_details.addTab(self.file_explorer, 'Files')
        self.tab_details.addTab(self.editor, 'Edit')
        self.timer_content_changed.setInterval(2000)
        self.timer_content_changed.timeout.connect(self.check_content_change)
        self.timer_content_changed.start()

        # Layouts

        layout_upload = QHBoxLayout()
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(self.label_status_message)
        layout_upload.addStretch()
        layout_upload.addWidget(self.button_cancel)
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(self.button_upload)
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(SpacerHorizontal())

        layout_footer = QVBoxLayout()
        layout_footer.addWidget(SpacerVertical())
        layout_footer.addWidget(self.tab_details)
        layout_footer.addLayout(layout_upload)
        layout_footer.addWidget(SpacerVertical())
        layout_footer.addWidget(SpacerVertical())
        self.frame_projects_footer.setLayout(layout_footer)

        layout = QVBoxLayout()
        layout.addWidget(self.frame_projects_footer)
        self.setLayout(layout)

        # Signals
        self.editor.sig_dirty_state.connect(self.set_dirty)
        self.editor.sig_saved.connect(self.save)
        self.button_upload.clicked.connect(self.upload)
        self.button_cancel.clicked.connect(self.cancel)
        self.file_explorer.sig_add_to_project.connect(self.add_to_project)
        self.button_cancel.setVisible(False)

        self.file_explorer.set_current_folder(HOME_PATH)
示例#17
0
class ProjectEditor(QWidget):
    sig_dirty_state = Signal(bool)
    sig_saved = Signal()

    def __init__(self, *args, **kwargs):
        super(ProjectEditor, self).__init__(*args, **kwargs)

        # Widgets
        self.editor = EditorBase(self)
        self.button_save = ButtonPrimary('Save')
        self.button_problems = ButtonProjectProblems('Problems')
        self.button_suggestions = ButtonProjectSuggestions('Suggestions')
        self.original_text = None
        self.problems = None
        self.suggestions = None

        # Layouts
        layout_buttons = QHBoxLayout()
        layout_buttons.addWidget(self.button_save)
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_problems)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_suggestions)

        layout = QVBoxLayout()
        layout.addLayout(layout_buttons)
        layout.addWidget(self.editor)

        self.setLayout(layout)

        # Signals
        self.editor.textChanged.connect(self.text_changed)
        self.button_save.clicked.connect(self.save)
        self.button_problems.clicked.connect(self.show_problems)
        self.button_suggestions.clicked.connect(self.show_suggestions)

    def show_problems(self):
        """Display problems in a dialog."""
        dlg = DialogProblems(parent=self, problems=self.problems)
        geo_tl = self.button_problems.geometry().topRight()
        tl = self.button_problems.parentWidget().mapToGlobal(geo_tl)
        x = tl.x() - dlg.width()
        y = tl.y() + self.button_problems.height()
        dlg.move(x, y)
        dlg.show()

    def show_suggestions(self):
        """Display suggestions in a dialog."""
        dlg = DialogProblems(parent=self, problems=self.suggestions)
        geo_tl = self.button_suggestions.geometry().topRight()
        tl = self.button_suggestions.parentWidget().mapToGlobal(geo_tl)
        x = tl.x() - dlg.width()
        y = tl.y() + self.button_suggestions.height()
        dlg.move(x, y)
        dlg.show()

    def save(self):
        """Save test to editor."""
        text = self.editor.toPlainText()
        self.original_text = text
        self.button_save.setDisabled(True)
        self.sig_saved.emit()

    def text_changed(self):
        """Callback on text change."""
        dirty = self.is_dirty()
        self.sig_dirty_state.emit(dirty)
        self.button_save.setEnabled(dirty)

    def is_dirty(self):
        """Return if the document is dirty."""
        current_text = self.editor.toPlainText()
        return current_text != self.original_text

    def text(self):
        """Return current plain text from editor."""
        return self.editor.toPlainText()

    def set_info(self, problems, suggestions):
        """Store problems and suggestions for display."""
        self.button_problems.setVisible(False)
        self.button_suggestions.setVisible(False)
        self.problems = None
        self.suggetsions = None

        if problems:
            self.problems = problems
            self.button_problems.setVisible(True)

        if suggestions:
            self.suggestions = suggestions
            self.button_suggestions.setVisible(True)

    def set_text(self, text):
        """Set editor text."""
        self.editor.setPlainText(text)
        self.original_text = text
        self.button_save.setDisabled(True)

    def scroll_value(self):
        """Get scroll value for vertical bar."""
        return self.editor.verticalScrollBar().value()

    def set_scroll_value(self, value):
        """Set scroll value for vertical bar."""
        return self.editor.verticalScrollBar().setValue(value)

    def ordered_widgets(self):
        """Return a list of the ordered widgets."""
        ordered_widgets = [
            self.button_save, self.button_problems, self.button_suggestions,
            self.editor
        ]
        return ordered_widgets
示例#18
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)
示例#19
0
class ProjectsWidget(WidgetBase):
    """Main projects widget."""

    sig_saved = Signal()
    sig_login_requested = Signal()

    def __init__(self, *args, **kwargs):
        super(ProjectsWidget, self).__init__(*args, **kwargs)

        self.api = AnacondaAPI()
        self.timer = None
        self.timer_content_changed = QTimer()
        self.project_path = None
        self.original_content = None
        self.config = CONF
        self.timer = None

        # Widgets
        self.frame_projects_header = FrameProjectDetailsHeader()
        self.frame_projects_footer = FrameProjectDetailsFooter()
        self.button_upload = ButtonPrimary('Upload to Anaconda Cloud')
        self.button_cancel = ButtonDanger('Cancel')
        self.label_project_location = LabelProjectLocation(
            '<b>Project location</b>')
        self.label_status_message = LabelBase('')
        self.text_project_location = TextProjectLocation()
        self.tab_details = QTabWidget()
        self.file_explorer = ExplorerWidget()
        self.editor = ProjectEditor(parent=self)

        # Wigets setup
        tabbar = self.tab_details.tabBar()
        tabbar.setFocusPolicy(Qt.StrongFocus)
        self.tab_details.addTab(self.file_explorer, 'Files')
        self.tab_details.addTab(self.editor, 'Edit')
        self.timer_content_changed.setInterval(2000)
        self.timer_content_changed.timeout.connect(self.check_content_change)
        self.timer_content_changed.start()

        # Layouts

        layout_upload = QHBoxLayout()
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(self.label_status_message)
        layout_upload.addStretch()
        layout_upload.addWidget(self.button_cancel)
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(self.button_upload)
        layout_upload.addWidget(SpacerHorizontal())
        layout_upload.addWidget(SpacerHorizontal())

        layout_footer = QVBoxLayout()
        layout_footer.addWidget(SpacerVertical())
        layout_footer.addWidget(self.tab_details)
        layout_footer.addLayout(layout_upload)
        layout_footer.addWidget(SpacerVertical())
        layout_footer.addWidget(SpacerVertical())
        self.frame_projects_footer.setLayout(layout_footer)

        layout = QVBoxLayout()
        layout.addWidget(self.frame_projects_footer)
        self.setLayout(layout)

        # Signals
        self.editor.sig_dirty_state.connect(self.set_dirty)
        self.editor.sig_saved.connect(self.save)
        self.button_upload.clicked.connect(self.upload)
        self.button_cancel.clicked.connect(self.cancel)
        self.file_explorer.sig_add_to_project.connect(self.add_to_project)
        self.button_cancel.setVisible(False)

        self.file_explorer.set_current_folder(HOME_PATH)

    def add_to_project(self, fname):
        """Add selected file to project."""
        file_path = os.path.join(
            self.project_path,
            os.path.basename(fname),
        )
        try:
            shutil.copyfile(fname, file_path)
        except Exception:
            pass

    def check_content_change(self):
        """Check if content of anaconda-project changed outside."""
        if self.project_path:
            project_config_path = os.path.join(self.project_path,
                                               'anaconda-project.yml')
            if os.path.isfile(project_config_path):
                current_content = self.editor.text()
                with open(project_config_path, 'r') as f:
                    data = f.read()

                if (current_content != data and data != self.original_content):
                    self.load_project(self.project_path)

    def set_dirty(self, state):
        """Set dirty state editor tab."""
        text = 'Edit*' if state else 'Edit'
        self.tab_details.setTabText(1, text)

    def before_delete(self):
        """Before deleting a folder, ensure it is not the same as the cwd."""
        self.file_explorer.set_current_folder(HOME_PATH)

    def clear(self):
        """Reset view for proect details."""
        self.text_project_location.setText('')
        self.editor.set_text('')

    def cancel(self):
        """Cancel ongoing project process."""
        # TODO: when running project. Cancel ongoing process!
        self.button_cancel.setVisible(False)
        self.button_upload.setEnabled(True)

    def _upload(self, worker, output, error):
        """Upload callback."""
        error = error if error else ''
        errors = []
        if output is not None:
            errors = output.errors
        # print(output.status_description)
        # print(output.logs)
        # print(errors)
        if error or errors:
            if errors:
                error_msg = error or '\n'.join(errors)
            elif error:
                error_msg = 'Upload failed!'
            self.update_status(error_msg)
        else:
            self.update_status('Project <b>{0}</b> upload successful'.format(
                worker.name))

        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.timer.setInterval(10000)
        self.timer.timeout.connect(lambda: self.update_status(''))
        self.timer.start()
        self.button_upload.setEnabled(True)
        self.button_cancel.setVisible(False)

    def update_status(self, message):
        """Update Status Bar message."""
        self.label_status_message.setText(message)

    def upload(self):
        """Upload project to Anaconda Cloud."""
        # Check if is logged in?
        if not self.api.is_logged_in():
            self.update_status('You need to log in to Anaconda Cloud')
            self.sig_login_requested.emit()
            self.update_status('')
            return

        project = self.api.project_load(self.project_path)
        name = project.name or os.path.basename(self.project_path)

        # Check if saved?
        if self.editor.is_dirty():
            self.update_status('Saving project <b>{0}</b>'.format(
                project.name))
            self.editor.save()

        project = self.api.project_load(self.project_path)

        if not project.problems:
            username, token = self.api.get_username_token()
            self.button_cancel.setVisible(True)
            worker = self.api.project_upload(
                project,
                username=username,
                token=token,
            )
            worker.sig_finished.connect(self._upload)
            worker.name = project.name
            self.button_upload.setEnabled(False)
            msg = 'Uploading project <b>{0}</b> to Anaconda Cloud.'.format(
                project.name)
            self.update_status(msg)
        else:
            self.update_status(
                'Problems must be fixed before uploading <b>{0}</b>'
                ''.format(name))

    def save(self):
        """Save current edited project."""
        project_config_path = os.path.join(self.project_path,
                                           'anaconda-project.yml')
        data = self.editor.text()
        if os.path.isfile(project_config_path):
            with open(project_config_path, 'w') as f:
                data = f.write(data)
        self.load_project(self.project_path, overwrite=False)
        self.sig_saved.emit()

    def load_project(self, project_path, overwrite=True):
        """Load a conda project located at path."""
        self.project_path = project_path
        project = self.api.project_load(project_path)
        self.project = project
        self.text_project_location.setText(project_path)
        self.file_explorer.set_current_folder(project_path)

        project_config_path = os.path.join(project_path,
                                           'anaconda-project.yml')
        data = ''
        if os.path.isfile(project_config_path):
            with open(project_config_path, 'r') as f:
                data = f.read()

        self.original_content = data
        if overwrite:
            self.editor.set_text(data)

        self.set_dirty(False)
        self.file_explorer.set_home(project_path)
        self.update_error_status(project)
        self.update_status('')

    def ordered_widgets(self):
        """Return a list of the ordered widgets."""
        tabbar = self.tab_details.tabBar()
        ordered_widgets = [tabbar]
        ordered_widgets += self.file_explorer.ordered_widgets()
        ordered_widgets += self.editor.ordered_widgets()
        ordered_widgets += [self.button_upload]
        return ordered_widgets

    def update_error_status(self, project):
        """Update problems and suggestions."""
        if project:
            problems = project.problems
            suggestions = project.suggestions
            if problems or suggestions:
                icon = QIcon(WARNING_ICON)
                self.tab_details.setTabIcon(1, icon)
            else:
                self.tab_details.setTabIcon(1, QIcon())
            self.editor.set_info(problems, suggestions)
示例#20
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
示例#21
0
class PreferencesDialog(DialogBase):
    """Application preferences dialog."""

    sig_urls_updated = Signal(str, str)
    sig_check_ready = Signal()
    sig_reset_ready = Signal()

    def __init__(self, config=CONF, **kwargs):
        """Application preferences dialog."""
        super(PreferencesDialog, self).__init__(**kwargs)

        self.api = AnacondaAPI()
        self.widgets_changed = set()
        self.widgets = []
        self.widgets_dic = {}
        self.config = config

        # Widgets
        self.button_ok = ButtonPrimary('Apply')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_reset = ButtonNormal('Reset to defaults')
        self.row = 0

        # Widget setup
        self.setWindowTitle("Preferences")

        # Layouts
        self.grid_layout = QGridLayout()

        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(self.button_reset)
        buttons_layout.addStretch()
        buttons_layout.addWidget(self.button_cancel)
        buttons_layout.addWidget(SpacerHorizontal())
        buttons_layout.addWidget(self.button_ok)

        main_layout = QVBoxLayout()
        main_layout.addLayout(self.grid_layout)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(buttons_layout)
        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_reset.clicked.connect(self.reset_to_defaults)
        self.button_reset.clicked.connect(
            lambda: self.button_ok.setEnabled(True)
        )

        # Setup
        self.grid_layout.setSpacing(0)
        self.setup()
        self.button_ok.setDisabled(True)
        self.widgets[0].setFocus()
        self.button_ok.setDefault(True)
        self.button_ok.setAutoDefault(True)

    # --- Helpers
    # -------------------------------------------------------------------------
    def get_option(self, option):
        """Get configuration option from `main` section."""
        return self.config.get('main', option, None)

    def set_option(self, option, value):
        """Set configuration option in `main` section."""
        self.config.set('main', option, value)

    def get_option_default(self, option):
        """Get configuration option default value in `main` section."""
        return self.config.get_default('main', option)

    def set_option_default(self, option):
        """Set configuration option default value in `main` section."""
        self.set_option(option, self.get_option_default(option))

    def create_widget(
        self,
        widget=None,
        label=None,
        option=None,
        hint=None,
        check=None,
        info=None,
    ):
        """Create preference option widget and add to layout."""
        config_value = self.get_option(option)
        widget._text = label
        widget.label = QLabel(label)
        widget.option = option
        widget.set_value(config_value)
        widget.label_information = QLabel()
        widget.label_information.setMinimumWidth(16)
        widget.label_information.setMaximumWidth(16)

        form_widget = QWidget()
        h_layout = QHBoxLayout()
        h_layout.addSpacing(4)
        h_layout.addWidget(widget.label_information, 0, Qt.AlignRight)
        h_layout.addWidget(widget, 0, Qt.AlignLeft)
        h_layout.addWidget(QLabel(hint or ''), 0, Qt.AlignLeft)
        form_widget.setLayout(h_layout)

        if check:
            widget.check_value = lambda value: check(value)
        else:
            widget.check_value = lambda value: (True, '')

        if info:
            label = widget.label_information
            label = PreferencesDialog.update_icon(label, INFO_ICON)
            label.setToolTip(info)

        self.widgets.append(widget)
        self.widgets_dic[option] = widget
        self.grid_layout.addWidget(
            widget.label, self.row, 0, Qt.AlignRight | Qt.AlignCenter
        )
        self.grid_layout.addWidget(
            form_widget, self.row, 1, Qt.AlignLeft | Qt.AlignCenter
        )
        self.row += 1

    def create_textbox(self, label, option, hint=None, check=None, info=None):
        """Create textbox (QLineEdit) preference option."""
        widget = QLineEdit()
        widget.setAttribute(Qt.WA_MacShowFocusRect, False)
        widget.setMinimumWidth(250)

        widget.get_value = lambda w=widget: w.text()
        widget.set_value = lambda value, w=widget: w.setText(value)
        widget.set_warning = lambda w=widget: w.setSelection(0, 1000)
        widget.textChanged.connect(
            lambda v=None, w=widget: self.options_changed(widget=w)
        )

        self.create_widget(
            widget=widget,
            option=option,
            label=label,
            hint=hint,
            check=check,
            info=info,
        )

    def create_checkbox(self, label, option, check=None, hint=None, info=None):
        """Create checkbox preference option."""
        widget = QCheckBox()
        widget.get_value = lambda w=widget: bool(w.checkState())
        widget.set_value = lambda value, w=widget: bool(
            w.setCheckState(Qt.Checked if value else Qt.Unchecked)
        )

        api_widget = self.widgets_dic['anaconda_api_url']
        widget.set_warning = lambda w=widget: api_widget
        widget.stateChanged.connect(
            lambda v=None, w=widget: self.options_changed(widget=w)
        )
        self.create_widget(
            widget=widget,
            option=option,
            label=label,
            hint=hint,
            check=check,
            info=info,
        )

    def options_changed(self, value=None, widget=None):
        """Callback helper triggered on preference value change."""
        config_value = self.get_option(widget.option)

        if config_value != widget.get_value():
            self.widgets_changed.add(widget)
        else:
            if widget in self.widgets_changed:
                self.widgets_changed.remove(widget)

        self.button_ok.setDisabled(not bool(len(self.widgets_changed)))

    def widget_for_option(self, option):
        """Return the widget for the given option."""
        return self.widgets_dic[option]

    # --- API
    # -------------------------------------------------------------------------
    def set_initial_values(self):
        """
        Set configuration values found in other config files.

        Some options of configuration are found in condarc or in
        anaconda-client configuration.
        """
        self.config.set(
            'main', 'anaconda_api_url', self.api.client_get_api_url()
        )

        # See https://conda.io/docs/install/central.html
        # ssl_verify overloads True/False/<Path to certificate>
        # Navigator splits that into 2 separate options for clarity
        ssl_verify = self.api.client_get_ssl()
        if isinstance(ssl_verify, bool):
            self.config.set('main', 'ssl_verification', ssl_verify)
            self.config.set('main', 'ssl_certificate', None)
        else:
            self.config.set('main', 'ssl_verification', True)
            self.config.set('main', 'ssl_certificate', ssl_verify)

    def setup(self):
        """Setup the preferences dialog."""

        def api_url_checker(value):
            """
            Custom checker to use selected ssl option instead of stored one.

            This allows to set an unsafe api url directly on the preferences
            dialog. Without this, one would have to first disable, click
            accept, then open preferences again and change api url for it to
            work.
            """
            # Ssl widget
            ssl_widget = self.widgets_dic.get('ssl_verification')
            verify = ssl_widget.get_value() if ssl_widget else True

            # Certificate path
            ssl_cert_widget = self.widgets_dic.get('ssl_certificate')
            if ssl_cert_widget:
                verify = ssl_cert_widget.get_value()

            # Offline mode
            offline_widget = self.widgets_dic.get('offline_mode')
            if ssl_widget or ssl_cert_widget:
                offline_mode = offline_widget.get_value()
            else:
                offline_mode = False

            if offline_mode:
                basic_check = (
                    False,
                    'API Domain cannot be modified when '
                    'working in <b>offline mode</b>.<br>',
                )
            else:
                basic_check = self.is_valid_api(value, verify=verify)

            return basic_check

        def ssl_checker(value):
            """Counterpart to api_url_checker."""
            api_url_widget = self.widgets_dic.get('anaconda_api_url')
            api_url = api_url_widget.get_value()
            return self.is_valid_api(api_url, verify=value)

        def ssl_certificate_checker(value):
            """Check if certificate path is valid/exists."""
            ssl_widget = self.widgets_dic.get('ssl_verification')
            verify = ssl_widget.get_value() if ssl_widget else True
            ssl_cert_widget = self.widgets_dic.get('ssl_certificate')
            path = ssl_cert_widget.get_value()
            return self.is_valid_cert_file(path, verify)

        self.set_initial_values()
        self.create_textbox(
            'Anaconda API domain',
            'anaconda_api_url',
            check=api_url_checker,
        )
        self.create_checkbox(
            'Enable SSL verification',
            'ssl_verification',
            check=ssl_checker,
            hint=(
                '<i>Disabling this option is not <br>'
                'recommended for security reasons</i>'
            ),
        )
        self.create_textbox(
            'SSL certificate path (Optional)',
            'ssl_certificate',
            check=ssl_certificate_checker,
        )
        info = '''To help us improve Anaconda Navigator, fix bugs,
and make it even easier for everyone to use Python,
we gather anonymized usage information, just like
most web browsers and mobile apps.'''
        self.create_checkbox(
            'Quality improvement reporting',
            'provide_analytics',
            info=info,
        )
        info_offline = DialogOfflineMode.MESSAGE_PREFERENCES
        extra = '<br><br>' if WIN7 else ''
        self.create_checkbox(
            'Enable offline mode',
            'offline_mode',
            info=info_offline + extra,
        )
        self.create_checkbox('Hide offline mode dialog', 'hide_offline_dialog')
        self.create_checkbox('Hide quit dialog', 'hide_quit_dialog')
        self.create_checkbox(
            'Hide update dialog on startup', 'hide_update_dialog'
        )
        self.create_checkbox(
            'Hide running applications dialog', 'hide_running_apps_dialog'
        )
        self.create_checkbox(
            'Enable high DPI scaling', 'enable_high_dpi_scaling'
        )
        self.create_checkbox(
            'Show application startup error messages', 'show_application_launch_errors'
        )

        ssl_ver_widget = self.widgets_dic.get('ssl_verification')
        ssl_ver_widget.stateChanged.connect(self.enable_disable_cert)
        ssl_cert_widget = self.widgets_dic.get('ssl_certificate')
        ssl_cert_widget.setPlaceholderText(
            'Certificate to verify SSL connections'
        )

        # Refresh enabled/disabled status of certificate textbox
        self.enable_disable_cert()

    def enable_disable_cert(self, value=None):
        """Refresh enabled/disabled status of certificate textbox."""
        ssl_cert_widget = self.widgets_dic.get('ssl_certificate')
        if value:
            value = bool(value)
        else:
            ssl_ver_widget = self.widgets_dic.get('ssl_verification')
            value = bool(ssl_ver_widget.checkState())
        ssl_cert_widget.setEnabled(value)

    @staticmethod
    def update_icon(label, icon):
        """Update icon for information or warning."""
        pixmap = QPixmap(icon)
        label.setScaledContents(True)
        label.setPixmap(
            pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        )
        return label

    @staticmethod
    def warn(widget, text=None):
        """Display warning for widget in preferences."""
        label = widget.label_information
        if text:
            label = PreferencesDialog.update_icon(label, WARNING_ICON)
            label.setToolTip(str(text))
            w = widget.label_information.width() / 2
            h = widget.label_information.height() / 2
            position = widget.label_information.mapToGlobal(QPoint(w, h))
            QCursor.setPos(position)
        else:
            label.setPixmap(QPixmap())
            label.setToolTip('')

    # --- Checkers
    # -------------------------------------------------------------------------
    def is_valid_url(self, url):
        """Check if a given URL returns a 200 code."""
        output = self.api.download_is_valid_url(url, non_blocking=False)
        error = ''
        if not output:
            error = 'Invalid api url.'
        return output, error

    def is_valid_cert_file(self, path, verify):
        """"Check if ssl certificate file in given path exists."""
        output = True
        error = ''

        # Only validate if it is not empty and if ssl_verification is checked
        if path.strip() and verify:
            output = os.path.isfile(path)
            if not output:
                error = 'File not found.'
        return output, error

    def is_valid_api(self, url, verify=True):
        """Check if a given URL is a valid anaconda api endpoint."""
        output = self.api.download_is_valid_api_url(
            url,
            non_blocking=False,
            verify=verify,
        )
        error = ''
        if not output:
            url_api_1 = ''
            url_api_2 = ''

            if '/api' not in url and self.is_valid_url(url)[0]:
                url_api_1 = url.replace('https://', 'https://api.')
                url_api_1 = url_api_1.replace('http://', 'http://api.')

                if url.endswith('/'):
                    url_api_2 = url + 'api'
                else:
                    url_api_2 = url + '/api'

                error = (
                    'Invalid Anaconda API url. <br>'
                    '<br>Try using:<br><b>{0}</b> or <br>'
                    '<b>{1}</b>'.format(url_api_1, url_api_2)
                )
            else:
                error = (
                    'Invalid Anaconda API url.<br><br>'
                    'Check the url is valid and corresponds to the api '
                    'endpoint.'
                )
        return output, error

    def run_checks(self):
        """
        Run all check functions on configuration options.

        This method checks and warns but it does not change/set values.
        """
        checks = []
        for widget in self.widgets_changed:
            value = widget.get_value()
            check, error = widget.check_value(value)
            checks.append(check)

            if check:
                self.warn(widget)
            else:
                self.button_ok.setDisabled(True)
                widget.set_warning()
                self.warn(widget, error)
                break

        # Emit checks ready
        self.sig_check_ready.emit()
        return checks

    def reset_to_defaults(self):
        """Reset the preferences to the default values."""
        for widget in self.widgets:
            option = widget.option
            default = self.get_option_default(option)
            widget.set_value(default)

            # Flag all values as updated
            self.options_changed(widget=widget, value=default)
        self.sig_reset_ready.emit()

    def accept(self):
        """Override Qt method."""
        sig_updated = False
        anaconda_api_url = None
        checks = self.run_checks()

        # Update values
        if checks and all(checks):
            for widget in self.widgets_changed:
                value = widget.get_value()
                self.set_option(widget.option, value)

                # Settings not stored on Navigator config, but taken from
                # anaconda-client config
                if widget.option == 'anaconda_api_url':
                    anaconda_api_url = value  # Store it to be emitted
                    self.api.client_set_api_url(value)
                    sig_updated = True

                # ssl_verify/verify_ssl handles True/False/<Path to cert>
                # On navi it is split in 2 options for clarity
                if widget.option in ['ssl_certificate', 'ssl_verification']:
                    ssl_veri = self.widgets_dic.get('ssl_verification')
                    ssl_cert = self.widgets_dic.get('ssl_certificate')
                    verify = ssl_veri.get_value()
                    path = ssl_cert.get_value()

                    if path.strip() and verify:
                        value = path
                    else:
                        value = verify

                    self.api.client_set_ssl(value)

            if sig_updated and anaconda_api_url:

                def _api_info(worker, output, error):
                    conda_url = output.get('conda_url')
                    try:
                        self.sig_urls_updated.emit(anaconda_api_url, conda_url)
                        super(PreferencesDialog, self).accept()
                    except RuntimeError:
                        # Some tests on appveyor/circleci fail
                        pass

                worker = self.api.api_urls()
                worker.sig_chain_finished.connect(_api_info)

            super(PreferencesDialog, self).accept()
示例#22
0
class DialogOfflineMode(DialogBase):
    """Offline mode dialog."""

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

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

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

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

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

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

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

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

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

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

    def handle_accept(self):
        """Handle not showing updates on startup."""
        value = bool(self.checkbox_hide.checkState())
        self.config.set('main', 'hide_offline_dialog', value)
        self.accept()
示例#23
0
class DialogChannels(DialogBase):
    """Dialog to add delete and select active conda package channels."""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        add = updated - original
        remove = original - updated

        return add, remove
示例#24
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()
示例#25
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))
示例#26
0
    def __init__(self, parent=None):
        """Dialog to add delete and select active conda pacakge channels ."""
        super(DialogChannels, self).__init__(parent)
        self._parent = parent
        self._conda_url = 'https://conda.anaconda.org'
        self.api = AnacondaAPI()
        self.initial_sources = None
        self.config_sources = None
        self.style_sheet = None
        self._setup_ready = False
        self._conda_url_setup_ready = False

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

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

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

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

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

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

        self.button_add.setDisabled(True)
        self.button_ok.setDisabled(True)
        self.button_cancel.setDisabled(True)
        self.update_status(action='Collecting sources...',
                           value=0,
                           max_value=0)
示例#27
0
class CreateDialog(DialogBase):
    """Create new project dialog."""
    def __init__(self, parent=None, projects=None):
        """Create new environment dialog."""
        super(CreateDialog, self).__init__(parent=parent)

        self.projects = projects

        # Widgets
        self.label_name = QLabel("Project name")
        self.text_name = QLineEdit()
        self.button_ok = ButtonPrimary('Create')
        self.button_cancel = ButtonNormal('Cancel')

        # Widgets setup
        self.text_name.setPlaceholderText("New project name")
        self.setMinimumWidth(380)
        self.setWindowTitle("Create new project")
        self.text_name.setValidator(get_regex_validator())

        # Layouts
        grid = QGridLayout()
        grid.addWidget(self.label_name, 0, 0)
        grid.addWidget(SpacerHorizontal(), 0, 1)
        grid.addWidget(self.text_name, 0, 2)
        grid.addWidget(SpacerVertical(), 1, 0)

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

        main_layout = QVBoxLayout()
        main_layout.addLayout(grid)
        main_layout.addWidget(SpacerVertical())
        main_layout.addWidget(SpacerVertical())
        main_layout.addLayout(layout_buttons)

        self.setLayout(main_layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.text_name.textChanged.connect(self.refresh)

        # Setup
        self.refresh()

    def refresh(self, text=''):
        """Update status of buttons based on combobox selection."""
        self.button_ok.setDisabled(True)
        text = self.text_name.text().strip()
        if self.projects is not None:
            if is_valid_project_name(text, self.projects):
                self.button_ok.setDisabled(False)
            else:
                self.button_ok.setDisabled(True)

    @property
    def name(self):
        """Return the project name."""
        return self.text_name.text().strip()
示例#28
0
class ImportDialog(DialogBase):
    """Import project from folder or specification files."""

    CONDA_ENV_FILES = 'Conda environment files (*.yaml *.yml)'
    CONDA_SPEC_FILES = 'Conda explicit specification files (*.txt)'
    PIP_REQUIREMENT_FILES = 'Pip requirement files (*.txt)'

    def __init__(self, parent=None, projects=None):
        """Import project from folder or environment files."""
        super(ImportDialog, self).__init__(parent=parent)

        self.projects = projects if projects else {}
        self.selected_file_filter = None
        self._path = None

        # Widgets
        self.label_info = LabelSpecInfo('', parent=self)
        self.label_name = QLabel("Project name")
        self.label_path = QLabel("Specification File")
        self.text_name = QLineEdit()
        self.text_path = QLineEdit()
        self.button_path = ButtonNormal("")
        self.radio_folder = QRadioButton('From folder')
        self.radio_spec = QRadioButton('From specification file')
        self.button_cancel = ButtonNormal('Cancel')
        self.button_ok = ButtonPrimary('Import')

        # Widgets setup
        self.button_path.setObjectName('import')
        self.button_ok.setDefault(True)
        self.text_path.setPlaceholderText("File to import from")
        self.text_name.setPlaceholderText("New project name")
        self.setMinimumWidth(380)
        self.setWindowTitle("Import new project")
        self.text_name.setValidator(get_regex_validator())

        # Layouts
        layout_radio = QHBoxLayout()
        layout_radio.addWidget(self.radio_folder)
        layout_radio.addWidget(SpacerHorizontal())
        layout_radio.addWidget(self.radio_spec)

        layout_infile = QHBoxLayout()
        layout_infile.addWidget(self.text_path)
        layout_infile.addWidget(SpacerHorizontal())
        layout_infile.addWidget(self.button_path)

        layout_grid = QGridLayout()
        layout_grid.addWidget(self.label_name, 0, 0, 1, 2)
        layout_grid.addWidget(SpacerHorizontal(), 0, 2)
        layout_grid.addWidget(self.text_name, 0, 3)
        layout_grid.addWidget(SpacerVertical(), 1, 0)
        layout_grid.addWidget(self.label_path, 2, 0)
        layout_grid.addWidget(self.label_info, 2, 1)
        layout_grid.addWidget(SpacerHorizontal(), 2, 2)
        layout_grid.addLayout(layout_infile, 2, 3)

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

        layout = QVBoxLayout()
        layout.addLayout(layout_radio)
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_grid)
        layout.addWidget(SpacerVertical())
        layout.addWidget(SpacerVertical())
        layout.addLayout(layout_buttons)

        self.setLayout(layout)

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)
        self.button_path.clicked.connect(self.choose)
        self.text_path.textChanged.connect(self.refresh)
        self.text_name.textChanged.connect(self.refresh)
        self.radio_folder.toggled.connect(self.refresh)
        self.radio_spec.toggled.connect(self.refresh)

        # Setup
        self.radio_folder.toggle()
        self.refresh()

    def refresh(self, text=''):
        """Update the status of buttons based on radio selection."""
        if self.radio_folder.isChecked():
            self.text_path.setPlaceholderText("Folder to import from")
            self.label_path.setText('Folder')
            self.label_info.setVisible(False)
        else:
            self.label_info.setVisible(True)
            self.label_path.setText('File ')
            self.text_path.setPlaceholderText("File to import from")

        text = self.text_name.text()
        path = self.text_path.text()

        if (text and path and os.path.exists(path)
                and is_valid_project_name(text, self.projects)):
            self.button_ok.setDisabled(False)
            self.button_ok.setDefault(True)
        else:
            self.button_ok.setDisabled(True)
            self.button_cancel.setDefault(True)

    def choose(self):
        """Display file dialog to select environment specification."""
        selected_filter = None
        if self.radio_spec.isChecked():
            path, selected_filter = getopenfilename(
                caption="Import Project",
                basedir=HOME_PATH,
                parent=None,
                filters="{0};;{1};;{2}".format(self.CONDA_ENV_FILES,
                                               self.CONDA_SPEC_FILES,
                                               self.PIP_REQUIREMENT_FILES))
        else:
            path = getexistingdirectory(
                caption="Import Project",
                basedir=HOME_PATH,
                parent=None,
            )

        if path:
            name = self.text_name.text()
            self.selected_file_filter = selected_filter
            self.text_path.setText(path)
            self.refresh(path)
            self.text_name.setText(name)

    @property
    def name(self):
        """Return the project name."""
        return self.text_name.text().strip()

    @property
    def path(self):
        """Return the project path to import (file or folder)."""
        return self.text_path.text()
示例#29
0
文件: update.py 项目: amccarty/ac0n3
class DialogUpdateApplication(DialogBase):
    """Update application dialog."""

    WIDTH = 460

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

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

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

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

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

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

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

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

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

        self.button_yes.setFocus()

    def no_show(self):
        """Handle not showing updates on startup."""
        self.config.set('main', 'hide_update_dialog', True)
        self.reject()
示例#30
0
class ProjectsPathDialog(DialogBase):
    """Select project path."""
    def __init__(self, parent=None, default=DEFAULT_PROJECTS_PATH):
        """Select project folder."""
        super(ProjectsPathDialog, self).__init__(parent=parent)

        # Widgets
        self.label_description = QLabel("If no path is selected, the default"
                                        " one will be used.")
        self.label_name = QLabel("Select the projects folder to use")
        self.label_path = QLabel("Projects path")
        self.label_info = QLabel('')
        self.text_path = QLineEdit()
        self.button_path = ButtonNormal("")
        self.button_default = ButtonNormal('Use default')
        self.button_ok = ButtonPrimary('Select')

        # Widgets setup
        self.button_path.setObjectName('import')
        self.default = default
        self.text_path.setPlaceholderText("projects folder path")
        self.text_path.setReadOnly(True)
        self.setMinimumWidth(580)
        self.setWindowTitle("Select Projects Path")

        # Layouts
        layout_grid = QGridLayout()
        layout_grid.addWidget(self.label_path, 0, 0)
        layout_grid.addWidget(SpacerHorizontal(), 0, 1)
        layout_grid.addWidget(self.text_path, 0, 2)
        layout_grid.addWidget(SpacerHorizontal(), 0, 3)
        layout_grid.addWidget(self.button_path, 0, 4)
        layout_grid.addWidget(SpacerVertical(), 1, 0, 1, 4)
        layout_grid.addWidget(self.label_info, 2, 2, 1, 2)

        layout_buttons = QHBoxLayout()
        layout_buttons.addStretch()
        layout_buttons.addWidget(self.button_default)
        layout_buttons.addWidget(SpacerHorizontal())
        layout_buttons.addWidget(self.button_ok)

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

        # Signals
        self.button_ok.clicked.connect(self.accept)
        self.button_default.clicked.connect(self.use_default)
        self.button_path.clicked.connect(self.choose)

        # Setup
        self.refresh()

    @property
    def path(self):
        """Return the folder project path."""
        return self.text_path.text().strip() or self.default

    def use_default(self):
        """Set the default projects path as the folder path to use."""
        self.text_path.setText('')
        self.accept()

    def choose(self):
        """Display directory dialog to select project folder."""
        path = getexistingdirectory(
            caption="Select Projects Folder",
            basedir=self.default,
            parent=self,
        )
        # Enforce the correct slashes.
        # See Issue https://github.com/ContinuumIO/navigator/issues/1164
        if WIN:
            path = path.replace('/', '\\')
        else:
            path = path.replace('\\', '/')

        if path:
            self.text_path.setText(path)
        self.refresh()
        return path

    def is_valid_path(self):
        """Check that entered path is valid."""
        check = False
        error = ''
        path = self.text_path.text().strip()
        if bool(path) and os.path.isdir(path):
            if ' ' in path:
                error = '<b>Please select a path without spaces</b>'
            elif path == HOME_PATH:
                error = ('<b>Please select a path different to the home '
                         'directory.</b>')
            elif not path_is_writable(path):
                error = ('<b>Please select a path that has write access.</b>')
            check = (' ' not in path and path != HOME_PATH
                     and path_is_writable(path))
        return check, error

    def refresh(self):
        """Refresh button status based on path."""
        check, error = self.is_valid_path()
        self.button_ok.setEnabled(check)
        if check or not bool(self.text_path.text()):
            self.label_info.setText('<i>Default: {0}</i>'.format(self.default))
        else:
            self.label_info.setText(error)

    def reject(self):
        """Override Qt method."""
        self.use_default()