From 5c4ab7a5922ed65b347c2989eb4c714ea2b3d9d2 Mon Sep 17 00:00:00 2001 From: David Vierra Date: Wed, 8 Jul 2015 13:32:36 -1000 Subject: [PATCH] Add an error dialog and use it to show plugin load/unload errors. Will be used later for errors in plugin execution and general errors from signal handlers and etc. "Copy to PasteBin" still not implemented. --- src/mcedit2/dialogs/error_dialog.py | 54 ++++++++++ src/mcedit2/dialogs/plugins_dialog.py | 9 +- src/mcedit2/editorapp.py | 10 +- src/mcedit2/plugins.py | 9 +- src/mcedit2/ui/error_dialog.ui | 136 ++++++++++++++++++++++++++ src/mcedit2/util/qglcontext.py | 8 ++ 6 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 src/mcedit2/dialogs/error_dialog.py create mode 100644 src/mcedit2/ui/error_dialog.ui diff --git a/src/mcedit2/dialogs/error_dialog.py b/src/mcedit2/dialogs/error_dialog.py new file mode 100644 index 0000000..34bc5ef --- /dev/null +++ b/src/mcedit2/dialogs/error_dialog.py @@ -0,0 +1,54 @@ +""" + error_dialog +""" +from __future__ import absolute_import, division, print_function, unicode_literals +import logging +import traceback +import sys +import platform + +from PySide import QtGui +from mcedit2.util import qglcontext + +from mcedit2.util.load_ui import load_ui + +log = logging.getLogger(__name__) + +def showErrorDialog(text, tb, fatal): + dialog = ErrorDialog(text, tb, fatal) + dialog.exec_() + +class ErrorDialog(QtGui.QDialog): + """ + A dialog for displaying an error traceback when something goes wrong. + + Used to report compile and run errors for plugin modules and classes, and might be + used to report errors in MCEdit itself during signal or event handlers. + """ + def __init__(self, text, exc_info, fatal): + super(ErrorDialog, self).__init__() + load_ui("error_dialog.ui", baseinstance=self) + + exc_type, exc_value, exc_tb = exc_info + + self.errorDescriptionLabel.setText(text) + tbText = "".join(traceback.format_exception(exc_type, exc_value, exc_tb)) + + contextInfo = qglcontext.getContextInfo() or "" + + from mcedit2 import __version__ + tbText = "MCEdit version: %s\n" \ + "Python version: %s\n" \ + "Platform: %s\n" \ + "System version: %s\n" \ + "Processor: %s\n" \ + "\n" \ + "%s\n" \ + "------\n\n" \ + "%s" % (__version__, sys.version, sys.platform, + platform.platform(), platform.processor(), + contextInfo, tbText) + self.tracebackView.setText(tbText) + + self.restartMCEditLabel.setVisible(fatal) + diff --git a/src/mcedit2/dialogs/plugins_dialog.py b/src/mcedit2/dialogs/plugins_dialog.py index c5f6a48..27b3f94 100644 --- a/src/mcedit2/dialogs/plugins_dialog.py +++ b/src/mcedit2/dialogs/plugins_dialog.py @@ -6,6 +6,7 @@ import logging from PySide import QtGui, QtCore from PySide.QtCore import Qt from mcedit2 import plugins +from mcedit2.dialogs.error_dialog import showErrorDialog from mcedit2.util.load_ui import load_ui from mcedit2.util.resources import resourcePath @@ -77,9 +78,13 @@ class PluginsTableModel(QtCore.QAbstractTableModel): pluginRef.enabled = value if value: - pluginRef.load() + if not pluginRef.load(): + showErrorDialog("%s while loading plugin \"%s\"" % (pluginRef.loadError[0].__name__, pluginRef.displayName), pluginRef.loadError, False) + else: - pluginRef.unload() + if not pluginRef.unload(): + showErrorDialog("%s while unloading plugin \"%s\"" % (pluginRef.unloadError[0].__name__, pluginRef.displayName), pluginRef.unloadError, False) + self.dataChanged.emit(index, index) diff --git a/src/mcedit2/editorapp.py b/src/mcedit2/editorapp.py index 13d998e..1c3f718 100644 --- a/src/mcedit2/editorapp.py +++ b/src/mcedit2/editorapp.py @@ -12,6 +12,7 @@ import numpy import sys from mcedit2 import plugins from mcedit2.appsettings import RecentFilesSetting, EnableLightingSetting, DevModeSetting +from mcedit2.dialogs.error_dialog import showErrorDialog from mcedit2.dialogs.plugins_dialog import PluginsDialog from mcedit2.library import LibraryWidget @@ -339,7 +340,8 @@ class MCEditApp(QtGui.QApplication): for pluginRef in plugins.getAllPlugins(): if pluginRef.enabled: - pluginRef.load() + if not pluginRef.load(): + showErrorDialog("%s while loading plugin \"%s\"" % (pluginRef.loadError[0].__name__, pluginRef.displayName), pluginRef.loadError, False) log.info("Opening worlds from command line.") @@ -956,5 +958,7 @@ class MCEditApp(QtGui.QApplication): for pluginRef in plugins.getAllPlugins(): if pluginRef.checkTimestamps(): log.info("Plugin %s changed. Reloading plugin module...", pluginRef.displayName) - pluginRef.unload() - pluginRef.load() + if not pluginRef.unload(): + showErrorDialog("%s while unloading plugin \"%s\"" % (pluginRef.unloadError[0].__name__, pluginRef.displayName), pluginRef.unloadError, False) + if not pluginRef.load(): + showErrorDialog("%s while loading plugin \"%s\"" % (pluginRef.loadError[0].__name__, pluginRef.displayName), pluginRef.loadError, False) diff --git a/src/mcedit2/plugins.py b/src/mcedit2/plugins.py index 887b6c8..0cc571f 100644 --- a/src/mcedit2/plugins.py +++ b/src/mcedit2/plugins.py @@ -121,14 +121,17 @@ class PluginRef(object): log.info("Loaded %s (%s)", self.filename, self.displayName) except Exception as e: - self.loadError = traceback.format_exc() + self.loadError = sys.exc_info() log.exception("Error while loading plugin from %s: %r", self.filename, e) + return False else: self.loadError = None finally: if io: io.close() + return True + def unload(self): if self.pluginModule is None: return @@ -139,12 +142,14 @@ class PluginRef(object): sys.modules.pop(k) break except Exception as e: - self.unloadError = traceback.format_exc() + self.loadError = sys.exc_info() log.exception("Error while unloading plugin from %s: %r", self.filename, e) + return False else: self.unloadError = None self.pluginModule = None + return True @property def isLoaded(self): diff --git a/src/mcedit2/ui/error_dialog.ui b/src/mcedit2/ui/error_dialog.ui new file mode 100644 index 0000000..a251a50 --- /dev/null +++ b/src/mcedit2/ui/error_dialog.ui @@ -0,0 +1,136 @@ + + + Dialog + + + + 0 + 0 + 690 + 550 + + + + MCEdit Error + + + + + + + + <html><head/><body><p><span style=" font-size:12pt;">An Error Has Occurred:</span></p></body></html> + + + Qt::RichText + + + false + + + + + + + + 1 + 0 + + + + (no error yet!) + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + + It is recommended to undo the last command, save your work, and restart MCEdit. + + + + + + + Error Details: + + + + + + + + + + Send this error report to the plugin or application developer to help get it fixed. + + + + + + + Clicking "Copy to PasteBin" will copy a Pastebin URL to your clipboard. + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy to PasteBin + + + + + + + Continue + + + true + + + + + + + + + + + continueButton + clicked() + Dialog + accept() + + + 650 + 523 + + + 505 + 5 + + + + + diff --git a/src/mcedit2/util/qglcontext.py b/src/mcedit2/util/qglcontext.py index 9487c9b..05f1579 100644 --- a/src/mcedit2/util/qglcontext.py +++ b/src/mcedit2/util/qglcontext.py @@ -9,6 +9,11 @@ import re log = logging.getLogger(__name__) +_lastAcquiredContextInfo = None + +def getContextInfo(): + return _lastAcquiredContextInfo + def setDefaultFormat(): oglFormat = QtOpenGL.QGLFormat() oglFormat.setVersion(1, 3) @@ -17,6 +22,7 @@ def setDefaultFormat(): def validateQGLContext(context): context.makeCurrent() + versionFlags = QtOpenGL.QGLFormat.openGLVersionFlags() log.info("OpenGL Version Info:") for flag in ( @@ -61,6 +67,8 @@ def validateQGLContext(context): detailedText += "GL_RENDERER: %s\n" % GL.glGetString(GL.GL_RENDERER) log.info("%s", detailedText) + global _lastAcquiredContextInfo + _lastAcquiredContextInfo = detailedText if (not context.isValid() or not actualFormat.directRendering() or not versionFlags & QtOpenGL.QGLFormat.OpenGL_Version_1_3