From 1e5b131e7d8bc17349dc5858f4a294b2ef9297a3 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sat, 5 Jan 2008 20:45:57 +0000 Subject: [PATCH] fixes #6, addresses #7 * GUI without gimpfu completed * interactive functionality is working * updated ChangeLog --- ChangeLog | 5 + btn4ws.py | 318 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 215 insertions(+), 108 deletions(-) diff --git a/ChangeLog b/ChangeLog index ffb8c84..510a75f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2008-01-05 Jan Dittberner + + * btn4ws.py: switch to non gimpfu gimpplugin, GtkAssistant GUI, + get interactive functionallity completed + 2007-12-04 Jan Dittberner * btn4ws.py: add the functionality available in btn4ws.pl 0.6 diff --git a/btn4ws.py b/btn4ws.py index 1fa371f..113a372 100644 --- a/btn4ws.py +++ b/btn4ws.py @@ -27,7 +27,7 @@ Gimp script to generate button images for websites. This script is a port of the older gimp-perl version to python. -(c) 2007 Jan Dittberner +(c) 2007, 2008 Jan Dittberner """ import os, urllib, logging, sys import gimp, gimpplugin, gimpui, gimpcolor @@ -44,13 +44,6 @@ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', stream=sys.stderr) -def parsefont(font): - """ - Parses a font into its fontname and size parts. - """ - parts = font.split(" ") - return (" ".join(parts[:-1]), parts[-1]) - class text_to_name_mapper: """ Text string to name mapper class. This class provides mappings for several target @@ -69,7 +62,7 @@ class text_to_name_mapper: if 'itemid' not in self.mapping[text]: self.mapping[text]['itemid'] = "id%03d" % (self.idnum) self.idnum += 1 - logging.debug("self.mapping=" + str(self.mapping)) + #logging.debug("self.mapping=" + str(self.mapping)) return self.mapping[text]['itemid'] def asjavascriptid(self, text): @@ -79,7 +72,7 @@ class text_to_name_mapper: if 'jsid' not in self.mapping[text]: self.mapping[text]['jsid'] = "img%03d" % (self.idnum) self.idnum += 1 - logging.debug("self.mapping=" + str(self.mapping)) + #logging.debug("self.mapping=" + str(self.mapping)) return self.mapping[text]['jsid'] def aslinktarget(self, text): @@ -88,7 +81,7 @@ class text_to_name_mapper: """ if 'link' not in self.mapping[text]: self.mapping[text]['link'] = urllib.quote(text) - logging.debug("self.mapping=" + str(self.mapping)) + #logging.debug("self.mapping=" + str(self.mapping)) return "%s.html" % (self.mapping[text]['link']) def asfilename(self, text, extension = 'png', prefix= '', dirname = None): @@ -98,7 +91,7 @@ class text_to_name_mapper: if 'file' not in self.mapping[text]: self.mapping[text]['file'] = text.encode('ascii', 'ignore') fname = "%s%s.%s" % (prefix, self.mapping[text]['file'], extension) - logging.debug("self.mapping=" + str(self.mapping)) + #logging.debug("self.mapping=" + str(self.mapping)) if dirname: return os.path.join(dirname, fname) return fname @@ -175,10 +168,9 @@ class Btn4wsDialog(gtk.Assistant): logging.debug("delete_event") return False - def __init__(self, **kwargs): - self.data = kwargs + def __init__(self, args): + self.data = args self.pages = {} - logging.debug("kwargs: " + str(kwargs)) gtk.Assistant.__init__(self) self._addIntroPage() @@ -223,6 +215,8 @@ class Btn4wsDialog(gtk.Assistant): table.attach(label, 0, 1, 0, 1) button = gtk.FileChooserButton("Choose file") button.set_action(gtk.FILE_CHOOSER_ACTION_OPEN) + if self.data["filename"]: + button.set_filename(self.data["filename"]) button.connect("selection-changed", self._cb_file_selected) button.show() table.attach(button, 1, 2, 0, 1) @@ -232,6 +226,10 @@ class Btn4wsDialog(gtk.Assistant): table.attach(label, 0, 1, 1, 2) button = gtk.FileChooserButton("Choose directory") button.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) + if self.data["outdir"]: + button.set_filename(self.data["outdir"]) + else: + self.data["outdir"] = button.get_filename() button.connect("selection-changed", self._cb_dir_selected) button.show() table.attach(button, 1, 2, 1, 2) @@ -264,6 +262,8 @@ class Btn4wsDialog(gtk.Assistant): fontsel.set_show_size(True) if self.data["font"]: fontsel.set_font_name(self.data["font"]) + else: + self.data["font"] = fontsel.get_font_name() fontsel.show() fontsel.connect("font-set", self._cb_set_font) table.attach(fontsel, 1, 2, 0, 1) @@ -275,6 +275,8 @@ class Btn4wsDialog(gtk.Assistant): colorsel = gimpui.ColorSelector() if self.data["strcolor"]: colorsel.set_color(self.data["strcolor"]) + else: + self.data["strcolor"] = colorsel.get_color() colorsel.show() colorsel.connect("color-changed", self._cb_set_color, "strcolor", "basicsettings") @@ -298,7 +300,9 @@ class Btn4wsDialog(gtk.Assistant): patternsel.connect("pattern-set", self._cb_set_pattern) colorsel = gimpui.ColorSelector() if self.data["buttoncolor"]: - colorsel.set_pattern(self.data["buttoncolor"]) + colorsel.set_color(self.data["buttoncolor"]) + else: + self.data["buttoncolor"] = colorsel.get_color() colorsel.connect("color-changed", self._cb_set_color, "buttoncolor", "basicsettings") bgtoggle.connect("toggled", self._cb_bgtoggle_toggle, label, @@ -377,8 +381,10 @@ class Btn4wsDialog(gtk.Assistant): label = gtk.Label("Background color") label.show() colorsel = gimpui.ColorSelector() - if self.data["buttoncolor"]: - colorsel.set_pattern(self.data["bgcolor"]) + if self.data["bgcolor"]: + colorsel.set_color(self.data["bgcolor"]) + else: + self.data["bgcolor"] = colorsel.get_color() colorsel.connect("color-changed", self._cb_set_color, "bgcolor", "layout") colorsel.show() @@ -397,7 +403,7 @@ class Btn4wsDialog(gtk.Assistant): "Select the effect settings") self.set_page_type(page, gtk.ASSISTANT_PAGE_CONTENT) - table = gtk.Table(rows=6, columns=2, homogeneous=False) + table = gtk.Table(rows=7, columns=2, homogeneous=False) table.show() #nova @@ -411,7 +417,9 @@ class Btn4wsDialog(gtk.Assistant): label.show() novacolor = gimpui.ColorSelector() if self.data["novacolor"]: - novacolor.set_pattern(self.data["novacolor"]) + novacolor.set_color(self.data["novacolor"]) + else: + self.data["novacolor"] = novacolor.get_color() novacolor.connect("color-changed", self._cb_set_color, "novacolor", "effects") novacolor.set_sensitive(self.data["nova"]) @@ -420,6 +428,8 @@ class Btn4wsDialog(gtk.Assistant): table.attach(novacolor, 1, 2, 1, 2) #novaradius + label = gtk.Label("Nova radius") + label.show() novaradius = IntEntry(max = 2) if self.data["novaradius"] is not None: novaradius.set_text(str(self.data["novaradius"])) @@ -427,9 +437,12 @@ class Btn4wsDialog(gtk.Assistant): "effects") novaradius.set_sensitive(self.data["nova"]) novaradius.show() + table.attach(label, 0, 1, 2, 3) table.attach(novaradius, 1, 2, 2, 3) #novasparkles + label = gtk.Label("Nova sparkles") + label.show() novasparkles = IntEntry(max = 2) if self.data["novasparkles"] is not None: novasparkles.set_text(str(self.data["novasparkles"])) @@ -437,6 +450,7 @@ class Btn4wsDialog(gtk.Assistant): "effects") novasparkles.set_sensitive(self.data["nova"]) novasparkles.show() + table.attach(label, 0, 1, 3, 4) table.attach(novasparkles, 1, 2, 3, 4) novatoggle.connect("toggled", self._cb_nova_toggle, novacolor, novaradius, novasparkles) @@ -452,14 +466,28 @@ class Btn4wsDialog(gtk.Assistant): label.show() glowcolor = gimpui.ColorSelector() if self.data["glowcolor"]: - glowcolor.set_pattern(self.data["glowcolor"]) + glowcolor.set_color(self.data["glowcolor"]) + else: + self.data["glowcolor"] = glowcolor.get_color() glowcolor.connect("color-changed", self._cb_set_color, "glowcolor", "effects") glowcolor.set_sensitive(self.data["glow"]) glowcolor.show() table.attach(label, 0, 1, 5, 6) table.attach(glowcolor, 1, 2, 5, 6) - glowtoggle.connect("toggled", self._cb_glow_toggle, glowcolor) + + #glowsize + label = gtk.Label("Glow size") + label.show() + glowsize = IntEntry(max = 2) + glowsize.connect("changed", self._cb_set_intvalue, "glowsize", + "effects") + glowsize.set_sensitive(self.data["glow"]) + glowsize.show() + table.attach(label, 0, 1, 6, 7) + table.attach(glowsize, 1, 2, 6, 7) + glowtoggle.connect("toggled", self._cb_glow_toggle, glowcolor, + glowsize) page.pack_end(table) @@ -538,13 +566,15 @@ class Btn4wsDialog(gtk.Assistant): self.data["novacolor"] is not None and \ self.data["novaradius"] is not None and \ self.data["novasparkles"] is not None)) and \ - (self.data["glow"] == False or \ - self.data["glowcolor"] is not None) + (self.data["glow"] == False or ( \ + self.data["glowcolor"] is not None and \ + self.data["glowsize"] is not None)) elif pagename == "output": criteriamatched = self.data["makejscript"] is not None and \ self.data["makeinactive"] is not None and \ self.data["makeactive"] is not None and \ - self.data["makepressed"] is not None + self.data["makepressed"] is not None and \ + self.data["writexcf"] is not None if criteriamatched: self.set_page_complete(self.pages[pagename], True) else: @@ -610,31 +640,40 @@ class Btn4wsDialog(gtk.Assistant): sparksfield.set_sensitive(False) self.checkcompletion("effects") - def _cb_glow_toggle(self, w, colorfield): + def _cb_glow_toggle(self, w, colorfield, sizefield): if w.get_active(): self.data["glow"] = True colorfield.set_sensitive(True) + sizefield.set_sensitive(True) else: self.data["glow"] = False colorfield.set_sensitive(False) + sizefield.set_sensitive(False) self.checkcompletion("effects") class btn4wsplugin(gimpplugin.plugin): """This is the btn4ws gimp plugin.""" - def gimp2html_color(color): + def gimp2html_color(self, color): """ Converts a color tuple to a hex encoded color for CSS. """ return "#%02x%02x%02x" % (color[0], color[1], color[2]) - def toprocess(item): + def parsefont(self, font): + """ + Parses a font into its fontname and size parts. + """ + parts = font.split(" ") + return (" ".join(parts[:-1]), parts[-1]) + + def toprocess(self, item): """ Decides whether the plugin is able to process the item or not. """ item = item.strip() return len(item) > 0 and not item.startswith('#') - def getmaxextents(strings, fontsize, fontname): + def getmaxextents(self, strings, fontsize, fontname): """ Gets the maximum width and height of texts in strings array with the given font. @@ -648,7 +687,7 @@ class btn4wsplugin(gimpplugin.plugin): maxy = max(maxy, extents[1]) return (maxx, maxy) - def writejs(dirname, strings): + def writejs(self, dirname, strings, width, height, t2nm): buf = [ "//", "// JavaScript generated by btn4ws version %s" % (btn4ws_version), @@ -677,16 +716,16 @@ class btn4wsplugin(gimpplugin.plugin): jsfile.write("\n".join(buf)) jsfile.close() - def writecss(dirname): + def writecss(self, dirname, bgcolor): buf = [ - "html, body { background-color:%s; }" % (gimp2html_color(bgcolor)), + "html, body { background-color:%s; }" % (self.gimp2html_color(bgcolor)), "a img { border-width: 0; }" ] cssfile = open(os.path.join(dirname, 'format.css'), 'w') cssfile.write("\n".join(buf)) cssfile.close() - def writehtml(dirname, strings): + def writehtml(self, dirname, strings, width, height, t2nm): buf = [ '', '', '' ]) - htmlfile = open(os.path.join(outdir, 'example.html'), 'w') + htmlfile = open(os.path.join(dirname, 'example.html'), 'w') htmlfile.write("\n".join(buf)) htmlfile.close() - def saveaspng(fname, image): - imgcopy = dupimage(image) + def saveaspng(self, fname, image, transparency): + imgcopy = pdb['gimp_image_duplicate'](image) if transparency: imgcopy.merge_visible_layers(CLIP_TO_BOTTOM_LAYER) else: imgcopy.flatten() - pngsave(imgcopy, imgcopy.active_layer, fname, fname, False, 9, - False, False, False, False, True) + pdb['file_png_save'](imgcopy, imgcopy.active_layer, fname, fname, + False, 9, False, False, False, False, True) gimp.delete(imgcopy) - def _cb_destroy(self, widget, data = None): - logging.debug("destroy") - gtk.main_quit() - - def _cb_apply(self, widget): - self.data = widget.data - logging.debug(str(self.data)) - def __init__(self): self.data = {} + self.inputdata = {} - def btn4ws(self, runmode, filename = None, outdir = None, font = None, + def checkdata(self, data): + logging.debug("checkdata " + str(data)) + valid = True + if data["filename"] is None: + logging.error("filename is None") + valid = False + else: + try: + if not os.path.isfile(data["filename"]): + logging.error("%s is not a file.", data["filename"]) + valid = False + except OSError, e: + logging.error(e) + valid = False + if data["outdir"] is None: + logging.error("outdir is None") + else: + try: + if not os.path.isdir(data["outdir"]): + logging.error("%s is not a directory.", data["outdir"]) + valid = False + except OSError, e: + logging.error(e) + valid = False + # simple None checks + for key in ("font", "strcolor", "roundradius", "bevelwidth", + "padding", "makejscript", "makeinactive", "makeactive", + "makepressed", "writexcf"): + if data[key] is None: + logging.error("%s is None" % (key)) + valid = False + if data["usepattern"]: + if data["pattern"] is None: + logging.error("usepattern is True and pattern is None") + valid = False + elif data["buttoncolor"] is None: + logging.error("usepattern is False and buttoncolor is None") + valid = False + if not data["transparency"] and data["bgcolor"] is None: + logging.error("transparency is not enabled and bgcolor is None") + valid = False + if data["nova"]: + if data["novacolor"] is None: + logging.error("nova is enabled and novacolor is None") + valid = False + if data["novaradius"] is None: + logging.error("nova is enabled and novaradius is None") + valid = False + if data["novasparkles"] is None: + logging.error("nova is enabled and novasparkles is None") + valid = False + if data["glow"]: + if data["glowcolor"] is None: + logging.error("glow is enabled and glowcolor is None") + valid = False + if data["glowsize"] is None: + logging.error("glow is enabled and glowsize is None") + return valid + + def makebuttons(self, filename = None, outdir = None, font = None, strcolor = None, transparency = False, bgcolor = None, glow = False, glowcolor = None, usepattern = False, pattern = None, buttoncolor = None, roundradius = None, @@ -757,41 +848,6 @@ class btn4wsplugin(gimpplugin.plugin): nova = False, novasparkles = None, novaradius = None, novacolor = None, writexcf = False, makeinactive = True, makeactive = True, makepressed = True, makejscript = True): - """ - This function controls the creation of the buttons and is - registered as gimp plugin. - """ - if runmode == RUN_INTERACTIVE: - logging.debug("runmode interactive") - dialog = Btn4wsDialog(filename = filename, outdir = outdir, - font = font, strcolor = strcolor, - transparency = transparency, - bgcolor = bgcolor, glow = glow, - glowcolor = glowcolor, - usepattern = usepattern, pattern = pattern, - buttoncolor = buttoncolor, - roundradius = roundradius, padding = padding, - glowsize = glowsize, bevelwidth = bevelwidth, - nova = nova, novasparkles = novasparkles, - novaradius = novaradius, - novacolor = novacolor, - writexcf = writexcf, - makeinactive = makeinactive, - makeactive = makeactive, - makepressed = makepressed, - makejscript = makejscript) - dialog.connect("close", self._cb_destroy) - dialog.connect("cancel", self._cb_destroy) - dialog.connect("destroy", self._cb_destroy) - dialog.connect("apply", self._cb_apply) - gtk.main() - elif runmode == RUN_NONINTERACTIVE: - logging.debug("runmode noninteractive") - elif runmode == RUN_WITH_LASTVALS: - logging.debug("runmode with lastvals") - if shelf.has_key("btn4ws"): - initialvalues = shelf["btn4ws"] - return # import used gimp pdb functions createtext = pdb['gimp_text_fontname'] selectionlayeralpha = pdb['gimp_selection_layer_alpha'] @@ -802,36 +858,21 @@ class btn4wsplugin(gimpplugin.plugin): rectselect = pdb['gimp_rect_select'] ellipseselect = pdb['gimp_ellipse_select'] selectionshrink = pdb['gimp_selection_shrink'] - selectionnone = pdb['gimp_selection_none'] + selectionnone = pdb['gimp_selection_none'] fill = pdb['gimp_edit_fill'] bumpmap = pdb['plug_in_bump_map'] novaplugin = pdb['plug_in_nova'] xcfsave = pdb['gimp_xcf_save'] - pngsave = pdb['file_png_save'] - dupimage = pdb['gimp_image_duplicate'] - - try: - if not os.path.isfile(filename): - logging.error("%s is not a file.", filename) - return - if not outdir: - outdir = os.path.dirname(filename) - if not os.path.isdir(outdir): - logging.error("%s is not a directory.", outdir) - return - except OSError, e: - logging.error(e) - return - + gimp.progress_init() stringfile = open(filename) strings = [line.strip() for line in stringfile.readlines() - if toprocess(line)] + if self.toprocess(line)] stringfile.close() t2nm = text_to_name_mapper(strings) - (fontname, fontsize) = parsefont(font) - (maxx, maxy) = getmaxextents(strings, fontsize, fontname) + (fontname, fontsize) = self.parsefont(font) + (maxx, maxy) = self.getmaxextents(strings, fontsize, fontname) logging.debug("fontname: %s, fontsize: %d, maxx: %d, maxy: %d", fontname, int(fontsize), maxx, maxy) width = maxx + (padding*4) @@ -962,29 +1003,90 @@ class btn4wsplugin(gimpplugin.plugin): btnlayer1.visible = False btnlayer2.visible = True if nova: novalayer.visible = True - saveaspng(t2nm.asfilename(text, 'png', 'p_', outdir), image) + self.saveaspng(t2nm.asfilename(text, 'png', 'p_', outdir), + image, transparency) if makeactive: btnlayer0.visible = False btnlayer1.visible = True btnlayer2.visible = False if nova: novalayer.visible = True - saveaspng(t2nm.asfilename(text, 'png', 'a_', outdir), image) + self.saveaspng(t2nm.asfilename(text, 'png', 'a_', outdir), + image, transparency) if makeinactive: btnlayer0.visible = True btnlayer1.visible = False btnlayer2.visible = False if nova: novalayer.visible = False - saveaspng(t2nm.asfilename(text, 'png', 'i_', outdir), image) + self.saveaspng(t2nm.asfilename(text, 'png', 'i_', outdir), + image, transparency) image.enable_undo() #gimp.Display(image) gimp.progress_update((strings.index(text)+1)/len(strings)) gimp.delete(image) if makejscript: - writejs(outdir, strings) - writecss(outdir) - writehtml(outdir, strings) + self.writejs(outdir, strings, width, height, t2nm) + self.writecss(outdir, bgcolor) + self.writehtml(outdir, strings, width, height, t2nm) #gimp.displays_flush() + def _cb_destroy(self, widget, data = None): + logging.debug("destroy") + gtk.main_quit() + + def _cb_apply(self, widget): + self.data = widget.data + logging.debug(str(self.data)) + if self.checkdata(self.data): + self.makebuttons(**self.data) + else: + logging.error("checking data failed") + + def btn4ws(self, runmode, filename = None, outdir = None, font = None, + strcolor = None, transparency = False, bgcolor = None, + glow = False, glowcolor = None, usepattern = False, + pattern = None, buttoncolor = None, roundradius = None, + padding = None, glowsize = None, bevelwidth = None, + nova = False, novasparkles = None, novaradius = None, + novacolor = None, writexcf = False, makeinactive = True, + makeactive = True, makepressed = True, makejscript = True): + """ + This function controls the creation of the buttons and is + registered as gimp plugin. + """ + self.inputdata = { + "filename" : filename, "outdir" : outdir, "font" : font, + "strcolor" : strcolor, "transparency" : transparency, + "bgcolor" : bgcolor, "glow" : glow, "glowcolor" : glowcolor, + "usepattern" : usepattern, "pattern" : pattern, + "buttoncolor" : buttoncolor, "roundradius" : roundradius, + "padding" : padding, "glowsize" : glowsize, + "bevelwidth" : bevelwidth, "nova" : nova, + "novasparkles" : novasparkles, "novaradius" : novaradius, + "novacolor" : novacolor, "writexcf" : writexcf, + "makeinactive" : makeinactive, "makeactive" : makeactive, + "makepressed" : makepressed, "makejscript" : makejscript + } + if runmode == RUN_WITH_LAST_VALS: + logging.debug("runmode with last vals") + if shelf.has_key("btn4ws"): + self.inputdata = shelf["btn4ws"] + if runmode in (RUN_INTERACTIVE, RUN_WITH_LAST_VALS): + dialog = Btn4wsDialog(self.inputdata) + dialog.connect("close", self._cb_destroy) + dialog.connect("cancel", self._cb_destroy) + dialog.connect("destroy", self._cb_destroy) + dialog.connect("apply", self._cb_apply) + gtk.main() + elif runmode == RUN_NONINTERACTIVE: + logging.debug("runmode noninteractive") + if self.checkdata(self.inputdata): + self.makebuttons(**self.inputdata) + else: + logging.error("checking data failed") + else: + logging.error("unknown runmode %d" % runmode) + return + def start(self): gimp.main(self.init, self.quit, self.query, self._run)