Module scribusscript
[hide private]
[frames] | no frames]

Source Code for Module scribusscript

  1  """ 
  2  Loader and handler for Scribus descriptor files/headers. 
  3  They help to integrate scripts into Scribus by definining menu-entries,  
  4  shortcuts, etc. Some additional metadata gives useful information and help. 
  5   
  6  See doc/TUTORIAL for a detailed explanation including examples. 
  7  """ 
  8  import sys 
  9  import re 
 10  import os 
 11  from ConfigParser import ConfigParser 
 12  from StringIO import StringIO 
 13  from PyQt4.QtGui import QKeySequence, QIcon 
 14   
 15  import excepthook 
 16  from scripterng_hooks import MenuHooks 
 17  from scripterng_runtime import run_filename 
18 19 20 -class ValidationError(Exception):
21 pass
22
23 -class EmptyDescriptor(ValidationError):
24 pass
25
26 27 -def validate_bool(value):
28 lvalue = value.lower() 29 if lvalue in ["0", "no", "false", "off"]: 30 return False 31 elif lvalue in ["1", "yes", "true", "on"]: 32 return True
33
34 35 -def validate_regex(pattern):
36 def check(value): 37 found = re.match(pattern, value) 38 if found: 39 return found.group(0) 40 raise ValidationError, \ 41 "Value %r does not match regular expression pattern %r" % pattern
42 return check 43 44 45 validate_ident = validate_regex("[A-Za-z_][A-Za-z_]*")
46 47 48 -def validate_list(value):
49 return value.split(",")
50
51 52 -def validate_intlist(value):
53 try: 54 return [int(v) for v in validate_list(value)] 55 except ValueError, e: 56 raise ValidationError, "Int-validation error: %s" % e
57
58 59 -def validate_enum(*args):
60 def check(value): 61 if value.lower() in args: 62 return value.lower() 63 raise ValidationError, "%r not in %r" % (value, args)
64 return check 65
66 67 -def validate_enumlist(*args):
68 def check(value): 69 l = [] 70 for v in value.split(","): 71 l.append(validate_enum(args)(v)) 72 return l
73 return check 74
75 76 -class Item(object):
77 78 _counter = 0 79
80 - def __init__(self, name, default=None, validate=None, 81 required=False):
82 self._item_id = Item._counter 83 Item._counter += 1 84 self.name = name 85 self.default = default 86 if isinstance(validate, basestring): 87 validate = validate_regex 88 self.validate = validate or (lambda v:v) 89 self.required = required
90 91
92 - def __call__(self, value, ignore_errors=False):
93 try: 94 pyvalue = self.validate(value) 95 except ValidationError, e: 96 if not ignore_errors: 97 raise 98 pyvalue = self.default 99 return pyvalue
100
101 102 103 104 -class ScribusScript(object):
105 106 # Some items are disabled for now. 107 # They will be supported in a future release. 108 items = [ 109 Item("name"), 110 Item("title"), 111 Item("description"), 112 Item("icon"), 113 #Item("category"), 114 Item("menu", "ScripterNG"), 115 #Item("context_menu"), 116 Item("shortcut"), 117 Item("filename"), 118 Item("subroutine"), 119 Item("author"), 120 Item("contact"), 121 Item("homepage"), 122 Item("version"), 123 Item("copyright", "Licensed under GPLl 2 or later"), 124 Item("scribus_version"), 125 #Item("tags", [], validate_list), 126 #Item("depends", [], validate_list), 127 #Item("requires", [], validate_enumlist( 128 # "document", "selection", "text", "image")), 129 Item("redraw", True, validate_bool), 130 Item("mode", "interactive", validate_enum("batch", "interactive", "extension")), 131 #Item("before_action"), 132 #Item("after_action"), 133 #Item("on_event"), 134 Item("language", "python", validate_enum("python", "qtscript")), 135 Item("separator_before", False, validate_bool), 136 Item("separator_after", False, validate_bool), 137 Item("background_mode", False, validate_bool), 138 ] 139 140
141 - def __init__(self, _data=None, **kwargs):
142 self.data = {} 143 d = dict(_data or {}, **kwargs) 144 for item in self.__class__.items: 145 self.data[item.name] = d.pop(item.name, item.default) 146 if d: 147 raise TypeError, "Unknown items: %s" % ", ".join(d.keys())
148 149
150 - def __repr__(self):
151 return "<%s %r>" % (self.__class__.__name__, self.data)
152 153
154 - def __getattr__(self, name):
155 return self.data[name]
156 157
158 - def __getitem__(self, name):
159 return self.data[name]
160 161
162 - def get(self, name, default=None):
163 return self.data.get(name, default)
164 165
166 - def __setitem__(self, name, value):
167 self.data[name] = value
168 169
170 - def install(self):
171 """ 172 currently only can create menu entries and sets shortcuts 173 """ 174 if self.menu: 175 mh = MenuHooks() 176 menu = mh.findMenu(self.menu) 177 if not menu: 178 menu = mh.createMenu(self.menu) 179 mh.appendMenu(menu) 180 if self.separator_before: 181 mh.appendSeparator(menu) 182 self.action = mh.appendItem(menu, self.title, lambda :self.run()) 183 if self.separator_after: 184 mh.appendSeparator(menu) 185 if self.icon: 186 icon_filename = os.path.join( 187 os.path.dirname(self.filename), self.icon) 188 if os.path.exists(icon_filename): 189 self.action.setIcon(QIcon(icon_filename)) 190 else: 191 print >> sys.stderr, "Icon %r not found" % icon_filename 192 if self.shortcut: 193 self.action.setShortcut(QKeySequence(self.shortcut))
194 195
196 - def run(self, catch_errors=True):
197 """ 198 uses scripterng_runtime to call a script 199 """ 200 try: 201 win = ScripterNG.activeWindow 202 if win: 203 win.redraw = self.redraw 204 run_filename(self.filename, self.subroutine, 205 extension=(self.mode == "extension"), 206 background=self.background_mode) 207 if win: 208 win.redraw = True 209 if not self.redraw: 210 win.update() 211 except: 212 if not catch_errors: 213 raise 214 excepthook.show_current_error("Error running %r" % os.path.basename(self.filename))
215 216 217 @classmethod
218 - def parse_filename(cls, filename):
219 s = open(filename).read(8192) 220 name, ext = os.path.splitext(os.path.basename(filename)) 221 parse = cls.filetypes[ext] 222 try: 223 obj = parse(s) 224 except EmptyDescriptor: 225 if ext in [".spy"]: 226 language = "python" 227 elif ext in [".sjs", ".sqts"]: 228 language = "qtscript" 229 obj = cls(name=name, title=name.capitalize(), 230 language=language) 231 if not obj.get("filename"): 232 obj["filename"] = filename 233 return obj
234 235 236 @classmethod
237 - def parse_python(cls, source):
238 s = "[ScribusScript]\nlanguage=python\n" 239 for line in source.splitlines(): 240 if not line.startswith("#"): 241 break 242 if line.startswith("##"): 243 s += line[2:].strip() + "\n" 244 return cls.parse(s)
245 246 247 @classmethod
248 - def parse_qtscript(cls, source):
249 s = "[ScribusScript]\nlanguage=qtscript\n" 250 for line in source.splitlines(): 251 if not line.startswith("//"): 252 break 253 if line.startswith("///"): 254 s += line[3:].strip() + "\n" 255 return cls.parse(s)
256 257 258 @classmethod
259 - def parse(cls, s):
260 data = {} 261 cfg = ConfigParser() 262 s = "[ScribusScript]\n" + s 263 cfg.readfp(StringIO(s)) 264 options = cfg.options("ScribusScript") 265 if not len(options): 266 raise EmptyDescriptor 267 for item in cls.items: 268 if not item.name in options: 269 if item.required: 270 raise ValidationError, "Option %r required but not set" % item.name 271 else: 272 continue 273 options.remove(item.name) 274 value = cfg.get("ScribusScript", item.name) 275 data[item.name] = item(value) 276 if options: 277 raise ValidationError, "Invalid options found: %s" % ", ".join(options) 278 279 return cls(**data)
280 281 282 283 ScribusScript.filetypes = { 284 # XXX support zip archives 285 ".spy": ScribusScript.parse_python, 286 ".sjs": ScribusScript.parse_qtscript, 287 ".sqts": ScribusScript.parse_qtscript, 288 ".scs": ScribusScript.parse 289 }
290 291 292 293 -def load_filename(filename):
294 return ScribusScript.parse_filename(filename)
295
296 297 298 -def load_scripts(path_or_filename):
299 if os.path.isdir(path_or_filename): 300 path = os.path.abspath(os.path.expanduser(path_or_filename)) 301 extensions = ScribusScript.filetypes.keys() 302 files = [ os.path.join(path, name) for name in os.listdir(path) \ 303 if os.path.splitext(name)[1] in extensions ] 304 else: 305 files = [ os.path.abspath(os.path.expanduser(path_or_filename)) ] 306 scripts = [] 307 for filename in files: 308 try: 309 sd = load_filename(filename) 310 scripts.append(sd) 311 except: 312 excepthook.show_current_error("Error loading %r" % os.path.basename(filename)) 313 return scripts
314 315 316 317 318 if __name__ == "__main__": 319 # Show script descriptor for given files 320 for filename in sys.argv[1:]: 321 script = ScribusScript.parse_filename(filename) 322 print filename 323 print script 324 if not sys.argv[1:]: 325 for sd in load_scripts("."): 326 print sd 327