2 # flp - Module to load fl forms from fd files
4 # Jack Jansen, December 1991
10 SPLITLINE
= '--------------------'
11 FORMLINE
= '=============== FORM ==============='
12 ENDLINE
= '=============================='
14 class error(Exception):
17 ##################################################################
18 # Part 1 - The parsing routines #
19 ##################################################################
22 # Externally visible function. Load form.
24 def parse_form(filename
, formname
):
25 forms
= checkcache(filename
)
27 forms
= parse_forms(filename
)
28 if forms
.has_key(formname
):
29 return forms
[formname
]
31 raise error
, 'No such form in fd file'
34 # Externally visible function. Load all forms.
36 def parse_forms(filename
):
37 forms
= checkcache(filename
)
38 if forms
is not None: return forms
39 fp
= _open_formfile(filename
)
40 nforms
= _parse_fd_header(fp
)
42 for i
in range(nforms
):
43 form
= _parse_fd_form(fp
, None)
44 forms
[form
[0].Name
] = form
45 writecache(filename
, forms
)
49 # Internal: see if a cached version of the file exists
52 _internal_cache
= {} # Used by frozen scripts only
53 def checkcache(filename
):
54 if _internal_cache
.has_key(filename
):
55 altforms
= _internal_cache
[filename
]
56 return _unpack_cache(altforms
)
58 fp
, filename
= _open_formfile2(filename
)
60 cachename
= filename
+ 'c'
62 fp
= open(cachename
, 'r')
64 #print 'flp: no cache file', cachename
67 if fp
.read(4) != MAGIC
:
68 print 'flp: bad magic word in cache file', cachename
70 cache_mtime
= rdlong(fp
)
71 file_mtime
= getmtime(filename
)
72 if cache_mtime
!= file_mtime
:
73 #print 'flp: outdated cache file', cachename
75 #print 'flp: valid cache file', cachename
76 altforms
= marshal
.load(fp
)
77 return _unpack_cache(altforms
)
81 def _unpack_cache(altforms
):
83 for name
in altforms
.keys():
84 altobj
, altlist
= altforms
[name
]
88 for altobj
in altlist
:
92 forms
[name
] = obj
, list
97 if len(s
) != 4: return None
98 a
, b
, c
, d
= s
[0], s
[1], s
[2], s
[3]
99 return ord(a
)<<24 |
ord(b
)<<16 |
ord(c
)<<8 |
ord(d
)
102 a
, b
, c
, d
= (x
>>24)&0xff, (x
>>16)&0xff, (x
>>8)&0xff, x
&0xff
103 fp
.write(chr(a
) + chr(b
) + chr(c
) + chr(d
))
105 def getmtime(filename
):
107 from stat
import ST_MTIME
109 return os
.stat(filename
)[ST_MTIME
]
114 # Internal: write cached version of the form (parsing is too slow!)
116 def writecache(filename
, forms
):
118 fp
, filename
= _open_formfile2(filename
)
120 cachename
= filename
+ 'c'
122 fp
= open(cachename
, 'w')
124 print 'flp: can\'t create cache file', cachename
126 fp
.write('\0\0\0\0') # Seek back and write MAGIC when done
127 wrlong(fp
, getmtime(filename
))
128 altforms
= _pack_cache(forms
)
129 marshal
.dump(altforms
, fp
)
133 #print 'flp: wrote cache file', cachename
136 # External: print some statements that set up the internal cache.
137 # This is for use with the "freeze" script. You should call
138 # flp.freeze(filename) for all forms used by the script, and collect
139 # the output on a file in a module file named "frozenforms.py". Then
140 # in the main program of the script import frozenforms.
141 # (Don't forget to take this out when using the unfrozen version of
144 def freeze(filename
):
145 forms
= parse_forms(filename
)
146 altforms
= _pack_cache(forms
)
148 print 'flp._internal_cache[', `filename`
, '] =', altforms
151 # Internal: create the data structure to be placed in the cache
153 def _pack_cache(forms
):
155 for name
in forms
.keys():
156 obj
, list = forms
[name
]
157 altobj
= obj
.__dict
__
159 for obj
in list: altlist
.append(obj
.__dict
__)
160 altforms
[name
] = altobj
, altlist
164 # Internal: Locate form file (using PYTHONPATH) and open file
166 def _open_formfile(filename
):
167 return _open_formfile2(filename
)[0]
169 def _open_formfile2(filename
):
170 if filename
[-3:] != '.fd':
171 filename
= filename
+ '.fd'
172 if filename
[0] == '/':
174 fp
= open(filename
,'r')
179 pn
= os
.path
.join(pc
, filename
)
187 raise error
, 'Cannot find forms file ' + filename
191 # Internal: parse the fd file header, return number of forms
193 def _parse_fd_header(file):
194 # First read the magic header line
195 datum
= _parse_1_line(file)
196 if datum
!= ('Magic', 12321):
197 raise error
, 'Not a forms definition file'
198 # Now skip until we know number of forms
200 datum
= _parse_1_line(file)
201 if type(datum
) == type(()) and datum
[0] == 'Numberofforms':
205 # Internal: parse fd form, or skip if name doesn't match.
206 # the special value None means 'always parse it'.
208 def _parse_fd_form(file, name
):
209 datum
= _parse_1_line(file)
210 if datum
!= FORMLINE
:
211 raise error
, 'Missing === FORM === line'
212 form
= _parse_object(file)
213 if form
.Name
== name
or name
is None:
215 for j
in range(form
.Numberofobjects
):
216 obj
= _parse_object(file)
220 for j
in range(form
.Numberofobjects
):
225 # Internal class: a convenient place to store object info fields
228 def add(self
, name
, value
):
229 self
.__dict
__[name
] = value
230 def make(self
, dict):
231 for name
in dict.keys():
232 self
.add(name
, dict[name
])
235 # Internal parsing routines.
237 def _parse_string(str):
239 s
= '\'' + str + '\''
249 def _parse_numlist(str):
253 nlist
.append(_parse_num(i
))
256 # This dictionary maps item names to parsing routines.
257 # If no routine is given '_parse_num' is default.
259 'Name': _parse_string
, \
260 'Box': _parse_numlist
, \
261 'Colors': _parse_numlist
, \
262 'Label': _parse_string
, \
263 'Name': _parse_string
, \
264 'Callback': _parse_string
, \
265 'Argument': _parse_string
}
267 # This function parses a line, and returns either
268 # a string or a tuple (name,value)
271 prog
= re
.compile('^([^:]*): *(.*)')
273 def _parse_line(line
):
274 match
= prog
.match(line
)
277 name
, value
= match
.group(1, 2)
279 name
= ''.join(name
.split())
281 name
= name
.capitalize()
283 pf
= _parse_func
[name
]
290 line
= file.readline()
295 def _parse_1_line(file):
296 line
= _readline(file)
298 line
= _readline(file)
299 return _parse_line(line
)
301 def _skip_object(file):
303 while not line
in (SPLITLINE
, FORMLINE
, ENDLINE
):
305 line
= _readline(file)
309 def _parse_object(file):
313 datum
= _parse_1_line(file)
314 if datum
in (SPLITLINE
, FORMLINE
, ENDLINE
):
315 if datum
== FORMLINE
:
318 if type(datum
) is not type(()) or len(datum
) != 2:
319 raise error
, 'Parse error, illegal line in object: '+datum
320 obj
.add(datum
[0], datum
[1])
322 #################################################################
323 # Part 2 - High-level object/form creation routines #
324 #################################################################
327 # External - Create a form an link to an instance variable.
329 def create_full_form(inst
, (fdata
, odatalist
)):
330 form
= create_form(fdata
)
331 exec 'inst.'+fdata
.Name
+' = form\n'
332 for odata
in odatalist
:
333 create_object_instance(inst
, form
, odata
)
336 # External - Merge a form into an existing form in an instance
339 def merge_full_form(inst
, form
, (fdata
, odatalist
)):
340 exec 'inst.'+fdata
.Name
+' = form\n'
341 if odatalist
[0].Class
!= FL
.BOX
:
342 raise error
, 'merge_full_form() expects FL.BOX as first obj'
343 for odata
in odatalist
[1:]:
344 create_object_instance(inst
, form
, odata
)
347 #################################################################
348 # Part 3 - Low-level object/form creation routines #
349 #################################################################
352 # External Create_form - Create form from parameters
354 def create_form(fdata
):
356 return fl
.make_form(FL
.NO_BOX
, fdata
.Width
, fdata
.Height
)
359 # External create_object - Create an object. Make sure there are
360 # no callbacks. Returns the object created.
362 def create_object(form
, odata
):
363 obj
= _create_object(form
, odata
)
365 raise error
, 'Creating free object with callback'
368 # External create_object_instance - Create object in an instance.
370 def create_object_instance(inst
, form
, odata
):
371 obj
= _create_object(form
, odata
)
373 cbfunc
= eval('inst.'+odata
.Callback
)
374 obj
.set_call_back(cbfunc
, odata
.Argument
)
376 exec 'inst.' + odata
.Name
+ ' = obj\n'
378 # Internal _create_object: Create the object and fill options
380 def _create_object(form
, odata
):
381 crfunc
= _select_crfunc(form
, odata
.Class
)
382 obj
= crfunc(odata
.Type
, odata
.Box
[0], odata
.Box
[1], odata
.Box
[2], \
383 odata
.Box
[3], odata
.Label
)
384 if not odata
.Class
in (FL
.BEGIN_GROUP
, FL
.END_GROUP
):
385 obj
.boxtype
= odata
.Boxtype
386 obj
.col1
= odata
.Colors
[0]
387 obj
.col2
= odata
.Colors
[1]
388 obj
.align
= odata
.Alignment
389 obj
.lstyle
= odata
.Style
390 obj
.lsize
= odata
.Size
391 obj
.lcol
= odata
.Lcol
394 # Internal crfunc: helper function that returns correct create function
396 def _select_crfunc(fm
, cl
):
397 if cl
== FL
.BEGIN_GROUP
: return fm
.bgn_group
398 elif cl
== FL
.END_GROUP
: return fm
.end_group
399 elif cl
== FL
.BITMAP
: return fm
.add_bitmap
400 elif cl
== FL
.BOX
: return fm
.add_box
401 elif cl
== FL
.BROWSER
: return fm
.add_browser
402 elif cl
== FL
.BUTTON
: return fm
.add_button
403 elif cl
== FL
.CHART
: return fm
.add_chart
404 elif cl
== FL
.CHOICE
: return fm
.add_choice
405 elif cl
== FL
.CLOCK
: return fm
.add_clock
406 elif cl
== FL
.COUNTER
: return fm
.add_counter
407 elif cl
== FL
.DIAL
: return fm
.add_dial
408 elif cl
== FL
.FREE
: return fm
.add_free
409 elif cl
== FL
.INPUT
: return fm
.add_input
410 elif cl
== FL
.LIGHTBUTTON
: return fm
.add_lightbutton
411 elif cl
== FL
.MENU
: return fm
.add_menu
412 elif cl
== FL
.POSITIONER
: return fm
.add_positioner
413 elif cl
== FL
.ROUNDBUTTON
: return fm
.add_roundbutton
414 elif cl
== FL
.SLIDER
: return fm
.add_slider
415 elif cl
== FL
.VALSLIDER
: return fm
.add_valslider
416 elif cl
== FL
.TEXT
: return fm
.add_text
417 elif cl
== FL
.TIMER
: return fm
.add_timer
419 raise error
, 'Unknown object type: ' + `cl`
425 if len(sys
.argv
) == 2:
426 forms
= parse_forms(sys
.argv
[1])
428 print 'parse time:', 0.001*(t1
-t0
), 'sec.'
433 elif len(sys
.argv
) == 3:
434 form
= parse_form(sys
.argv
[1], sys
.argv
[2])
436 print 'parse time:', round(t1
-t0
, 3), 'sec.'
439 print 'Usage: test fdfile [form]'
441 def _printform(form
):
444 print 'Form ', f
.Name
, ', size: ', f
.Width
, f
.Height
, ' Nobj ', f
.Numberofobjects
446 print ' Obj ', i
.Name
, ' type ', i
.Class
, i
.Type
447 print ' Box ', i
.Box
, ' btype ', i
.Boxtype
448 print ' Label ', i
.Label
, ' size/style/col/align ', i
.Size
,i
.Style
, i
.Lcol
, i
.Alignment
449 print ' cols ', i
.Colors
450 print ' cback ', i
.Callback
, i
.Argument