コード例 #1
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)
コード例 #2
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)