comparison engine/extensions/pychan/widgets.py @ 157:bb9902910067

input_rework merged! Bad features: * Broken DND for zero-projekt. * Design short-comings.
author phoku@33b003aa-7bff-0310-803a-e67f0ece8222
date Tue, 14 Oct 2008 07:41:48 +0000
parents d29593182f40
children 63de2dea08e6
comparison
equal deleted inserted replaced
156:376b8afc9a18 157:bb9902910067
7 Please look at the documentation of L{Widget} for details. 7 Please look at the documentation of L{Widget} for details.
8 """ 8 """
9 9
10 import fife, pythonize 10 import fife, pythonize
11 import tools 11 import tools
12 import events
12 from exceptions import * 13 from exceptions import *
13 from attrs import Attr,PointAttr,ColorAttr,BoolAttr,IntAttr,FloatAttr 14 from attrs import Attr,PointAttr,ColorAttr,BoolAttr,IntAttr,FloatAttr
14 15
15 def get_manager(): 16 def get_manager():
16 import pychan 17 import pychan
93 def __init__(self,parent = None, name = DEFAULT_NAME, 94 def __init__(self,parent = None, name = DEFAULT_NAME,
94 size = (-1,-1), min_size=(0,0), max_size=(5000,5000), 95 size = (-1,-1), min_size=(0,0), max_size=(5000,5000),
95 style = None, **kwargs): 96 style = None, **kwargs):
96 97
97 assert( hasattr(self,'real_widget') ) 98 assert( hasattr(self,'real_widget') )
98 self._has_listener = False 99 self.event_mapper = events.EventMapper(self)
99 self._visible = False 100 self._visible = False
100 101
101 # Data distribution & retrieval settings 102 # Data distribution & retrieval settings
102 self.accepts_data = False 103 self.accepts_data = False
103 self.accepts_initial_data = False 104 self.accepts_initial_data = False
104 105
105 self._parent = parent 106 self.parent = parent
106 107
107 # This will also set the _event_id and call real_widget.setActionEventId 108 # This will also set the _event_id and call real_widget.setActionEventId
108 self.name = name 109 self.name = name
109 110
110 self.min_size = min_size 111 self.min_size = min_size
160 for k,v in kwargs.items(): 161 for k,v in kwargs.items():
161 if v != getattr(self,k,None): 162 if v != getattr(self,k,None):
162 return False 163 return False
163 return True 164 return True
164 165
165 def capture(self, callback): 166 def capture(self, callback, event_name="action"):
166 """ 167 """
167 Add a callback to be executed when the widget event occurs on this widget. 168 Add a callback to be executed when the widget event occurs on this widget.
168 169
169 The callback must be either a callable or None. 170 The callback must be either a callable or None.
170 The old event handler (if any) will be overridden by the callback. 171 The old event handler (if any) will be overridden by the callback.
171 If None is given, the event will be disabled. You can query L{isCaptured} 172 If None is given, the event will be disabled. You can query L{isCaptured}
172 wether this widgets events are currently captured. 173 wether this widgets events are currently captured.
173 174
174 It might be useful to check out L{tools.callbackWithArguments}. 175 It might be useful to check out L{tools.callbackWithArguments}.
175 176
176 """ 177 @param callback: Event callback - may accept keyword arguments event and widget.
177 if callback is None: 178 @paran event_name: The event to capture - may be one of L{events.EVENTS} and defaults to "action"
178 if not get_manager().widgetEvents.has_key(self._event_id): 179 """
179 if get_manager().debug: 180 self.event_mapper.capture( event_name, callback )
180 print "You passed None as parameter to %s.capture, which would normally remove a mapped event." % str(self)
181 print "But there was no event mapped. Did you accidently call a function instead of passing it?"
182 else:
183 del get_manager().widgetEvents[self._event_id]
184 if self._has_listener:
185 self.real_widget.removeActionListener(get_manager().guimanager)
186 self._has_listener = None
187 return
188
189 if not callable(callback):
190 raise RuntimeError("An event callback must be either a callable or None - not %s" % repr(callback))
191
192 def captured_f(event):
193 tools.applyOnlySuitable(callback,event=event,widget=self)
194
195 get_manager().widgetEvents[self._event_id] = captured_f
196 if not self._has_listener:
197 self.real_widget.addActionListener(get_manager().guimanager)
198 self._has_listener = True
199 181
200 def isCaptured(self): 182 def isCaptured(self):
201 """ 183 """
202 Check whether this widgets events are captured 184 Check whether this widgets events are captured
203 (a callback is installed) or not. 185 (a callback is installed) or not.
204 """ 186 """
205 return self._has_listener 187 return bool(self.event_mapper.getCapturedEvents())
206 188
207 def show(self): 189 def show(self):
208 """ 190 """
209 Show the widget and all contained widgets. 191 Show the widget and all contained widgets.
210 """ 192 """
221 Hide the widget and all contained widgets. 203 Hide the widget and all contained widgets.
222 """ 204 """
223 if self._parent: 205 if self._parent:
224 raise RuntimeError(Widget.HIDE_SHOW_ERROR) 206 raise RuntimeError(Widget.HIDE_SHOW_ERROR)
225 if not self._visible: return 207 if not self._visible: return
208
226 get_manager().hide(self) 209 get_manager().hide(self)
210
227 self.afterHide() 211 self.afterHide()
228 self._visible = False 212 self._visible = False
229 213
230 def isVisible(self): 214 def isVisible(self):
231 """ 215 """
316 Subsequent calls of mapEvents will merge events with different 300 Subsequent calls of mapEvents will merge events with different
317 widget names and override the previously set callback. 301 widget names and override the previously set callback.
318 You can also pass C{None} instead of a callback, which will 302 You can also pass C{None} instead of a callback, which will
319 disable the event completely. 303 disable the event completely.
320 304
321 @param eventMap: A dictionary with widget names as keys and callbacks as values. 305 @param eventMap: A dictionary with widget/event names as keys and callbacks as values.
322 @param ignoreMissing: Normally this method raises an RuntimeError, when a widget 306 @param ignoreMissing: Normally this method raises an RuntimeError, when a widget
323 can not be found - this behaviour can be overriden by passing True here. 307 can not be found - this behaviour can be overriden by passing True here.
324 """ 308
325 for name,func in eventMap.items(): 309 The keys in the dictionary are parsed as "widgetName/eventName" with the slash
310 separating the two. If no slash is found the eventName is assumed to be "action".
311
312 Example::
313 guiElement.mapEvents({
314 "button" : guiElement.hide,
315 "button/mouseEntered" : toggleButtonColorGreen,
316 "button/mouseExited" : toggleButtonColorBlue,
317 })
318
319 """
320 for descr,func in eventMap.items():
321 name, event_name = events.splitEventDescriptor(descr)
322 print name, event_name
326 widget = self.findChild(name=name) 323 widget = self.findChild(name=name)
327 if widget: 324 if widget:
328 widget.capture( func ) 325 widget.capture( func, event_name = event_name )
329 elif not ignoreMissing: 326 elif not ignoreMissing:
330 raise RuntimeError("No widget with the name: %s" % name) 327 raise RuntimeError("No widget with the name: %s" % name)
331 328
332 def setInitialData(self,data): 329 def setInitialData(self,data):
333 """ 330 """
592 if isinstance(color,type(())): 589 if isinstance(color,type(())):
593 color = fife.Color(*color) 590 color = fife.Color(*color)
594 self.real_widget.setSelectionColor(color) 591 self.real_widget.setSelectionColor(color)
595 selection_color = property(_getSelectionColor,_setSelectionColor) 592 selection_color = property(_getSelectionColor,_setSelectionColor)
596 593
597 def _getName(self): return self._name
598 def _setName(self,name):
599 from pychan import manager
600 self._name = name
601 # Do not change the event id while we are captured.
602 if not self.isCaptured():
603 self._event_id = "%s(name=%s,id=%d)" % (str(self.__class__),name,id(self))
604 else:
605 # Print some notfication, so obscure behaviour might get debugged.
606 print "%s already captured, but changing the name attribute. Just a notification :-)" % str(self)
607 self.real_widget.setActionEventId(self._event_id)
608 name = property(_getName,_setName)
609
610 def _getStyle(self): return self._style 594 def _getStyle(self): return self._style
611 def _setStyle(self,style): 595 def _setStyle(self,style):
612 self._style = style 596 self._style = style
613 get_manager().stylize(self,style) 597 get_manager().stylize(self,style)
614 style = property(_getStyle,_setStyle) 598 style = property(_getStyle,_setStyle)
599
600 def _getParent(self): return self._parent
601 def _setParent(self,parent):
602 self._parent = parent
603 parent = property(_getParent,_setParent)
604
605 def _setName(self,name): self._name = name
606 def _getName(self): return self._name
607 name = property(_getName,_setName)
615 608
616 x = property(_getX,_setX) 609 x = property(_getX,_setX)
617 y = property(_getY,_setY) 610 y = property(_getY,_setY)
618 width = property(_getWidth,_setWidth) 611 width = property(_getWidth,_setWidth)
619 height = property(_getHeight,_setHeight) 612 height = property(_getHeight,_setHeight)
620 size = property(_getSize,_setSize) 613 size = property(_getSize,_setSize)
621 position = property(_getPosition,_setPosition) 614 position = property(_getPosition,_setPosition)
622 font = property(_getFont,_setFont) 615 font = property(_getFont,_setFont)
623 border_size = property(_getBorderSize,_setBorderSize) 616 border_size = property(_getBorderSize,_setBorderSize)
624 617
618 def setEnterCallback(self, cb):
619 """
620 *DEPRECATED*
621
622 Callback is called when mouse enters the area of Widget
623 callback should have form of function(button)
624 """
625 def callback(widget=None):
626 return cb(widget)
627 print "PyChan: You are using the DEPRECATED functionality: setEnterCallback."
628 self.capture(callback, event_name = "mouseEntered" )
629
630 def setExitCallback(self, cb):
631 """
632 *DEPRECATED*
633
634 Callback is called when mouse exits the area of Widget
635 callback should have form of function(button)
636 """
637 def callback(widget=None):
638 return cb(widget)
639 print "PyChan: You are using the DEPRECATED functionality: setExitCallback."
640 self.capture(callback, event_name = "mouseExited" )
641
642
643
625 ### Containers + Layout code ### 644 ### Containers + Layout code ###
626 645
627 class Container(Widget): 646 class Container(Widget):
628 """ 647 """
629 This is the basic container class. It provides space in which child widgets can 648 This is the basic container class. It provides space in which child widgets can
652 self._background = [] 671 self._background = []
653 self._background_image = None 672 self._background_image = None
654 super(Container,self).__init__(**kwargs) 673 super(Container,self).__init__(**kwargs)
655 674
656 def addChild(self, widget): 675 def addChild(self, widget):
657 widget._parent = self 676 widget.parent = self
658 self.children.append(widget) 677 self.children.append(widget)
659 self.real_widget.add(widget.real_widget) 678 self.real_widget.add(widget.real_widget)
660 679
661 def removeChild(self,widget): 680 def removeChild(self,widget):
662 if not widget in self.children: 681 if not widget in self.children:
663 raise RuntimeError("%s does not have %s as direct child widget." % (str(self),str(widget))) 682 raise RuntimeError("%s does not have %s as direct child widget." % (str(self),str(widget)))
664 self.children.remove(widget) 683 self.children.remove(widget)
665 self.real_widget.remove(widget.real_widget) 684 self.real_widget.remove(widget.real_widget)
666 widget._parent = None 685 widget.parent = None
667 686
668 def add(self,*widgets): 687 def add(self,*widgets):
669 print "PyChan: Deprecation warning: Please use 'addChild' or 'addChildren' instead." 688 print "PyChan: Deprecation warning: Please use 'addChild' or 'addChildren' instead."
670 self.addChildren(*widgets) 689 self.addChildren(*widgets)
671 690
1026 if self._source is not None: 1045 if self._source is not None:
1027 return self._source 1046 return self._source
1028 return self._image 1047 return self._image
1029 image = property(_getImage,_setImage) 1048 image = property(_getImage,_setImage)
1030 1049
1031 class LabelListener(fife.ClickLabelListener):
1032 """ the listener class for label onMouse events
1033
1034 @type btn: object
1035 @param btn: the label widget
1036 """
1037 def __init__(self, lbl):
1038 fife.ClickLabelListener.__init__(self)
1039 self.lbl = lbl
1040 self.entercb = None
1041 self.exitcb = None
1042
1043 def mouseEntered(self, lbl):
1044 if self.entercb:
1045 self.entercb(self.lbl)
1046
1047 def mouseExited(self, lbl):
1048 if self.exitcb:
1049 self.exitcb(self.lbl)
1050
1051 class Label(BasicTextWidget): 1050 class Label(BasicTextWidget):
1052 """ 1051 """
1053 A basic label - displaying a string. 1052 A basic label - displaying a string.
1054 1053
1055 Also allows text wrapping and onMouse hover callbacks. 1054 Also allows text wrapping and onMouse hover callbacks.
1068 1067
1069 def __init__(self,wrap_text=False,**kwargs): 1068 def __init__(self,wrap_text=False,**kwargs):
1070 self.real_widget = fife.Label("") 1069 self.real_widget = fife.Label("")
1071 self.wrap_text = wrap_text 1070 self.wrap_text = wrap_text
1072 super(Label,self).__init__(**kwargs) 1071 super(Label,self).__init__(**kwargs)
1073 self.listener = LabelListener(self)
1074 self.real_widget.setListener(self.listener)
1075 1072
1076 def resizeToContent(self): 1073 def resizeToContent(self):
1077 self.real_widget.setWidth( self.max_size[0] ) 1074 self.real_widget.setWidth( self.max_size[0] )
1078 self.real_widget.adjustSize() 1075 self.real_widget.adjustSize()
1079 self.height = self.real_widget.getHeight() + self.margins[1]*2 1076 self.height = self.real_widget.getHeight() + self.margins[1]*2
1082 1079
1083 def _setTextWrapping(self,wrapping): self.real_widget.setTextWrapping(wrapping) 1080 def _setTextWrapping(self,wrapping): self.real_widget.setTextWrapping(wrapping)
1084 def _getTextWrapping(self): self.real_widget.isTextWrapping() 1081 def _getTextWrapping(self): self.real_widget.isTextWrapping()
1085 wrap_text = property(_getTextWrapping,_setTextWrapping) 1082 wrap_text = property(_getTextWrapping,_setTextWrapping)
1086 1083
1087 def setEnterCallback(self, cb):
1088 """
1089 Callback is called when mouse enters the area of Label
1090 callback should have form of function(button)
1091 """
1092 self.listener.entercb = cb
1093
1094 def setExitCallback(self, cb):
1095 """
1096 Callback is called when mouse enters the area of Label
1097 callback should have form of function(button)
1098 """
1099 self.listener.exitcb = cb
1100
1101 class ClickLabel(Label): 1084 class ClickLabel(Label):
1102 """ 1085 """
1103 Deprecated - use L{Label} instead. 1086 Deprecated - use L{Label} instead.
1104 """ 1087 """
1105 __init__ = tools.this_is_deprecated(Label.__init__,message = "ClickLabel - Use Label instead") 1088 __init__ = tools.this_is_deprecated(Label.__init__,message = "ClickLabel - Use Label instead")
1111 """ 1094 """
1112 def __init__(self,**kwargs): 1095 def __init__(self,**kwargs):
1113 self.real_widget = fife.Button("") 1096 self.real_widget = fife.Button("")
1114 super(Button,self).__init__(**kwargs) 1097 super(Button,self).__init__(**kwargs)
1115 1098
1116 class ImageButtonListener(fife.TwoButtonListener):
1117 def __init__(self, btn):
1118 fife.TwoButtonListener.__init__(self)
1119 self.btn = btn
1120 self.entercb = None
1121 self.exitcb = None
1122
1123 def mouseEntered(self, btn):
1124 if self.entercb:
1125 self.entercb(self.btn)
1126
1127 def mouseExited(self, btn):
1128 if self.exitcb:
1129 self.exitcb(self.btn)
1130
1131 class ImageButton(BasicTextWidget): 1099 class ImageButton(BasicTextWidget):
1132 """ 1100 """
1133 A basic push button with three different images for the up, down and hover state. 1101 A basic push button with three different images for the up, down and hover state.
1134 1102
1135 B{Work in progress.} 1103 B{Work in progress.}
1145 ATTRIBUTES = BasicTextWidget.ATTRIBUTES + [Attr('up_image'),Attr('down_image'),PointAttr('offset'),Attr('helptext'),Attr('hover_image')] 1113 ATTRIBUTES = BasicTextWidget.ATTRIBUTES + [Attr('up_image'),Attr('down_image'),PointAttr('offset'),Attr('helptext'),Attr('hover_image')]
1146 1114
1147 def __init__(self,up_image="",down_image="",hover_image="",offset=(0,0),**kwargs): 1115 def __init__(self,up_image="",down_image="",hover_image="",offset=(0,0),**kwargs):
1148 self.real_widget = fife.TwoButton() 1116 self.real_widget = fife.TwoButton()
1149 super(ImageButton,self).__init__(**kwargs) 1117 super(ImageButton,self).__init__(**kwargs)
1150 self.listener = ImageButtonListener(self)
1151 self.real_widget.setListener(self.listener)
1152 1118
1153 self.up_image = up_image 1119 self.up_image = up_image
1154 self.down_image = down_image 1120 self.down_image = down_image
1155 self.hover_image = hover_image 1121 self.hover_image = hover_image
1156 self.offset = offset 1122 self.offset = offset
1199 1165
1200 def resizeToContent(self): 1166 def resizeToContent(self):
1201 self.height = max(self._upimage.getHeight(),self._downimage.getHeight(),self._hoverimage.getHeight()) + self.margins[1]*2 1167 self.height = max(self._upimage.getHeight(),self._downimage.getHeight(),self._hoverimage.getHeight()) + self.margins[1]*2
1202 self.width = max(self._upimage.getWidth(),self._downimage.getWidth(),self._hoverimage.getWidth()) + self.margins[1]*2 1168 self.width = max(self._upimage.getWidth(),self._downimage.getWidth(),self._hoverimage.getWidth()) + self.margins[1]*2
1203 1169
1204 def setEnterCallback(self, cb):
1205 """
1206 Callback is called when mouse enters the area of ImageButton
1207 callback should have form of function(button)
1208 """
1209 self.listener.entercb = cb
1210
1211 def setExitCallback(self, cb):
1212 """
1213 Callback is called when mouse enters the area of ImageButton
1214 callback should have form of function(button)
1215 """
1216 self.listener.exitcb = cb
1217
1218
1219 1170
1220 class CheckBox(BasicTextWidget): 1171 class CheckBox(BasicTextWidget):
1221 """ 1172 """
1222 A basic checkbox. 1173 A basic checkbox.
1223 1174
1546 self._content = None 1497 self._content = None
1547 super(ScrollArea,self).__init__(**kwargs) 1498 super(ScrollArea,self).__init__(**kwargs)
1548 1499
1549 def addChild(self,widget): 1500 def addChild(self,widget):
1550 self.content = widget 1501 self.content = widget
1502 widget.parent = self
1551 1503
1552 def removeChild(self,widget): 1504 def removeChild(self,widget):
1553 if self._content != widget: 1505 if self._content != widget:
1554 raise RuntimeError("%s does not have %s as direct child widget." % (str(self),str(widget))) 1506 raise RuntimeError("%s does not have %s as direct child widget." % (str(self),str(widget)))
1555 self.content = None 1507 self.content = None
1508 widget.parent = None
1556 1509
1557 def _setContent(self,content): 1510 def _setContent(self,content):
1558 self.real_widget.setContent(content.real_widget) 1511 self.real_widget.setContent(content.real_widget)
1559 self._content = content 1512 self._content = content
1560 def _getContent(self): return self._content 1513 def _getContent(self): return self._content