8 from pygame
.locals import *
12 _amap
= {'left':-1,'right':1,'center':0,None:None,'':None,}
13 _vamap
= {'top':-1,'bottom':1,'center':0,'middle':0,None:None,'':None,}
15 # Used by the HTML parser to load external resources (like images). This
16 # class loads content from the local file system. But you can pass your own
17 # resource loader to the HTML parser to find images by other means.
18 class ResourceLoader(object):
19 # Loads an image and returns it as a pygame image
20 def load_image(this
, path
):
21 return pygame
.image
.load(path
)
29 self
.style
.font
= None
30 self
.style
.color
= None
33 def space(self
,v
): pass
36 def __init__(self
,**params
):
37 gui
.Color
.__init
__(self
,(0,0,0),**params
)
38 def resize(self
,width
=None,height
=None):
39 w
,h
= self
.style
.width
,self
.style
.height
40 #if width != None: self.rect.w = width
41 #else: self.rect.w = 1
43 #xt,xr,xb,xl = self.getspacing()
45 if width
!= None: w
= max(w
,width
)
46 if height
!= None: h
= max(h
,height
)
50 return w
,h
#self.container.rect.w,h
52 #self.rect.w = max(1,width,self.container.rect.w-(xl+xr))
57 class _html(htmllib
.HTMLParser
):
58 def init(self
,doc
,font
,color
,_globals
,_locals
,loader
=None):
64 # Use the default resource loader
65 self
.loader
= ResourceLoader()
66 self
.myopen('document',self
.document
)
68 self
.myfont
= self
.font
= font
69 self
.mycolor
= self
.color
= color
73 self
._globals
= _globals
74 self
._locals
= _locals
76 def myopen(self
,type_
,w
):
78 self
.mystack
.append((type_
,w
))
79 self
.type,self
.item
= type_
,w
81 self
.font
= self
.item
.style
.font
82 self
.color
= self
.item
.style
.color
84 if not self
.font
: self
.font
= self
.myfont
85 if not self
.color
: self
.color
= self
.mycolor
87 def myclose(self
,type_
):
91 #if len(self.mystack)==0: return
92 t
,w
= self
.mystack
.pop()
93 t
,w
= self
.mystack
.pop()
96 def myback(self
,type_
):
97 if type(type_
) == str: type_
= [type_
,]
101 while t
not in type_
:
102 #if len(self.mystack)==0: return
103 t
,w
= self
.mystack
.pop()
107 #clearing out the last </p>
108 if not hasattr(self
.item
,'layout'): return
109 if len(self
.item
.layout
._widgets
) == 0: return
110 w
= self
.item
.layout
._widgets
[-1]
112 del self
.item
.layout
._widgets
[-1]
115 def start_b(self
,attrs
): self
.font
.set_bold(1)
116 def end_b(self
): self
.font
.set_bold(0)
117 def start_i(self
,attrs
): self
.font
.set_italic(1)
118 def end_i(self
): self
.font
.set_italic(0)
119 def start_u(self
,attrs
): self
.font
.set_underline(1)
120 def end_u(self
): self
.font
.set_underline(0)
121 def start_br(self
,attrs
): self
.do_br(attrs
)
122 def do_br(self
,attrs
): self
.item
.br(self
.font
.size(" ")[1])
123 def attrs_to_map(self
,attrs
):
126 for k
,v
in attrs
: r
[k
] = v
129 def map_to_params(self
,r
):
130 anum
= re
.compile("\D")
132 params
= {'style':{}}
133 style
= params
['style']
136 style
['background'] = gui
.parse_color(r
['bgcolor'])
137 if 'background' in r
:
138 style
['background'] = self
.loader
.load_image(r
['background'])
139 if 'border' in r
: style
['border'] = int(r
['border'])
141 for k
in ['width','height','colspan','rowspan','size','min','max']:
142 if k
in r
: params
[k
] = int(anum
.sub("",r
[k
]))
144 for k
in ['name','value']:
145 if k
in r
: params
[k
] = r
[k
]
147 if 'class' in r
: params
['cls'] = r
['class']
150 params
['align'] = _amap
[r
['align']]
152 params
['valign'] = _vamap
[r
['valign']]
155 for st
in r
['style'].split(";"):
160 k
= k
.replace("-","_")
161 k
= k
.replace(" ","")
162 v
= v
.replace(" ","")
163 if k
== 'color' or k
== 'border_color' or k
== 'background':
164 v
= gui
.parse_color(v
)
166 v
= int(anum
.sub("",v
))
170 def map_to_connects(self
,e
,r
):
171 for k
,evt
in [('onclick',gui
.CLICK
),('onchange',gui
.CHANGE
)]: #blah blah blah
175 e
.connect(evt
,self
.myexec
,(e
,r
[k
]))
177 def start_p(self
,attrs
):
178 r
= self
.attrs_to_map(attrs
)
179 align
= r
.get("align","left")
182 self
.item
.block(_amap
[align
])
185 if len(self
.item
.layout
._widgets
) == 0: return
186 if type(self
.item
.layout
._widgets
[-1]) == tuple:
187 w
,h
= self
.item
.layout
._widgets
[-1]
196 def start_block(self
,t
,attrs
,align
=-1):
197 r
= self
.attrs_to_map(attrs
)
198 params
= self
.map_to_params(r
)
199 if 'cls' in params
: params
['cls'] = t
+"."+params
['cls']
200 else: params
['cls'] = t
201 b
= gui
.Document(**params
)
202 b
.style
.font
= self
.item
.style
.font
203 if 'align' in params
:
204 align
= params
['align']
205 self
.item
.block(align
)
211 def end_block(self
,t
):
215 def start_div(self
,attrs
): self
.start_block('div',attrs
)
216 def end_div(self
): self
.end_block('div')
217 def start_center(self
,attrs
): self
.start_block('div',attrs
,0)
218 def end_center(self
): self
.end_block('div')
220 def start_h1(self
,attrs
): self
.start_block('h1',attrs
)
221 def end_h1(self
): self
.end_block('h1')
222 def start_h2(self
,attrs
): self
.start_block('h2',attrs
)
223 def end_h2(self
): self
.end_block('h2')
224 def start_h3(self
,attrs
): self
.start_block('h3',attrs
)
225 def end_h3(self
): self
.end_block('h3')
226 def start_h4(self
,attrs
): self
.start_block('h4',attrs
)
227 def end_h4(self
): self
.end_block('h4')
228 def start_h5(self
,attrs
): self
.start_block('h5',attrs
)
229 def end_h5(self
): self
.end_block('h5')
230 def start_h6(self
,attrs
): self
.start_block('h6',attrs
)
231 def end_h6(self
): self
.end_block('h6')
233 def start_ul(self
,attrs
): self
.start_block('ul',attrs
)
234 def end_ul(self
): self
.end_block('ul')
235 def start_ol(self
,attrs
):
236 self
.start_block('ol',attrs
)
237 self
.item
.counter
= 0
238 def end_ol(self
): self
.end_block('ol')
239 def start_li(self
,attrs
):
240 self
.myback(['ul','ol'])
242 self
.start_block('li',attrs
)
243 if hasattr(cur
,'counter'):
245 self
.handle_data("%d. "%cur.counter
)
247 self
.handle_data("- ")
248 #def end_li(self): self.end_block('li') #this isn't needed because of how the parser works
250 def start_pre(self
,attrs
): self
.start_block('pre',attrs
)
251 def end_pre(self
): self
.end_block('pre')
252 def start_code(self
,attrs
): self
.start_block('code',attrs
)
253 def end_code(self
): self
.end_block('code')
255 def start_table(self
,attrs
):
256 r
= self
.attrs_to_map(attrs
)
257 params
= self
.map_to_params(r
)
259 align
= r
.get("align","left")
260 self
.item
.block(_amap
[align
])
262 t
= gui
.Table(**params
)
265 self
.myopen('table',t
)
267 def start_tr(self
,attrs
):
271 def _start_td(self
,t
,attrs
):
272 r
= self
.attrs_to_map(attrs
)
273 params
= self
.map_to_params(r
)
274 if 'cls' in params
: params
['cls'] = t
+"."+params
['cls']
275 else: params
['cls'] = t
276 b
= gui
.Document(cls
=t
)
279 self
.item
.td(b
,**params
)
282 self
.font
= self
.item
.style
.font
283 self
.color
= self
.item
.style
.color
285 def start_td(self
,attrs
):
286 self
._start
_td
('td',attrs
)
288 def start_th(self
,attrs
):
289 self
._start
_td
('th',attrs
)
292 self
.myclose('table')
295 def start_form(self
,attrs
):
296 r
= self
.attrs_to_map(attrs
)
297 e
= self
.form
= gui
.Form()
300 self
._locals
[r
.get('id',None)] = e
302 def start_input(self
,attrs
):
303 r
= self
.attrs_to_map(attrs
)
304 params
= self
.map_to_params(r
) #why bother
307 type_
,name
,value
= r
.get('type','text'),r
.get('name',None),r
.get('value',None)
310 e
= gui
.Input(**params
)
311 self
.map_to_connects(e
,r
)
313 elif type_
== 'radio':
314 if name
not in f
.groups
:
315 f
.groups
[name
] = gui
.Group(name
=name
)
318 e
= gui
.Radio(group
=g
,**params
)
319 self
.map_to_connects(e
,r
)
321 if 'checked' in r
: g
.value
= value
322 elif type_
== 'checkbox':
323 if name
not in f
.groups
:
324 f
.groups
[name
] = gui
.Group(name
=name
)
327 e
= gui
.Checkbox(group
=g
,**params
)
328 self
.map_to_connects(e
,r
)
330 if 'checked' in r
: g
.value
= value
332 elif type_
== 'button':
333 e
= gui
.Button(**params
)
334 self
.map_to_connects(e
,r
)
336 elif type_
== 'submit':
337 e
= gui
.Button(**params
)
338 self
.map_to_connects(e
,r
)
340 elif type_
== 'file':
341 e
= gui
.Input(**params
)
342 self
.map_to_connects(e
,r
)
344 b
= gui
.Button(value
='Browse...')
347 d
= gui
.FileDialog();
348 d
.connect(gui
.CHANGE
,gui
.action_setvalue
,(d
,e
))
350 b
.connect(gui
.CLICK
,_browse
,None)
352 self
._locals
[r
.get('id',None)] = e
354 def start_object(self
,attrs
):
355 r
= self
.attrs_to_map(attrs
)
356 params
= self
.map_to_params(r
)
357 code
= "e = %s(**params)"%r['type']
362 #print e.style.width,e.style.height
363 self
.map_to_connects(e
,r
)
366 self
._locals
[r
.get('id',None)] = e
368 def start_select(self
,attrs
):
369 r
= self
.attrs_to_map(attrs
)
372 name
,value
= r
.get('name',None),r
.get('value',None)
373 e
= gui
.Select(name
=name
,value
=value
,**params
)
374 self
.map_to_connects(e
,r
)
376 self
.myopen('select',e
)
378 def start_option(self
,attrs
):
379 r
= self
.attrs_to_map(attrs
)
380 params
= {} #style = self.map_to_style(r)
382 self
.myback('select')
383 e
= gui
.Document(**params
)
384 self
.item
.add(e
,value
=r
.get('value',None))
385 self
.myopen('option',e
)
388 def end_select(self
):
389 self
.myclose('select')
391 def start_hr(self
,attrs
):
393 def do_hr(self
,attrs
):
394 h
= self
.font
.size(" ")[1]/2
396 r
= self
.attrs_to_map(attrs
)
397 params
= self
.map_to_params(r
)
398 params
['style']['padding'] = h
402 self
.item
.add(_hr(**params
))
405 def anchor_begin(self
,href
,name
,type_
):
408 def anchor_end(self
):
411 def start_title(self
,attrs
): self
.myopen('title',_flush())
412 def end_title(self
): self
.myclose('title')
414 def myexec(self
,value
):
421 def handle_image(self
,src
,alt
,ismap
,align
,width
,height
):
423 w
= gui
.Image(self
.loader
.load_image(src
))
425 self
.item
.add(w
,_amap
[align
])
429 print 'handle_image: missing %s'%src
431 def handle_data(self
,txt
):
432 if self
.type == 'table': return
433 elif self
.type in ('pre','code'):
434 txt
= txt
.replace("\t"," ")
436 if ss
[-1] == "": del ss
[-1]
438 img
= self
.font
.render(sentence
,1,self
.color
)
444 txt
= re
.compile("^[\t\r\n]+").sub("",txt
)
445 txt
= re
.compile("[\t\r\n]+$").sub("",txt
)
447 tst
= re
.compile("[\t\r\n]+").sub("",txt
)
450 txt
= re
.compile("\s+").sub(" ",txt
)
454 self
.item
.space(self
.font
.size(" "))
457 for word
in txt
.split(" "):
458 word
= word
.replace(chr(160)," ") #
460 w
= gui
.Image(self
.font
.render(word
,1,self
.color
))
462 self
.item
.space(self
.font
.size(" "))
465 class HTML(gui
.Document
):
468 <pre>HTML(data,globals=None,locals=None)</pre>
471 <dt>data <dd>html data
472 <dt>globals <dd>global variables (for scripting)
473 <dt>locals <dd>local variables (for scripting)
474 <dt>loader <dd>the resource loader
477 <p>you may access html elements that have an id via widget[id]</p>
479 def __init__(self
,data
,globals=None,locals=None,loader
=None,**params
):
480 gui
.Document
.__init
__(self
,**params
)
481 # This ensures that the whole HTML document is left-aligned within
482 # the rendered surface.
483 self
.style
.align
= -1
485 _globals
,_locals
= globals,locals
487 if _globals
== None: _globals
= {}
488 if _locals
== None: _locals
= {}
489 self
._globals
= _globals
490 self
._locals
= _locals
492 #font = gui.theme.get("label","","font")
493 p
= _html(htmllib
.AS_IS
,0)
494 p
.init(self
,self
.style
.font
,self
.style
.color
,_globals
,_locals
,
501 def __getitem__(self
,k
):
502 return self
._locals
[k
]
504 # Returns a box (pygame rectangle) surrounding all widgets in this document
505 def get_bounding_box(this
):
506 minx
= miny
= sys
.maxint
507 maxx
= maxy
= -sys
.maxint
508 for e
in this
.layout
.widgets
:
509 minx
= min(minx
, e
.rect
.left
)
510 miny
= min(miny
, e
.rect
.top
)
511 maxx
= max(maxx
, e
.rect
.right
+1)
512 maxy
= max(maxy
, e
.rect
.bottom
+1)
513 return pygame
.Rect(minx
, miny
, maxx
-minx
, maxy
-miny
)
516 def render_ext(font
, rect
, text
, aa
, color
, bgcolor
=(0,0,0,0), **params
):
517 """Renders some html and returns the rendered surface, plus the
518 HTML instance that produced it.
521 htm
= HTML(text
, font
=font
, color
=color
, **params
)
524 # Make the surface large enough to fit the rendered text
525 htm
.resize(width
=sys
.maxint
)
526 (width
, height
) = htm
.get_bounding_box().size
527 # Now set the proper document width (computed from the bounding box)
528 htm
.resize(width
=width
)
529 elif (type(rect
) == int):
530 # Fix the width of the document, while the height is variable
532 height
= htm
.resize(width
=width
)[1]
534 # Otherwise the width and height of the document is fixed
535 (width
, height
) = rect
.size
536 htm
.resize(width
=width
)
538 # Now construct a surface and paint to it
539 surf
= pygame
.Surface((width
, height
)).convert_alpha()
544 def render(font
, rect
, text
, aa
, color
, bgcolor
=(0,0,0,0), **params
):
547 <pre>render(font,rect,text,aa,color,bgcolor=(0,0,0,0))</pre>
549 return render_ext(font
, rect
, text
, aa
, color
, bgcolor
, **params
)[0]
551 def rendertrim(font
,rect
,text
,aa
,color
,bgcolor
=(0,0,0,0),**params
):
552 """render html, and make sure to trim the size
554 rendertrim(font,rect,text,aa,color,bgcolor=(0,0,0,0))
557 (surf
, htm
) = render_ext(font
, rect
, text
, aa
, color
, bgcolor
, **params
)
558 return surf
.subsurface(htm
.get_bounding_box())
561 def write(s
,font
,rect
,text
,aa
=0,color
=(0,0,0), **params
):
562 """write html to a surface
564 write(s,font,rect,text,aa=0,color=(0,0,0))
566 htm
= HTML(text
, font
=font
, color
=color
, **params
)
567 htm
.resize(width
=rect
.w
)
568 s
= s
.subsurface(rect
)
571 # vim: set filetype=python sts=4 sw=4 noet si :