I'm not entirely sure where I should file a blueprint so I'll post
what I have so far in this message.
The basic idea is to have a tool that reads a description of a command
line together with a description of the allowed values of the
arguments that then either generates the action classes on the fly or
write files for later use by Phatch.
Because I don't really understand the inner workings of Phatch (and
also because I suspect that the architecture doesn't support multiple
action classes in the same file) I chose to make a simple tool that
would create action class files.
Of course on the fly generation is preferable for a production version
but I think that might need some changes to Phatch itself.
The prototype code only handles slider and choice fields but I think
it serves well enough as a proof of concept.
As you point out the overhead is considerable but the convenience of
being able to generate syntactically correct command lines every time
makes experimentation much more comfortable as you can concentrate on
the effect instead of the technicalities of shell scripting.
Also the technique can be used to execute scripts that do not actually
process the image but do some meta-data processing (in principle at least
but I must learn more about Phatch before I can say what might be done).
The other reason for doing it is that there are a lot of image
manipulation command line programs about and it seems to me much
better to make them available to Phatch users as they are rather than
to expect the same effects to be added to Phatch.
It is reasonable to expect people to prefer one implementation of,
say, edge detection over another and by adding such a feature we can
give people the best of both worlds.
In the prototype I have chosen to describe the actions using small
Python scripts to be executed in a restricted environment by execfile.
I have given them the extension .gph so that they will be easy to
distinguish from other Python scripts
As you can see the structure closely resemble the essential parts of
the action template.
To create the action classes from these I run the generic.py script in
the same folder (see under).
the script is not especially elegantly written and it has pretty much
zero error checking. In particular it wll fail if any of the label,
author, etc., variables are not set.
Here are two example descriptions files and the script that transforms them into action files:
-- begin imsolarize.gph -------------------------------
# ImageMagick solarize
label='IM Solarize'
author = 'KJW'
email = 'kwhitefoot@hotmail.com'
tags = ['ImageMagick']
doc = 'Solarize'
cmd='convert "%(source)s" -solarize %(threshold)s "%(dest)s"'
fields = [
{'name': 'threshold', 'min': 1,'max':100, 'default':50}
]
-- end imsolarize.gph -------------------------------
-- begin imdither.gph --------------------------------
# ImageMagick Floyd-Steinberg dither
label='IM Dither'
author = 'KJW'
email = 'kwhitefoot@hotmail.com'
tags = ['ImageMagick']
doc = 'Dither'
cmd='convert "%(source)s" -colorspace %(colorspace)s -colors %(colors)s -dither "%(dest)s"'
fields = [
{'name': 'colors', 'min': 1,'max':100, 'default':50},
{'name': 'colorspace',
'choices':['CMYK', 'GRAY', 'HSL', 'HWB', 'OHTA', 'Rec601Luma',
'Rec709Luma', 'RGB', 'Transparent', 'XYZ', 'YCbCr',
'YIQ', 'YPbPr', 'YUV'],
'default': 'RGB'}]
-- end imdither.py -------------------------------
-- begin generic.py -------------------------------
#! /usr/bin/python
#
# Phatch custom action - generic two port
# Copyright (C) 2008 Kevin Whitefoot
#
# 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 http://www.gnu.org/licenses/
# Notes:
# Convert is used instead of mogrify because mogrify fails on my
# Ubuntu 7.04 machine. I don't know why but it simply does nothing
# even when I run it by hand in a terminal window.
# Also subprocess.call is used with a string command line and
# shell=True because convert fails if you try to use a sequence.
# The point here is to create Phatch Action classes on the fly from a
# text file listing the attributes and a command line template.
# Finally the save command must save to png not jpeg because saving to
# jpeg gives this error message: 'Cannot write modeP as JPEG'
#from core import models
#from core.translation import _t
#import os
#from tempfile import mkstemp
#import subprocess
import glob
import os
templatePhatch = r"""
from core import models
from core.translation import _t
import os
from tempfile import mkstemp
import subprocess
def init():
global Image, ImageOps
import Image, ImageOps
def impil(image %(pilargs)s):
(handle,source) = mkstemp('.png', 'temp')
os.close(handle)
(handle,dest) = mkstemp('.png', 'temp')
os.close(handle)
image.save(source)
cmd = '%(cmd)s' %% %(cmdargs)s
print "cmd:" + cmd
subprocess.call(cmd, shell=True)
# reload the file
result = Image.open(dest)
# delete it
#os.remove(source)
#os.remove(dest)
return result
#--- Phatch Action
class Action(models.Action):
label = _t('%(label)s')
author = '%(author)s'
email = '%(email)s'
init = staticmethod(init)
pil = staticmethod(impil)
version = '0.1'
tags = %(tags)s
__doc__ = _t('%(doc)s')
def interface(self,fields):
%(fields)s
"""
# templates for various fields
templateSlider = "SliderField(%(default)u, %(min)u, %(max)u)"
templateChoice = "ChoiceField('%(default)s', %(choices)s)"
# fields[_t('Boolean')] = self.BooleanField(True)
# fields[_t('String')] = self.CharField('hello world')
# fields[_t('Choice')] = self.ChoiceField(CHOICES[0], CHOICES)
# fields[_t('Colour')] = self.ColourField('#FFFFFF')
# fields[_t('Resolution')]= self.DpiField('<dpi>')
# fields[_t('File')] = self.FileField('/home/images/logo.jpg')
# fields[_t('Filename')] = self.FileNameField('<filename>')
# fields[_t('In')] = self.FilePathField('<folder>') #folder
# fields[_t('Float')] = self.FloatField(3.14)
# fields[_t('As')] = self.ImageTypeField('<type>')#png, jpg
# fields[_t('As')] = self.ImageReadTypeField('<type>')#png, jpg
# fields[_t('As')] = self.ImageWriteTypeField('<type>')#png, jpg
# fields[_t('Mode')] = self.ImageModeField('<mode>')#png, jpg
# fields[_t('Resample')] = self.ImageResampleField(_t('bicubic'))
# fields[_t('Integer')] = self.IntegerField(-4)
# fields[_t('Integer+')] = self.PositiveIntegerField(0)
# fields[_t('Integer+0')] = self.PositiveNoneZeroIntegerField(0)
# fields[_t('Horizontal')]= self.PixelField('5%') #accepts %,cm, inch
# fields[_t('Slider')] = self.SliderField(60,1,100)
# Load all the gph files and create action classes for each one.
def loadgphs():
# get a list of gph files
# for each gph file do:
# read file into variable
d = glob.glob("*.gph")
for f in d:
print f
l = {}
execfile(f, {}, l)
print 'cmd: ', l['cmd']
pilargs, cmdargs, fields = makefields(l['fields'])
print fields
l['fields'] = fields
l['pilargs'] = pilargs
l['cmdargs'] = cmdargs
tags = ''
for t in l['tags']:
tags += ',' "_t('" + t + "')"
l['tags'] = '[' + tags[1:] + ']'
fout = open(os.path.splitext(f)[0] + '.py', "w+")
print fout.name
s = templatePhatch % l
fout.write(s)
def makefields(fieldsdef):
indent = ' '
fields = ''
pilargs = ''
cmdargs = ''
for fielddef in fieldsdef:
name, field = makefield(fielddef)
fields += indent + field + '\n'
pilargs += ", " + name
cmdargs = cmdargs + ", '%s': %s" % (name, name)
cmdargs = '{"source": source, "dest": dest, ' + cmdargs[1:] + '}'
return pilargs, cmdargs, fields
def makefield(fdef):
print 'makefield'
print fdef
if 'min' in fdef:
# use slider
f = ("fields[_t('%(name)s')] = self." + templateSlider) % fdef
elif 'choices' in fdef:
# use choice field
f = ("fields[_t('%(name)s')] = self." + templateChoice) % fdef
else:
print 'Unexpected field type: %s' % fdef
return fdef['name'], f
# Load all gph files and create all classes
loadgphs()
print 'finished'
-- end generic.py -------------------------------
insert the code here