blob: f8388c6d5514ed31e1b51c55c114b87a221be0ce [file] [log] [blame]
Guido van Rossum1ce7c6f1997-01-15 19:19:19 +00001# Module 'panel'
2#
3# Support for the Panel library.
4# Uses built-in module 'pnl'.
Jeremy Hyltona05e2932000-06-28 14:48:01 +00005# Applications should use 'panel.function' instead of 'pnl.function';
Guido van Rossum1ce7c6f1997-01-15 19:19:19 +00006# most 'pnl' functions are transparently exported by 'panel',
7# but dopanel() is overridden and you have to use this version
8# if you want to use callbacks.
9
10
11import pnl
12
13
14debug = 0
15
16
17# Test if an object is a list.
18#
19def is_list(x):
20 return type(x) == type([])
21
22
23# Reverse a list.
24#
25def reverse(list):
26 res = []
27 for item in list:
28 res.insert(0, item)
29 return res
30
31
32# Get an attribute of a list, which may itself be another list.
33# Don't use 'prop' for name.
34#
35def getattrlist(list, name):
36 for item in list:
37 if item and is_list(item) and item[0] == name:
38 return item[1:]
39 return []
40
41
42# Get a property of a list, which may itself be another list.
43#
44def getproplist(list, name):
45 for item in list:
46 if item and is_list(item) and item[0] == 'prop':
47 if len(item) > 1 and item[1] == name:
48 return item[2:]
49 return []
50
51
52# Test if an actuator description contains the property 'end-of-group'
53#
54def is_endgroup(list):
55 x = getproplist(list, 'end-of-group')
56 return (x and x[0] == '#t')
57
58
59# Neatly display an actuator definition given as S-expression
60# the prefix string is printed before each line.
61#
62def show_actuator(prefix, a):
63 for item in a:
64 if not is_list(item):
65 print prefix, item
66 elif item and item[0] == 'al':
67 print prefix, 'Subactuator list:'
68 for a in item[1:]:
69 show_actuator(prefix + ' ', a)
70 elif len(item) == 2:
71 print prefix, item[0], '=>', item[1]
72 elif len(item) == 3 and item[0] == 'prop':
73 print prefix, 'Prop', item[1], '=>',
74 print item[2]
75 else:
76 print prefix, '?', item
77
78
79# Neatly display a panel.
80#
81def show_panel(prefix, p):
82 for item in p:
83 if not is_list(item):
84 print prefix, item
85 elif item and item[0] == 'al':
86 print prefix, 'Actuator list:'
87 for a in item[1:]:
88 show_actuator(prefix + ' ', a)
89 elif len(item) == 2:
90 print prefix, item[0], '=>', item[1]
91 elif len(item) == 3 and item[0] == 'prop':
92 print prefix, 'Prop', item[1], '=>',
93 print item[2]
94 else:
95 print prefix, '?', item
96
97
98# Exception raised by build_actuator or build_panel.
99#
100panel_error = 'panel error'
101
102
103# Dummy callback used to initialize the callbacks.
104#
105def dummy_callback(arg):
106 pass
107
108
109# Assign attributes to members of the target.
110# Attribute names in exclist are ignored.
111# The member name is the attribute name prefixed with the prefix.
112#
113def assign_members(target, attrlist, exclist, prefix):
114 for item in attrlist:
115 if is_list(item) and len(item) == 2 and item[0] not in exclist:
116 name, value = item[0], item[1]
117 ok = 1
118 if value[0] in '-0123456789':
119 value = eval(value)
120 elif value[0] == '"':
121 value = value[1:-1]
122 elif value == 'move-then-resize':
123 # Strange default set by Panel Editor...
124 ok = 0
125 else:
126 print 'unknown value', value, 'for', name
127 ok = 0
128 if ok:
129 lhs = 'target.' + prefix + name
130 stmt = lhs + '=' + `value`
131 if debug: print 'exec', stmt
132 try:
133 exec stmt + '\n'
134 except KeyboardInterrupt: # Don't catch this!
135 raise KeyboardInterrupt
136 except:
137 print 'assign failed:', stmt
138
139
Jeremy Hyltona05e2932000-06-28 14:48:01 +0000140# Build a real actuator from an actuator description.
Guido van Rossum1ce7c6f1997-01-15 19:19:19 +0000141# Return a pair (actuator, name).
142#
143def build_actuator(descr):
144 namelist = getattrlist(descr, 'name')
145 if namelist:
146 # Assume it is a string
147 actuatorname = namelist[0][1:-1]
148 else:
149 actuatorname = ''
150 type = descr[0]
151 if type[:4] == 'pnl_': type = type[4:]
152 act = pnl.mkact(type)
153 act.downfunc = act.activefunc = act.upfunc = dummy_callback
154 #
155 assign_members(act, descr[1:], ['al', 'data', 'name'], '')
156 #
157 # Treat actuator-specific data
158 #
159 datalist = getattrlist(descr, 'data')
160 prefix = ''
161 if type[-4:] == 'puck':
162 prefix = 'puck_'
163 elif type == 'mouse':
164 prefix = 'mouse_'
165 assign_members(act, datalist, [], prefix)
166 #
167 return act, actuatorname
168
169
170# Build all sub-actuators and add them to the super-actuator.
171# The super-actuator must already have been added to the panel.
172# Sub-actuators with defined names are added as members to the panel
173# so they can be referenced as p.name.
174#
175# Note: I have no idea how panel.endgroup() works when applied
176# to a sub-actuator.
177#
178def build_subactuators(panel, super_act, al):
179 #
180 # This is nearly the same loop as below in build_panel(),
181 # except a call is made to addsubact() instead of addact().
182 #
183 for a in al:
184 act, name = build_actuator(a)
185 act.addsubact(super_act)
186 if name:
187 stmt = 'panel.' + name + ' = act'
188 if debug: print 'exec', stmt
189 exec stmt + '\n'
190 if is_endgroup(a):
191 panel.endgroup()
192 sub_al = getattrlist(a, 'al')
193 if sub_al:
194 build_subactuators(panel, act, sub_al)
195 #
196 # Fix the actuator to which whe just added subactuators.
197 # This can't hurt (I hope) and is needed for the scroll actuator.
198 #
199 super_act.fixact()
200
201
202# Build a real panel from a panel definition.
203# Return a panel object p, where for each named actuator a, p.name is a
204# reference to a.
205#
206def build_panel(descr):
207 #
208 # Sanity check
209 #
Fred Drake132dce22000-12-12 23:11:42 +0000210 if (not descr) or descr[0] != 'panel':
Guido van Rossum1ce7c6f1997-01-15 19:19:19 +0000211 raise panel_error, 'panel description must start with "panel"'
212 #
213 if debug: show_panel('', descr)
214 #
215 # Create an empty panel
216 #
217 panel = pnl.mkpanel()
218 #
219 # Assign panel attributes
220 #
221 assign_members(panel, descr[1:], ['al'], '')
222 #
223 # Look for actuator list
224 #
225 al = getattrlist(descr, 'al')
226 #
227 # The order in which actuators are created is important
228 # because of the endgroup() operator.
229 # Unfortunately the Panel Editor outputs the actuator list
230 # in reverse order, so we reverse it here.
231 #
232 al = reverse(al)
233 #
234 for a in al:
235 act, name = build_actuator(a)
236 act.addact(panel)
237 if name:
238 stmt = 'panel.' + name + ' = act'
239 exec stmt + '\n'
240 if is_endgroup(a):
241 panel.endgroup()
242 sub_al = getattrlist(a, 'al')
243 if sub_al:
244 build_subactuators(panel, act, sub_al)
245 #
246 return panel
247
248
249# Wrapper around pnl.dopanel() which calls call-back functions.
250#
251def my_dopanel():
252 # Extract only the first 4 elements to allow for future expansion
253 a, down, active, up = pnl.dopanel()[:4]
254 if down:
255 down.downfunc(down)
256 if active:
257 active.activefunc(active)
258 if up:
259 up.upfunc(up)
260 return a
261
262
263# Create one or more panels from a description file (S-expressions)
264# generated by the Panel Editor.
265#
266def defpanellist(file):
267 import panelparser
268 descrlist = panelparser.parse_file(open(file, 'r'))
269 panellist = []
270 for descr in descrlist:
271 panellist.append(build_panel(descr))
272 return panellist
273
274
275# Import everything from built-in method pnl, so the user can always
276# use panel.foo() instead of pnl.foo().
277# This gives *no* performance penalty once this module is imported.
278#
279from pnl import * # for export
280
281dopanel = my_dopanel # override pnl.dopanel