def fatal_error_qt(exc_formatted: str) -> None: """Display a "friendly" dialog box on fatal error using Qt.""" from PyQt5.QtCore import Qt, QUrl from PyQt5.QtGui import QDesktopServices, QIcon from PyQt5.QtWidgets import ( QApplication, QDialog, QDialogButtonBox, QLabel, QTextEdit, QVBoxLayout, ) from nxdrive.translator import Translator from nxdrive.utils import find_icon, find_resource def section(header: str, content: str) -> str: """Format a "section" of information.""" return f"{header}\n```\n{content.strip()}\n```" Translator(find_resource("i18n")) tr = Translator.get app = QApplication([]) app.setQuitOnLastWindowClosed(True) dialog = QDialog() dialog.setWindowTitle(tr("FATAL_ERROR_TITLE", [APP_NAME])) dialog.setWindowIcon(QIcon(str(find_icon("app_icon.svg")))) dialog.resize(800, 600) layout = QVBoxLayout() css = "font-family: monospace; font-size: 12px;" details = [] # Display a little message to apologize info = QLabel(tr("FATAL_ERROR_MSG", [APP_NAME, COMPANY])) info.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) layout.addWidget(info) # Display CLI arguments if sys.argv[1:]: text = tr("FATAL_ERROR_CLI_ARGS") label_cli = QLabel(text) label_cli.setAlignment(Qt.AlignVCenter) cli_args = QTextEdit() cli_args.setStyleSheet(css) cli_args.setReadOnly(True) args = "\n".join(arg for arg in sys.argv[1:]) details.append(section(text, args)) cli_args.setText(args) cli_args.setSizeAdjustPolicy(QTextEdit.AdjustToContents) layout.addWidget(label_cli) layout.addWidget(cli_args) # Display the exception text = tr("FATAL_ERROR_EXCEPTION") label_exc = QLabel(text) label_exc.setAlignment(Qt.AlignVCenter) exception = QTextEdit() exception.setStyleSheet(css) exception.setReadOnly(True) details.append(section(text, exc_formatted)) exception.setText(exc_formatted) layout.addWidget(label_exc) layout.addWidget(exception) # Display last lines from the memory log with suppress(Exception): from nxdrive.report import Report # Last 20th lines raw_lines = Report.export_logs(-20) lines = b"\n".join(raw_lines).decode(errors="replace") if lines: text = tr("FATAL_ERROR_LOGS") label_log = QLabel(text) details.append(section(text, lines)) label_log.setAlignment(Qt.AlignVCenter) layout.addWidget(label_log) logs = QTextEdit() logs.setStyleSheet(css) logs.setReadOnly(True) logs.setLineWrapColumnOrWidth(4096) logs.setLineWrapMode(QTextEdit.FixedPixelWidth) logs.setText(lines) layout.addWidget(logs) def open_update_site() -> None: """Open the update web site.""" with suppress(Exception): QDesktopServices.openUrl(QUrl(Options.update_site_url)) # Buttons buttons = QDialogButtonBox() buttons.setStandardButtons(QDialogButtonBox.Ok) buttons.accepted.connect(dialog.close) update_button = buttons.addButton(tr("FATAL_ERROR_UPDATE_BTN"), QDialogButtonBox.ActionRole) update_button.setToolTip(tr("FATAL_ERROR_UPDATE_TOOLTIP", [APP_NAME])) update_button.clicked.connect(open_update_site) layout.addWidget(buttons) def copy() -> None: """Copy details to the clipboard and change the text of the button. """ osi.cb_set("\n".join(details)) copy_paste.setText(tr("FATAL_ERROR_DETAILS_COPIED")) # "Copy details" button with suppress(Exception): from nxdrive.osi import AbstractOSIntegration osi = AbstractOSIntegration.get(None) copy_paste = buttons.addButton(tr("FATAL_ERROR_DETAILS_COPY"), QDialogButtonBox.ActionRole) copy_paste.clicked.connect(copy) dialog.setLayout(layout) dialog.show() app.exec_()
def show_critical_error() -> None: """ Display a "friendly" dialog box on fatal error. """ import traceback from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import ( QApplication, QDialog, QDialogButtonBox, QLabel, QTextEdit, QVBoxLayout, ) app = QApplication([]) app.setQuitOnLastWindowClosed(True) dialog = QDialog() dialog.setWindowTitle("Nuxeo Drive - Fatal error") dialog.resize(600, 400) layout = QVBoxLayout() css = "font-family: monospace; font-size: 12px;" details = ["Exception:"] with suppress(Exception): from nxdrive.utils import find_icon dialog.setWindowIcon(QIcon(find_icon("app_icon.svg"))) # Display a little message to apologize text = f"""Ooops! Unfortunately, a fatal error occurred and Nuxeo Drive has stopped. Please share the following informations with Nuxeo support : we’ll do our best to fix it! """ info = QLabel(text) info.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) layout.addWidget(info) # Display the the exception label_exc = QLabel("Exception:") label_exc.setAlignment(Qt.AlignVCenter) exception = QTextEdit() exception.setStyleSheet(css) exception.setReadOnly(True) exc_formatted = traceback.format_exception(*sys.exc_info()) details += exc_formatted exception.setText("".join(exc_formatted)) layout.addWidget(label_exc) layout.addWidget(exception) # Display last lines from the memory log with suppress(Exception): from nxdrive.report import Report # Last 20th lines lines = Report.export_logs(-20) lines = b"\n".join(lines).decode(errors="replace") details += ["Logs before the crash:", lines] label_log = QLabel("Logs before the crash:") label_log.setAlignment(Qt.AlignVCenter) layout.addWidget(label_log) logs = QTextEdit() logs.setStyleSheet(css) logs.setReadOnly(True) logs.setLineWrapColumnOrWidth(4096) logs.setLineWrapMode(QTextEdit.FixedPixelWidth) logs.setText(lines) layout.addWidget(logs) # Buttons buttons = QDialogButtonBox() buttons.setStandardButtons(QDialogButtonBox.Ok) buttons.accepted.connect(dialog.close) layout.addWidget(buttons) def copy() -> None: """Copy details to the clipboard and change the text of the button. """ copy_to_clipboard("\n".join(details)) copy_paste.setText("Details copied!") # "Copy details" button with suppress(Exception): from nxdrive.utils import copy_to_clipboard copy_paste = buttons.addButton("Copy details", QDialogButtonBox.ActionRole) copy_paste.clicked.connect(copy) dialog.setLayout(layout) dialog.show() app.exec_()