1#!/usr/local/bin/python 2 3# Gimp-Python - allows the writing of Gimp plugins in Python. 4# Copyright (C) 1997 James Henstridge <james@daa.com.au> 5# 6# This program is free software: you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 3 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <https://www.gnu.org/licenses/>. 18 19from gimpfu import * 20 21t = gettext.translation('gimp20-python', gimp.locale_directory, fallback=True) 22_ = t.ugettext 23 24PROC_NAME = 'python-fu-console' 25 26RESPONSE_BROWSE, RESPONSE_CLEAR, RESPONSE_SAVE = range(3) 27 28def do_console(): 29 import pygtk 30 pygtk.require('2.0') 31 32 import sys, gobject, gtk, gimpenums, gimpshelf, gimpui, pyconsole 33 gimpui.gimp_ui_init () 34 35 namespace = {'__builtins__': __builtins__, 36 '__name__': '__main__', '__doc__': None, 37 'gimp': gimp, 'pdb': gimp.pdb, 38 'shelf': gimpshelf.shelf} 39 40 for s in gimpenums.__dict__.keys(): 41 if s[0] != '_': 42 namespace[s] = getattr(gimpenums, s) 43 44 class GimpConsole(pyconsole.Console): 45 def __init__(self, quit_func=None): 46 banner = ('GIMP %s Python Console\nPython %s\n' % 47 (gimp.pdb.gimp_version(), sys.version)) 48 pyconsole.Console.__init__(self, 49 locals=namespace, banner=banner, 50 quit_func=quit_func) 51 def _commit(self): 52 pyconsole.Console._commit(self) 53 gimp.displays_flush() 54 55 class ConsoleDialog(gimpui.Dialog): 56 def __init__(self): 57 gimpui.Dialog.__init__(self, title=_("Python Console"), 58 role=PROC_NAME, help_id=PROC_NAME, 59 buttons=(gtk.STOCK_SAVE, RESPONSE_SAVE, 60 gtk.STOCK_CLEAR, RESPONSE_CLEAR, 61 _("_Browse..."), RESPONSE_BROWSE, 62 gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) 63 64 self.set_name (PROC_NAME) 65 self.set_alternative_button_order((gtk.RESPONSE_CLOSE, 66 RESPONSE_BROWSE, 67 RESPONSE_CLEAR, 68 RESPONSE_SAVE)) 69 70 self.cons = GimpConsole(quit_func=lambda: gtk.main_quit()) 71 72 self.style_set (None, None) 73 74 self.connect('response', self.response) 75 self.connect('style-set', self.style_set) 76 77 self.browse_dlg = None 78 self.save_dlg = None 79 80 vbox = gtk.VBox(False, 12) 81 vbox.set_border_width(12) 82 self.vbox.pack_start(vbox) 83 84 scrl_win = gtk.ScrolledWindow() 85 scrl_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) 86 vbox.pack_start(scrl_win) 87 88 scrl_win.add(self.cons) 89 90 width, height = self.cons.get_default_size() 91 sb_width, sb_height = scrl_win.get_vscrollbar().size_request() 92 93 # Account for scrollbar width and border width to ensure 94 # the text view gets a width of 80 characters. We don't care 95 # so much whether the height will be exactly 40 characters. 96 self.set_default_size(width + sb_width + 2 * 12, height) 97 98 def style_set(self, old_style, user_data): 99 style = self.get_style () 100 self.cons.stdout_tag.set_property ("foreground", style.text[gtk.STATE_NORMAL]) 101 self.cons.stderr_tag.set_property ("foreground", style.text[gtk.STATE_INSENSITIVE]) 102 103 def response(self, dialog, response_id): 104 if response_id == RESPONSE_BROWSE: 105 self.browse() 106 elif response_id == RESPONSE_CLEAR: 107 self.cons.banner = None 108 self.cons.clear() 109 elif response_id == RESPONSE_SAVE: 110 self.save_dialog() 111 else: 112 gtk.main_quit() 113 114 self.cons.grab_focus() 115 116 def browse_response(self, dlg, response_id): 117 if response_id != gtk.RESPONSE_APPLY: 118 dlg.hide() 119 return 120 121 proc_name = dlg.get_selected() 122 123 if not proc_name: 124 return 125 126 proc = pdb[proc_name] 127 128 cmd = '' 129 130 if len(proc.return_vals) > 0: 131 cmd = ', '.join([x[1].replace('-', '_') 132 for x in proc.return_vals]) + ' = ' 133 134 cmd = cmd + 'pdb.%s' % proc.proc_name.replace('-', '_') 135 136 if len(proc.params) > 0 and proc.params[0][1] == 'run-mode': 137 params = proc.params[1:] 138 else: 139 params = proc.params 140 141 cmd = cmd + '(%s)' % ', '.join([x[1].replace('-', '_') 142 for x in params]) 143 144 buffer = self.cons.buffer 145 146 lines = buffer.get_line_count() 147 iter = buffer.get_iter_at_line_offset(lines - 1, 4) 148 buffer.delete(iter, buffer.get_end_iter()) 149 buffer.place_cursor(buffer.get_end_iter()) 150 buffer.insert_at_cursor(cmd) 151 152 def browse(self): 153 if not self.browse_dlg: 154 dlg = gimpui.ProcBrowserDialog(_("Python Procedure Browser"), 155 role=PROC_NAME, 156 buttons=(gtk.STOCK_APPLY, 157 gtk.RESPONSE_APPLY, 158 gtk.STOCK_CLOSE, 159 gtk.RESPONSE_CLOSE)) 160 161 dlg.set_default_response(gtk.RESPONSE_APPLY) 162 dlg.set_alternative_button_order((gtk.RESPONSE_CLOSE, 163 gtk.RESPONSE_APPLY)) 164 165 dlg.connect('response', self.browse_response) 166 dlg.connect('row-activated', 167 lambda dlg: dlg.response(gtk.RESPONSE_APPLY)) 168 169 self.browse_dlg = dlg 170 171 self.browse_dlg.present() 172 173 def save_response(self, dlg, response_id): 174 if response_id == gtk.RESPONSE_DELETE_EVENT: 175 self.save_dlg = None 176 return 177 elif response_id == gtk.RESPONSE_OK: 178 filename = dlg.get_filename() 179 180 try: 181 logfile = open(filename, 'w') 182 except IOError, e: 183 gimp.message(_("Could not open '%s' for writing: %s") % 184 (filename, e.strerror)) 185 return 186 187 buffer = self.cons.buffer 188 189 start = buffer.get_start_iter() 190 end = buffer.get_end_iter() 191 192 log = buffer.get_text(start, end, False) 193 194 try: 195 logfile.write(log) 196 logfile.close() 197 except IOError, e: 198 gimp.message(_("Could not write to '%s': %s") % 199 (filename, e.strerror)) 200 return 201 202 dlg.hide() 203 204 def save_dialog(self): 205 if not self.save_dlg: 206 dlg = gtk.FileChooserDialog(_("Save Python-Fu Console Output"), 207 parent=self, 208 action=gtk.FILE_CHOOSER_ACTION_SAVE, 209 buttons=(gtk.STOCK_CANCEL, 210 gtk.RESPONSE_CANCEL, 211 gtk.STOCK_SAVE, 212 gtk.RESPONSE_OK)) 213 214 dlg.set_default_response(gtk.RESPONSE_OK) 215 dlg.set_alternative_button_order((gtk.RESPONSE_OK, 216 gtk.RESPONSE_CANCEL)) 217 218 dlg.connect('response', self.save_response) 219 220 self.save_dlg = dlg 221 222 self.save_dlg.present() 223 224 def run(self): 225 self.show_all() 226 gtk.main() 227 228 ConsoleDialog().run() 229 230register( 231 PROC_NAME, 232 N_("Interactive GIMP Python interpreter"), 233 "Type in commands and see results", 234 "James Henstridge", 235 "James Henstridge", 236 "1997-1999", 237 N_("_Console"), 238 "", 239 [], 240 [], 241 do_console, 242 menu="<Image>/Filters/Languages/Python-Fu", 243 domain=("gimp20-python", gimp.locale_directory)) 244 245main() 246