From fd4405cc794d6fd3d560f06cfbaecca08e16a51e Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Wed, 22 May 2019 18:02:59 +0200 Subject: pythonconsole plugin: change source code for Python 2 & 3 compatibility. Also drop mateconf and use gsettings for preferences. Preferences are now triggered as a PeasGtk.Configurable. --- configure.ac | 1 + plugins/pythonconsole/Makefile.am | 19 +- ...mate.pluma.plugins.pythonconsole.gschema.xml.in | 30 +++ plugins/pythonconsole/pythonconsole/__init__.py | 37 ++-- plugins/pythonconsole/pythonconsole/config.py | 158 ++++++++------- plugins/pythonconsole/pythonconsole/config.ui | 224 ++++++++++----------- plugins/pythonconsole/pythonconsole/console.py | 68 +++++-- 7 files changed, 304 insertions(+), 233 deletions(-) create mode 100644 plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in diff --git a/configure.ac b/configure.ac index 1cdbb7e8..ceb9a5cd 100644 --- a/configure.ac +++ b/configure.ac @@ -250,6 +250,7 @@ plugins/filebrowser/org.mate.pluma.plugins.filebrowser.gschema.xml plugins/modelines/Makefile plugins/pythonconsole/Makefile plugins/pythonconsole/pythonconsole/Makefile +plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml plugins/quickopen/Makefile plugins/quickopen/quickopen/Makefile plugins/snippets/data/lang/Makefile diff --git a/plugins/pythonconsole/Makefile.am b/plugins/pythonconsole/Makefile.am index 0a9ff965..51a2e4a4 100644 --- a/plugins/pythonconsole/Makefile.am +++ b/plugins/pythonconsole/Makefile.am @@ -7,9 +7,22 @@ plugin_in_files = pythonconsole.plugin.desktop.in plugin_DATA = $(plugin_in_files:.plugin.desktop.in=.plugin) -EXTRA_DIST = $(plugin_in_files) +pythonconsole_gschema_in = org.mate.pluma.plugins.pythonconsole.gschema.xml.in +gsettings_SCHEMAS = $(pythonconsole_gschema_in:.xml.in=.xml) +@GSETTINGS_RULES@ -CLEANFILES = $(plugin_DATA) -DISTCLEANFILES = $(plugin_DATA) +EXTRA_DIST = \ + $(plugin_in_files) \ + $(pythonconsole_gschema_in) + +CLEANFILES = \ + $(plugin_DATA) \ + $(gsettings_SCHEMAS_in) \ + $(gsettings_SCHEMAS) + +DISTCLEANFILES = \ + $(plugin_DATA) \ + $(gsettings_SCHEMAS_in) \ + $(gsettings_SCHEMAS) -include $(top_srcdir)/git.mk diff --git a/plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in b/plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in new file mode 100644 index 00000000..7897fa91 --- /dev/null +++ b/plugins/pythonconsole/org.mate.pluma.plugins.pythonconsole.gschema.xml.in @@ -0,0 +1,30 @@ + + + + '#314e6c' + Command Color Text + The command color text + + + '#990000' + Error Color Text + The error color text + + + true + Whether to use the system font + + If true, the terminal will use the desktop-global standard + font if it’s monospace (and the most similar font it can + come up with otherwise). + + + + 'Monospace 10' + Font + + A Pango font name. Examples are “Sans 12” or “Monospace Bold 14”. + + + + diff --git a/plugins/pythonconsole/pythonconsole/__init__.py b/plugins/pythonconsole/pythonconsole/__init__.py index 07d13c70..171b3672 100755 --- a/plugins/pythonconsole/pythonconsole/__init__.py +++ b/plugins/pythonconsole/pythonconsole/__init__.py @@ -24,22 +24,22 @@ # Bits from pluma Python Console Plugin # Copyrignt (C), 2005 Raphaël Slinckx -from gi.repository import GObject, Gtk, Peas, Pluma +from gi.repository import GObject, Gtk, Peas, PeasGtk, Pluma -from console import PythonConsole -from config import PythonConsoleConfigDialog -from config import PythonConsoleConfig +from .console import PythonConsole +from .config import PythonConsoleConfigWidget +from .config import PythonConsoleConfig PYTHON_ICON = 'text-x-python' -class PythonConsolePlugin(GObject.Object, Peas.Activatable): +class PythonConsolePlugin(GObject.Object, Peas.Activatable, PeasGtk.Configurable): __gtype_name__ = "PythonConsolePlugin" object = GObject.Property(type=GObject.Object) def __init__(self): GObject.Object.__init__(self) - self.dlg = None + self.config_widget = None def do_activate(self): window = self.object @@ -47,8 +47,8 @@ class PythonConsolePlugin(GObject.Object, Peas.Activatable): self._console = PythonConsole(namespace = {'__builtins__' : __builtins__, 'pluma' : Pluma, 'window' : window}) - self._console.eval('print "You can access the main window through ' \ - '\'window\' :\\n%s" % window', False) + self._console.eval('print("You can access the main window through ' \ + '\'window\' :\\n%s" % window)', False) bottom = window.get_bottom_panel() image = Gtk.Image() image.set_from_icon_name(PYTHON_ICON, Gtk.IconSize.MENU) @@ -61,22 +61,9 @@ class PythonConsolePlugin(GObject.Object, Peas.Activatable): bottom = window.get_bottom_panel() bottom.remove_item(self._console) -def create_configure_dialog(self): - - if not self.dlg: - self.dlg = PythonConsoleConfigDialog(self.get_data_dir()) - - dialog = self.dlg.dialog() - window = pluma.app_get_default().get_active_window() - if window: - dialog.set_transient_for(window) - - return dialog - -# Here we dynamically insert create_configure_dialog based on if configuration -# is enabled. This has to be done like this because pluma checks if a plugin -# is configurable solely on the fact that it has this member defined or not -if PythonConsoleConfig.enabled(): - PythonConsolePlugin.create_configure_dialog = create_configure_dialog + def do_create_configure_widget(self): + if not self.config_widget: + self.config_widget = PythonConsoleConfigWidget(self.plugin_info.get_data_dir()) + return self.config_widget.configure_widget() # ex:et:ts=4: diff --git a/plugins/pythonconsole/pythonconsole/config.py b/plugins/pythonconsole/pythonconsole/config.py index fce0c9d8..f973e387 100755 --- a/plugins/pythonconsole/pythonconsole/config.py +++ b/plugins/pythonconsole/pythonconsole/config.py @@ -25,110 +25,126 @@ # Copyrignt (C), 2005 Raphaël Slinckx import os -import gtk +from gi.repository import Gio, Gtk, Gdk -__all__ = ('PythonConsoleConfig', 'PythonConsoleConfigDialog') - -MATECONF_KEY_BASE = '/apps/pluma/plugins/pythonconsole' -MATECONF_KEY_COMMAND_COLOR = MATECONF_KEY_BASE + '/command-color' -MATECONF_KEY_ERROR_COLOR = MATECONF_KEY_BASE + '/error-color' - -DEFAULT_COMMAND_COLOR = '#314e6c' # Blue Shadow -DEFAULT_ERROR_COLOR = '#990000' # Accent Red Dark +__all__ = ('PythonConsoleConfig', 'PythonConsoleConfigWidget') class PythonConsoleConfig(object): - try: - import mateconf - except ImportError: - mateconf = None - - def __init__(self): - pass - @staticmethod - def enabled(): - return PythonConsoleConfig.mateconf != None + CONSOLE_KEY_BASE = 'org.mate.pluma.plugins.pythonconsole' + CONSOLE_KEY_COMMAND_COLOR = 'command-color' + CONSOLE_KEY_ERROR_COLOR = 'error-color' + CONSOLE_KEY_USE_SYSTEM_FONT = 'use-system-font' + CONSOLE_KEY_FONT = 'font' - @staticmethod - def add_handler(handler): - if PythonConsoleConfig.mateconf: - PythonConsoleConfig.mateconf.client_get_default().notify_add(MATECONF_KEY_BASE, handler) + INTERFACE_KEY_BASE = 'org.mate.interface' + INTERFACE_KEY_MONOSPACE_FONT_NAME = 'monospace-font-name' color_command = property( - lambda self: self.mateconf_get_str(MATECONF_KEY_COMMAND_COLOR, DEFAULT_COMMAND_COLOR), - lambda self, value: self.mateconf_set_str(MATECONF_KEY_COMMAND_COLOR, value)) + lambda self: self.console_settings.get_string(self.CONSOLE_KEY_COMMAND_COLOR), + lambda self, value: self.console_settings.set_string(self.CONSOLE_KEY_COMMAND_COLOR, value) + ) color_error = property( - lambda self: self.mateconf_get_str(MATECONF_KEY_ERROR_COLOR, DEFAULT_ERROR_COLOR), - lambda self, value: self.mateconf_set_str(MATECONF_KEY_ERROR_COLOR, value)) + lambda self: self.console_settings.get_string(self.CONSOLE_KEY_ERROR_COLOR), + lambda self, value: self.console_settings.set_string(self.CONSOLE_KEY_ERROR_COLOR, value) + ) - @staticmethod - def mateconf_get_str(key, default=''): - if not PythonConsoleConfig.mateconf: - return default + use_system_font = property( + lambda self: self.console_settings.get_boolean(self.CONSOLE_KEY_USE_SYSTEM_FONT), + lambda self, value: self.console_settings.set_boolean(self.CONSOLE_KEY_USE_SYSTEM_FONT, value) + ) - val = PythonConsoleConfig.mateconf.client_get_default().get(key) - if val is not None and val.type == mateconf.VALUE_STRING: - return val.get_string() - else: - return default + font = property( + lambda self: self.console_settings.get_string(self.CONSOLE_KEY_FONT), + lambda self, value: self.console_settings.set_string(self.CONSOLE_KEY_FONT, value) + ) - @staticmethod - def mateconf_set_str(key, value): - if not PythonConsoleConfig.mateconf: - return + monospace_font_name = property( + lambda self: self.interface_settings.get_string(self.INTERFACE_KEY_MONOSPACE_FONT_NAME) + ) + + console_settings = Gio.Settings.new(CONSOLE_KEY_BASE) + interface_settings = Gio.Settings.new(INTERFACE_KEY_BASE) + + def __init__(self): + object.__init__(self) + + @classmethod + def enabled(self): + return self.console_settings != None + + @classmethod + def add_handler(self, handler): + self.console_settings.connect("changed", handler) + self.interface_settings.connect("changed", handler) - v = PythonConsoleConfig.mateconf.Value(mateconf.VALUE_STRING) - v.set_string(value) - PythonConsoleConfig.mateconf.client_get_default().set(key, v) -class PythonConsoleConfigDialog(object): +class PythonConsoleConfigWidget(object): + + CONSOLE_KEY_BASE = 'org.mate.pluma.plugins.pythonconsole' + CONSOLE_KEY_COMMAND_COLOR = 'command-color' + CONSOLE_KEY_ERROR_COLOR = 'error-color' def __init__(self, datadir): object.__init__(self) - self._dialog = None + self._widget = None self._ui_path = os.path.join(datadir, 'ui', 'config.ui') - self.config = PythonConsoleConfig() + self._config = PythonConsoleConfig() + self._ui = Gtk.Builder() - def dialog(self): - if self._dialog is None: - self._ui = gtk.Builder() + def configure_widget(self): + if self._widget is None: self._ui.add_from_file(self._ui_path) self.set_colorbutton_color(self._ui.get_object('colorbutton-command'), - self.config.color_command) + self._config.color_command) self.set_colorbutton_color(self._ui.get_object('colorbutton-error'), - self.config.color_error) - + self._config.color_error) + checkbox = self._ui.get_object('checkbox-system-font') + checkbox.set_active(self._config.use_system_font) + self._fontbutton = self._ui.get_object('fontbutton-font') + self._fontbutton.set_font_name(self._config.font) + self.on_checkbox_system_font_toggled(checkbox) self._ui.connect_signals(self) - self._dialog = self._ui.get_object('dialog-config') - self._dialog.show_all() - else: - self._dialog.present() + self._widget = self._ui.get_object('widget-config') + self._widget.show_all() - return self._dialog + return self._widget @staticmethod def set_colorbutton_color(colorbutton, value): - try: - color = gtk.gdk.color_parse(value) - except ValueError: - pass # Default color in config.ui used - else: - colorbutton.set_color(color) + rgba = Gdk.RGBA() + parsed = rgba.parse(value) - def on_dialog_config_response(self, dialog, response_id): - self._dialog.destroy() - - def on_dialog_config_destroy(self, dialog): - self._dialog = None - self._ui = None + if parsed: + colorbutton.set_rgba(rgba) def on_colorbutton_command_color_set(self, colorbutton): - self.config.color_command = colorbutton.get_color().to_string() + self._config.color_command = colorbutton.get_color().to_string() def on_colorbutton_error_color_set(self, colorbutton): - self.config.color_error = colorbutton.get_color().to_string() + self._config.color_error = colorbutton.get_color().to_string() + + def on_checkbox_system_font_toggled(self, checkbox): + val = checkbox.get_active() + self._config.use_system_font = val + self._fontbutton.set_sensitive(not val) + + def on_fontbutton_font_set(self, fontbutton): + self._config.font = fontbutton.get_font_name() + + def on_widget_config_parent_set(self, widget, oldparent): + # Set icon in dialog close button. + try: + actionarea = widget.get_toplevel().get_action_area() + image = Gtk.Image.new_from_icon_name("window-close", + Gtk.IconSize.BUTTON) + for button in actionarea.get_children(): + button.set_image(image) + button.set_property("always-show-image", True) + except: + pass # ex:et:ts=4: diff --git a/plugins/pythonconsole/pythonconsole/config.ui b/plugins/pythonconsole/pythonconsole/config.ui index 8d337d6b..392be7d7 100644 --- a/plugins/pythonconsole/pythonconsole/config.ui +++ b/plugins/pythonconsole/pythonconsole/config.ui @@ -1,126 +1,124 @@ - + - + True False - window-close - - - False - center-on-parent - True - dialog - - - - + 6 + 6 + 6 + 6 + vertical + 6 + 6 + + + + True + False + _Error color: + True + 0 + + + 0 + 1 + + + + + True + True + True + rgb(153,0,0) + + + + 1 + 1 + + + + + True + True + True + rgb(49,78,108) + + + + 1 + 0 + + + + True False - - - True - False - end - - - _Close - True - True - True - image1 - True - - - True - True - 0 - - - - - False - False - end - 0 - - - - - True - False - 6 - 2 - 2 - 6 - 6 - - - True - False - C_ommand color: - True - colorbutton-command - 0 - - - - - True - False - _Error color: - True - colorbutton-error - 0 - - - 1 - 2 - - - - - True - True - True - #31314e4e6c6c - - - - 1 - 2 - - - - - True - True - True - #999900000000 - - - - 1 - 2 - 1 - 2 - - - - - False - True - 1 - - + C_ommand color: + True + 0 + + 0 + 0 + - - button1 - - + + True + False + True + + + 0 + 2 + 2 + + + + + Use system fixed width font + True + True + False + start + True + True + + + + 0 + 3 + 2 + + + + + True + False + start + Font: + + + 0 + 4 + + + + + True + True + True + Sans 12 + + + + + 1 + 4 + diff --git a/plugins/pythonconsole/pythonconsole/console.py b/plugins/pythonconsole/pythonconsole/console.py index 75f60e4d..0fd9c7c2 100755 --- a/plugins/pythonconsole/pythonconsole/console.py +++ b/plugins/pythonconsole/pythonconsole/console.py @@ -30,7 +30,7 @@ import re import traceback from gi.repository import GObject, Gdk, Gtk, Pango -from config import PythonConsoleConfig +from .config import PythonConsoleConfig __all__ = ('PythonConsole', 'OutFile') @@ -40,13 +40,14 @@ class PythonConsole(Gtk.ScrolledWindow): 'grab-focus' : 'override', } + DEFAULT_FONT = "Monospace 10" + def __init__(self, namespace = {}): Gtk.ScrolledWindow.__init__(self) self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.set_shadow_type(Gtk.ShadowType.IN) self.view = Gtk.TextView() - self.view.modify_font(Pango.font_description_from_string('Monospace')) self.view.set_editable(True) self.view.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) self.add(self.view) @@ -57,7 +58,8 @@ class PythonConsole(Gtk.ScrolledWindow): self.error = buffer.create_tag("error") self.command = buffer.create_tag("command") - PythonConsoleConfig.add_handler(self.apply_preferences) + self.config = PythonConsoleConfig() + self.config.add_handler(self.apply_preferences) self.apply_preferences() self.__spaces_pattern = re.compile(r'^\s+') @@ -88,9 +90,28 @@ class PythonConsole(Gtk.ScrolledWindow): self.view.grab_focus() def apply_preferences(self, *args): - config = PythonConsoleConfig() - self.error.set_property("foreground", config.color_error) - self.command.set_property("foreground", config.color_command) + self.error.set_property("foreground", self.config.color_error) + self.command.set_property("foreground", self.config.color_command) + + if self.config.use_system_font: + font_name = self.config.monospace_font_name + else: + font_name = self.config.font + + font_desc = None + try: + font_desc = Pango.FontDescription(font_name) + except: + try: + font_desc = Pango.FontDescription(self.config.monospace_font_name) + except: + try: + font_desc = Pango.FontDescription(self.DEFAULT_FONT) + except: + pass + + if font_desc: + self.view.modify_font(font_desc) def stop(self): self.namespace = None @@ -98,11 +119,13 @@ class PythonConsole(Gtk.ScrolledWindow): def __key_press_event_cb(self, view, event): modifier_mask = Gtk.accelerator_get_default_mod_mask() event_state = event.state & modifier_mask + keyname = Gdk.keyval_name(event.keyval) - if event.keyval == Gdk.KEY_D and event_state == Gdk.ModifierType.CONTROL_MASK: + if keyname == "d" and event_state == Gdk.ModifierType.CONTROL_MASK: self.destroy() - elif event.keyval == Gdk.KEY_Return and event_state == Gdk.ModifierType.CONTROL_MASK: + elif keyname == "Return" and \ + event_state == Gdk.ModifierType.CONTROL_MASK: # Get the command buffer = view.get_buffer() inp_mark = buffer.get_mark("input") @@ -128,7 +151,7 @@ class PythonConsole(Gtk.ScrolledWindow): GObject.idle_add(self.scroll_to_end) return True - elif event.keyval == Gdk.KEY_Return: + elif keyname == "Return": # Get the marks buffer = view.get_buffer() lin_mark = buffer.get_mark("input-line") @@ -172,22 +195,22 @@ class PythonConsole(Gtk.ScrolledWindow): GObject.idle_add(self.scroll_to_end) return True - elif event.keyval == Gdk.KEY_KP_Down or event.keyval == Gdk.KEY_Down: + elif keyname == "KP_Down" or keyname == "Down": # Next entry from history view.emit_stop_by_name("key_press_event") self.history_down() GObject.idle_add(self.scroll_to_end) return True - elif event.keyval == Gdk.KEY_KP_Up or event.keyval == Gdk.KEY_Up: + elif keyname == "KP_Up" or keyname == "Up": # Previous entry from history view.emit_stop_by_name("key_press_event") self.history_up() GObject.idle_add(self.scroll_to_end) return True - elif event.keyval == Gdk.KEY_KP_Left or event.keyval == Gdk.KEY_Left or \ - event.keyval == Gdk.KEY_BackSpace: + elif keyname == "KP_Left" or keyname == "Left" or \ + keyname == "BackSpace": buffer = view.get_buffer() inp = buffer.get_iter_at_mark(buffer.get_mark("input")) cur = buffer.get_iter_at_mark(buffer.get_insert()) @@ -200,7 +223,7 @@ class PythonConsole(Gtk.ScrolledWindow): # For the console we enable smart/home end behavior incoditionally # since it is useful when editing python - elif (event.keyval == Gdk.KEY_KP_Home or event.keyval == Gdk.KEY_Home) and \ + elif (keyname == "KP_Home" or keyname == "Home") and \ event_state == event_state & (Gdk.ModifierType.SHIFT_MASK|Gdk.ModifierType.CONTROL_MASK): # Go to the begin of the command instead of the begin of the line buffer = view.get_buffer() @@ -219,7 +242,7 @@ class PythonConsole(Gtk.ScrolledWindow): buffer.place_cursor(iter) return True - elif (event.keyval == Gdk.KEY_KP_End or event.keyval == Gdk.KEY_End) and \ + elif (keyname == "KP_End" or keyname == "End") and \ event_state == event_state & (Gdk.ModifierType.SHIFT_MASK|Gdk.ModifierType.CONTROL_MASK): buffer = view.get_buffer() @@ -323,15 +346,18 @@ class PythonConsole(Gtk.ScrolledWindow): # eval and exec are broken in how they deal with utf8-encoded # strings so we have to explicitly decode the command before # passing it along - command = command.decode('utf8') + try: + command = command.decode('utf8') + except: + pass try: try: r = eval(command, self.namespace, self.namespace) if r is not None: - print `r` + print(repr(r)) except SyntaxError: - exec command in self.namespace + exec(command, self.namespace) except: if hasattr(sys, 'last_type') and sys.last_type == SystemExit: self.destroy() @@ -343,7 +369,7 @@ class PythonConsole(Gtk.ScrolledWindow): def destroy(self): pass - #gtk.ScrolledWindow.destroy(self) + #Gtk.ScrolledWindow.destroy(self) class OutFile: """A fake output file object. It sends output to a TK test widget, @@ -361,8 +387,8 @@ class OutFile: def readlines(self): return [] def write(self, s): self.console.write(s, self.tag) def writelines(self, l): self.console.write(l, self.tag) - def seek(self, a): raise IOError, (29, 'Illegal seek') - def tell(self): raise IOError, (29, 'Illegal seek') + def seek(self, a): raise IOError(29, 'Illegal seek') + def tell(self): raise IOError(29, 'Illegal seek') truncate = tell # ex:et:ts=4: -- cgit v1.2.1