<gh_stars>1-10
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""Report Error Dialog"""

# Stdlib imports
import sys

# Third party imports
from qtpy.QtWidgets import (QApplication, QCheckBox, QDialog, QHBoxLayout,
                            QLabel, QPlainTextEdit, QPushButton, QVBoxLayout)
from qtpy.QtCore import Qt, Signal

# Local Imports
from spyder.config.base import _
from spyder.config.gui import get_font
from spyder.utils.qthelpers import restore_keyevent
from spyder.widgets.sourcecode.codeeditor import CodeEditor
from spyder.widgets.mixins import BaseEditMixin, TracebackLinksMixin
from spyder.widgets.sourcecode.base import ConsoleBaseWidget


# Minimum number of characters to introduce in the description
# field before being able to send the report to Github.
MIN_CHARS = 20


class DescriptionWidget(CodeEditor):
    """Widget to enter error description."""

    def __init__(self, parent=None):
        CodeEditor.__init__(self, parent)

        # Editor options
        self.setup_editor(
            language='md',
            color_scheme='Scintilla',
            linenumbers=False,
            scrollflagarea=False,
            wrap=True,
            edge_line=False,
            highlight_current_line=False,
            highlight_current_cell=False,
            occurrence_highlighting=False,
            auto_unindent=False)

        # Set font
        self.set_font(get_font())

        # Header
        self.header = (
            "**What steps will reproduce your problem?**\n\n"
            "<!--- You can use Markdown here --->\n\n")
        self.set_text(self.header)
        self.move_cursor(len(self.header))
        self.header_end_pos = self.get_position('eof')

    def remove_text(self):
        """Remove text."""
        self.truncate_selection(self.header_end_pos)
        self.remove_selected_text()

    def cut(self):
        """Cut text"""
        self.truncate_selection(self.header_end_pos)
        if self.has_selected_text():
            CodeEditor.cut(self)

    def keyPressEvent(self, event):
        """Reimplemented Qt Method to avoid removing the header."""
        event, text, key, ctrl, shift = restore_keyevent(event)
        cursor_position = self.get_position('cursor')

        if cursor_position < self.header_end_pos:
            self.restrict_cursor_position(self.header_end_pos, 'eof')
        elif key == Qt.Key_Delete:
            if self.has_selected_text():
                self.remove_text()
            else:
                self.stdkey_clear()
        elif key == Qt.Key_Backspace:
            if self.has_selected_text():
                self.remove_text()
            elif self.header_end_pos == cursor_position:
                return
            else:
                self.stdkey_backspace()
        elif key == Qt.Key_X and ctrl:
            self.cut()
        else:
            CodeEditor.keyPressEvent(self, event)

    def contextMenuEvent(self, event):
        """Reimplemented Qt Method to not show the context menu."""
        pass


class ShowErrorWidget(TracebackLinksMixin, ConsoleBaseWidget, BaseEditMixin):
    """Widget to show errors as they appear in the Internal console."""
    QT_CLASS = QPlainTextEdit
    go_to_error = Signal(str)

    def __init__(self, parent=None):
        ConsoleBaseWidget.__init__(self, parent)
        BaseEditMixin.__init__(self)
        TracebackLinksMixin.__init__(self)
        self.setReadOnly(True)


class SpyderErrorDialog(QDialog):
    """Custom error dialog for error reporting."""

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_("Spyder internal error"))
        self.setModal(True)

        # To save the traceback sent to the internal console
        self.error_traceback = ""

        # Dialog main label
        self.main_label = QLabel(
            _("""<b>Spyder has encountered an internal problem</b><hr>
              Please enter below a step-by-step description of 
              your problem (in English). Issue reports without 
              a clear way to reproduce them will be closed.
              <br><br>
              <b>Note</b>: You need a Github account for this.
              """))
        self.main_label.setWordWrap(True)
        self.main_label.setAlignment(Qt.AlignJustify)

        # Field to input the description of the problem
        self.input_description = DescriptionWidget(self)

        # Only allow to submit to Github if we have a long enough description
        self.input_description.textChanged.connect(self._description_changed)

        # Widget to show errors
        self.details = ShowErrorWidget(self)
        self.details.set_pythonshell_font(get_font())
        self.details.hide()

        # Label to show missing chars
        self.initial_chars = len(self.input_description.toPlainText())
        self.chars_label = QLabel(_("Enter at least {} "
                                    "characters".format(MIN_CHARS)))

        # Checkbox to dismiss future errors
        self.dismiss_box = QCheckBox()
        self.dismiss_box.setText(_("Don't show again during this session"))

        # Labels layout
        labels_layout = QHBoxLayout()
        labels_layout.addWidget(self.chars_label)
        labels_layout.addWidget(self.dismiss_box, 0, Qt.AlignRight)

        # Dialog buttons
        self.submit_btn = QPushButton(_('Submit to Github'))
        self.submit_btn.setEnabled(False)
        self.submit_btn.clicked.connect(self._submit_to_github)

        self.details_btn = QPushButton(_('Show details'))
        self.details_btn.clicked.connect(self._show_details)

        self.close_btn = QPushButton(_('Close'))

        # Buttons layout
        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(self.submit_btn)
        buttons_layout.addWidget(self.details_btn)
        buttons_layout.addWidget(self.close_btn)

        # Main layout
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.main_label)
        vlayout.addWidget(self.input_description)
        vlayout.addWidget(self.details)
        vlayout.addLayout(labels_layout)
        vlayout.addLayout(buttons_layout)
        self.setLayout(vlayout)

        self.resize(600, 420)
        self.input_description.setFocus()

    def _submit_to_github(self):
        """Action to take when pressing the submit button."""
        main = self.parent().main

        # Getting description and traceback
        description = self.input_description.toPlainText()
        traceback = self.error_traceback[:-1] # Remove last eol

        # Render issue
        issue_text  = main.render_issue(description=description,
                                        traceback=traceback)

        # Copy issue to clipboard
        QApplication.clipboard().setText(issue_text)

        # Submit issue to Github
        issue_body=("<!--- "
                    "Please paste the contents of your clipboard "
                    "below to complete reporting your problem. "
                    "--->\n\n")
        main.report_issue(body=issue_body,
                          title="Automatic error report")

    def append_traceback(self, text):
        """Append text to the traceback, to be displayed in details."""
        self.error_traceback += text

    def _show_details(self):
        """Show traceback on its own dialog"""
        if self.details.isVisible():
            self.details.hide()
            self.details_btn.setText(_('Show details'))
        else:
            self.resize(600, 550)
            self.details.document().setPlainText('')
            self.details.append_text_to_shell(self.error_traceback,
                                              error=True,
                                              prompt=False)
            self.details.show()
            self.details_btn.setText(_('Hide details'))

    def _description_changed(self):
        """Activate submit_btn if we have a long enough description."""
        chars = len(self.input_description.toPlainText()) - self.initial_chars
        if chars < MIN_CHARS:
            self.chars_label.setText(
                u"{} {}".format(MIN_CHARS - chars,
                                _("more characters to go...")))
        else:
            self.chars_label.setText(_("Ready to submit! Thanks!"))
        self.submit_btn.setEnabled(chars >= MIN_CHARS)


def test():
    from spyder.utils.qthelpers import qapplication
    app = qapplication()
    dlg = SpyderErrorDialog()
    dlg.show()
    sys.exit(dlg.exec_())


if __name__ == "__main__":
    test()

# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""Report Error Dialog"""

# Stdlib imports
import sys

# Third party imports
from qtpy.QtWidgets import (QApplication, QCheckBox, QDialog, QHBoxLayout,
                            QLabel, QPlainTextEdit, QPushButton, QVBoxLayout)
from qtpy.QtCore import Qt, Signal

# Local Imports
from spyder.config.base import _
from spyder.config.gui import get_font
from spyder.utils.qthelpers import restore_keyevent
from spyder.widgets.sourcecode.codeeditor import CodeEditor
from spyder.widgets.mixins import BaseEditMixin, TracebackLinksMixin
from spyder.widgets.sourcecode.base import ConsoleBaseWidget


# Minimum number of characters to introduce in the description
# field before being able to send the report to Github.
MIN_CHARS = 20


class DescriptionWidget(CodeEditor):
    """Widget to enter error description."""

    def __init__(self, parent=None):
        CodeEditor.__init__(self, parent)

        # Editor options
        self.setup_editor(
            language='md',
            color_scheme='Scintilla',
            linenumbers=False,
            scrollflagarea=False,
            wrap=True,
            edge_line=False,
            highlight_current_line=False,
            highlight_current_cell=False,
            occurrence_highlighting=False,
            auto_unindent=False)

        # Set font
        self.set_font(get_font())

        # Header
        self.header = (
            "**What steps will reproduce your problem?**\n\n"
            "<!--- You can use Markdown here --->\n\n")
        self.set_text(self.header)
        self.move_cursor(len(self.header))
        self.header_end_pos = self.get_position('eof')

    def remove_text(self):
        """Remove text."""
        self.truncate_selection(self.header_end_pos)
        self.remove_selected_text()

    def cut(self):
        """Cut text"""
        self.truncate_selection(self.header_end_pos)
        if self.has_selected_text():
            CodeEditor.cut(self)

    def keyPressEvent(self, event):
        """Reimplemented Qt Method to avoid removing the header."""
        event, text, key, ctrl, shift = restore_keyevent(event)
        cursor_position = self.get_position('cursor')

        if cursor_position < self.header_end_pos:
            self.restrict_cursor_position(self.header_end_pos, 'eof')
        elif key == Qt.Key_Delete:
            if self.has_selected_text():
                self.remove_text()
            else:
                self.stdkey_clear()
        elif key == Qt.Key_Backspace:
            if self.has_selected_text():
                self.remove_text()
            elif self.header_end_pos == cursor_position:
                return
            else:
                self.stdkey_backspace()
        elif key == Qt.Key_X and ctrl:
            self.cut()
        else:
            CodeEditor.keyPressEvent(self, event)

    def contextMenuEvent(self, event):
        """Reimplemented Qt Method to not show the context menu."""
        pass


class ShowErrorWidget(TracebackLinksMixin, ConsoleBaseWidget, BaseEditMixin):
    """Widget to show errors as they appear in the Internal console."""
    QT_CLASS = QPlainTextEdit
    go_to_error = Signal(str)

    def __init__(self, parent=None):
        ConsoleBaseWidget.__init__(self, parent)
        BaseEditMixin.__init__(self)
        TracebackLinksMixin.__init__(self)
        self.setReadOnly(True)


class SpyderErrorDialog(QDialog):
    """Custom error dialog for error reporting."""

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(_("Spyder internal error"))
        self.setModal(True)

        # To save the traceback sent to the internal console
        self.error_traceback = ""

        # Dialog main label
        self.main_label = QLabel(
            _("""<b>Spyder has encountered an internal problem</b><hr>
              Please enter below a step-by-step description of 
              your problem (in English). Issue reports without 
              a clear way to reproduce them will be closed.
              <br><br>
              <b>Note</b>: You need a Github account for this.
              """))
        self.main_label.setWordWrap(True)
        self.main_label.setAlignment(Qt.AlignJustify)

        # Field to input the description of the problem
        self.input_description = DescriptionWidget(self)

        # Only allow to submit to Github if we have a long enough description
        self.input_description.textChanged.connect(self._description_changed)

        # Widget to show errors
        self.details = ShowErrorWidget(self)
        self.details.set_pythonshell_font(get_font())
        self.details.hide()

        # Label to show missing chars
        self.initial_chars = len(self.input_description.toPlainText())
        self.chars_label = QLabel(_("Enter at least {} "
                                    "characters".format(MIN_CHARS)))

        # Checkbox to dismiss future errors
        self.dismiss_box = QCheckBox()
        self.dismiss_box.setText(_("Don't show again during this session"))

        # Labels layout
        labels_layout = QHBoxLayout()
        labels_layout.addWidget(self.chars_label)
        labels_layout.addWidget(self.dismiss_box, 0, Qt.AlignRight)

        # Dialog buttons
        self.submit_btn = QPushButton(_('Submit to Github'))
        self.submit_btn.setEnabled(False)
        self.submit_btn.clicked.connect(self._submit_to_github)

        self.details_btn = QPushButton(_('Show details'))
        self.details_btn.clicked.connect(self._show_details)

        self.close_btn = QPushButton(_('Close'))

        # Buttons layout
        buttons_layout = QHBoxLayout()
        buttons_layout.addWidget(self.submit_btn)
        buttons_layout.addWidget(self.details_btn)
        buttons_layout.addWidget(self.close_btn)

        # Main layout
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.main_label)
        vlayout.addWidget(self.input_description)
        vlayout.addWidget(self.details)
        vlayout.addLayout(labels_layout)
        vlayout.addLayout(buttons_layout)
        self.setLayout(vlayout)

        self.resize(600, 420)
        self.input_description.setFocus()

    def _submit_to_github(self):
        """Action to take when pressing the submit button."""
        main = self.parent().main

        # Getting description and traceback
        description = self.input_description.toPlainText()
        traceback = self.error_traceback[:-1] # Remove last eol

        # Render issue
        issue_text  = main.render_issue(description=description,
                                        traceback=traceback)

        # Copy issue to clipboard
        QApplication.clipboard().setText(issue_text)

        # Submit issue to Github
        issue_body=("<!--- "
                    "Please paste the contents of your clipboard "
                    "below to complete reporting your problem. "
                    "--->\n\n")
        main.report_issue(body=issue_body,
                          title="Automatic error report")

    def append_traceback(self, text):
        """Append text to the traceback, to be displayed in details."""
        self.error_traceback += text

    def _show_details(self):
        """Show traceback on its own dialog"""
        if self.details.isVisible():
            self.details.hide()
            self.details_btn.setText(_('Show details'))
        else:
            self.resize(600, 550)
            self.details.document().setPlainText('')
            self.details.append_text_to_shell(self.error_traceback,
                                              error=True,
                                              prompt=False)
            self.details.show()
            self.details_btn.setText(_('Hide details'))

    def _description_changed(self):
        """Activate submit_btn if we have a long enough description."""
        chars = len(self.input_description.toPlainText()) - self.initial_chars
        if chars < MIN_CHARS:
            self.chars_label.setText(
                u"{} {}".format(MIN_CHARS - chars,
                                _("more characters to go...")))
        else:
            self.chars_label.setText(_("Ready to submit! Thanks!"))
        self.submit_btn.setEnabled(chars >= MIN_CHARS)


def test():
    from spyder.utils.qthelpers import qapplication
    app = qapplication()
    dlg = SpyderErrorDialog()
    dlg.show()
    sys.exit(dlg.exec_())


if __name__ == "__main__":
    test()
# -*- coding: utf-8 -*- # # Copyright © Spyder Project Contributors # Licensed under the terms of the MIT License # (see spyder/__init__.py for details) Report Error Dialog # Stdlib imports # Third party imports # Local Imports # Minimum number of characters to introduce in the description # field before being able to send the report to Github. Widget to enter error description. # Editor options # Set font # Header Remove text. Cut text Reimplemented Qt Method to avoid removing the header. Reimplemented Qt Method to not show the context menu. Widget to show errors as they appear in the Internal console. Custom error dialog for error reporting. # To save the traceback sent to the internal console # Dialog main label <b>Spyder has encountered an internal problem</b><hr>               Please enter below a step-by-step description of                your problem (in English). Issue reports without                a clear way to reproduce them will be closed.               <br><br>               <b>Note</b>: You need a Github account for this. # Field to input the description of the problem # Only allow to submit to Github if we have a long enough description # Widget to show errors # Label to show missing chars # Checkbox to dismiss future errors # Labels layout # Dialog buttons # Buttons layout # Main layout Action to take when pressing the submit button. # Getting description and traceback # Remove last eol # Render issue # Copy issue to clipboard # Submit issue to Github Append text to the traceback, to be displayed in details. Show traceback on its own dialog Activate submit_btn if we have a long enough description.