1
2 """
3 Fancy directgui wrapper.
4 It contains helper classes to speed up the making of menus and other visual stuff like that.
5
6 @author: fabius
7 @copyright: fabius@2010
8 @license: GCTA give credit to (the) authors
9 @version: 0.1
10 @date: 2010-09
11 @contact: astelix (U{panda3d forums<http://www.panda3d.org/forums/profile.php?mode=viewprofile&u=752>})
12 @status: eternal WIP
13
14 @note: see the __main__ section for a sample usage and read comments to understand how it works.
15
16 Post your dubts and inquiries in U{this forum thread<http://www.panda3d.org/phpbb2/viewtopic.php?p=56992>}
17
18 """
19 import random
20 from pandac.PandaModules import *
21 from direct.gui.DirectGui import *
22 from direct.showbase.DirectObject import DirectObject
23
24
25
27 _DEBUG=False
29 self.defaultdata={}
30
31 self._setdefaultdata()
32
33
34 self.data=data
35 self.defaultdata.update(self.data)
36 self.data.update(self.defaultdata)
37 self._postdatamerge()
38 self._buildcanvas()
39 self.play()
40
41
42 - def play(self): pass
45 - def _postdatamerge(self): pass
47
48
51
52
53 - def settexture(self, model, texture=None, wrapmode='clamp', scale=None):
54 """ Apply a texture over the menu panel model and set transparency automatically
55 """
56 wraps={'repeat': Texture.WMRepeat, 'clamp': Texture.WMClamp,}
57 if texture:
58 tex = loader.loadTexture(texture)
59 model.clearTexture()
60 tex.setWrapU(wraps[wrapmode])
61 tex.setWrapV(wraps[wrapmode])
62 tex.setMinfilter(Texture.FTLinearMipmapNearest)
63 ts = TextureStage('ts')
64 model.setTexture(ts, tex, 1)
65 if scale: model.setTexScale(ts, scale[0], scale[1])
66
67 if texture.endswith('.png'):
68 model.setTransparency(TransparencyAttrib.MAlpha)
69
70
71
192
193
194
196
197
199 """ Add items as buttons to the scrolling panel.
200 @note: color, textscale and font could be either assigned overall or per item
201 """
202 for idx in range(len(self.data['items'])):
203 default={
204 'color': self.data['itemscolor'],
205 'textscale': self.data['itemsscale'],
206 'textfont': self.data['textfont'],
207 'width': w-(self.data['margin'][0]*2.),
208 }
209 self.data['items'][idx].update(default)
210 self.addItem(idx, **self.data['items'][idx])
211
212
213
215 """ An item is a clickable label that fires a callback.
216
217 There are 2 possible scenarios:
218 - 1 common callback for all (onClick) overridable (defined in the main data, see __init__)
219 - 1 custom callback carried on the item via 'callback' data element
220 for each of them will be passed the item index in the list
221 @note: textfont is mandatorily passed by the caller
222 """
223 itemdata={
224 'label':'label_%d'%index, 'width':.4, 'textscale':.05,
225 'color':(0,0,0,1), 'callback':self.defaultcallback,
226 }
227
228 itemdata.update(keys)
229
230 w,h=(itemdata['width'], itemdata['textscale'])
231 butt = DirectButton(
232 text = tuple([itemdata['label']]*4), text_fg=itemdata['color'],
233 text_scale=itemdata['textscale'], text_align=TextNode.ALeft,
234 text_font=itemdata['textfont'],
235 relief=2, borderWidth = (0.0, 0.0),
236 frameSize = (0.0, w, (-h/2.)+.02, (h/2.)+.01), frameColor=(0,0,0,0),
237 command=itemdata['callback'], extraArgs=[index],
238 )
239
240 butt.bind(DGG.ENTER, self.butenex, [DGG.ENTER, index])
241 butt.bind(DGG.EXIT, self.butenex, [DGG.EXIT, index])
242 self.canvas.addItem(butt, True)
243
244
245
247 """ Helper class to ease making of a scrolling table og columnar data.
248 """
249
250
252 dgscroll._setdefaultdata(self)
253
254 self.defaultdata.update({'head':[], 'exit': None})
255
256
257 - def _postdatamerge(self):
258
259 if not len(self.data['items']): return
260 dgscroll._postdatamerge(self)
261
262 self.data['itemswidth']=self.data.get(
263 'itemswidth',
264 [
265 self.data['scale'][1] / len(self.data['items'])
266 ]*len(self.data['items'])
267 )
268
269
271
272 for icol in range(len(self.data['head'])):
273 idata={
274 'color': self.data['itemscolor'],
275 'textfont': self.data['textfont'],
276 'textscale': self.data['itemsscale'],
277 }
278 self.data['head'][icol]['width']=self.data['itemswidth'][icol]
279 idata.update(self.data['head'][icol])
280 self.data['head'][icol]=idata
281 self.addHead(self.data['head'], -(w/2)+self.data['margin'][0],
282 (h/2)-self.data['margin'][1]-self.data['titlescale']-.02
283 )
284 """ add items as buttons
285 NOTE color, textscale and font could be either assigned overall or per item
286 """
287 for idx in range(len(self.data['items'])):
288 for icol in range(len(self.data['items'][idx])):
289 idata={
290 'color': self.data['itemscolor'],
291 'textfont': self.data['textfont'],
292 'textscale': self.data['itemsscale'],
293 }
294 idata.update(self.data['items'][idx][icol])
295 self.data['items'][idx][icol]=idata
296 self.data['items'][idx][icol]['width']=self.data['itemswidth'][icol]
297
298 self.addRow(idx, self.data['items'][idx])
299
300
301 - def addRow(self, index, rowdata):
302 """
303 """
304 w0=0.0
305 h0=0.0
306 l=[]
307 for col in rowdata:
308 itemdata={
309 'label':'label_%d'%index, 'width':.4, 'textscale':.05,
310 'color':(0,0,0,1),
311 }
312
313 itemdata.update(col)
314
315 w,h=(itemdata['width'], itemdata['textscale'])
316 butt = DirectButton(
317 text = itemdata['label'], text_fg=itemdata['color'],
318 text_scale=itemdata['textscale'], text_align=TextNode.ALeft,
319 text_font=itemdata['textfont'],
320 text_pos=(w0,0.01),
321 relief=2, borderWidth = (0.0, 0.0),
322 frameSize = (w0, w0+w, (-h/2.)+.03, (h/2.)+.03), frameColor=(1,0,0,0),
323 )
324 butt.bind(DGG.ENTER, self.butenex, [DGG.ENTER, index])
325 butt.bind(DGG.EXIT, self.butenex, [DGG.EXIT, index])
326 l.append(butt)
327 w0+=w
328 h0=max(h0,h)
329 row=DirectFrame(
330 frameColor=(0,0,0,0) , frameSize=(0,w0,0,h),
331 )
332
333 row.commandFunc=lambda foo=None: self.defaultcallback(index)
334 for c in l: c.reparentTo(row)
335 self.canvas.addItem(row, True)
336
337
338 - def addHead(self, rowdata, w0, h0):
339 """
340 """
341 l=[]
342 i=0
343 for col in rowdata:
344 itemdata={
345 'label':'COL_%d'%i, 'width':.4, 'textscale':.05, 'color':(0,0,0,1),
346 }
347 i+=1
348
349 itemdata.update(col)
350
351 w,h=(itemdata['width'], itemdata['textscale'])
352 butt = DirectLabel(
353 text = itemdata['label'], text_fg=itemdata['color'],
354 text_scale=itemdata['textscale'], text_align=TextNode.ALeft,
355 text_font=itemdata['textfont'],
356 text_pos=(w0,0.01),
357 relief=2, borderWidth = (0.0, 0.0),
358 frameSize = (w0, w0+w, (-h/2.)+.03, (h/2.)+.03), frameColor=(1,0,0,0),
359 )
360 l.append(butt)
361 w0+=w
362
363 row=DirectFrame(
364 frameColor=(0,0,0,0) , frameSize=(0,w0,0,h),
365 )
366 for c in l: c.reparentTo(row)
367 row.setZ(h0)
368 row.reparentTo(self.canvas)
369
370
470
471
472
473 if __name__ == "__main__":
474 """ dgscrollers sample usage """
475 import direct.directbase.DirectStart
476 import sys
477
478
479 title="Fancy DirectGUI"
480 content="Use of gui stuff to ease to make menus, tables\nand inputs using a compact data interface"
481 infotext={}
482 infotext['title']=OnscreenText(
483 text = title, pos = (0, .92), scale = 0.08, mayChange=True, fg=(1,1,1,1),
484 bg=(0,0,1,.7)
485 )
486 infotext['content']=OnscreenText(
487 text = content, pos = (0, 0.84), scale = 0.05, mayChange=True, fg=(1,1,0,1),
488 bg=(0,0,0,.5)
489 )
490 infotext['message']=OnscreenText(
491 text = '', pos = (0, -0.85), scale = 0.07, mayChange=True, fg=(1,1,1,1),
492 bg=(1,0,0,.65)
493 )
494
495 env=loader.loadModel("environment")
496 env.reparentTo(render)
497
498
500 global menu_options_video
501 def _setresolution(res, fullscreen=False):
502 wp = WindowProperties()
503 wp.setSize(int(res[0]), int(res[1]))
504 wp.setFullscreen(fullscreen)
505 base.win.requestProperties(wp)
506 if index <> None:
507 video=menu_options_video['items'][index]['label']
508 infotext['message'].setText("Video changed to %r"%video)
509 _setresolution(video.split('x'))
510
511 menulist=[]
512
514 global menulist
515 if len(menulist): menulist[-1].pause()
516 menulist.append(dgmenu(menudata))
517
518
520 global menulist
521 if len(menulist): menulist[-1].pause()
522 menulist.append(dgtable(menudata))
523
524
525 - def push_entry(menudata):
526 global menulist
527 if len(menulist): menulist[-1].pause()
528 menulist.append(dginput(menudata))
529
530
532 global menulist
533 menu=menulist.pop()
534 menu.finish()
535 if len(menulist): menulist[-1].play()
536
537 - def exit(foo=None):
538 self.dbgprint("quitting out...")
539 sys.exit(0)
540
541 menu_options_video={'title': 'Video Options',
542 'scale':(.4, .6), 'itemsvisible':10, 'titlescale':.05,
543 'texture':'data/models/textures/iphone.png', 'margin':(.12, .34),
544 'callback': setvideo,
545 'items': [
546 {'label': '800x600'},
547 {'label': '1024x768'},
548 {'label': 'back', 'callback': pop_menu, },
549 ],
550 'exit': pop_menu,
551 }
552
553 menu_options={'title': 'Options',
554 'scale':(.4, .6), 'itemsvisible':10, 'titlescale':.05,
555 'texture':'data/models/textures/iphone.png', 'margin':(.12, .34),
556 'callback': None,
557 'items': [
558 {'label': 'video',
559 'callback': lambda foo=None: push_menu(menu_options_video),
560 },
561 {'label': 'opponent', 'callback':None, 'choice': 1},
562 {'label': 'back', 'callback': pop_menu, },
563 ],
564 'exit': pop_menu,
565 }
566 menu_quitgame={'title': 'Sure to quit "game"?',
567 'scale':(.46, .34), 'itemsvisible':6, 'margin':(.25, .2),
568 'texture':'data/models/textures/round256x128.png', 'selected': 1,
569 'items': [
570 {'label': 'Yes', 'width': .1, 'callback': exit},
571 {'label': 'No', 'width': .1, 'callback': pop_menu, },
572 ],
573 'itemselected': 1, 'exit': pop_menu,
574 }
575
577 table0={'title': 'TOP 10', 'titlescale': .08,
578 'scale':(.62, .44), 'itemsvisible':6, 'pos':(-.5, .1), 'margin': (.25,.23),
579 'texture':'data/models/textures/menubg01.png',
580 'itemswidth': (.45,.15,.15),
581 'items': [
582 [ {'label': 'Row0Col0',}, {'label': 'R0C1',}, {'label': 'R0C2',}, ],
583 [ {'label': 'Row1Col0',}, {'label': 'R1C1',}, {'label': 'R1C2',}, ],
584 [ {'label': 'Row2Col0',}, {'label': 'R2C1',}, {'label': 'R2C2',}, ],
585 [ {'label': 'Row3Col0',}, {'label': 'R3C1',}, {'label': 'R3C2',}, ],
586 [ {'label': 'Row4Col0',}, {'label': 'R4C1',}, {'label': 'R4C2',}, ],
587 [ {'label': 'Row5Col0',}, {'label': 'R5C1',}, {'label': 'R5C2',}, ],
588 [ {'label': 'Row6Col0',}, {'label': 'R6C1',}, {'label': 'R6C2',}, ],
589 [ {'label': 'Row7Col0',}, {'label': 'R7C1',}, {'label': 'R7C2',}, ],
590 [ {'label': 'Row8Col0',}, {'label': 'R8C1',}, {'label': 'R8C2',}, ],
591 [ {'label': 'Row9Col0',}, {'label': 'R9C1',}, {'label': 'R9C2',}, ],
592 ],
593 'head': [
594 { 'label': 'COL0'},
595 { 'label': 'COL1'},
596 { 'label': 'COL2'},
597 ],
598 'callback': prot, 'exit': pop_menu
599 }
600
601 - def printv(v): infotext['message'].setText("Player Name: '%s'"%v)
602 entry0={
603 'xpos': (-.5,.0), 'align': 'center',
604 'title': 'Player Name', 'scale': (1.2,.6), 'margin':(.07, .05),
605 'titlecolor': (0,0,1,1), 'texture':'data/models/textures/round256x128.png',
606 'initialtext': "Type Something", 'inputscale': .07, 'inputwidth': 25,
607 'inputcolor':(0,1,0,1),
608 'callback': lambda v: printv(v), 'exit': pop_menu, 'autoexit': True,
609 }
610
611 menu_main={'title': 'MAIN MENU', 'titlescale': .08,
612 'scale':(.62, .6), 'itemsvisible':14, 'pos':(-.8, .2), 'margin': (.25,.23),
613 'texture':'data/models/textures/menubg01.png', 'itemsscale': .07,
614 'items': [
615 {'label': 'Options',
616 'callback': lambda foo=None: push_menu(menu_options),
617 },
618 {'label': 'TOP10',
619 'callback': lambda foo=None: push_table(table0),
620 },
621 {'label': 'New Game',
622 'callback': lambda foo=None: push_entry(entry0),
623 },
624 {'label': 'Exit', 'callback': lambda foo=None: push_menu(menu_quitgame),
625 },
626 ],
627 'exit': lambda foo=None: push_menu(menu_quitgame),
628 }
629
630 push_menu(menu_main)
631
632 run()
633