diff --git a/src/virtManager/addhardware.py b/src/virtManager/addhardware.py index 7822d69af..7e25e0b18 100644 --- a/src/virtManager/addhardware.py +++ b/src/virtManager/addhardware.py @@ -1025,15 +1025,20 @@ class vmmAddHardware(gobject.GObject): self.vm.attach_device(self._dev) except Exception, e: logging.debug("Device could not be hotplugged: %s" % str(e)) - attach_err = True + attach_err = (str(e), "".join(traceback.format_exc())) if attach_err: - if not self.err.yes_no(_("Are you sure you want to add this " - "device?"), - _("This device could not be attached to " - "the running machine. Would you like to " - "make the device available after the " - "next VM shutdown?")): + res = self.err.show_err( + _("Are you sure you want to add this device?"), + attach_err[0] + "\n\n" + attach_err[1], + text2=( + _("This device could not be attached to the running machine. " + "Would you like to make the device available after the " + "next VM shutdown?")), + dialog_type=gtk.MESSAGE_WARNING, + buttons=gtk.BUTTONS_YES_NO) + + if not res: return (False, None) # Alter persistent config diff --git a/src/virtManager/details.py b/src/virtManager/details.py index 110187a57..b7764c31b 100644 --- a/src/virtManager/details.py +++ b/src/virtManager/details.py @@ -1597,12 +1597,16 @@ class vmmDetails(gobject.GObject): self.vm.detach_device(dev_id_info) except Exception, e: logging.debug("Device could not be hotUNplugged: %s" % str(e)) - detach_err = True + detach_err = (str(e), "".join(traceback.format_exc())) - if detach_err: - self.err.show_info( - _("Device could not be removed from the running machine"), - _("This change will take effect after the next VM reboot.")) + if not detach_err: + return + + self.err.show_err( + _("Device could not be removed from the running machine"), + detach_err[0] + "\n\n" + detach_err[1], + text2=_("This change will take effect after the next VM reboot."), + dialog_type=gtk.MESSAGE_INFO) # Generic config change helpers def _change_config_helper(self, @@ -1627,7 +1631,7 @@ class vmmDetails(gobject.GObject): hotplug_funcs = listify(hotplug_funcs) hotplug_funcs_args = listify(hotplug_funcs_args) - hotplug_err = False + hotplug_err = [] active = self.vm.is_active() # Hotplug change @@ -1641,7 +1645,8 @@ class vmmDetails(gobject.GObject): except Exception, e: logging.debug("Hotplug failed: func=%s: %s" % (func, str(e))) - hotplug_err = True + hotplug_err.append((str(e), + "".join(traceback.format_exc()))) # Persistent config change try: @@ -1662,11 +1667,21 @@ class vmmDetails(gobject.GObject): if (hotplug_err or (active and not len(hotplug_funcs) == len(define_funcs))): if len(define_funcs) > 1: - self.err.show_info(_("Some changes may require a guest reboot " - "to take effect.")) + msg = _("Some changes may require a guest reboot " + "to take effect.") else: - self.err.show_info(_("These changes will take effect after " - "the next guest reboot.")) + msg = _("These changes will take effect after " + "the next guest reboot.") + + dtype = hotplug_err and gtk.MESSAGE_WARNING or gtk.MESSAGE_INFO + hotplug_msg = "" + for err1, tb in hotplug_err: + hotplug_msg += (err1 + "\n\n" + tb + "\n") + + self.err.show_err(msg, details=hotplug_msg, + buttons=gtk.BUTTONS_YES_NO, + dialog_type=dtype) + return True ######################## diff --git a/src/virtManager/error.py b/src/virtManager/error.py index adce1794c..135cb0654 100644 --- a/src/virtManager/error.py +++ b/src/virtManager/error.py @@ -28,107 +28,64 @@ def safe_set_text(self, text): if not util.safe_set_prop(self, "text", text): self.set_markup(text) +def _launch_dialog(dialog, primary_text, secondary_text, title, + sync=True): + safe_set_text(dialog, primary_text) + dialog.format_secondary_text(secondary_text or None) + dialog.set_title(title) -class vmmErrorDialog (gtk.MessageDialog): + res = False + if sync: + res = dialog.run() + res = bool(res in [gtk.RESPONSE_YES, gtk.RESPONSE_OK]) + dialog.destroy() + else: + def response_destroy(src, ignore): + src.destroy() + dialog.connect("response", response_destroy) + dialog.show() + + return res + +class vmmErrorDialog (object): def __init__ (self, parent=None): - typ = gtk.MESSAGE_ERROR - message_format = _("Unexpected Error") - message_details = _("An unexpected error occurred") - buttons = gtk.BUTTONS_CLOSE - default_title = _("Error") - flags = 0 - - gtk.MessageDialog.__init__ (self, - parent, flags, typ, buttons, - message_format) - - self.val_err_box = None - - self.message_format = message_format - self.message_details = message_details - self.buffer = None - self.default_title = default_title - self.set_title(self.default_title) - self.connect("response", self.response_cb) - self.connect("delete-event", self.hide_on_delete) - - if not message_details is None: - # Expander section with details. - expander = gtk.Expander (_("Details")) - self.buffer = gtk.TextBuffer () - self.buffer.set_text (self.message_details) - sw = gtk.ScrolledWindow () - sw.set_shadow_type (gtk.SHADOW_IN) - sw.set_size_request (400, 240) - sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - details = gtk.TextView (self.buffer) - details.set_editable (False) - details.set_overwrite (False) - details.set_cursor_visible (False) - details.set_wrap_mode (gtk.WRAP_WORD) - sw.add (details) - details.show () - expander.add (sw) - sw.show () - self.vbox.pack_start (expander) - expander.show () + self._parent = parent def set_parent(self, parent): - self.set_transient_for(parent) - - def response_cb(self, src, ignore): - src.hide() - - def show_err(self, summary, details, title=None, - async=True, debug=True): - self.hide() - - if title is None: - title = self.default_title - self.set_title(title) - safe_set_text(self, summary) - self.buffer.set_text(details) + self._parent = parent + def get_parent(self): + return self._parent + def show_err(self, summary, details, title="", + async=True, debug=True, + dialog_type=gtk.MESSAGE_ERROR, + buttons=gtk.BUTTONS_CLOSE, + text2=None): if debug: logging.debug("Uncaught Error: %s : %s" % (summary, details)) - if async: - self.show() - else: - self.run() + dialog = _errorDialog(parent=self.get_parent(), + type=dialog_type, buttons=buttons) + + return dialog.show_dialog(primary_text=summary, + secondary_text=text2, + details=details, title=title, + sync=not async) ################################### # Simple one shot message dialogs # ################################### def _simple_dialog(self, dialog_type, buttons, text1, - text2, title, async=True): - message_box = gtk.MessageDialog(self.get_transient_for(), - gtk.DIALOG_DESTROY_WITH_PARENT, - dialog_type, buttons, - text1) - if title is not None: - message_box.set_title(title) + text2, title, async=False): - if text2 is not None: - message_box.format_secondary_text(text2) + dialog = gtk.MessageDialog(self.get_parent(), + gtk.DIALOG_DESTROY_WITH_PARENT, + type=dialog_type, buttons=buttons) - def response_destroy(src, ignore): - src.destroy() - - if self.val_err_box: - self.val_err_box.destroy() - self.val_err_box = message_box - - self.val_err_box.connect("response", response_destroy) - res = False - if async: - self.val_err_box.show() - else: - res = self.val_err_box.run() - res = bool(res in [gtk.RESPONSE_YES, gtk.RESPONSE_OK]) - - return res + return _launch_dialog(dialog, + text1, text2 or "", title or "", + sync=not async) def val_err(self, text1, text2=None, title=_("Input Error"), async=True): logging.debug("Validation Error: %s" % text1) @@ -166,23 +123,42 @@ class vmmErrorDialog (gtk.MessageDialog): def warn_chkbox(self, text1, text2=None, chktext=None, buttons=None): dtype = gtk.MESSAGE_WARNING buttons = buttons or gtk.BUTTONS_OK_CANCEL - chkbox = _vmmCheckDialog(self.get_transient_for(), dtype, buttons) - return chkbox.show_chkbox(text1, text2, chktext) + chkbox = _errorDialog(parent=self.get_parent(), + type=dtype, + buttons=buttons) + return chkbox.show_dialog(primary_text=text1, + secondary_text=text2, + chktext=chktext) def err_chkbox(self, text1, text2=None, chktext=None, buttons=None): dtype = gtk.MESSAGE_ERROR buttons = buttons or gtk.BUTTONS_OK - chkbox = _vmmCheckDialog(self.get_transient_for(), dtype, buttons) - return chkbox.show_chkbox(text1, text2, chktext) + chkbox = _errorDialog(parent=self.get_parent(), + type=dtype, + buttons=buttons) + return chkbox.show_dialog(primary_text=text1, + secondary_text=text2, + chktext=chktext) -class _vmmCheckDialog (gtk.MessageDialog): - def __init__ (self, parent, typ, buttons): - gtk.MessageDialog.__init__ (self, parent, 0, typ, buttons) - self.connect("response", self.response_cb) - self.connect("delete-event", self.hide_on_delete) +class _errorDialog (gtk.MessageDialog): + """ + Custom error dialog with optional check boxes or details drop down + """ + def __init__ (self, *args, **kwargs): + gtk.MessageDialog.__init__ (self, *args, **kwargs) self.set_title("") + self.chk_vbox = None + self.chk_align = None + self.init_chkbox() + + self.buffer = None + self.buf_expander = None + self.init_details() + + def init_chkbox(self): + # Init check items self.chk_vbox = gtk.VBox(False, False) self.chk_vbox.set_spacing(0) @@ -193,28 +169,51 @@ class _vmmCheckDialog (gtk.MessageDialog): self.chk_align.show_all() self.vbox.pack_start(self.chk_align) - def response_cb(self, src, ignore): - src.hide() + def init_details(self): + # Init details buffer + self.buffer = gtk.TextBuffer() + self.buf_expander = gtk.Expander (_("Details")) + sw = gtk.ScrolledWindow () + sw.set_shadow_type (gtk.SHADOW_IN) + sw.set_size_request (400, 240) + sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + details = gtk.TextView (self.buffer) + details.set_editable (False) + details.set_overwrite (False) + details.set_cursor_visible (False) + details.set_wrap_mode (gtk.WRAP_WORD) + sw.add(details) + self.buf_expander.add(sw) + self.vbox.pack_start(self.buf_expander) + self.buf_expander.show_all() - def show_chkbox(self, text1, text2=None, chktext=None): + def show_dialog(self, primary_text, secondary_text="", + title="", details="", chktext="", + sync=True): chkbox = None res = None + # Hide starting widgets self.hide() + self.buf_expander.hide() for c in self.chk_vbox.get_children(): self.chk_vbox.remove(c) - safe_set_text(self, text1) - - if text2: - self.format_secondary_text(text2) + if details: + self.buffer.set_text(details) + title = title or _("Error") + self.buf_expander.show() if chktext: chkbox = gtk.CheckButton(chktext) self.chk_vbox.add(chkbox) chkbox.show() - res = self.run() in [ gtk.RESPONSE_YES, gtk.RESPONSE_OK ] + res = _launch_dialog(self, + primary_text, secondary_text or "", + title, + sync=sync) + if chktext: res = [res, chkbox.get_active()]