Commit f6cdd1ad authored by Fabián Orccón's avatar Fabián Orccón Committed by Alexandru Băluț

plugins: Add Pitivi Developer Console plugin

Closes #2055
parent 756c69d7
[Plugin]
Module=console
Name=Pitivi Developer Console
Loader=Python3
Description=Add a developer console to Pitivi.
Authors=Fabian Orccon <cfoch.fabian@gmail.com>
Copyright=Copyright © 2017 Fabian Orccon
Website=http://pitivi.org
Help=http://developer.pitivi.org
# -*- coding: utf-8 -*-
# Pitivi Developer Console
# Copyright (c) 2017-2018, Fabian Orccon <cfoch.fabian@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""Python console for inspecting and interacting with Pitivi and the project."""
import sys
from gettext import gettext as _
from gi.repository import GObject
from gi.repository import Gtk
from gi.repository import Peas
from widgets import ConsoleWidget
class Console(GObject.GObject, Peas.Activatable):
"""Plugin which adds a Python console for development purposes."""
__gtype_name__ = "ConsolePlugin"
object = GObject.Property(type=GObject.Object)
def __init__(self):
GObject.GObject.__init__(self)
self.window = None
self.terminal = None
self.menu_item = None
self.app = None
# Set prompt.
sys.ps1 = ">>> "
sys.ps2 = "... "
def do_activate(self):
api = self.object
self.app = api.app
self._setup_dialog()
self.add_menu_item()
self.menu_item.show()
def do_deactivate(self):
self.window.destroy()
self.remove_menu_item()
self.window = None
self.terminal = None
self.menu_item = None
self.app = None
def add_menu_item(self):
"""Inserts a menu item into the Pitivi menu."""
menu = self.app.gui.editor.builder.get_object("menu")
self.menu_item = Gtk.MenuItem.new_with_label(_("Developer Console"))
self.menu_item.connect("activate", self.__menu_item_activate_cb)
menu.add(self.menu_item)
def remove_menu_item(self):
"""Removes a menu item from the Pitivi menu."""
menu = self.app.gui.editor.builder.get_object("menu")
menu.remove(self.menu_item)
self.menu_item = None
def _setup_dialog(self):
namespace = {"app": self.app}
self.window = Gtk.Window()
self.terminal = ConsoleWidget(namespace)
self.window.set_default_size(600, 400)
self.window.set_title(_("Pitivi Console"))
self.window.connect("delete-event", self.__delete_event_cb)
self.window.add(self.terminal)
def __menu_item_activate_cb(self, unused_data):
self.window.show_all()
self.window.set_keep_above(True)
def __delete_event_cb(self, unused_widget, unused_data):
return self.window.hide_on_delete()
# pylint: disable=missing-docstring
# -*- coding: utf-8 -*-
# Pitivi Developer Console
# Copyright (c) 2017-2018, Fabian Orccon <cfoch.fabian@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
import code
import sys
from gi.repository import Gtk
from utils import FakeOut
from utils import swap_std
class ConsoleBuffer(Gtk.TextBuffer):
def __init__(self, namespace):
Gtk.TextBuffer.__init__(self)
self.insert_at_cursor(sys.ps1)
self.prompt_mark = self.create_mark("after-prompt", self.get_end_iter(), left_gravity=True)
self._stdout = FakeOut(self)
self._stderr = FakeOut(self)
self._console = code.InteractiveConsole(namespace)
def process_command_line(self):
"""Process the current input command line executing it if complete."""
cmd = self.get_command_line()
with swap_std(self._stdout, self._stderr):
self.write("\n")
is_command_incomplete = self._console.push(cmd)
if is_command_incomplete:
prompt = sys.ps2
else:
prompt = sys.ps1
self.write(prompt)
self.move_mark(self.prompt_mark, self.get_end_iter())
self.place_cursor(self.get_end_iter())
def is_cursor(self, before=False, at=False, after=False):
"""Compares the position of the cursor compared to the prompt."""
prompt_iter = self.get_iter_at_mark(self.prompt_mark)
cursor_iter = self.get_iter_at_mark(self.get_insert())
res = cursor_iter.compare(prompt_iter)
return (before and res == -1) or (at and res == 0) or (after and res == 1)
def write(self, text):
"""Writes a text to the buffer."""
self.insert(self.get_end_iter(), text)
def get_command_line(self):
"""Gets the last command line after the prompt.
A command line can be a single line or multiple lines for example when
a function or a class is defined.
"""
after_prompt_iter = self.get_iter_at_mark(self.prompt_mark)
end_iter = self.get_end_iter()
return self.get_text(after_prompt_iter, end_iter, include_hidden_chars=False)
def set_command_line(self, cmd):
"""Inserts a command line after the prompt."""
after_prompt_iter = self.get_iter_at_mark(self.prompt_mark)
end_iter = self.get_end_iter()
self.delete(after_prompt_iter, end_iter)
self.write(cmd)
# pylint: disable=missing-docstring
# -*- coding: utf-8 -*-
# Pitivi Developer Console
# Copyright (c) 2017-2018, Fabian Orccon <cfoch.fabian@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
import sys
from contextlib import contextmanager
from io import TextIOBase
@contextmanager
def swap_std(stdout=None, stderr=None):
"""Swaps temporarily stdout and stderr with the respective arguments."""
try:
if stdout:
sys.stdout, stdout = stdout, sys.stdout
if stderr:
sys.stderr, stderr = stderr, sys.stderr
yield
finally:
if stdout:
sys.stdout = stdout
if stderr:
sys.stderr = stderr
class FakeOut(TextIOBase):
"""Replacement for sys.stdout/err which redirects writes."""
def __init__(self, buf):
TextIOBase.__init__(self)
self.buf = buf
def write(self, string):
self.buf.write(string)
def writelines(self, lines):
self.buf.write(lines)
# -*- coding: utf-8 -*-
# Pitivi Developer Console
# Copyright (c) 2017-2018, Fabian Orccon <cfoch.fabian@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
# Boston, MA 02110-1301, USA.
"""The developer console widget:"""
from gi.repository import Gdk
from gi.repository import GLib
from gi.repository import Gtk
from consolebuffer import ConsoleBuffer
class ConsoleWidget(Gtk.ScrolledWindow):
"""An emulated Python console.
The console can be used to access an app, window, or anything through the
provided namespace. It works redirecting stdout and stderr to a
GtkTextBuffer. This class is (and should be) independent of the application
it is integrated with.
"""
def __init__(self, namespace):
Gtk.ScrolledWindow.__init__(self)
self._view = Gtk.TextView()
buf = ConsoleBuffer(namespace)
self._view.set_buffer(buf)
self._view.set_editable(True)
self.add(self._view)
self._view.connect("key-press-event", self.__key_press_event_cb)
buf.connect("mark-set", self.__mark_set_cb)
buf.connect("insert-text", self.__insert_text_cb)
def scroll_to_end(self):
"""Scrolls the view to the end."""
end_iter = self._view.get_buffer().get_end_iter()
self._view.scroll_to_iter(end_iter, within_margin=0.0, use_align=False,
xalign=0, yalign=0)
return False
@classmethod
def __key_press_event_cb(cls, view, event):
buf = view.get_buffer()
if event.keyval == Gdk.KEY_Return:
buf.process_command_line()
return True
if event.keyval in (Gdk.KEY_KP_Down, Gdk.KEY_Down):
return True
if event.keyval in (Gdk.KEY_KP_Up, Gdk.KEY_Up):
return True
if event.keyval in (Gdk.KEY_KP_Left, Gdk.KEY_Left, Gdk.KEY_BackSpace):
return buf.is_cursor(at=True)
if event.keyval in (Gdk.KEY_KP_Home, Gdk.KEY_Home):
buf.place_cursor(buf.get_iter_at_mark(buf.prompt_mark))
return True
return False
def __mark_set_cb(self, buf, unused_iter, mark):
if not mark.props.name == "insert":
return
self._view.set_editable(buf.is_cursor(at=True, after=True))
def __insert_text_cb(self, buf, unused_iter, unused_text, unused_len):
GLib.idle_add(self.scroll_to_end)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment