def _key_release_cb(self, widget, event): """Application-wide key release handler.""" consumed = widget.propagate_key_event(event) if consumed: return True if not self.enabled: return def released(hardware_keycode): action = self.pressed[hardware_keycode] del self.pressed[hardware_keycode] if action.keyup_callback: action.keyup_callback(widget, event) action.keyup_callback = None if event.keyval == Gdk.KEY_Escape: # emergency exit in case of bugs for hardware_keycode in list(self.pressed.keys()): released(hardware_keycode) # Pop all stacked modes; they should release grabs self.app.doc.modes.reset() # Just in case... Gdk.pointer_ungrab(event.time) else: # note: event.keyval would not be suited for this because # it can be different from the one we have seen in # key_press_cb if the user has released a modifier first if event.hardware_keycode in self.pressed: released(event.hardware_keycode) return True # Fallthru handler: dispatch doc-specific stuff. return self._dispatch_fallthru_key_release_event(widget, event)
def _info(exctyp, value, tb): global exception_dialog_active if exctyp is KeyboardInterrupt: return original_excepthook(exctyp, value, tb) sys.stderr.write(analyse_simple(exctyp, value, tb).getvalue()) if exception_dialog_active: return Gdk.pointer_ungrab(Gdk.CURRENT_TIME) Gdk.keyboard_ungrab(Gdk.CURRENT_TIME) exception_dialog_active = True # Create the dialog dialog = Gtk.MessageDialog(message_type=Gtk.MessageType.WARNING) dialog.set_title(_("Bug Detected")) primary = _( "<big><b>A programming error has been detected.</b></big>" ) secondary = _( "You may be able to ignore this error and carry on working, " "but you should probably save your work soon.\n\n" "Please tell the developers about this using the issue tracker " "if no-one else has reported it yet." ) dialog.set_markup(primary) dialog.format_secondary_text(secondary) dialog.add_button(_(u"Search Tracker…"), RESPONSE_SEARCH) if "-" in lib.meta.MYPAINT_VERSION: # only development and prereleases dialog.add_button(_("Report…"), RESPONSE_REPORT) dialog.set_response_sensitive(RESPONSE_REPORT, False) dialog.add_button(_("Ignore Error"), Gtk.ResponseType.CLOSE) dialog.add_button(_("Quit MyPaint"), RESPONSE_QUIT) # Add an expander with details of the problem to the dialog def expander_cb(expander, *ignore): # Ensures that on deactivating the expander, the dialog is resized down if expander.get_expanded(): dialog.set_resizable(True) else: dialog.set_resizable(False) details_expander = Gtk.Expander() details_expander.set_label(_(u"Details…")) details_expander.connect("notify::expanded", expander_cb) textview = Gtk.TextView() textview.show() textview.set_editable(False) textview.modify_font(Pango.FontDescription("Monospace normal")) sw = Gtk.ScrolledWindow() sw.show() sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) sw.add(textview) # Set window sizing so that it's always at least 600 pixels wide, and # increases by 300 pixels in height once the details panel is open sw.set_size_request(0, 300) dialog.set_size_request(600, 0) details_expander.add(sw) details_expander.show_all() dialog.get_content_area().pack_start(details_expander, True, True, 0) # Get the traceback and set contents of the details try: trace = analyse(exctyp, value, tb).getvalue() except: try: trace = _("Exception while analyzing the exception.") + "\n" trace += analyse_simple(exctyp, value, tb).getvalue() except: trace = _("Exception while analyzing the exception.") buf = textview.get_buffer() trace = "\n".join(["```python", trace, "```"]) buf.set_text(trace) ## Would be nice to scroll to the bottom automatically, but @#&%*@ #first, last = buf.get_bounds() #buf.place_cursor(last) #mark = buf.get_insert() ##buf.scroll_mark_onscreen() ##textview.scroll_mark_onscreen(buf.get_insert(), 0) #textview.scroll_to_mark(mark, 0.0) # Connect callback and present the dialog dialog.connect('response', _dialog_response_cb, trace, exctyp, value) #dialog.set_modal(True) # this might actually be contra-productive... dialog.show()