]>
Commit | Line | Data |
---|---|---|
d14a1e28 RD |
1 | #---------------------------------------------------------------------- |
2 | # Name: wxPython.tools.img2py | |
3 | # Purpose: Convert an image to Python code. | |
4 | # | |
5 | # Author: Robin Dunn | |
6 | # | |
7 | # RCS-ID: $Id$ | |
8 | # Copyright: (c) 2002 by Total Control Software | |
9 | # Licence: wxWindows license | |
10 | #---------------------------------------------------------------------- | |
d4b73b1b RD |
11 | # 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
12 | # | |
13 | # o V2.5 compatability update | |
14 | # | |
1fded56b | 15 | |
1fded56b | 16 | |
d14a1e28 RD |
17 | """ |
18 | img2py.py -- Convert an image to PNG format and embed it in a Python | |
19 | module with appropriate code so it can be loaded into | |
20 | a program at runtime. The benefit is that since it is | |
21 | Python source code it can be delivered as a .pyc or | |
22 | 'compiled' into the program using freeze, py2exe, etc. | |
23 | ||
24 | Usage: | |
25 | ||
26 | img2py.py [options] image_file python_file | |
27 | ||
28 | Options: | |
29 | ||
30 | -m <#rrggbb> If the original image has a mask or transparency defined | |
31 | it will be used by default. You can use this option to | |
32 | override the default or provide a new mask by specifying | |
33 | a colour in the image to mark as transparent. | |
34 | ||
35 | -n <name> Normally generic names (getBitmap, etc.) are used for the | |
36 | image access functions. If you use this option you can | |
37 | specify a name that should be used to customize the access | |
38 | fucntions, (getNameBitmap, etc.) | |
39 | ||
40 | -c Maintain a catalog of names that can be used to reference | |
41 | images. Catalog can be accessed via catalog and index attributes | |
42 | of the module. If the -n <name> option is specified then <name> | |
43 | is used for the catalog key and index value, otherwise | |
44 | the filename without any path or extension is used as the key. | |
45 | ||
46 | -a This flag specifies that the python_file should be appended | |
47 | to instead of overwritten. This in combination with -n will | |
48 | allow you to put multiple images in one Python source file. | |
49 | ||
50 | -u Don't use compression. Leaves the data uncompressed. | |
51 | ||
52 | -i Also output a function to return the image as a wxIcon. | |
53 | ||
54 | """ | |
55 | ||
56 | # | |
57 | # Changes: | |
58 | # - Cliff Wells <LogiplexSoftware@earthlink.net> | |
59 | # 20021206: Added catalog (-c) option. | |
60 | # | |
d4b73b1b RD |
61 | # 12/21/2003 - Jeff Grimmett (grimmtooth@softhome.net) |
62 | # | |
63 | # o V2.5 compatability update | |
64 | # | |
65 | ||
66 | import cPickle | |
67 | import cStringIO | |
68 | import getopt | |
69 | import glob | |
70 | import os | |
71 | import sys | |
72 | import tempfile | |
73 | import zlib | |
d14a1e28 | 74 | |
d4b73b1b | 75 | import wx |
d14a1e28 | 76 | |
d4b73b1b | 77 | import img2img |
d14a1e28 RD |
78 | |
79 | ||
80 | def crunch_data(data, compressed): | |
81 | # compress it? | |
82 | if compressed: | |
83 | data = zlib.compress(data, 9) | |
84 | ||
85 | # convert to a printable format, so it can be in a Python source file | |
86 | data = repr(data) | |
87 | ||
88 | # This next bit is borrowed from PIL. It is used to wrap the text intelligently. | |
89 | fp = cStringIO.StringIO() | |
90 | data = data + " " # buffer for the +1 test | |
91 | c = i = 0 | |
92 | word = "" | |
93 | octdigits = "01234567" | |
94 | hexdigits = "0123456789abcdef" | |
95 | while i < len(data): | |
96 | if data[i] != "\\": | |
97 | word = data[i] | |
98 | i = i + 1 | |
99 | else: | |
100 | if data[i+1] in octdigits: | |
101 | for n in range(2, 5): | |
102 | if data[i+n] not in octdigits: | |
103 | break | |
104 | word = data[i:i+n] | |
105 | i = i + n | |
106 | elif data[i+1] == 'x': | |
107 | for n in range(2, 5): | |
108 | if data[i+n] not in hexdigits: | |
109 | break | |
110 | word = data[i:i+n] | |
111 | i = i + n | |
112 | else: | |
113 | word = data[i:i+2] | |
114 | i = i + 2 | |
115 | ||
116 | l = len(word) | |
117 | if c + l >= 78-1: | |
118 | fp.write("\\\n") | |
119 | c = 0 | |
120 | fp.write(word) | |
121 | c = c + l | |
122 | ||
123 | # return the formatted compressed data | |
124 | return fp.getvalue() | |
125 | ||
126 | ||
127 | ||
128 | def main(args): | |
129 | if not args or ("-h" in args): | |
130 | print __doc__ | |
131 | return | |
132 | ||
133 | # some bitmap related things need to have a wxApp initialized... | |
d4b73b1b RD |
134 | if wx.GetApp() is None: |
135 | app = wx.PySimpleApp() | |
d14a1e28 RD |
136 | |
137 | append = 0 | |
138 | compressed = 1 | |
139 | maskClr = None | |
140 | imgName = "" | |
141 | icon = 0 | |
142 | catalog = 0 | |
143 | ||
144 | try: | |
145 | opts, fileArgs = getopt.getopt(args, "auicn:m:") | |
146 | except getopt.GetoptError: | |
147 | print __doc__ | |
148 | return | |
149 | ||
150 | for opt, val in opts: | |
151 | if opt == "-a": | |
152 | append = 1 | |
153 | elif opt == "-u": | |
154 | compressed = 0 | |
155 | elif opt == "-n": | |
156 | imgName = val | |
157 | elif opt == "-m": | |
158 | maskClr = val | |
159 | elif opt == "-i": | |
160 | icon = 1 | |
161 | elif opt == "-c": | |
162 | catalog = 1 | |
163 | ||
164 | if len(fileArgs) != 2: | |
165 | print __doc__ | |
166 | return | |
167 | ||
168 | image_file, python_file = fileArgs | |
169 | ||
170 | # convert the image file to a temporary file | |
171 | tfname = tempfile.mktemp() | |
d4b73b1b | 172 | ok, msg = img2img.convert(image_file, maskClr, None, tfname, wx.BITMAP_TYPE_PNG, ".png") |
d14a1e28 RD |
173 | if not ok: |
174 | print msg | |
175 | return | |
176 | ||
177 | data = open(tfname, "rb").read() | |
178 | data = crunch_data(data, compressed) | |
179 | os.unlink(tfname) | |
180 | ||
181 | if append: | |
182 | out = open(python_file, "a") | |
183 | else: | |
184 | out = open(python_file, "w") | |
185 | ||
186 | if catalog: | |
187 | pyPath, pyFile = os.path.split(python_file) | |
188 | imgPath, imgFile = os.path.split(image_file) | |
189 | ||
190 | if not imgName: | |
191 | imgName = os.path.splitext(imgFile)[0] | |
192 | print "\nWarning: -n not specified. Using filename (%s) for catalog entry." % imgName | |
193 | ||
194 | old_index = [] | |
195 | if append: | |
196 | # check to see if catalog exists already (file may have been created | |
197 | # with an earlier version of img2py or without -c option) | |
198 | oldSysPath = sys.path[:] | |
199 | sys.path = [pyPath] # make sure we don't import something else by accident | |
200 | mod = __import__(os.path.splitext(pyFile)[0]) | |
201 | if 'index' not in dir(mod): | |
202 | print "\nWarning: %s was originally created without catalog." % python_file | |
203 | print " Any images already in file will not be cataloged.\n" | |
204 | out.write("\n# ***************** Catalog starts here *******************") | |
205 | out.write("\n\ncatalog = {}\n") | |
206 | out.write("index = []\n\n") | |
207 | out.write("class ImageClass: pass\n\n") | |
208 | else: # save a copy of the old index so we can warn about duplicate names | |
209 | old_index[:] = mod.index[:] | |
210 | del mod | |
211 | sys.path = oldSysPath[:] | |
212 | ||
213 | out.write("#" + "-" * 70 + "\n") | |
214 | if not append: | |
215 | out.write("# This file was generated by %s\n#\n" % sys.argv[0]) | |
d4b73b1b | 216 | out.write("from wx import ImageFromStream, BitmapFromImage\n") |
d14a1e28 | 217 | if icon: |
d4b73b1b | 218 | out.write("from wx import EmptyIcon\n") |
d14a1e28 RD |
219 | if compressed: |
220 | out.write("import cStringIO, zlib\n\n\n") | |
221 | else: | |
222 | out.write("import cStringIO\n\n\n") | |
223 | ||
224 | if catalog: | |
225 | out.write("catalog = {}\n") | |
226 | out.write("index = []\n\n") | |
227 | out.write("class ImageClass: pass\n\n") | |
228 | ||
229 | if compressed: | |
230 | out.write("def get%sData():\n" | |
231 | " return zlib.decompress(\n%s)\n\n" | |
232 | % (imgName, data)) | |
233 | else: | |
234 | out.write("def get%sData():\n" | |
235 | " return \\\n%s\n\n" | |
236 | % (imgName, data)) | |
237 | ||
238 | ||
239 | out.write("def get%sBitmap():\n" | |
d4b73b1b | 240 | " return BitmapFromImage(get%sImage())\n\n" |
d14a1e28 RD |
241 | "def get%sImage():\n" |
242 | " stream = cStringIO.StringIO(get%sData())\n" | |
d4b73b1b | 243 | " return ImageFromStream(stream)\n\n" |
d14a1e28 RD |
244 | % tuple([imgName] * 4)) |
245 | if icon: | |
246 | out.write("def get%sIcon():\n" | |
d4b73b1b | 247 | " icon = EmptyIcon()\n" |
d14a1e28 RD |
248 | " icon.CopyFromBitmap(get%sBitmap())\n" |
249 | " return icon\n\n" | |
250 | % tuple([imgName] * 2)) | |
251 | ||
252 | if catalog: | |
253 | if imgName in old_index: | |
254 | print "Warning: %s already in catalog." % imgName | |
255 | print " Only the last entry will be accessible.\n" | |
256 | old_index.append(imgName) | |
257 | out.write("index.append('%s')\n" % imgName) | |
258 | out.write("catalog['%s'] = ImageClass()\n" % imgName) | |
259 | out.write("catalog['%s'].getData = get%sData\n" % tuple([imgName] * 2)) | |
260 | out.write("catalog['%s'].getImage = get%sImage\n" % tuple([imgName] * 2)) | |
261 | out.write("catalog['%s'].getBitmap = get%sBitmap\n" % tuple([imgName] * 2)) | |
262 | if icon: | |
263 | out.write("catalog['%s'].getIcon = get%sIcon\n" % tuple([imgName] * 2)) | |
264 | out.write("\n\n") | |
265 | ||
266 | if imgName: | |
267 | n_msg = ' using "%s"' % imgName | |
268 | else: | |
269 | n_msg = "" | |
270 | if maskClr: | |
271 | m_msg = " with mask %s" % maskClr | |
272 | else: | |
273 | m_msg = "" | |
274 | print "Embedded %s%s into %s%s" % (image_file, n_msg, python_file, m_msg) | |
275 | ||
276 | ||
277 | if __name__ == "__main__": | |
278 | main(sys.argv[1:]) | |
279 |