mirror of https://github.com/GNOME/gimp.git
plug-ins: port python-console to new GObject-introspected API.
I may have missed things. That is the problem of non-compiled script languages. There is also a known warning: > DeprecationWarning: Gtk.Dialog.set_alternative_button_order_from_array is deprecated I'll see later about this one. Push-time note: calling various functions is actually broken right now in the console since the late API changes (it was working fine yesterday evening when I tested the same python-console code). Pushing anyway for now.
This commit is contained in:
parent
afe0bcbc58
commit
62d87f15d9
|
@ -2870,6 +2870,7 @@ dnl plug-ins/pygimp/Makefile
|
|||
dnl plug-ins/pygimp/plug-ins/Makefile
|
||||
[
|
||||
plug-ins/python/Makefile
|
||||
plug-ins/python/python-console/Makefile
|
||||
plug-ins/screenshot/Makefile
|
||||
plug-ins/script-fu/Makefile
|
||||
plug-ins/script-fu/ftx/Makefile
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
#!/usr/bin/env python2
|
||||
|
||||
# Gimp-Python - allows the writing of Gimp plugins in Python.
|
||||
# Copyright (C) 1997 James Henstridge <james@daa.com.au>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from gimpfu import *
|
||||
|
||||
t = gettext.translation('gimp20-python', gimp.locale_directory, fallback=True)
|
||||
_ = t.ugettext
|
||||
|
||||
PROC_NAME = 'python-fu-console'
|
||||
|
||||
RESPONSE_BROWSE, RESPONSE_CLEAR, RESPONSE_SAVE = range(3)
|
||||
|
||||
def do_console():
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
|
||||
import sys, gobject, gtk, gimpenums, gimpshelf, gimpui, pyconsole
|
||||
gimpui.gimp_ui_init ()
|
||||
|
||||
namespace = {'__builtins__': __builtins__,
|
||||
'__name__': '__main__', '__doc__': None,
|
||||
'gimp': gimp, 'pdb': gimp.pdb,
|
||||
'shelf': gimpshelf.shelf}
|
||||
|
||||
for s in gimpenums.__dict__.keys():
|
||||
if s[0] != '_':
|
||||
namespace[s] = getattr(gimpenums, s)
|
||||
|
||||
class GimpConsole(pyconsole.Console):
|
||||
def __init__(self, quit_func=None):
|
||||
banner = ('GIMP %s Python Console\nPython %s\n' %
|
||||
(gimp.pdb.gimp_version(), sys.version))
|
||||
pyconsole.Console.__init__(self,
|
||||
locals=namespace, banner=banner,
|
||||
quit_func=quit_func)
|
||||
def _commit(self):
|
||||
pyconsole.Console._commit(self)
|
||||
gimp.displays_flush()
|
||||
|
||||
class ConsoleDialog(gimpui.Dialog):
|
||||
def __init__(self):
|
||||
gimpui.Dialog.__init__(self, title=_("Python Console"),
|
||||
role=PROC_NAME, help_id=PROC_NAME,
|
||||
buttons=(gtk.STOCK_SAVE, RESPONSE_SAVE,
|
||||
gtk.STOCK_CLEAR, RESPONSE_CLEAR,
|
||||
_("_Browse..."), RESPONSE_BROWSE,
|
||||
gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
|
||||
|
||||
self.set_name (PROC_NAME)
|
||||
self.set_alternative_button_order((gtk.RESPONSE_CLOSE,
|
||||
RESPONSE_BROWSE,
|
||||
RESPONSE_CLEAR,
|
||||
RESPONSE_SAVE))
|
||||
|
||||
self.cons = GimpConsole(quit_func=lambda: gtk.main_quit())
|
||||
|
||||
self.style_set (None, None)
|
||||
|
||||
self.connect('response', self.response)
|
||||
self.connect('style-set', self.style_set)
|
||||
|
||||
self.browse_dlg = None
|
||||
self.save_dlg = None
|
||||
|
||||
vbox = gtk.VBox(False, 12)
|
||||
vbox.set_border_width(12)
|
||||
self.vbox.pack_start(vbox)
|
||||
|
||||
scrl_win = gtk.ScrolledWindow()
|
||||
scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
|
||||
vbox.pack_start(scrl_win)
|
||||
|
||||
scrl_win.add(self.cons)
|
||||
|
||||
width, height = self.cons.get_default_size()
|
||||
sb_width, sb_height = scrl_win.get_vscrollbar().size_request()
|
||||
|
||||
# Account for scrollbar width and border width to ensure
|
||||
# the text view gets a width of 80 characters. We don't care
|
||||
# so much whether the height will be exactly 40 characters.
|
||||
self.set_default_size(width + sb_width + 2 * 12, height)
|
||||
|
||||
def style_set(self, old_style, user_data):
|
||||
style = self.get_style ()
|
||||
self.cons.stdout_tag.set_property ("foreground", style.text[gtk.STATE_NORMAL])
|
||||
self.cons.stderr_tag.set_property ("foreground", style.text[gtk.STATE_INSENSITIVE])
|
||||
|
||||
def response(self, dialog, response_id):
|
||||
if response_id == RESPONSE_BROWSE:
|
||||
self.browse()
|
||||
elif response_id == RESPONSE_CLEAR:
|
||||
self.cons.banner = None
|
||||
self.cons.clear()
|
||||
elif response_id == RESPONSE_SAVE:
|
||||
self.save_dialog()
|
||||
else:
|
||||
gtk.main_quit()
|
||||
|
||||
self.cons.grab_focus()
|
||||
|
||||
def browse_response(self, dlg, response_id):
|
||||
if response_id != gtk.RESPONSE_APPLY:
|
||||
dlg.hide()
|
||||
return
|
||||
|
||||
proc_name = dlg.get_selected()
|
||||
|
||||
if not proc_name:
|
||||
return
|
||||
|
||||
proc = pdb[proc_name]
|
||||
|
||||
cmd = ''
|
||||
|
||||
if len(proc.return_vals) > 0:
|
||||
cmd = ', '.join(x[1].replace('-', '_')
|
||||
for x in proc.return_vals) + ' = '
|
||||
|
||||
cmd = cmd + 'pdb.%s' % proc.proc_name.replace('-', '_')
|
||||
|
||||
if len(proc.params) > 0 and proc.params[0][1] == 'run-mode':
|
||||
params = proc.params[1:]
|
||||
else:
|
||||
params = proc.params
|
||||
|
||||
cmd = cmd + '(%s)' % ', '.join(x[1].replace('-', '_')
|
||||
for x in params)
|
||||
|
||||
buffer = self.cons.buffer
|
||||
|
||||
lines = buffer.get_line_count()
|
||||
iter = buffer.get_iter_at_line_offset(lines - 1, 4)
|
||||
buffer.delete(iter, buffer.get_end_iter())
|
||||
buffer.place_cursor(buffer.get_end_iter())
|
||||
buffer.insert_at_cursor(cmd)
|
||||
|
||||
def browse(self):
|
||||
if not self.browse_dlg:
|
||||
dlg = gimpui.ProcBrowserDialog(_("Python Procedure Browser"),
|
||||
role=PROC_NAME,
|
||||
buttons=(gtk.STOCK_APPLY,
|
||||
gtk.RESPONSE_APPLY,
|
||||
gtk.STOCK_CLOSE,
|
||||
gtk.RESPONSE_CLOSE))
|
||||
|
||||
dlg.set_default_response(gtk.RESPONSE_APPLY)
|
||||
dlg.set_alternative_button_order((gtk.RESPONSE_CLOSE,
|
||||
gtk.RESPONSE_APPLY))
|
||||
|
||||
dlg.connect('response', self.browse_response)
|
||||
dlg.connect('row-activated',
|
||||
lambda dlg: dlg.response(gtk.RESPONSE_APPLY))
|
||||
|
||||
self.browse_dlg = dlg
|
||||
|
||||
self.browse_dlg.present()
|
||||
|
||||
def save_response(self, dlg, response_id):
|
||||
if response_id == gtk.RESPONSE_DELETE_EVENT:
|
||||
self.save_dlg = None
|
||||
return
|
||||
elif response_id == gtk.RESPONSE_OK:
|
||||
filename = dlg.get_filename()
|
||||
|
||||
try:
|
||||
logfile = open(filename, 'w')
|
||||
except IOError, e:
|
||||
gimp.message(_("Could not open '%s' for writing: %s") %
|
||||
(filename, e.strerror))
|
||||
return
|
||||
|
||||
buffer = self.cons.buffer
|
||||
|
||||
start = buffer.get_start_iter()
|
||||
end = buffer.get_end_iter()
|
||||
|
||||
log = buffer.get_text(start, end, False)
|
||||
|
||||
try:
|
||||
logfile.write(log)
|
||||
logfile.close()
|
||||
except IOError, e:
|
||||
gimp.message(_("Could not write to '%s': %s") %
|
||||
(filename, e.strerror))
|
||||
return
|
||||
|
||||
dlg.hide()
|
||||
|
||||
def save_dialog(self):
|
||||
if not self.save_dlg:
|
||||
dlg = gtk.FileChooserDialog(_("Save Python-Fu Console Output"),
|
||||
parent=self,
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE,
|
||||
buttons=(gtk.STOCK_CANCEL,
|
||||
gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_SAVE,
|
||||
gtk.RESPONSE_OK))
|
||||
|
||||
dlg.set_default_response(gtk.RESPONSE_OK)
|
||||
dlg.set_alternative_button_order((gtk.RESPONSE_OK,
|
||||
gtk.RESPONSE_CANCEL))
|
||||
|
||||
dlg.connect('response', self.save_response)
|
||||
|
||||
self.save_dlg = dlg
|
||||
|
||||
self.save_dlg.present()
|
||||
|
||||
def run(self):
|
||||
self.show_all()
|
||||
gtk.main()
|
||||
|
||||
ConsoleDialog().run()
|
||||
|
||||
register(
|
||||
PROC_NAME,
|
||||
N_("Interactive GIMP Python interpreter"),
|
||||
"Type in commands and see results",
|
||||
"James Henstridge",
|
||||
"James Henstridge",
|
||||
"1997-1999",
|
||||
N_("_Console"),
|
||||
"",
|
||||
[],
|
||||
[],
|
||||
do_console,
|
||||
menu="<Image>/Filters/Languages/Python-Fu",
|
||||
domain=("gimp20-python", gimp.locale_directory))
|
||||
|
||||
main()
|
|
@ -1,5 +1,8 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
SUBDIRS = \
|
||||
python-console
|
||||
|
||||
pluginexecdir = $(gimpplugindir)/plug-ins
|
||||
|
||||
source_scripts = \
|
||||
|
@ -50,16 +53,8 @@ if GIMP_UNSTABLE
|
|||
nobase_pluginexec_SCRIPTS += $(test_scripts)
|
||||
endif
|
||||
|
||||
# python-console has a data file.
|
||||
# Therefore let's move it to its own sub-directory.
|
||||
#consoleexecdir = $(gimpplugindir)/plug-ins/python-console
|
||||
#console_scripts = python-console.py
|
||||
#consoleexec_SCRIPTS = $(console_scripts)
|
||||
#dist_consoleexec_DATA = pyconsole.py
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(source_scripts)
|
||||
# $(console_scripts)
|
||||
|
||||
CLEANFILES = $(scripts) $(test_scripts)
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
consoleexecdir = $(gimpplugindir)/plug-ins/python-console
|
||||
console_scripts = python-console.py
|
||||
consoleexec_SCRIPTS = $(console_scripts)
|
||||
dist_consoleexec_DATA = pyconsole.py
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(console_scripts)
|
|
@ -38,16 +38,24 @@
|
|||
# The use case is: you have a python program, you create this widget,
|
||||
# and inspect your program interiors.
|
||||
|
||||
import gtk
|
||||
import gtk.gdk as gdk
|
||||
import gobject
|
||||
import pango
|
||||
import gtk.keysyms as _keys
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
gi.require_version('Gdk', '3.0')
|
||||
from gi.repository import Gdk
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Pango
|
||||
import code
|
||||
import sys
|
||||
import keyword
|
||||
import re
|
||||
|
||||
def pango_pixels(value):
|
||||
# The PANGO_PIXELS macro is not accessible through GObject
|
||||
# Introspection. Just reimplement it:
|
||||
# #define PANGO_PIXELS(d) (((int)(d) + 512) >> 10)
|
||||
return (value + 512) >> 10
|
||||
|
||||
# commonprefix() from posixpath
|
||||
def _commonprefix(m):
|
||||
"Given a list of pathnames, returns the longest common leading component"
|
||||
|
@ -92,7 +100,7 @@ class _ReadLine(object):
|
|||
|
||||
if text != self.items[self.ptr]:
|
||||
self.edited[self.ptr] = text
|
||||
elif self.edited.has_key(self.ptr):
|
||||
elif self.ptr in self.edited:
|
||||
del self.edited[self.ptr]
|
||||
|
||||
self.ptr = self.ptr + dir
|
||||
|
@ -111,8 +119,8 @@ class _ReadLine(object):
|
|||
|
||||
self.quit_func = quit_func
|
||||
|
||||
self.set_wrap_mode(gtk.WRAP_CHAR)
|
||||
self.modify_font(pango.FontDescription("Monospace"))
|
||||
self.set_wrap_mode(Gtk.WrapMode.CHAR)
|
||||
self.modify_font(Pango.FontDescription("Monospace"))
|
||||
|
||||
self.buffer = self.get_buffer()
|
||||
self.buffer.connect("insert-text", self.on_buf_insert)
|
||||
|
@ -166,7 +174,7 @@ class _ReadLine(object):
|
|||
self.thaw_undo()
|
||||
|
||||
self.__move_cursor_to(iter)
|
||||
self.scroll_to_mark(self.cursor, 0.2)
|
||||
self.scroll_to_mark(self.cursor, 0.2, False, 0.0, 0.0)
|
||||
|
||||
self.in_raw_input = True
|
||||
|
||||
|
@ -184,7 +192,7 @@ class _ReadLine(object):
|
|||
self.in_modal_raw_input = True
|
||||
|
||||
while self.in_modal_raw_input:
|
||||
gtk.main_iteration()
|
||||
Gtk.main_iteration()
|
||||
|
||||
self.ps = orig_ps
|
||||
self.in_modal_raw_input = False
|
||||
|
@ -204,7 +212,7 @@ class _ReadLine(object):
|
|||
if iter.compare(self.__get_start()) >= 0 and \
|
||||
iter.compare(self.__get_end()) <= 0:
|
||||
buffer.move_mark_by_name("cursor", iter)
|
||||
self.scroll_to_mark(self.cursor, 0.2)
|
||||
self.scroll_to_mark(self.cursor, 0.2, False, 0.0, 0.0)
|
||||
|
||||
def __insert(self, iter, text):
|
||||
self.do_insert = True
|
||||
|
@ -267,37 +275,37 @@ class _ReadLine(object):
|
|||
|
||||
# We overload the key press event handler to handle "special keys"
|
||||
# when in input mode to make history browsing, completions, etc. work.
|
||||
def do_key_press_event(self, event, parent_type):
|
||||
def do_key_press_event(self, event):
|
||||
if not self.in_raw_input:
|
||||
return parent_type.do_key_press_event(self, event)
|
||||
return Gtk.TextView.do_key_press_event(self, event)
|
||||
|
||||
tab_pressed = self.tab_pressed
|
||||
self.tab_pressed = 0
|
||||
handled = True
|
||||
|
||||
state = event.state & (gdk.SHIFT_MASK |
|
||||
gdk.CONTROL_MASK |
|
||||
gdk.MOD1_MASK)
|
||||
state = event.state & (Gdk.ModifierType.SHIFT_MASK |
|
||||
Gdk.ModifierType.CONTROL_MASK |
|
||||
Gdk.ModifierType.MOD1_MASK)
|
||||
keyval = event.keyval
|
||||
|
||||
if not state:
|
||||
if keyval == _keys.Escape:
|
||||
if keyval == Gdk.KEY_Escape:
|
||||
pass
|
||||
elif keyval == _keys.Return:
|
||||
elif keyval == Gdk.KEY_Return:
|
||||
self._commit()
|
||||
elif keyval == _keys.Up:
|
||||
elif keyval == Gdk.KEY_Up:
|
||||
self.__history(-1)
|
||||
elif keyval == _keys.Down:
|
||||
elif keyval == Gdk.KEY_Down:
|
||||
self.__history(1)
|
||||
elif keyval == _keys.Left:
|
||||
elif keyval == Gdk.KEY_Left:
|
||||
self.__move_cursor(-1)
|
||||
elif keyval == _keys.Right:
|
||||
elif keyval == Gdk.KEY_Right:
|
||||
self.__move_cursor(1)
|
||||
elif keyval == _keys.Home:
|
||||
elif keyval == Gdk.KEY_Home:
|
||||
self.__move_cursor(-10000)
|
||||
elif keyval == _keys.End:
|
||||
elif keyval == Gdk.KEY_End:
|
||||
self.__move_cursor(10000)
|
||||
elif keyval == _keys.Tab:
|
||||
elif keyval == Gdk.KEY_Tab:
|
||||
cursor = self.__get_cursor()
|
||||
if cursor.starts_line():
|
||||
handled = False
|
||||
|
@ -310,12 +318,12 @@ class _ReadLine(object):
|
|||
self.__complete()
|
||||
else:
|
||||
handled = False
|
||||
elif state == gdk.CONTROL_MASK:
|
||||
if keyval == _keys.u:
|
||||
elif state == Gdk.ModifierType.CONTROL_MASK:
|
||||
if keyval == Gdk.KEY_u:
|
||||
start = self.__get_start()
|
||||
end = self.__get_cursor()
|
||||
self.__delete(start, end)
|
||||
elif keyval == _keys.d:
|
||||
elif keyval == Gdk.KEY_d:
|
||||
if self.quit_func:
|
||||
self.quit_func()
|
||||
else:
|
||||
|
@ -325,7 +333,7 @@ class _ReadLine(object):
|
|||
|
||||
# Handle ordinary keys
|
||||
if not handled:
|
||||
return parent_type.do_key_press_event(self, event)
|
||||
return Gtk.TextView.do_key_press_event(self, event)
|
||||
else:
|
||||
return True
|
||||
|
||||
|
@ -335,7 +343,7 @@ class _ReadLine(object):
|
|||
if not new_text is None:
|
||||
self.__replace_line(new_text)
|
||||
self.__move_cursor(0)
|
||||
self.scroll_to_mark(self.cursor, 0.2)
|
||||
self.scroll_to_mark(self.cursor, 0.2, False, 0.0, 0.0)
|
||||
|
||||
def __get_cursor(self):
|
||||
'''Returns an iterator at the current cursor position.'''
|
||||
|
@ -393,14 +401,15 @@ class _ReadLine(object):
|
|||
'''Estimate the number of characters that will fit in the area
|
||||
currently allocated to this widget.'''
|
||||
|
||||
if not (self.flags() & gtk.REALIZED):
|
||||
if not self.get_realized():
|
||||
return 80
|
||||
|
||||
context = self.get_pango_context()
|
||||
metrics = context.get_metrics(context.get_font_description(),
|
||||
context.get_language())
|
||||
pix_width = metrics.get_approximate_char_width()
|
||||
return self.allocation.width * pango.SCALE / pix_width
|
||||
allocation = Gtk.Widget.get_allocation(self)
|
||||
return allocation.width * Pango.SCALE / pix_width
|
||||
|
||||
def __print_completions(self, completions):
|
||||
line_start = self.__get_text(self.__get_start(), self.__get_cursor())
|
||||
|
@ -423,21 +432,21 @@ class _ReadLine(object):
|
|||
n_columns = total
|
||||
col_width = width / total
|
||||
|
||||
for i in range(col_length):
|
||||
for i in range(int(col_length)):
|
||||
for j in range(n_columns):
|
||||
ind = i + j*col_length
|
||||
if ind < total:
|
||||
if j == n_columns - 1:
|
||||
n_spaces = 0
|
||||
else:
|
||||
n_spaces = col_width - len(completions[ind])
|
||||
self.__insert(iter, completions[ind] + " " * n_spaces)
|
||||
n_spaces = int(col_width - len(completions[int(ind)]))
|
||||
self.__insert(iter, completions[int(ind)] + " " * n_spaces)
|
||||
self.__insert(iter, "\n")
|
||||
|
||||
self.__insert(iter, "%s%s%s" % (self.ps, line_start, line_end))
|
||||
iter.set_line_offset(len(self.ps) + len(line_start))
|
||||
self.__move_cursor_to(iter)
|
||||
self.scroll_to_mark(self.cursor, 0.2)
|
||||
self.scroll_to_mark(self.cursor, 0.2, False, 0.0, 0.0)
|
||||
|
||||
def __complete(self):
|
||||
text = self.__get_text(self.__get_start(), self.__get_cursor())
|
||||
|
@ -524,9 +533,9 @@ class _Console(_ReadLine, code.InteractiveInterpreter):
|
|||
# The builtin raw_input function reads from stdin, we don't want
|
||||
# this. Therefore, replace this function with our own modal raw
|
||||
# input function.
|
||||
exec "import __builtin__" in self.locals
|
||||
self.locals['__builtin__'].__dict__['raw_input'] = lambda text='': self.modal_raw_input(text)
|
||||
self.locals['__builtin__'].__dict__['input'] = lambda text='': self.modal_input(text)
|
||||
exec ("import builtins", self.locals)
|
||||
#self.locals['builtins'].__dict__['raw_input'] = lambda text='': self.modal_raw_input(text)
|
||||
self.locals['builtins'].__dict__['input'] = lambda text='': self.modal_input(text)
|
||||
|
||||
self.start_script = start_script
|
||||
self.completer = completer
|
||||
|
@ -601,10 +610,10 @@ class _Console(_ReadLine, code.InteractiveInterpreter):
|
|||
self.showtraceback()
|
||||
|
||||
def runcode(self, code):
|
||||
if gtk.pygtk_version[1] < 8:
|
||||
#if gtk.pygtk_version[1] < 8:
|
||||
self.do_command(code)
|
||||
else:
|
||||
self.emit("command", code)
|
||||
#else:
|
||||
#self.emit("command", code)
|
||||
|
||||
def complete_attr(self, start, end):
|
||||
try:
|
||||
|
@ -655,7 +664,7 @@ class _Console(_ReadLine, code.InteractiveInterpreter):
|
|||
except: pass
|
||||
|
||||
try:
|
||||
exec "import __builtin__" in self.locals
|
||||
exec("import __builtin__", self.locals)
|
||||
strings.extend(eval("dir(__builtin__)", self.locals))
|
||||
except:
|
||||
pass
|
||||
|
@ -668,35 +677,34 @@ class _Console(_ReadLine, code.InteractiveInterpreter):
|
|||
return completions
|
||||
|
||||
|
||||
def ReadLineType(t=gtk.TextView):
|
||||
def ReadLineType(t=Gtk.TextView):
|
||||
class readline(t, _ReadLine):
|
||||
def __init__(self, *args, **kwargs):
|
||||
t.__init__(self)
|
||||
_ReadLine.__init__(self, *args, **kwargs)
|
||||
def do_key_press_event(self, event):
|
||||
return _ReadLine.do_key_press_event(self, event, t)
|
||||
gobject.type_register(readline)
|
||||
return _ReadLine.do_key_press_event(self, event)
|
||||
GObject.type_register(readline)
|
||||
return readline
|
||||
|
||||
def ConsoleType(t=gtk.TextView):
|
||||
def ConsoleType(t=Gtk.TextView):
|
||||
class console_type(t, _Console):
|
||||
__gsignals__ = {
|
||||
'command' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (object,)),
|
||||
'key-press-event' : 'override'
|
||||
'command' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (object,)),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if gtk.pygtk_version[1] < 8:
|
||||
gobject.GObject.__init__(self)
|
||||
else:
|
||||
t.__init__(self)
|
||||
#if gtk.pygtk_version[1] < 8:
|
||||
GObject.GObject.__init__(self)
|
||||
#else:
|
||||
#t.__init__(self)
|
||||
_Console.__init__(self, *args, **kwargs)
|
||||
|
||||
def do_command(self, code):
|
||||
return _Console.do_command(self, code)
|
||||
|
||||
def do_key_press_event(self, event):
|
||||
return _Console.do_key_press_event(self, event, t)
|
||||
return _Console.do_key_press_event(self, event)
|
||||
|
||||
def get_default_size(self):
|
||||
context = self.get_pango_context()
|
||||
|
@ -706,13 +714,13 @@ def ConsoleType(t=gtk.TextView):
|
|||
height = metrics.get_ascent() + metrics.get_descent()
|
||||
|
||||
# Default to a 80x40 console
|
||||
width = pango.PIXELS(int(width * 80 * 1.05))
|
||||
height = pango.PIXELS(height * 40)
|
||||
width = pango_pixels(int(width * 80 * 1.05))
|
||||
height = pango_pixels(height * 40)
|
||||
|
||||
return width, height
|
||||
|
||||
if gtk.pygtk_version[1] < 8:
|
||||
gobject.type_register(console_type)
|
||||
#if gtk.pygtk_version[1] < 8:
|
||||
GObject.type_register(console_type)
|
||||
|
||||
return console_type
|
||||
|
||||
|
@ -720,14 +728,14 @@ ReadLine = ReadLineType()
|
|||
Console = ConsoleType()
|
||||
|
||||
def _make_window():
|
||||
window = gtk.Window()
|
||||
window = Gtk.Window()
|
||||
window.set_title("pyconsole.py")
|
||||
swin = gtk.ScrolledWindow()
|
||||
swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
|
||||
swin = Gtk.ScrolledWindow()
|
||||
swin.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
|
||||
window.add(swin)
|
||||
console = Console(banner="Hello there!",
|
||||
use_rlcompleter=False,
|
||||
start_script="from gtk import *\n")
|
||||
start_script="gi.require_version('Gimp', '3.0')\nfrom gi.repository import Gimp\n")
|
||||
swin.add(console)
|
||||
|
||||
width, height = console.get_default_size()
|
||||
|
@ -736,9 +744,9 @@ def _make_window():
|
|||
window.set_default_size(width + sb_width, height)
|
||||
window.show_all()
|
||||
|
||||
if not gtk.main_level():
|
||||
window.connect("destroy", gtk.main_quit)
|
||||
gtk.main()
|
||||
if not Gtk.main_level():
|
||||
window.connect("destroy", Gtk.main_quit)
|
||||
Gtk.main()
|
||||
|
||||
return console
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Gimp-Python - allows the writing of Gimp plugins in Python.
|
||||
# Copyright (C) 1997 James Henstridge <james@daa.com.au>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import gi
|
||||
gi.require_version('Gimp', '3.0')
|
||||
from gi.repository import Gimp
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
import sys
|
||||
import pyconsole
|
||||
#import gimpshelf, gimpui, pyconsole
|
||||
|
||||
import gettext
|
||||
_ = gettext.gettext
|
||||
def N_(message): return message
|
||||
|
||||
PROC_NAME = 'python-fu-console'
|
||||
|
||||
RESPONSE_BROWSE, RESPONSE_CLEAR, RESPONSE_SAVE = range(3)
|
||||
|
||||
def run(name, n_params, params):
|
||||
Gimp.ui_init ("python-console.py", False)
|
||||
|
||||
namespace = {'__builtins__': __builtins__,
|
||||
'__name__': '__main__', '__doc__': None,
|
||||
'cairo': gi.repository.cairo,
|
||||
'Gdk': gi.repository.Gdk,
|
||||
'Gegl': gi.repository.Gegl,
|
||||
'Gimp': gi.repository.Gimp,
|
||||
'Gio': gi.repository.Gio,
|
||||
'Gtk': gi.repository.Gtk,
|
||||
'GdkPixbuf': gi.repository.GdkPixbuf,
|
||||
'GLib': gi.repository.GLib,
|
||||
'GObject': gi.repository.GObject,
|
||||
'Pango': gi.repository.Pango }
|
||||
|
||||
class GimpConsole(pyconsole.Console):
|
||||
def __init__(self, quit_func=None):
|
||||
banner = ('GIMP %s Python Console\nPython %s\n' %
|
||||
(Gimp.version(), sys.version))
|
||||
pyconsole.Console.__init__(self,
|
||||
locals=namespace, banner=banner,
|
||||
quit_func=quit_func)
|
||||
def _commit(self):
|
||||
pyconsole.Console._commit(self)
|
||||
Gimp.displays_flush()
|
||||
|
||||
class ConsoleDialog(Gimp.Dialog):
|
||||
def __init__(self):
|
||||
Gimp.Dialog.__init__(self)
|
||||
self.set_property("help-id", PROC_NAME)
|
||||
Gtk.Window.set_title(self, _("Python Console"))
|
||||
Gtk.Window.set_role(self, PROC_NAME)
|
||||
Gtk.Dialog.add_button(self, "Save", Gtk.ResponseType.OK)
|
||||
Gtk.Dialog.add_button(self, "Clear", RESPONSE_CLEAR)
|
||||
Gtk.Dialog.add_button(self, _("_Browse..."), RESPONSE_BROWSE)
|
||||
Gtk.Dialog.add_button(self, "Close", Gtk.ResponseType.CLOSE)
|
||||
|
||||
Gtk.Widget.set_name (self, PROC_NAME)
|
||||
Gtk.Dialog.set_alternative_button_order_from_array(self,
|
||||
[ Gtk.ResponseType.CLOSE,
|
||||
RESPONSE_BROWSE,
|
||||
RESPONSE_CLEAR,
|
||||
Gtk.ResponseType.OK ])
|
||||
|
||||
self.cons = GimpConsole(quit_func=lambda: Gtk.main_quit())
|
||||
|
||||
self.style_set (None, None)
|
||||
|
||||
self.connect('response', self.response)
|
||||
self.connect('style-set', self.style_set)
|
||||
|
||||
self.browse_dlg = None
|
||||
self.save_dlg = None
|
||||
|
||||
vbox = Gtk.VBox(homogeneous=False, spacing=12)
|
||||
vbox.set_border_width(12)
|
||||
contents_area = Gtk.Dialog.get_content_area(self)
|
||||
contents_area.pack_start(vbox, True, True, 0)
|
||||
|
||||
scrl_win = Gtk.ScrolledWindow()
|
||||
scrl_win.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
|
||||
vbox.pack_start(scrl_win, True, True, 0)
|
||||
|
||||
scrl_win.add(self.cons)
|
||||
|
||||
width, height = self.cons.get_default_size()
|
||||
minreq, requisition = Gtk.Widget.get_preferred_size(scrl_win.get_vscrollbar())
|
||||
sb_width = requisition.width
|
||||
sb_height = requisition.height
|
||||
|
||||
# Account for scrollbar width and border width to ensure
|
||||
# the text view gets a width of 80 characters. We don't care
|
||||
# so much whether the height will be exactly 40 characters.
|
||||
Gtk.Window.set_default_size(self, width + sb_width + 2 * 12, height)
|
||||
|
||||
def style_set(self, old_style, user_data):
|
||||
pass
|
||||
#style = Gtk.Widget.get_style (self)
|
||||
#self.cons.stdout_tag.set_property ("foreground", style.text[Gtk.StateType.NORMAL])
|
||||
#self.cons.stderr_tag.set_property ("foreground", style.text[Gtk.StateType.INSENSITIVE])
|
||||
|
||||
def response(self, dialog, response_id):
|
||||
if response_id == RESPONSE_BROWSE:
|
||||
self.browse()
|
||||
elif response_id == RESPONSE_CLEAR:
|
||||
self.cons.banner = None
|
||||
self.cons.clear()
|
||||
elif response_id == Gtk.ResponseType.OK:
|
||||
self.save_dialog()
|
||||
else:
|
||||
Gtk.main_quit()
|
||||
|
||||
self.cons.grab_focus()
|
||||
|
||||
def browse_response(self, dlg, response_id):
|
||||
if response_id != Gtk.ResponseType.APPLY:
|
||||
Gtk.Widget.hide(dlg)
|
||||
return
|
||||
|
||||
proc_name = dlg.get_selected()
|
||||
|
||||
if not proc_name:
|
||||
return
|
||||
|
||||
proc = pdb[proc_name]
|
||||
|
||||
cmd = ''
|
||||
|
||||
if len(proc.return_vals) > 0:
|
||||
cmd = ', '.join(x[1].replace('-', '_')
|
||||
for x in proc.return_vals) + ' = '
|
||||
|
||||
cmd = cmd + 'pdb.%s' % proc.proc_name.replace('-', '_')
|
||||
|
||||
if len(proc.params) > 0 and proc.params[0][1] == 'run-mode':
|
||||
params = proc.params[1:]
|
||||
else:
|
||||
params = proc.params
|
||||
|
||||
cmd = cmd + '(%s)' % ', '.join(x[1].replace('-', '_')
|
||||
for x in params)
|
||||
|
||||
buffer = self.cons.buffer
|
||||
|
||||
lines = buffer.get_line_count()
|
||||
iter = buffer.get_iter_at_line_offset(lines - 1, 4)
|
||||
buffer.delete(iter, buffer.get_end_iter())
|
||||
buffer.place_cursor(buffer.get_end_iter())
|
||||
buffer.insert_at_cursor(cmd)
|
||||
|
||||
def browse(self):
|
||||
if not self.browse_dlg:
|
||||
dlg = Gimp.ProcBrowserDialog()
|
||||
Gtk.Window.set_title(dlg, _("Python Procedure Browser"))
|
||||
Gtk.Window.set_role(dlg, PROC_NAME)
|
||||
Gtk.Dialog.add_button(dlg, "Apply", Gtk.ResponseType.APPLY)
|
||||
Gtk.Dialog.add_button(dlg, "Close", Gtk.ResponseType.CLOSE)
|
||||
|
||||
Gtk.Dialog.set_default_response(self, Gtk.ResponseType.OK)
|
||||
Gtk.Dialog.set_alternative_button_order_from_array(dlg,
|
||||
[ Gtk.ResponseType.CLOSE,
|
||||
Gtk.ResponseType.APPLY ])
|
||||
|
||||
dlg.connect('response', self.browse_response)
|
||||
dlg.connect('row-activated',
|
||||
lambda dlg: dlg.response(Gtk.ResponseType.APPLY))
|
||||
|
||||
self.browse_dlg = dlg
|
||||
|
||||
Gtk.Window.present(self.browse_dlg)
|
||||
|
||||
def save_response(self, dlg, response_id):
|
||||
if response_id == Gtk.ResponseType.DELETE_EVENT:
|
||||
self.save_dlg = None
|
||||
return
|
||||
elif response_id == Gtk.ResponseType.OK:
|
||||
filename = dlg.get_filename()
|
||||
|
||||
try:
|
||||
logfile = open(filename, 'w')
|
||||
except IOError as e:
|
||||
Gimp.message(_("Could not open '%s' for writing: %s") %
|
||||
(filename, e.strerror))
|
||||
return
|
||||
|
||||
buffer = self.cons.buffer
|
||||
|
||||
start = buffer.get_start_iter()
|
||||
end = buffer.get_end_iter()
|
||||
|
||||
log = buffer.get_text(start, end, False)
|
||||
|
||||
try:
|
||||
logfile.write(log)
|
||||
logfile.close()
|
||||
except IOError as e:
|
||||
Gimp.message(_("Could not write to '%s': %s") %
|
||||
(filename, e.strerror))
|
||||
return
|
||||
|
||||
Gtk.Widget.hide(dlg)
|
||||
|
||||
def save_dialog(self):
|
||||
if not self.save_dlg:
|
||||
dlg = Gtk.FileChooserDialog()
|
||||
Gtk.Window.set_title(dlg, _("Save Python-Fu Console Output"))
|
||||
Gtk.Window.set_transient_for(dlg, self)
|
||||
Gtk.Dialog.add_button(dlg, "Cancel", Gtk.ResponseType.CANCEL)
|
||||
Gtk.Dialog.add_button(dlg, "Save", Gtk.ResponseType.OK)
|
||||
Gtk.Dialog.set_default_response(self, Gtk.ResponseType.OK)
|
||||
Gtk.Dialog.set_alternative_button_order_from_array(dlg,
|
||||
[ Gtk.ResponseType.OK,
|
||||
Gtk.ResponseType.CANCEL ])
|
||||
|
||||
dlg.connect('response', self.save_response)
|
||||
|
||||
self.save_dlg = dlg
|
||||
|
||||
self.save_dlg.present()
|
||||
|
||||
def run(self):
|
||||
Gtk.Widget.show_all(self)
|
||||
Gtk.main()
|
||||
|
||||
ConsoleDialog().run()
|
||||
retval = [Gimp.param_from_status (Gimp.PDBStatusType.SUCCESS)]
|
||||
return len(retval), retval
|
||||
|
||||
def query():
|
||||
param = Gimp.ParamDef()
|
||||
param.type = Gimp.PDBArgType.INT32
|
||||
param.name = "run-mode"
|
||||
param.description = _("Run mode")
|
||||
|
||||
Gimp.install_procedure(
|
||||
PROC_NAME,
|
||||
N_("Interactive GIMP Python interpreter"),
|
||||
"Type in commands and see results",
|
||||
"James Henstridge",
|
||||
"James Henstridge",
|
||||
"1997-1999",
|
||||
N_("_Console"),
|
||||
"",
|
||||
Gimp.PDBProcType.PLUGIN,
|
||||
[ param ],
|
||||
[])
|
||||
Gimp.plugin_menu_register(PROC_NAME, "<Image>/Filters/Languages/Python-Fu")
|
||||
Gimp.plugin_domain_register("gimp30-python", Gimp.locale_directory())
|
||||
|
||||
info = Gimp.PlugInInfo ()
|
||||
info.set_callbacks (None, None, query, run)
|
||||
Gimp.main_legacy (info, sys.argv)
|
Loading…
Reference in New Issue