summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/python/scripting/Makefile.am6
-rw-r--r--plugins/python/scripting/core.py43
-rw-r--r--plugins/python/scripting/manager.py53
-rw-r--r--plugins/python/scripting/panel.py140
-rw-r--r--plugins/python/scripting/panel.ui70
-rw-r--r--plugins/python/scripting/python-script-icon-16x16.pngbin0 -> 601 bytes
6 files changed, 307 insertions, 5 deletions
diff --git a/plugins/python/scripting/Makefile.am b/plugins/python/scripting/Makefile.am
index 5d38d6e..3c44f1f 100644
--- a/plugins/python/scripting/Makefile.am
+++ b/plugins/python/scripting/Makefile.am
@@ -3,6 +3,10 @@ scriptingdir = $(pluginsdatadir)/python/scripting
scripting_DATA = \
__init__.py \
- core.py
+ core.py \
+ manager.py \
+ panel.py \
+ panel.ui \
+ python-script-icon-16x16.png
EXTRA_DIST = $(scripting_DATA)
diff --git a/plugins/python/scripting/core.py b/plugins/python/scripting/core.py
index 7aff551..71afaf2 100644
--- a/plugins/python/scripting/core.py
+++ b/plugins/python/scripting/core.py
@@ -4,8 +4,12 @@ from gi.repository import Gtk
from pychrysalide import PluginModule
from pychrysalide import core
from pychrysalide.gui import core as gcore
+from pychrysalide.gui import MenuBar
from pychrysalide.gtkext import EasyGtk
+from .manager import get_recent_python_script_manager, remember_python_script
+from .panel import ScriptPanel
+
class ScriptingEngine(PluginModule):
"""Extend the GUI to run external Python scripts."""
@@ -15,7 +19,7 @@ class ScriptingEngine(PluginModule):
_version = '0.1'
_url = 'https://www.chrysalide.re/'
- _actions = ( )
+ _actions = ( PluginModule.PluginAction.PLUGINS_LOADED, PluginModule.PluginAction.PANEL_CREATION )
def __init__(self):
@@ -23,9 +27,13 @@ class ScriptingEngine(PluginModule):
super(ScriptingEngine, self).__init__()
+ # Scripts panel
+
+ gcore.register_panel(ScriptPanel)
+
# Insert the new menu item into 'File' submenu
- bar = gcore.find_editor_item_by_key('menubar')
+ bar = gcore.find_editor_item_by_type(MenuBar)
builder = gcore.get_editor_builder()
@@ -54,6 +62,25 @@ class ScriptingEngine(PluginModule):
file_menu.insert(item, index)
+ def _notify_plugins_loaded(self, action):
+ """Ack the full loading of all plugins."""
+
+ if action == PluginModule.PluginAction.PLUGINS_LOADED:
+
+ filename = self.build_config_filename('recents.xbel', True)
+
+ get_recent_python_script_manager(filename)
+
+
+ def _on_panel_creation(self, action, item):
+ """Get notified of a new panel creation."""
+
+ if type(item) == ScriptPanel:
+
+ item.connect('run-requested', self._on_run_requested)
+ item.connect('ask-for-new-script', lambda x: self._on_file_run_script_activate(None))
+
+
def _on_file_run_script_activate(self, widget):
"""Look for a new script to run."""
@@ -87,7 +114,9 @@ class ScriptingEngine(PluginModule):
def _run_script_file(self, filename):
"""Run a given script file."""
- core.log_message(core.LogMessageType.INFO, 'Execute the script file \'%s\'' % filename)
+ self.log_message(core.LogMessageType.INFO, 'Execute the script file \'%s\'' % filename)
+
+ remember_python_script(filename)
try:
with open(filename, 'r') as fd:
@@ -98,4 +127,10 @@ class ScriptingEngine(PluginModule):
eval(code)
except Exception as e:
- core.log_message(core.LogMessageType.EXT_ERROR, 'Error while running the script: %s' % str(e))
+ self.log_message(core.LogMessageType.EXT_ERROR, 'Error while running the script: %s' % str(e))
+
+
+ def _on_run_requested(self, panel, filename):
+ """Run a script file from the recents panel."""
+
+ self._run_script_file(filename)
diff --git a/plugins/python/scripting/manager.py b/plugins/python/scripting/manager.py
new file mode 100644
index 0000000..6b27b48
--- /dev/null
+++ b/plugins/python/scripting/manager.py
@@ -0,0 +1,53 @@
+
+import os
+
+from gi.repository import GLib, Gtk
+
+
+_manager = None
+
+
+def get_recent_python_script_manager(xbel = None):
+ """Provide the manager for the recently run Python scripts."""
+
+ global _manager
+
+ # As a first panel creation is forced by the Chrysalide core to register
+ # its final GType, xbel is not defined at the first call of this function.
+ # Thus relying on the definition of xbel is a better filter than relying
+ # on the existence of _manager.
+ #
+ # In that special initial case, result is None
+
+ if not(xbel is None):
+
+ assert(_manager is None)
+
+ _manager = Gtk.RecentManager(filename=xbel)
+
+ return _manager
+
+
+def remember_python_script(filename):
+ """Register a Python script into the recents list."""
+
+ uri = GLib.filename_to_uri(filename)
+
+ recent_data = Gtk.RecentData()
+ recent_data.app_name = 'Chrysalide Python plugin'
+ recent_data.app_exec = 'chrysalide'
+ recent_data.display_name = os.path.basename(filename)
+ recent_data.description = 'Python script run inside Chrysalide'
+ recent_data.mime_type = 'text/x-python'
+
+ manager = get_recent_python_script_manager()
+ manager.add_full(uri, recent_data)
+
+
+def forget_python_script(filename):
+ """Unregister a Python script from the recents list."""
+
+ uri = GLib.filename_to_uri(filename)
+
+ manager = get_recent_python_script_manager()
+ manager.remove_item(uri)
diff --git a/plugins/python/scripting/panel.py b/plugins/python/scripting/panel.py
new file mode 100644
index 0000000..75b50e3
--- /dev/null
+++ b/plugins/python/scripting/panel.py
@@ -0,0 +1,140 @@
+
+import os
+from gi.repository import Gdk, GdkPixbuf, GLib, GObject
+from pychrysalide.gtkext import BuiltNamedWidget
+from pychrysalide.gui import core
+from pychrysalide.gui import PanelItem
+
+from .manager import get_recent_python_script_manager, forget_python_script
+
+
+class ScriptPanel(PanelItem):
+
+ _key = 'pyscripting'
+
+ _path = 'MEN'
+ _key_bindings = '<Shift>F5'
+
+
+ def __init__(self):
+ """Initialize the GUI panel."""
+
+ directory = os.path.dirname(os.path.realpath(__file__))
+ filename = os.path.join(directory, 'panel.ui')
+
+ widget = BuiltNamedWidget('Python scripts', 'Recently run Python scripts', filename)
+
+ super(ScriptPanel, self).__init__(widget)
+
+ if not('run-requested' in GObject.signal_list_names(ScriptPanel)):
+
+ GObject.signal_new('run-requested', ScriptPanel, GObject.SignalFlags.RUN_FIRST,
+ GObject.TYPE_NONE, (GObject.TYPE_STRING, ))
+
+ GObject.signal_new('ask-for-new-script', ScriptPanel, GObject.SignalFlags.RUN_FIRST,
+ GObject.TYPE_NONE, ())
+
+ self._last_selected = None
+
+ builder = self.named_widget.builder
+
+ icon_renderer = builder.get_object('icon_renderer')
+ icon_renderer.props.xpad = 8
+
+ builder.connect_signals(self)
+
+ manager = get_recent_python_script_manager()
+
+ if manager:
+
+ manager.connect("changed", self._on_recent_list_changed)
+
+ self._on_recent_list_changed(manager)
+
+
+ def _on_row_activated(self, treeview, path, column):
+ """React on a row activation."""
+
+ store = self.named_widget.builder.get_object('store')
+
+ siter = store.get_iter(path)
+
+ self.emit('run-requested', store[siter][3])
+
+
+ def _on_selection_changed(self, selection):
+ """React on tree selection change."""
+
+ model, treeiter = selection.get_selected()
+
+ if treeiter:
+ self._last_selected = model[treeiter][3]
+ else:
+ self._last_selected = None
+
+
+ def on_key_press_event(self, widget, event):
+ """React on a key press inside the tree view."""
+
+ if event.keyval == Gdk.KEY_Delete:
+
+ selection = self.named_widget.builder.get_object('selection')
+
+ model, treeiter = selection.get_selected()
+
+ if treeiter:
+
+ forget_python_script(model[treeiter][3])
+
+ elif event.keyval == Gdk.KEY_Insert:
+
+ self.emit('ask-for-new-script')
+
+
+ def _add_entry(self, filename):
+ """Add an entry for a new recent Python script."""
+
+ directory = os.path.dirname(os.path.realpath(__file__))
+ icon_filename = os.path.join(directory, 'python-script-icon-16x16.png')
+
+ icon = GdkPixbuf.Pixbuf.new_from_file(icon_filename)
+ name = os.path.basename(filename)
+ path = os.path.dirname(filename)
+
+ store = self.named_widget.builder.get_object('store')
+
+ store.append([ icon, name, path, filename ])
+
+
+ def _on_recent_list_changed(self, manager):
+ """React on resources manager content change."""
+
+ saved = self._last_selected
+
+ # Register the new item
+
+ builder = self.named_widget.builder
+
+ store = builder.get_object('store')
+ store.clear()
+
+ for item in manager.get_items():
+
+ filename = GLib.filename_from_uri(item.get_uri())[0]
+
+ self._add_entry(filename)
+
+ # Restore previous selection
+
+ selection = builder.get_object('selection')
+
+ siter = store.iter_children()
+
+ while siter:
+
+ if store[siter][3] == saved:
+
+ selection.select_iter(siter)
+ break
+
+ siter = store.iter_next(siter)
diff --git a/plugins/python/scripting/panel.ui b/plugins/python/scripting/panel.ui
new file mode 100644
index 0000000..f87088a
--- /dev/null
+++ b/plugins/python/scripting/panel.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.37.0 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkListStore" id="store">
+ <columns>
+ <!-- column-name icon -->
+ <column type="GdkPixbuf"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ <!-- column-name path -->
+ <column type="gchararray"/>
+ <!-- column-name filename -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkScrolledWindow" id="box">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="shadow-type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkTreeView" id="treeview">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="model">store</property>
+ <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+ <signal name="row-activated" handler="_on_row_activated" swapped="no"/>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="selection">
+ <signal name="changed" handler="_on_selection_changed" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="title" translatable="yes">Filename</property>
+ <child>
+ <object class="GtkCellRendererPixbuf" id="icon_renderer"/>
+ <attributes>
+ <attribute name="pixbuf">0</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="name_renderer"/>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn">
+ <property name="title" translatable="yes">Path</property>
+ <child>
+ <object class="GtkCellRendererText" id="path_renderer"/>
+ <attributes>
+ <attribute name="text">2</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/plugins/python/scripting/python-script-icon-16x16.png b/plugins/python/scripting/python-script-icon-16x16.png
new file mode 100644
index 0000000..c96599e
--- /dev/null
+++ b/plugins/python/scripting/python-script-icon-16x16.png
Binary files differ