1 from inspect
import getmro
2 from panda3d
.core
import TextNode
, Texture
3 from direct
.gui
.DirectGuiGlobals
import FLAT
, ENTER
, EXIT
, DISABLED
, NORMAL
, \
5 from direct
.showbase
.DirectObject
import DirectObject
6 from direct
.gui
.DirectButton
import DirectButton
7 from direct
.gui
.DirectCheckButton
import DirectCheckButton
8 from direct
.gui
.DirectOptionMenu
import DirectOptionMenu
9 from direct
.gui
.OnscreenImage
import OnscreenImage
10 from direct
.gui
.DirectSlider
import DirectSlider
11 from direct
.gui
.DirectEntry
import DirectEntry
, ENTRY_FOCUS_STATE
12 from direct
.gui
.DirectLabel
import DirectLabel
13 from direct
.gui
.DirectFrame
import DirectFrame
14 from direct
.gui
.OnscreenText
import OnscreenText
15 from direct
.gui
.DirectScrolledFrame
import DirectScrolledFrame
16 from lib
.observer
import Subject
17 from lib
.lib
.ivals
import Seq
, Wait
, PosIval
, Func
23 from lib
.lib
.gui
import Frame
, Slider
, Btn
, Label
, OptionMenu
, \
24 CheckBtn
, Entry
, Img
, Text
, ScrolledFrame
25 from lib
.lib
.p3d
.widget
import FrameMixin
, SliderMixin
, BtnMixin
, \
26 OptionMenuMixin
, CheckBtnMixin
, EntryMixin
, ImgMixin
, \
28 self
.__class
__ = self
.__class
__ # for pylint
31 ScrolledFrameMixin
: [ScrolledFrame
],
32 SliderMixin
: [Slider
],
33 BtnMixin
: [Btn
, Label
],
34 OptionMenuMixin
: [OptionMenu
],
35 CheckBtnMixin
: [CheckBtn
],
37 ImgMixin
: [Img
, Text
]}
38 for libwdg
, wdgcls
in libwdg2wdg
.items():
39 if any(cls
in getmro(self
.__class
__) for cls
in wdgcls
):
41 clsname
= self
.__class
__.__name
__ + 'Widget'
42 self
.__class
__ = type(clsname
, (self
.__class
__, par_cls
), {})
44 if not hasattr(self
, 'bind'): return
45 bind_args
= [(ENTER
, self
.on_wdg_enter
), (EXIT
, self
.on_wdg_exit
)]
46 list(map(lambda args
: self
.bind(*args
), bind_args
))
48 def set_enter_transition(self
):
49 start_pos
= self
.get_pos()
50 pos
= self
.pos
- (3.6, 0)
51 self
.set_pos((pos
.x
, 1, pos
.y
))
53 Wait(abs(pos
.y
- 1) / 4),
54 PosIval(self
.get_np(), .5, start_pos
)
57 def set_exit_transition(self
, destroy
):
58 start_pos
= self
.get_pos()
59 end_pos
= (self
.pos
.x
+ 3.6, 1, self
.pos
.y
)
61 Wait(abs(self
.pos
.y
- 1) / 4),
62 PosIval(self
.get_np(), .5, end_pos
),
63 Func(self
.destroy
if destroy
else self
.hide
))
64 if not destroy
: seq
+= Func(self
.set_pos
, start_pos
)
68 if hasattr(self
, 'bind_transl'): self
.wdg
['text'] = self
.bind_transl
71 class P3dImg(CommonBase
):
73 def __init__(self
, filepath
, pos
=(0, 0), scale
=1.0, background
=False,
74 foreground
=False, parent
=None):
75 self
.img
= OnscreenImage(
76 filepath
, pos
=(pos
[0], 1, pos
[1]), scale
=scale
, parent
=parent
)
77 if background
: self
.img
.set_bin('background', 10)
78 alpha_formats
= [12] # panda3d.core.texture.Frgba
79 if self
.img
.get_texture().get_format() in alpha_formats
:
80 self
.img
.set_transparency(True)
81 if foreground
: self
.img
.set_bin('gui-popup', 50)
83 def reparent_to(self
, node
): return self
.img
.reparent_to(node
)
84 def show(self
): return self
.img
.show()
85 def hide(self
): return self
.img
.hide()
86 def set_shader(self
, shader
): return self
.img
.set_shader(shader
)
87 def set_shader_input(self
, name
, val
):
88 return self
.img
.set_shader_input(name
, val
)
89 def set_texture(self
, texturestage
, texture
):
90 return self
.img
.set_texture(texturestage
, texture
)
92 def set_exit_transition(self
, destroy
):
93 start_pos
= self
.get_pos()
94 end_pos
= (self
.pos
.x
+ 3.6, 1, self
.pos
.y
)
96 Wait(abs(self
.pos
.y
- 1) / 4),
97 PosIval(self
.get_np(), .5, end_pos
),
98 Func(self
.destroy
if destroy
else self
.hide
))
99 if not destroy
: seq
+= Func(self
.set_pos
, (start_pos
[0], start_pos
[2]))
102 def set_pos(self
, pos
): return self
.img
.set_pos(pos
[0], 1, pos
[1])
104 def get_pos(self
, pos
=None): return self
.img
.get_pos(*[pos
] if pos
else [])
107 def parent(self
): return self
.img
.get_parent()
110 def hidden(self
): return self
.img
.is_hidden()
112 def set_transparent(self
): return self
.img
.set_transparency(True)
114 def destroy(self
): self
.img
= self
.img
.destroy()
117 class P3dBase(CommonBase
):
119 def __init__(self
, tra_src
=None, tra_tra
=None):
120 # self.text_src_tra = None # it breaks the gui
121 if tra_src
and tra_tra
: self
.bind_tra(tra_src
, tra_tra
)
123 def set_pos(self
, pos
): return self
.wdg
.set_pos(pos
)
124 def show(self
): return self
.wdg
.show()
125 def hide(self
): return self
.wdg
.hide()
127 def bind_tra(self
, text_src
, text_transl
):
128 # text_transl is not used, anyway we need it since we have this kind of
129 # use: self.bind_transl('example str', _('example str'))
130 # this allows to change translations on the fly keeping the source
131 # text for remapping it later
132 # TODO: try reverse mapping? i.e. retrieve the src string from the
134 self
.text_src_tra
= text_src
135 self
.text_tra_tra
= text_transl
136 tra
= lambda self
: _(self
.text_tra_tra
)
137 self
.__class
__.bind_transl
= property(tra
)
138 self
['text'] = self
.bind_transl
140 def get_pos(self
, pos
=None):
141 return self
.wdg
.get_pos(*[pos
] if pos
else [])
143 def __setitem__(self
, key
, value
): self
.wdg
[key
] = value
145 def __getitem__(self
, key
): return self
.wdg
[key
]
147 def get_np(self
): return self
.wdg
150 def hidden(self
): return self
.wdg
.is_hidden()
152 def destroy(self
): self
.wdg
.destroy()
155 class P3dAbs(P3dBase
):
157 def get_value(self
): return self
.wdg
.getValue()
158 def initialiseoptions(self
): return self
.wdg
.initialiseoptions()
159 def set_z(self
, val
): return self
.wdg
.set_z(val
)
160 def set_shader(self
, shader
): return self
.wdg
.set_shader(shader
)
161 def set_shader_input(self
, name
, val
):
162 return self
.wdg
.set_shader_input(name
, val
)
163 def set_transparency(self
, val
): return self
.wdg
.set_transparency(val
)
164 def bind(self
, evt
, mth
): return self
.wdg
.bind(evt
, mth
)
166 def attachNewNode(self
, gui_itm
, sort_order
):
167 # it won't work if we name it attach_node. hopefully this will be
168 # possible when we'll use decorators in place of mixins
169 return self
.wdg
.attachNewNode(gui_itm
, sort_order
)
172 def is_enabled(self
): return self
.wdg
['state'] != DISABLED
175 class P3dBtn(P3dAbs
):
178 self
, text
='', parent
=None, pos
=(0, 0), scale
=(1, 1),
179 cmd
=None, frame_size
=(-1, 1, -1, 1), click_snd
=None,
180 text_fg
=(1, 1, 1, 1), frame_col
=(1, 1, 1, 1), text_font
=None,
181 over_snd
=None, extra_args
=None, frame_texture
=None, img
=None,
182 tra_src
=None, tra_tra
=None, text_scale
=1.0):
183 str2par
= {'bottomcenter': base
.a2dBottomCenter
}
184 parent
= str2par
.get(parent
, parent
)
185 extra_args
= extra_args
or []
186 self
.wdg
= DirectButton(
187 text
=text
, parent
=parent
, pos
=(pos
[0], 1, pos
[1]),
188 scale
=(scale
[0], 1, scale
[1]), command
=cmd
,
189 frameSize
=frame_size
, clickSound
=click_snd
, text_fg
=text_fg
,
190 frameColor
=frame_col
, text_font
=text_font
, rolloverSound
=over_snd
,
191 extraArgs
=extra_args
, frameTexture
=frame_texture
, image
=img
,
192 text_scale
=text_scale
)
193 P3dAbs
.__init
__(self
, tra_src
, tra_tra
)
194 self
['relief'] = FLAT
195 args
= [(ENTER
, self
._on
_enter
), (EXIT
, self
._on
_exit
)]
196 list(map(lambda args
: self
.bind(*args
), args
))
198 def _on_enter(self
, pos
): pass # pos comes from mouse
200 def _on_exit(self
, pos
): pass # pos comes from mouse
202 # we add these with the mixins
203 # def enable(self): self['state'] = NORMAL
205 # def disable(self): self['state'] = DISABLED
208 class P3dSlider(P3dAbs
):
211 self
, parent
=None, pos
=(0, 0), scale
=1, val
=0,
212 frame_col
=(1, 1, 1, 1), thumb_frame_col
=(1, 1, 1, 1),
213 cmd
=None, range_
=(0, 1), tra_src
=None, tra_tra
=None):
214 self
.wdg
= DirectSlider(
215 parent
=parent
, pos
=(pos
[0], 1, pos
[1]), scale
=scale
, value
=val
,
216 frameColor
=frame_col
, thumb_frameColor
=thumb_frame_col
,
217 command
=cmd
, range=range_
)
218 P3dAbs
.__init
__(self
, tra_src
, tra_tra
)
221 class P3dCheckBtn(P3dAbs
):
224 self
, pos
=(0, 0), text
='', indicator_val
=False,
225 indicator_frame_col
=(1, 1, 1, 1), frame_col
=(1, 1, 1, 1),
226 scale
=(1, 1, 1), click_snd
=None, over_snd
=None,
227 text_fg
=(1, 1, 1, 1), text_font
=None, cmd
=None, tra_src
=None,
229 self
.wdg
= DirectCheckButton(
230 pos
=(pos
[0], 1, pos
[1]), text
=text
, indicatorValue
=indicator_val
,
231 indicator_frameColor
=indicator_frame_col
,
232 frameColor
=frame_col
, scale
=scale
, clickSound
=click_snd
,
233 rolloverSound
=over_snd
, text_fg
=text_fg
, text_font
=text_font
,
235 P3dAbs
.__init
__(self
, tra_src
, tra_tra
)
238 class P3dOptionMenu(P3dAbs
):
241 self
, text
='', items
=None, pos
=(0, 0), scale
=(1, 1, 1),
242 initialitem
='', cmd
=None, frame_size
=(-1, 1, -1, 1),
243 click_snd
=None, over_snd
=None, text_may_change
=False,
244 text_fg
=(1, 1, 1, 1), item_frame_col
=(1, 1, 1, 1),
245 frame_col
=(1, 1, 1, 1), highlight_col
=(1, 1, 1, 1),
246 text_scale
=.05, popup_marker_col
=(1, 1, 1, 1),
247 item_relief
=None, item_text_font
=None, text_font
=None,
248 tra_src
=None, tra_tra
=None):
250 self
.wdg
= DirectOptionMenu(
251 text
=text
, items
=items
, pos
=(pos
[0], 1, pos
[1]), scale
=scale
,
252 initialitem
=initialitem
, command
=cmd
, frameSize
=frame_size
,
253 clickSound
=click_snd
, rolloverSound
=over_snd
,
254 textMayChange
=text_may_change
, text_fg
=text_fg
,
255 item_frameColor
=item_frame_col
, frameColor
=frame_col
,
256 highlightColor
=highlight_col
, text_scale
=text_scale
,
257 popupMarker_frameColor
=popup_marker_col
,
258 item_relief
=item_relief
, item_text_font
=item_text_font
,
260 P3dAbs
.__init
__(self
, tra_src
, tra_tra
)
262 def set(self
, idx
, f_cmd
=1): return self
.wdg
.set(idx
, f_cmd
)
265 def curr_val(self
): return self
.wdg
.get()
268 def curr_idx(self
): return self
.wdg
.selectedIndex
271 class P3dEntry(P3dAbs
, DirectObject
, Subject
):
274 self
, scale
=.05, pos
=(0, 0), entry_font
=None, width
=12,
275 frame_col
=(1, 1, 1, 1), initial_text
='', obscured
=False,
276 cmd
=None, focus_in_cmd
=None, focus_in_args
=None,
277 focus_out_cmd
=None, focus_out_args
=None, parent
=None,
278 tra_src
=None, tra_tra
=None, text_fg
=(1, 1, 1, 1), on_tab
=None,
280 self
.__focused
= False
281 self
.__focus
_in
_cmd
= focus_in_cmd
282 self
.__focus
_out
_cmd
= focus_out_cmd
283 DirectObject
.__init
__(self
)
284 Subject
.__init
__(self
)
285 focus_in_args
= focus_in_args
or []
286 focus_out_args
= focus_out_args
or []
287 self
.wdg
= DirectEntry(
288 scale
=scale
, pos
=(pos
[0], 1, pos
[1]), entryFont
=entry_font
,
289 width
=width
, frameColor
=frame_col
, initialText
=initial_text
,
290 obscured
=obscured
, command
=cmd
, focusInCommand
=self
._focus
_in
_cmd
,
291 focusInExtraArgs
=focus_in_args
,
292 focusOutCommand
=self
._focus
_out
_cmd
,
293 focusOutExtraArgs
=focus_out_args
, parent
=parent
,
295 P3dAbs
.__init
__(self
, tra_src
, tra_tra
)
297 self
.on_tab_cb
= on_tab
298 self
.accept('tab-up', self
.on_tab
)
299 if on_click
: self
.wdg
.bind(B1PRESS
, on_click
)
301 def set(self
, txt
): return self
.wdg
.set(txt
)
303 def _focus_in_cmd(self
, *args
):
304 self
.__focused
= True
305 if self
.__focus
_in
_cmd
: self
.__focus
_in
_cmd
(*args
)
306 self
.notify('on_entry_enter')
308 def _focus_out_cmd(self
, *args
):
309 self
.__focused
= False
310 if self
.__focus
_out
_cmd
: self
.__focus
_out
_cmd
(*args
)
311 self
.notify('on_entry_exit')
314 if self
.wdg
['focus'] == ENTRY_FOCUS_STATE
: self
.on_tab_cb()
317 def focused(self
): return self
.__focused
320 def text(self
): return self
.wdg
.get()
322 def enter_text(self
, txt
):
323 return self
.wdg
.enterText(txt
)
325 def enable(self
): self
['state'] = NORMAL
327 def disable(self
): self
['state'] = DISABLED
330 self
.ignore('tab-up')
331 self
.on_tab_cb
= None
332 Subject
.destroy(self
)
336 class P3dLabel(P3dAbs
):
339 self
, text
='', pos
=(0, 0), parent
=None, text_wordwrap
=12,
340 text_align
=None, text_fg
=(1, 1, 1, 1), text_font
=None, scale
=.05,
341 frame_col
=(1, 1, 1, 1), tra_src
=None, tra_tra
=None, hpr
=(0, 0, 0)):
342 self
.wdg
= DirectLabel(
343 text
=text
, pos
=(pos
[0], 1, pos
[1]), parent
=parent
,
344 text_wordwrap
=text_wordwrap
, text_align
=text_align
,
345 text_fg
=text_fg
, text_font
=text_font
, scale
=scale
,
346 frameColor
=frame_col
, hpr
=hpr
)
347 P3dAbs
.__init
__(self
, tra_src
, tra_tra
)
349 def set_bin(self
, bin_name
, priority
): return self
.wdg
.set_bin(bin_name
, priority
)
351 def set_x(self
, x
): return self
.wdg
.set_x(x
)
353 def set_alpha_scale(self
, alpha
): return self
.wdg
.set_alpha_scale(alpha
)
356 class P3dTxt(P3dBase
):
359 self
, txt
='', pos
=(0, 0), scale
=.05, wordwrap
=12, parent
=None,
360 fg
=(1, 1, 1, 1), font
=None, align
=None, tra_src
=None,
362 str2par
= {'bottomleft': base
.a2dBottomLeft
,
363 'bottomright': base
.a2dBottomRight
,
364 'leftcenter': base
.a2dLeftCenter
}
365 str2al
= {'left': TextNode
.A_left
, 'right': TextNode
.A_right
,
366 'center': TextNode
.A_center
}
367 if parent
and parent
in str2par
: parent
= str2par
[parent
]
368 if align
: align
= str2al
[align
]
369 self
.wdg
= OnscreenText(
370 text
=txt
, pos
=pos
, scale
=scale
, wordwrap
=wordwrap
,
371 parent
=parent
, fg
=fg
, font
=font
, align
=align
)
372 P3dBase
.__init
__(self
, tra_src
, tra_tra
)
374 def set_r(self
, r
): return self
.wdg
.set_r(r
)
377 class P3dFrame(P3dAbs
):
379 def __init__(self
, frame_size
=(-1, 1, -1, 1), frame_col
=(1, 1, 1, 1),
380 pos
=(0, 0), parent
=None, texture_coord
=False):
381 P3dAbs
.__init
__(self
)
382 self
.wdg
= DirectFrame(frameSize
=frame_size
, frameColor
=frame_col
,
383 pos
=(pos
[0], 1, pos
[1]), parent
=parent
)
384 if texture_coord
: self
.wdg
['frameTexture'] = Texture()
387 class P3dScrolledFrame(P3dAbs
):
390 self
, frame_sz
=(-1, 1, -1, 1), canvas_sz
=(0, 1, 0, 1),
391 scrollbar_width
=.05, frame_col
=(1, 1, 1, 1),
392 pos
=(0, 0), parent
='topleft'):
393 P3dAbs
.__init
__(self
)
394 par2p3d
= {'topleft': base
.a2dTopLeft
}
395 if parent
and parent
in par2p3d
: parent
= par2p3d
[parent
]
396 self
.wdg
= DirectScrolledFrame(
398 canvasSize
=canvas_sz
,
399 scrollBarWidth
=scrollbar_width
,
400 frameColor
=frame_col
,
401 pos
=(pos
[0], 1, pos
[1]),
405 def canvas(self
): return self
.wdg
.getCanvas()