"Fossies" - the Fresh Open Source Software Archive 
Member "btn4ws-0.7.0/btn4ws.py" (5 Jan 2008, 46656 Bytes) of package /linux/privat/old/btn4ws-0.7.0.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "btn4ws.py" see the
Fossies "Dox" file reference documentation.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This is the main file of btn4ws.
4 #
5 # Copyright (c) 1999-2008 Jan Dittberner <jan@dittberner.info>
6 #
7 # This file is part of btn4ws.
8 #
9 # btn4ws is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
13 #
14 # btn4ws is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with btn4ws; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #
23 # version: $Id: btn4ws.py 14 2008-01-05 22:19:03Z jan $
24 #
25
26 """
27 Gimp script to generate button images for websites. This script is a
28 port of the older gimp-perl version to python.
29
30 (c) 2007, 2008 Jan Dittberner <jan@dittberner.info>
31 """
32 import os, urllib, logging, sys, pickle
33 import gimp, gimpplugin, gimpui, gimpcolor
34 import pygtk
35 pygtk.require('2.0')
36 import gtk
37 from gimpenums import *
38 from gimpshelf import shelf
39 pdb = gimp.pdb
40
41 btn4ws_version = "0.7.0"
42
43 logging.basicConfig(level=logging.DEBUG,
44 format='%(asctime)s %(levelname)s %(message)s',
45 stream=sys.stderr)
46
47 class text_to_name_mapper:
48 """
49 Text string to name mapper class. This class provides mappings for several target
50 environments.
51 """
52 def __init__(self, strings):
53 self.mapping = {}
54 for string in strings: self.mapping[string] = {}
55 self.idnum = 1
56 logging.debug("self.mapping=" + str(self.mapping))
57
58 def asitemid(self, text):
59 """
60 Get a img itemid for the given text.
61 """
62 if 'itemid' not in self.mapping[text]:
63 self.mapping[text]['itemid'] = "id%03d" % (self.idnum)
64 self.idnum += 1
65 #logging.debug("self.mapping=" + str(self.mapping))
66 return self.mapping[text]['itemid']
67
68 def asjavascriptid(self, text):
69 """
70 Get a javascript itemid for the given text.
71 """
72 if 'jsid' not in self.mapping[text]:
73 self.mapping[text]['jsid'] = "img%03d" % (self.idnum)
74 self.idnum += 1
75 #logging.debug("self.mapping=" + str(self.mapping))
76 return self.mapping[text]['jsid']
77
78 def aslinktarget(self, text):
79 """
80 Get a link target for the given text.
81 """
82 if 'link' not in self.mapping[text]:
83 self.mapping[text]['link'] = urllib.quote(text)
84 #logging.debug("self.mapping=" + str(self.mapping))
85 return "%s.html" % (self.mapping[text]['link'])
86
87 def asfilename(self, text, extension = 'png', prefix= '', dirname = None):
88 """
89 Get a filename for the given text with optional extension, prefix and dirname.
90 """
91 if 'file' not in self.mapping[text]:
92 self.mapping[text]['file'] = text.encode('ascii', 'ignore')
93 fname = "%s%s.%s" % (prefix, self.mapping[text]['file'], extension)
94 #logging.debug("self.mapping=" + str(self.mapping))
95 if dirname:
96 return os.path.join(dirname, fname)
97 return fname
98
99 class text_to_name_mapper:
100 """
101 Text string to name mapper class. This class provides mappings for
102 several target environments.
103 """
104 def __init__(self, strings):
105 self.mapping = {}
106 for string in strings: self.mapping[string] = {}
107 self.idnum = 1
108 logging.debug("self.mapping=" + str(self.mapping))
109
110 def asitemid(self, text):
111 """
112 Get a img itemid for the given text.
113 """
114 if 'itemid' not in self.mapping[text]:
115 self.mapping[text]['itemid'] = "id%03d" % (self.idnum)
116 self.idnum += 1
117 logging.debug("self.mapping=" + str(self.mapping))
118 return self.mapping[text]['itemid']
119
120 def asjavascriptid(self, text):
121 """
122 Get a javascript itemid for the given text.
123 """
124 if 'jsid' not in self.mapping[text]:
125 self.mapping[text]['jsid'] = "img%03d" % (self.idnum)
126 self.idnum += 1
127 logging.debug("self.mapping=" + str(self.mapping))
128 return self.mapping[text]['jsid']
129
130 def aslinktarget(self, text):
131 """
132 Get a link target for the given text.
133 """
134 if 'link' not in self.mapping[text]:
135 self.mapping[text]['link'] = urllib.quote(text)
136 logging.debug("self.mapping=" + str(self.mapping))
137 return "%s.html" % (self.mapping[text]['link'])
138
139 def asfilename(self, text, extension = 'png', prefix= '', dirname = None):
140 """
141 Get a filename for the given text with optional extension,
142 prefix and dirname.
143 """
144 if 'file' not in self.mapping[text]:
145 self.mapping[text]['file'] = text.encode('ascii', 'ignore')
146 fname = "%s%s.%s" % (prefix, self.mapping[text]['file'], extension)
147 logging.debug("self.mapping=" + str(self.mapping))
148 if dirname:
149 return os.path.join(dirname, fname)
150 return fname
151
152 class IntEntry(gtk.Entry):
153 """Input field for integer numbers."""
154
155 def __init__(self, max = 0):
156 gtk.Entry.__init__(self, max)
157 self.set_property("truncate-multiline", True)
158 self.connect("insert-text", self._cb_int_field_insert)
159
160 def _cb_int_field_insert(self, w, new_text, new_text_length, position):
161 """Allow integer input only."""
162 if not new_text.isdigit():
163 w.stop_emission("insert-text")
164
165 class Btn4wsDialog(gtk.Assistant):
166 """This class is the input dialog field for btn4ws"""
167 def _cb_delete_event(self, widget, event, data = None):
168 logging.debug("delete_event")
169 return False
170
171 def __init__(self, args):
172 self.data = args
173 self.pages = {}
174 gtk.Assistant.__init__(self)
175
176 self._addIntroPage()
177 self._addPathSelectionPage()
178 self._addBasicSettingsPage()
179 self._addLayoutPage()
180 self._addEffectsPage()
181 self._addLastPage()
182 self.show()
183 self.connect("delete_event", self._cb_delete_event)
184
185 for pagename in self.pages.iterkeys():
186 self.checkcompletion(pagename)
187
188 def _addIntroPage(self):
189 label = gtk.Label("""Buttons for website allows you to produce a series of buttons for use on a website. On the next pages you may choose several options to change the content and the look of the buttons.""")
190 label.set_line_wrap(True)
191 label.show()
192 self.append_page(label)
193 self.set_page_title(label, "Introduction")
194 self.set_page_complete(label, True)
195
196 def _addPathSelectionPage(self):
197 page = gtk.VBox(False, 5)
198 self.pages["pathselection"] = page
199 page.set_border_width(5)
200 page.show()
201 self.append_page(page)
202 self.set_page_title(page,
203 "Select the input file and output directory")
204 self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT)
205
206 label = gtk.Label("Please choose the file containing your button labels and the directory where the generated files should be put.")
207 label.set_line_wrap(True)
208 label.show()
209 page.pack_start(label, True, True, 0)
210
211 table = gtk.Table(rows=2, columns=2, homogeneous=False)
212 table.show()
213 label = gtk.Label("Button label file")
214 label.show()
215 table.attach(label, 0, 1, 0, 1)
216 button = gtk.FileChooserButton("Choose file")
217 button.set_action(gtk.FILE_CHOOSER_ACTION_OPEN)
218 if self.data["filename"]:
219 button.set_filename(self.data["filename"])
220 button.connect("selection-changed", self._cb_file_selected)
221 button.show()
222 table.attach(button, 1, 2, 0, 1)
223
224 label = gtk.Label("Output directory")
225 label.show()
226 table.attach(label, 0, 1, 1, 2)
227 button = gtk.FileChooserButton("Choose directory")
228 button.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
229 if self.data["outdir"]:
230 button.set_filename(self.data["outdir"])
231 else:
232 self.data["outdir"] = button.get_filename()
233 button.connect("selection-changed", self._cb_dir_selected)
234 button.show()
235 table.attach(button, 1, 2, 1, 2)
236
237 page.pack_end(table)
238
239 def _addBasicSettingsPage(self):
240 page = gtk.VBox(False, 5)
241 self.pages["basicsettings"] = page
242 page.set_border_width(5)
243 page.show()
244 self.append_page(page)
245 self.set_page_title(page,
246 "Select the basic button settings")
247 self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT)
248
249 label = gtk.Label("Please choose the basic layout settings of your buttons.")
250 label.set_line_wrap(True)
251 label.show()
252 page.pack_start(label, True, True, 0)
253
254 table = gtk.Table(rows=4, columns=2, homogeneous=False)
255 table.show()
256
257 # font
258 label = gtk.Label("Button text font")
259 label.show()
260 table.attach(label, 0, 1, 0, 1)
261 fontsel = gtk.FontButton()
262 fontsel.set_show_size(True)
263 if self.data["font"]:
264 fontsel.set_font_name(self.data["font"])
265 else:
266 self.data["font"] = fontsel.get_font_name()
267 fontsel.show()
268 fontsel.connect("font-set", self._cb_set_font)
269 table.attach(fontsel, 1, 2, 0, 1)
270
271 # strcolor
272 label = gtk.Label("Button text color")
273 label.show()
274 table.attach(label, 0, 1, 1, 2)
275 colorsel = gimpui.ColorSelector()
276 if self.data["strcolor"]:
277 colorsel.set_color(self.data["strcolor"])
278 else:
279 self.data["strcolor"] = colorsel.get_color()
280 colorsel.show()
281 colorsel.connect("color-changed", self._cb_set_color, "strcolor",
282 "basicsettings")
283 table.attach(colorsel, 1, 2, 1, 2)
284
285 # background toggle
286 bgtoggle = gtk.CheckButton("Use a pattern for button")
287 bgtoggle.set_active(self.data["usepattern"])
288 bgtoggle.show()
289 table.attach(bgtoggle, 1, 2, 2, 3)
290
291 # background color / pattern
292 if self.data["usepattern"]:
293 label = gtk.Label("Button pattern")
294 else:
295 label = gtk.Label("Button color")
296 label.show()
297 patternsel = gimpui.PatternSelectButton()
298 if self.data["pattern"]:
299 patternsel.set_pattern(self.data["pattern"])
300 patternsel.connect("pattern-set", self._cb_set_pattern)
301 colorsel = gimpui.ColorSelector()
302 if self.data["buttoncolor"]:
303 colorsel.set_color(self.data["buttoncolor"])
304 else:
305 self.data["buttoncolor"] = colorsel.get_color()
306 colorsel.connect("color-changed", self._cb_set_color, "buttoncolor",
307 "basicsettings")
308 bgtoggle.connect("toggled", self._cb_bgtoggle_toggle, label,
309 "Button pattern", patternsel,
310 "Button color", colorsel)
311 if self.data["usepattern"]:
312 patternsel.show()
313 else:
314 colorsel.show()
315
316 table.attach(label, 0, 1, 3, 4)
317 table.attach(patternsel, 1, 2, 3, 4)
318 table.attach(colorsel, 1, 2, 3, 4)
319
320 page.pack_end(table)
321
322 def _addLayoutPage(self):
323 page = gtk.VBox(False, 5)
324 self.pages["layout"] = page
325 page.set_border_width(5)
326 page.show()
327 self.append_page(page)
328 self.set_page_title(page,
329 "Select the layout settings")
330 self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT)
331
332 label = gtk.Label("Select the layout options for your buttons")
333 label.set_line_wrap(True)
334 label.show()
335 page.pack_start(label, True, True, 0)
336
337 table = gtk.Table(rows=5, columns=2, homogeneous=False)
338 table.show()
339
340 #roundradius
341 label = gtk.Label("Round radius")
342 label.show()
343 entry = IntEntry(max = 2)
344 if self.data["roundradius"]:
345 entry.set_text(str(self.data["roundradius"]))
346 entry.connect("changed", self._cb_set_intvalue, "roundradius",
347 "layout")
348 entry.show()
349 table.attach(label, 0, 1, 0, 1)
350 table.attach(entry, 1, 2, 0, 1)
351
352 #bevelwidth
353 label = gtk.Label("Bevel width")
354 label.show()
355 entry = IntEntry(max = 2)
356 entry.connect("changed", self._cb_set_intvalue, "bevelwidth",
357 "layout")
358 entry.show()
359 table.attach(label, 0, 1, 1, 2)
360 table.attach(entry, 1, 2, 1, 2)
361
362 #padding
363 label = gtk.Label("Padding")
364 label.show()
365 entry = IntEntry(max = 2)
366 entry.connect("changed", self._cb_set_intvalue, "padding",
367 "layout")
368 entry.show()
369 table.attach(label, 0, 1, 2, 3)
370 table.attach(entry, 1, 2, 2, 3)
371
372 #transparency
373 transp = gtk.CheckButton("Transparent button background")
374 transp.set_active(self.data["transparency"])
375 transp.show()
376 transp.connect("toggled", self._cb_toggle_simple, "transparency",
377 "layout")
378 table.attach(transp, 1, 2, 3, 4)
379
380 #bgcolor
381 label = gtk.Label("Background color")
382 label.show()
383 colorsel = gimpui.ColorSelector()
384 if self.data["bgcolor"]:
385 colorsel.set_color(self.data["bgcolor"])
386 else:
387 self.data["bgcolor"] = colorsel.get_color()
388 colorsel.connect("color-changed", self._cb_set_color, "bgcolor",
389 "layout")
390 colorsel.show()
391 table.attach(label, 0, 1, 4, 5)
392 table.attach(colorsel, 1, 2, 4, 5)
393
394 page.pack_end(table)
395
396 def _addEffectsPage(self):
397 page = gtk.VBox(False, 5)
398 self.pages["effects"] = page
399 page.set_border_width(5)
400 page.show()
401 self.append_page(page)
402 self.set_page_title(page,
403 "Select the effect settings")
404 self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT)
405
406 table = gtk.Table(rows=7, columns=2, homogeneous=False)
407 table.show()
408
409 #nova
410 novatoggle = gtk.CheckButton("Enable nova effect")
411 novatoggle.set_active(self.data["nova"])
412 novatoggle.show()
413 table.attach(novatoggle, 1, 2, 0, 1)
414
415 #novacolor
416 label = gtk.Label("Nova color")
417 label.show()
418 novacolor = gimpui.ColorSelector()
419 if self.data["novacolor"]:
420 novacolor.set_color(self.data["novacolor"])
421 else:
422 self.data["novacolor"] = novacolor.get_color()
423 novacolor.connect("color-changed", self._cb_set_color, "novacolor",
424 "effects")
425 novacolor.set_sensitive(self.data["nova"])
426 novacolor.show()
427 table.attach(label, 0, 1, 1, 2)
428 table.attach(novacolor, 1, 2, 1, 2)
429
430 #novaradius
431 label = gtk.Label("Nova radius")
432 label.show()
433 novaradius = IntEntry(max = 2)
434 if self.data["novaradius"] is not None:
435 novaradius.set_text(str(self.data["novaradius"]))
436 novaradius.connect("changed", self._cb_set_intvalue, "novaradius",
437 "effects")
438 novaradius.set_sensitive(self.data["nova"])
439 novaradius.show()
440 table.attach(label, 0, 1, 2, 3)
441 table.attach(novaradius, 1, 2, 2, 3)
442
443 #novasparkles
444 label = gtk.Label("Nova sparkles")
445 label.show()
446 novasparkles = IntEntry(max = 2)
447 if self.data["novasparkles"] is not None:
448 novasparkles.set_text(str(self.data["novasparkles"]))
449 novasparkles.connect("changed", self._cb_set_intvalue, "novasparkles",
450 "effects")
451 novasparkles.set_sensitive(self.data["nova"])
452 novasparkles.show()
453 table.attach(label, 0, 1, 3, 4)
454 table.attach(novasparkles, 1, 2, 3, 4)
455 novatoggle.connect("toggled", self._cb_nova_toggle, novacolor,
456 novaradius, novasparkles)
457
458 #glow
459 glowtoggle = gtk.CheckButton("Enable glow effect")
460 glowtoggle.set_active(self.data["glow"])
461 glowtoggle.show()
462 table.attach(glowtoggle, 1, 2, 4, 5)
463
464 #glowcolor
465 label = gtk.Label("Glow color")
466 label.show()
467 glowcolor = gimpui.ColorSelector()
468 if self.data["glowcolor"]:
469 glowcolor.set_color(self.data["glowcolor"])
470 else:
471 self.data["glowcolor"] = glowcolor.get_color()
472 glowcolor.connect("color-changed", self._cb_set_color, "glowcolor",
473 "effects")
474 glowcolor.set_sensitive(self.data["glow"])
475 glowcolor.show()
476 table.attach(label, 0, 1, 5, 6)
477 table.attach(glowcolor, 1, 2, 5, 6)
478
479 #glowsize
480 label = gtk.Label("Glow size")
481 label.show()
482 glowsize = IntEntry(max = 2)
483 glowsize.connect("changed", self._cb_set_intvalue, "glowsize",
484 "effects")
485 glowsize.set_sensitive(self.data["glow"])
486 glowsize.show()
487 table.attach(label, 0, 1, 6, 7)
488 table.attach(glowsize, 1, 2, 6, 7)
489 glowtoggle.connect("toggled", self._cb_glow_toggle, glowcolor,
490 glowsize)
491
492 page.pack_end(table)
493
494 def _addLastPage(self):
495 page = gtk.VBox(False, 5)
496 self.pages["output"] = page
497 page.set_border_width(5)
498 page.show()
499 self.append_page(page)
500 self.set_page_title(page,
501 "Output formats")
502 self.set_page_type(page, gtk.ASSISTANT_PAGE_CONFIRM)
503
504 label = gtk.Label("Choose the output data.")
505 label.show()
506 page.pack_start(label)
507
508 #makejscript
509 toggle = gtk.CheckButton("Make HTML, CSS and JavaScript")
510 toggle.set_active(self.data["makejscript"])
511 toggle.connect("toggled", self._cb_toggle_simple, "makejscript",
512 "output")
513 toggle.show()
514 page.pack_start(toggle)
515
516 #makeinactive
517 toggle = gtk.CheckButton("Make inactive buttons")
518 toggle.set_active(self.data["makeinactive"])
519 toggle.connect("toggled", self._cb_toggle_simple, "makeinactive",
520 "output")
521 toggle.show()
522 page.pack_start(toggle)
523
524 #makeactive
525 toggle = gtk.CheckButton("Make active buttons")
526 toggle.set_active(self.data["makeactive"])
527 toggle.connect("toggled", self._cb_toggle_simple, "makeactive",
528 "output")
529 toggle.show()
530 page.pack_start(toggle)
531
532 #makepressed
533 toggle = gtk.CheckButton("Make pressed buttons")
534 toggle.set_active(self.data["makepressed"])
535 toggle.connect("toggled", self._cb_toggle_simple, "makepressed",
536 "output")
537 toggle.show()
538 page.pack_start(toggle)
539
540 #writexcf
541 toggle = gtk.CheckButton("Write the XCF file")
542 toggle.set_active(self.data["writexcf"])
543 toggle.connect("toggled", self._cb_toggle_simple, "writexcf",
544 "output")
545 toggle.show()
546 page.pack_start(toggle)
547
548 def checkcompletion(self, pagename):
549 criteriamatched = False
550 if pagename == "pathselection":
551 criteriamatched = self.data["filename"] is not None and \
552 self.data["outdir"] is not None
553 elif pagename == "basicsettings":
554 criteriamatched = self.data["font"] is not None and \
555 self.data["strcolor"] is not None and \
556 ((self.data["usepattern"] and self.data["pattern"]) or \
557 (not self.data["usepattern"] and \
558 self.data["buttoncolor"]))
559 elif pagename == "layout":
560 criteriamatched = self.data["roundradius"] is not None and \
561 self.data["bevelwidth"] is not None and \
562 self.data["padding"] is not None and \
563 (self.data["transparency"] or self.data["bgcolor"] is not None)
564 elif pagename == "effects":
565 criteriamatched = (self.data["nova"] == False or ( \
566 self.data["novacolor"] is not None and \
567 self.data["novaradius"] is not None and \
568 self.data["novasparkles"] is not None)) and \
569 (self.data["glow"] == False or ( \
570 self.data["glowcolor"] is not None and \
571 self.data["glowsize"] is not None))
572 elif pagename == "output":
573 criteriamatched = self.data["makejscript"] is not None and \
574 self.data["makeinactive"] is not None and \
575 self.data["makeactive"] is not None and \
576 self.data["makepressed"] is not None and \
577 self.data["writexcf"] is not None
578 if criteriamatched:
579 self.set_page_complete(self.pages[pagename], True)
580 else:
581 self.set_page_complete(self.pages[pagename], False)
582
583 def _cb_set_intvalue(self, w, fieldname, pagename):
584 try:
585 self.data[fieldname] = int (w.get_text())
586 except ValueError:
587 pass
588 self.checkcompletion(pagename)
589
590 def _cb_toggle_simple(self, w, datafield, pagename):
591 self.data[datafield] = w.get_active()
592 self.checkcompletion(pagename)
593
594 def _cb_file_selected(self, w):
595 self.data["filename"] = w.get_filename()
596 self.checkcompletion("pathselection")
597
598 def _cb_dir_selected(self, w):
599 self.data["outdir"] = w.get_filename()
600 self.checkcompletion("pathselection")
601
602 def _cb_set_font(self, w):
603 self.data["font"] = w.get_font_name()
604 self.checkcompletion("basicsettings")
605
606 def _cb_set_color(self, w, fieldname, pagename):
607 self.data[fieldname] = w.get_color()
608 self.checkcompletion(pagename)
609
610 def _cb_set_pattern(self, w, patternname, width, height, bpp, mask_data,
611 finished):
612 if finished:
613 self.data["pattern"] = patternname
614 self.checkcompletion("basicsettings")
615
616 def _cb_bgtoggle_toggle(self, w, label, patterntext, patternsel,
617 colortext, colorsel):
618 if w.get_active():
619 self.data["usepattern"] = True
620 colorsel.hide()
621 patternsel.show()
622 label.set_text(patterntext)
623 else:
624 self.data["usepattern"] = False
625 patternsel.hide()
626 colorsel.show()
627 label.set_text(colortext)
628 self.checkcompletion("basicsettings")
629
630 def _cb_nova_toggle(self, w, colorfield, radiusfield, sparksfield):
631 if w.get_active():
632 self.data["nova"] = True
633 colorfield.set_sensitive(True)
634 radiusfield.set_sensitive(True)
635 sparksfield.set_sensitive(True)
636 else:
637 self.data["nova"] = False
638 colorfield.set_sensitive(False)
639 radiusfield.set_sensitive(False)
640 sparksfield.set_sensitive(False)
641 self.checkcompletion("effects")
642
643 def _cb_glow_toggle(self, w, colorfield, sizefield):
644 if w.get_active():
645 self.data["glow"] = True
646 colorfield.set_sensitive(True)
647 sizefield.set_sensitive(True)
648 else:
649 self.data["glow"] = False
650 colorfield.set_sensitive(False)
651 sizefield.set_sensitive(False)
652 self.checkcompletion("effects")
653
654 class btn4wsplugin(gimpplugin.plugin):
655 """This is the btn4ws gimp plugin."""
656 def gimp2html_color(self, color):
657 """
658 Converts a color tuple to a hex encoded color for CSS.
659 """
660 return "#%02x%02x%02x" % (color[0], color[1], color[2])
661
662 def parsefont(self, font):
663 """
664 Parses a font into its fontname and size parts.
665 """
666 parts = font.split(" ")
667 return (" ".join(parts[:-1]), parts[-1])
668
669 def toprocess(self, item):
670 """
671 Decides whether the plugin is able to process the item or not.
672 """
673 item = item.strip()
674 return len(item) > 0 and not item.startswith('#')
675
676 def getmaxextents(self, strings, fontsize, fontname):
677 """
678 Gets the maximum width and height of texts in strings array
679 with the given font.
680 """
681 getextents = pdb['gimp_text_get_extents_fontname']
682 maxx = 0
683 maxy = 0
684 for extents in [getextents(string, fontsize, 1, fontname)
685 for string in strings]:
686 maxx = max(maxx, extents[0])
687 maxy = max(maxy, extents[1])
688 return (maxx, maxy)
689
690 def writejs(self, dirname, strings, width, height, t2nm):
691 buf = [
692 "//",
693 "// JavaScript generated by btn4ws version %s" % (btn4ws_version),
694 "//",
695 "",
696 "// function to show image for given image_object",
697 "function hilite(ObjID, imgObjName) {",
698 " ObjID.src = eval(imgObjName + '.src');",
699 " return true;",
700 "}",
701 ""
702 ]
703 for item in strings:
704 for prefix in ('a_', 'i_', 'p_'):
705 buf.append(
706 "%(prefix)s%(jsid)s = new Image(%(width)d, %(height)d); "
707 "%(prefix)s%(jsid)s.src = '%(fname)s';"
708 % {
709 'prefix' : prefix,
710 'jsid' : t2nm.asjavascriptid(item),
711 'width' : width,
712 'height' : height,
713 'fname' : urllib.quote(t2nm.asfilename(item, 'png',
714 prefix))})
715 jsfile = open(os.path.join(dirname, 'imgobjs.js'), 'w')
716 jsfile.write("\n".join(buf))
717 jsfile.close()
718
719 def writecss(self, dirname, bgcolor):
720 buf = [
721 "html, body { background-color:%s; }" % (self.gimp2html_color(bgcolor)),
722 "a img { border-width: 0; }"
723 ]
724 cssfile = open(os.path.join(dirname, 'format.css'), 'w')
725 cssfile.write("\n".join(buf))
726 cssfile.close()
727
728 def writehtml(self, dirname, strings, width, height, t2nm):
729 buf = [
730 '<?xml version="1.0" encoding="UTF-8"?>',
731 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"',
732 ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
733 '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">',
734 '<head>',
735 ' <title>A JavaScript MouseOver Example</title>',
736 ' <script src="imgobjs.js" type="text/javascript"></script>',
737 ' <link rel="stylesheet" type="text/css" href="format.css"/>',
738 '</head>',
739 '<body>',
740 ' <div>'
741 ]
742 for item in strings:
743 buf.append(
744 '<a href="%(target)s"'
745 ' onmouseover="return hilite(%(imgid)s, \'a_%(jsid)s\');"'
746 ' onmouseout="return hilite(%(imgid)s, \'i_%(jsid)s\');"'
747 ' onmousedown="return hilite(%(imgid)s, \'p_%(jsid)s\');"'
748 ' onmouseup="return hilite(%(imgid)s, \'a_%(jsid)s\');">'
749 '<img src="%(fname)s" class="nav" id="%(imgid)s" width="%(width)d" height="%(height)d"'
750 ' alt="%(text)s" /></a><br />' % {
751 'target' : t2nm.aslinktarget(item),
752 'imgid' : t2nm.asitemid(item),
753 'jsid' : t2nm.asjavascriptid(item),
754 'fname' : urllib.quote(t2nm.asfilename(item, 'png', 'i_')),
755 'width' : width,
756 'height' : height,
757 'text' : item})
758 buf.extend([
759 ' </div>',
760 ' <p><a href="http://validator.w3.org/check/referer">'
761 '<img src="http://www.w3.org/Icons/valid-xhtml11" alt="Valid XHTML 1.1!" height="31"'
762 'width="88" /></a></p>',
763 '</html>',
764 ''
765 ])
766 htmlfile = open(os.path.join(dirname, 'example.html'), 'w')
767 htmlfile.write("\n".join(buf))
768 htmlfile.close()
769
770 def saveaspng(self, fname, image, transparency):
771 imgcopy = pdb['gimp_image_duplicate'](image)
772 if transparency:
773 imgcopy.merge_visible_layers(CLIP_TO_BOTTOM_LAYER)
774 else:
775 imgcopy.flatten()
776 pdb['file_png_save'](imgcopy, imgcopy.active_layer, fname, fname,
777 False, 9, False, False, False, False, True)
778 gimp.delete(imgcopy)
779
780 def __init__(self):
781 self.data = {}
782 self.inputdata = {}
783 self.datafile = os.path.join(gimp.directory, "btn4wsrc")
784
785 def checkdata(self, data):
786 logging.debug("checkdata " + str(data))
787 valid = True
788 if data["filename"] is None:
789 logging.error("filename is None")
790 valid = False
791 else:
792 try:
793 if not os.path.isfile(data["filename"]):
794 logging.error("%s is not a file.", data["filename"])
795 valid = False
796 except OSError, e:
797 logging.error(e)
798 valid = False
799 if data["outdir"] is None:
800 logging.error("outdir is None")
801 else:
802 try:
803 if not os.path.isdir(data["outdir"]):
804 logging.error("%s is not a directory.", data["outdir"])
805 valid = False
806 except OSError, e:
807 logging.error(e)
808 valid = False
809 # simple None checks
810 for key in ("font", "strcolor", "roundradius", "bevelwidth",
811 "padding", "makejscript", "makeinactive", "makeactive",
812 "makepressed", "writexcf"):
813 if data[key] is None:
814 logging.error("%s is None" % (key))
815 valid = False
816 if data["usepattern"]:
817 if data["pattern"] is None:
818 logging.error("usepattern is True and pattern is None")
819 valid = False
820 elif data["buttoncolor"] is None:
821 logging.error("usepattern is False and buttoncolor is None")
822 valid = False
823 if not data["transparency"] and data["bgcolor"] is None:
824 logging.error("transparency is not enabled and bgcolor is None")
825 valid = False
826 if data["nova"]:
827 if data["novacolor"] is None:
828 logging.error("nova is enabled and novacolor is None")
829 valid = False
830 if data["novaradius"] is None:
831 logging.error("nova is enabled and novaradius is None")
832 valid = False
833 if data["novasparkles"] is None:
834 logging.error("nova is enabled and novasparkles is None")
835 valid = False
836 if data["glow"]:
837 if data["glowcolor"] is None:
838 logging.error("glow is enabled and glowcolor is None")
839 valid = False
840 if data["glowsize"] is None:
841 logging.error("glow is enabled and glowsize is None")
842 return valid
843
844 def makebuttons(self, filename = None, outdir = None, font = None,
845 strcolor = None, transparency = False, bgcolor = None,
846 glow = False, glowcolor = None, usepattern = False,
847 pattern = None, buttoncolor = None, roundradius = None,
848 padding = None, glowsize = None, bevelwidth = None,
849 nova = False, novasparkles = None, novaradius = None,
850 novacolor = None, writexcf = False, makeinactive = True,
851 makeactive = True, makepressed = True, makejscript = True):
852 # import used gimp pdb functions
853 createtext = pdb['gimp_text_fontname']
854 selectionlayeralpha = pdb['gimp_selection_layer_alpha']
855 selectionfeather = pdb['gimp_selection_feather']
856 bucketfill = pdb['gimp_edit_bucket_fill']
857 selectionall = pdb['gimp_selection_all']
858 editclear = pdb['gimp_edit_clear']
859 rectselect = pdb['gimp_rect_select']
860 ellipseselect = pdb['gimp_ellipse_select']
861 selectionshrink = pdb['gimp_selection_shrink']
862 selectionnone = pdb['gimp_selection_none']
863 fill = pdb['gimp_edit_fill']
864 bumpmap = pdb['plug_in_bump_map']
865 novaplugin = pdb['plug_in_nova']
866 xcfsave = pdb['gimp_xcf_save']
867
868 gimp.progress_init()
869 stringfile = open(filename)
870 strings = [line.strip()
871 for line in stringfile.readlines()
872 if self.toprocess(line)]
873 stringfile.close()
874 t2nm = text_to_name_mapper(strings)
875 (fontname, fontsize) = self.parsefont(font)
876 (maxx, maxy) = self.getmaxextents(strings, fontsize, fontname)
877 logging.debug("fontname: %s, fontsize: %d, maxx: %d, maxy: %d",
878 fontname, int(fontsize), maxx, maxy)
879 width = maxx + (padding*4)
880 height = maxy + (padding*4)
881 logging.debug("width: %d, height: %d", width, height)
882
883 if roundradius > height/2:
884 roundradius = height/2 - 1
885 if roundradius > width/2:
886 roundradius = width/2 - 1
887 logging.debug("roundradius: %d", roundradius)
888
889 for text in strings:
890 image = gimp.Image(width, height, RGB)
891 image.disable_undo()
892 gimp.set_foreground(strcolor)
893 textlayer = createtext(image, None, padding*2, padding*2, text,
894 0, 1, fontsize, 1, fontname)
895 # center the text
896 textlayer.set_offsets((image.width - textlayer.width)/2 - 1,
897 (image.height - textlayer.height)/2 - 1)
898 textlayer.lock_alpha = True
899 textlayer.name = text
900 if glow:
901 texteffect = textlayer.copy(True)
902 image.add_layer(texteffect, len(image.layers))
903 offs = texteffect.offsets
904 texteffect.resize(image.width, image.height, offs[0], offs[1])
905 texteffect.lock_alpha = False
906 image.active_layer = texteffect
907 selectionlayeralpha(texteffect)
908 selectionfeather(image, glowsize)
909 gimp.set_foreground(glowcolor)
910 bucketfill(texteffect, FG_BUCKET_FILL, NORMAL_MODE, 100, 0,
911 True, 0, 0)
912 btnlayer0 = gimp.Layer(image, "Background", width, height,
913 RGBA_IMAGE, 100, NORMAL_MODE)
914 image.add_layer(btnlayer0, len(image.layers))
915 selectionall(image)
916 editclear(btnlayer0)
917 offs = btnlayer0.offsets
918 rectselect(image, offs[0] + roundradius, offs[1],
919 btnlayer0.width - roundradius*2, btnlayer0.height,
920 CHANNEL_OP_REPLACE, 0, 0)
921 rectselect(image, offs[0], offs[1] + roundradius,
922 btnlayer0.width, btnlayer0.height - roundradius*2,
923 CHANNEL_OP_ADD, 0, 0)
924 ellipseselect(image, offs[0], offs[1],
925 roundradius*2, roundradius*2,
926 CHANNEL_OP_ADD, False, 0, 0)
927 ellipseselect(image, offs[0] + btnlayer0.width - roundradius*2,
928 offs[1],
929 roundradius*2, roundradius*2,
930 CHANNEL_OP_ADD, False, 0, 0)
931 ellipseselect(image, offs[0],
932 offs[1] + btnlayer0.height - roundradius*2,
933 roundradius*2, roundradius*2,
934 CHANNEL_OP_ADD, False, 0, 0)
935 ellipseselect(image, offs[0] + btnlayer0.width - roundradius*2,
936 offs[1] + btnlayer0.height - roundradius*2,
937 roundradius*2, roundradius*2,
938 CHANNEL_OP_ADD, False, 0, 0)
939 selectionshrink(image, 1)
940 selectionfeather(image, 2)
941 if usepattern:
942 pdb['gimp_context_set_pattern'](pattern)
943 bucketfill(btnlayer0, PATTERN_BUCKET_FILL, NORMAL_MODE, 100, 0,
944 True, 0, 0)
945 else:
946 gimp.set_background(buttoncolor)
947 bucketfill(btnlayer0, BG_BUCKET_FILL, NORMAL_MODE, 100, 0,
948 True, 0, 0)
949 selectionnone(image)
950 selectionlayeralpha(btnlayer0)
951 selectionfeather(image, 2)
952 bumplayer = gimp.Layer(image, "Bumpmap", width, height, RGBA_IMAGE,
953 100, NORMAL_MODE)
954 gimp.set_background(0, 0, 0)
955 image.add_layer(bumplayer, 0)
956 fill(bumplayer, BACKGROUND_FILL)
957 for index in range(1, bevelwidth -1):
958 greyness = index*255/bevelwidth;
959 gimp.set_background(greyness, greyness, greyness)
960 bucketfill(bumplayer, BG_BUCKET_FILL, NORMAL_MODE, 100, 0,
961 False, 0, 0)
962 selectionshrink(image, 1)
963 gimp.set_background(255, 255, 255)
964 bucketfill(bumplayer, BG_BUCKET_FILL, NORMAL_MODE, 100, 0, False,
965 0, 0)
966 selectionnone(image)
967 btnlayer1 = btnlayer0.copy(True)
968 btnlayer2 = btnlayer0.copy(True)
969 image.add_layer(btnlayer1, len(image.layers))
970 image.add_layer(btnlayer2, len(image.layers))
971 bumpmap(image, btnlayer1, bumplayer, 125, 45, 3, 0, 0, 0, 0, 0,
972 0, 1)
973 bumpmap(image, btnlayer2, bumplayer, 125, 45, 3, 0, 0, 0, 0, 0,
974 1, 1)
975 image.remove_layer(bumplayer)
976 #gimp.delete(bumplayer)
977 if nova:
978 novalayer = gimp.Layer(image, "Nova", width, height,
979 RGBA_IMAGE, 75, NORMAL_MODE)
980 image.add_layer(novalayer, 0)
981 selectionall(image)
982 image.active_layer = novalayer
983 editclear(novalayer)
984 selectionnone(image)
985 novaplugin(image, novalayer, width/4, height/4,
986 novacolor, novaradius, novasparkles, 0)
987 blackboard = gimp.Layer(image, "Blackboard", width, height,
988 RGBA_IMAGE, 100, NORMAL_MODE)
989 image.add_layer(blackboard, len(image.layers))
990 selectionall(image)
991 if transparency:
992 blackboard.preserve_trans = True
993 editclear(blackboard)
994 else:
995 gimp.set_background(bgcolor)
996 bucketfill(blackboard, BG_BUCKET_FILL, NORMAL_MODE, 100, 0,
997 False, 0, 0)
998 selectionnone(image)
999 if writexcf:
1000 fname = t2nm.asfilename(text, 'xcf', dirname = outdir)
1001 xcfsave(0, image, textlayer, fname, fname)
1002 if makepressed:
1003 btnlayer0.visible = False
1004 btnlayer1.visible = False
1005 btnlayer2.visible = True
1006 if nova: novalayer.visible = True
1007 self.saveaspng(t2nm.asfilename(text, 'png', 'p_', outdir),
1008 image, transparency)
1009 if makeactive:
1010 btnlayer0.visible = False
1011 btnlayer1.visible = True
1012 btnlayer2.visible = False
1013 if nova: novalayer.visible = True
1014 self.saveaspng(t2nm.asfilename(text, 'png', 'a_', outdir),
1015 image, transparency)
1016 if makeinactive:
1017 btnlayer0.visible = True
1018 btnlayer1.visible = False
1019 btnlayer2.visible = False
1020 if nova: novalayer.visible = False
1021 self.saveaspng(t2nm.asfilename(text, 'png', 'i_', outdir),
1022 image, transparency)
1023 image.enable_undo()
1024 #gimp.Display(image)
1025 gimp.progress_update((strings.index(text)+1)/len(strings))
1026 gimp.delete(image)
1027 if makejscript:
1028 self.writejs(outdir, strings, width, height, t2nm)
1029 self.writecss(outdir, bgcolor)
1030 self.writehtml(outdir, strings, width, height, t2nm)
1031 #gimp.displays_flush()
1032
1033 def _cb_destroy(self, widget, data = None):
1034 logging.debug("destroy")
1035 gtk.main_quit()
1036
1037 def _loaddata(self, data):
1038 try:
1039 if os.path.exists(self.datafile):
1040 return pickle.load(open(self.datafile))
1041 except OSError, e:
1042 pass
1043 return data
1044
1045 def _storedata(self, data):
1046 try:
1047 pickle.dump(data, open(self.datafile, 'w'))
1048 except OSError, e:
1049 pass
1050
1051 def _cb_apply(self, widget):
1052 self.data = widget.data
1053 logging.debug(str(self.data))
1054 if self.checkdata(self.data):
1055 self.makebuttons(**self.data)
1056 shelf["btn4ws"] = self.data
1057 self._storedata(self.data)
1058 else:
1059 logging.error("checking data failed")
1060
1061 def btn4ws(self, runmode, filename = None, outdir = None, font = None,
1062 strcolor = None, transparency = False, bgcolor = None,
1063 glow = False, glowcolor = None, usepattern = False,
1064 pattern = None, buttoncolor = None, roundradius = None,
1065 padding = None, glowsize = None, bevelwidth = None,
1066 nova = False, novasparkles = None, novaradius = None,
1067 novacolor = None, writexcf = False, makeinactive = True,
1068 makeactive = True, makepressed = True, makejscript = True):
1069 """
1070 This function controls the creation of the buttons and is
1071 registered as gimp plugin.
1072 """
1073 self.inputdata = {
1074 "filename" : filename, "outdir" : outdir, "font" : font,
1075 "strcolor" : strcolor, "transparency" : transparency,
1076 "bgcolor" : bgcolor, "glow" : glow, "glowcolor" : glowcolor,
1077 "usepattern" : usepattern, "pattern" : pattern,
1078 "buttoncolor" : buttoncolor, "roundradius" : roundradius,
1079 "padding" : padding, "glowsize" : glowsize,
1080 "bevelwidth" : bevelwidth, "nova" : nova,
1081 "novasparkles" : novasparkles, "novaradius" : novaradius,
1082 "novacolor" : novacolor, "writexcf" : writexcf,
1083 "makeinactive" : makeinactive, "makeactive" : makeactive,
1084 "makepressed" : makepressed, "makejscript" : makejscript
1085 }
1086 if runmode in (RUN_INTERACTIVE, RUN_WITH_LAST_VALS):
1087 if shelf.has_key("btn4ws"):
1088 self.inputdata = shelf["btn4ws"]
1089 else:
1090 self.inputdata = self._loaddata(self.inputdata)
1091 dialog = Btn4wsDialog(self.inputdata)
1092 dialog.connect("close", self._cb_destroy)
1093 dialog.connect("cancel", self._cb_destroy)
1094 dialog.connect("destroy", self._cb_destroy)
1095 dialog.connect("apply", self._cb_apply)
1096 gtk.main()
1097 elif runmode == RUN_NONINTERACTIVE:
1098 logging.debug("runmode noninteractive")
1099 if self.checkdata(self.inputdata):
1100 self.makebuttons(**self.inputdata)
1101 else:
1102 logging.error("checking data failed")
1103 else:
1104 logging.error("unknown runmode %d" % runmode)
1105 return
1106
1107 def start(self):
1108 gimp.main(self.init, self.quit, self.query, self._run)
1109
1110 def init(self):
1111 logging.debug("init")
1112
1113 def quit(self):
1114 logging.debug("quit")
1115
1116 def query(self):
1117 logging.debug("query")
1118 gimp.install_procedure(
1119 "btn4ws",
1120 "Buttons for website", """Creates buttons for a website. Which have the same size, layout, effects on it. It's possible to create JavaScript code, CSS and XHTML examples for MouseOver effects also.""",
1121 "Jan Dittberner",
1122 "Jan Dittberner <jan@dittberner.info>",
1123 "%s, %s" % (btn4ws_version,
1124 "$Date: 2008-01-05 23:19:03 +0100 (Sa, 05 Jan 2008) $"),
1125 "<Toolbox>/Xtns/Render/Buttons for website ...",
1126 "", PLUGIN,
1127 [(PDB_INT32, "run_mode", "Run mode"),
1128 (PDB_STRING, "string_filename", "File containing the strings"),
1129 (PDB_STRING, "output_directory", "Directory for the output files"),
1130 (PDB_STRING, "font", "Font for the strings"), # "Sans 18"),
1131 (PDB_COLOR, "string_color", "Color of the strings"), #, (255, 255, 0)),
1132 (PDB_INT8, "transparent_background", "Keep the background transparent (This doesn't work in MS Internet Explorer <= 6.0)"), #, 0),
1133 (PDB_COLOR, "background_color", "Color of the background"), #, (7, 135, 255)),
1134 (PDB_INT8, "apply_glow", "Enable glow effect"), #, 1),
1135 (PDB_COLOR, "glow_color", "Color of the Glow effect"), #, (255, 180, 0)),
1136 (PDB_INT8, "use_pattern", "Use a pattern for the button"), #, 1),
1137 (PDB_STRING, "button_pattern", "Fill pattern of the button"), #, "Rain"),
1138 (PDB_COLOR, "button_color", "Button color (if you don't use a pattern)"), #, (0, 0, 255)),
1139 (PDB_INT32, "round_radius", "Radius of the round corners"), #, 20),
1140 (PDB_INT32, "padding", "Space around the text"), #, 3),
1141 (PDB_INT32, "glow_size", "Size of the Glow effect"), #, 10),
1142 (PDB_INT32, "bevel_width", "Width of the bevel"), #, 5),
1143 (PDB_INT8, "apply_nova", "Nova or not Nova?"), #, 1),
1144 (PDB_INT32, "nova_sparkles", "Sparkles of the Nova"), #, 5),
1145 (PDB_INT32, "nova_radius", "Radius of the Nova"), #, 2),
1146 (PDB_COLOR, "nova_color", "Color of the Nova effect"), #, (255, 238, 0)),
1147 (PDB_INT8, "write_xcf", "Write a GIMP xcf file"), #, 0),
1148 (PDB_INT8, "create_inactive", "Create Inactive Button"), #, 1),
1149 (PDB_INT8, "create_active", "Create Active Button"), #, 1),
1150 (PDB_INT8, "create_pressed", "Create Pressed Button"), #, 1),
1151 (PDB_INT8, "create_jscript", "Create JavaScript, HTML and CSS"), #, 1)
1152 ],
1153 [])
1154
1155 if __name__ == '__main__':
1156 btn4wsplugin().start()