Package horizons :: Package engine :: Module pychan_util
[hide private]
[frames] | no frames]

Source Code for Module horizons.engine.pychan_util

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  import functools 
 23  import traceback 
 24   
 25  from fife.extensions import pychan 
 26   
 27  import horizons.globals 
 28  from horizons.gui.style import STYLES 
 29  from horizons.gui.widgets.imagebutton import ImageButton 
 30  from horizons.messaging import GuiAction, GuiCancelAction, GuiHover 
 31  from horizons.util.python.callback import Callback 
32 33 34 -class RenameLabel(pychan.widgets.Label):
35 """A regular label that signals that it will display a rename dialog when clicked upon (by changing the cursor)""" 36 pass # implementation added dynamically below
37
38 39 -class RenameImageButton(ImageButton):
40 pass # as above
41
42 43 -def handle_gcn_exception(e, msg=None):
44 """Called for RuntimeErrors after gcn::exceptions that smell like guichan bugs. 45 @param e: RuntimeError (python, not pychan) 46 @param msg: additional info as string 47 """ 48 traceback.print_stack() 49 print('Caught RuntimeError on gui interaction, assuming irrelevant gcn::exception.') 50 if msg: 51 print(msg)
52
53 54 -def init_pychan():
55 """General pychan initiation for uh""" 56 global STYLES 57 58 # quick hack to allow up_image/down_image values to be unicode 59 # TODO solve this problem in a better way (e.g. passing str explicitly) 60 # or waiting for a fix of http://github.com/fifengine/fifengine/issues/701 61 from fife.extensions.pychan.properties import ImageProperty 62 63 def patch_imageproperty(func): 64 def wrapper(self, obj, image): 65 if isinstance(image, str): 66 image = str(image) 67 return func(self, obj, image)
68 return wrapper 69 70 ImageProperty.__set__ = patch_imageproperty(ImageProperty.__set__) 71 72 # register custom widgets 73 from horizons.gui.widgets.inventory import Inventory 74 from horizons.gui.widgets.buysellinventory import BuySellInventory 75 from horizons.gui.widgets.imagefillstatusbutton import ImageFillStatusButton 76 from horizons.gui.widgets.progressbar import ProgressBar, TilingProgressBar 77 # additionally, ImageButton is imported from widgets.imagebutton above 78 from horizons.gui.widgets.imagebutton import CancelButton, DeleteButton, MainmenuButton, OkButton 79 from horizons.gui.widgets.icongroup import TabBG, TilingHBox, hr 80 from horizons.gui.widgets.stepslider import StepSlider 81 from horizons.gui.widgets.unitoverview import HealthWidget, StanceWidget, WeaponStorageWidget 82 from horizons.gui.widgets.tooltip import _Tooltip 83 84 widgets = [OkButton, CancelButton, DeleteButton, MainmenuButton, 85 Inventory, BuySellInventory, ImageFillStatusButton, 86 ProgressBar, StepSlider, TabBG, 87 HealthWidget, StanceWidget, WeaponStorageWidget, 88 RenameLabel, RenameImageButton, 89 TilingHBox, TilingProgressBar, hr, 90 # This overwrites the ImageButton provided by FIFE! 91 ImageButton] 92 93 for widget in widgets: 94 pychan.widgets.registerWidget(widget) 95 96 # add uh styles 97 for name, stylepart in STYLES.items(): 98 pychan.manager.addStyle(name, stylepart) 99 100 # patch default widgets 101 for name, widget in list(pychan.widgets.WIDGETS.items()): 102 103 def catch_gcn_exception_decorator(func): 104 @functools.wraps(func) 105 def wrapper(*args, **kwargs): 106 try: 107 # only apply usable args, else it would crash when called through fife timers 108 pychan.tools.applyOnlySuitable(func, *args, **kwargs) 109 except RuntimeError as e: 110 handle_gcn_exception(e) 111 return wrapper 112 113 widget.hide = catch_gcn_exception_decorator(widget.hide) 114 115 from fife.extensions.pychan import ABox, HBox, Icon, Label, VBox 116 # this is white list of widgets with tooltip. 117 widgets_with_tooltip = [ABox, HBox, Icon, ImageButton, Label, VBox] 118 119 for widget in widgets_with_tooltip: 120 # Copy everything we need from the tooltip class (manual mixin). 121 # TODO: Figure out if it is safe to use this instead: 122 # widget.__bases__ += (_Tooltip, ) 123 for key, value in _Tooltip.__dict__.items(): 124 if not key.startswith("__"): 125 setattr(widget, key, value) 126 127 def add_tooltip_init(func): 128 @functools.wraps(func) 129 def wrapper(self, *args, **kwargs): 130 func(self, *args, **kwargs) 131 self.init_tooltip() 132 return wrapper 133 134 widget.__init__ = add_tooltip_init(widget.__init__) 135 136 # these sometimes fail with "No focushandler set (did you add the widget to the gui?)." 137 # see #1597 and #1647 138 widget.requestFocus = catch_gcn_exception_decorator(widget.requestFocus) 139 140 # FIXME hack pychan's text2gui function, it does an isinstance check that breaks 141 # the lazy string from horizons.i18n. we should be passing unicode to 142 # widgets all the time, therefore we don't need the additional check. 143 def text2gui(text): 144 # Drop unicode encoding for now. 145 #unicodePolicy = horizons.globals.fife.pychan.manager.unicodePolicy 146 #return text.encode("utf8",*unicodePolicy).replace("\t"," "*4).replace("[br]","\n") 147 return text.replace("\t", " " * 4).replace("[br]", "\n") 148 149 pychan.widgets.textfield.text2gui = text2gui 150 pychan.widgets.basictextwidget.text2gui = text2gui 151 152 setup_cursor_change_on_hover() 153 154 setup_trigger_signals_on_action() 155 156 setup_trigger_signals_on_hover() 157
158 159 -def setup_cursor_change_on_hover():
160 161 # set cursor to rename on hover for certain widgets 162 def set_cursor(): 163 horizons.globals.fife.set_cursor_image("rename")
164 165 def unset_cursor(): 166 horizons.globals.fife.set_cursor_image("default") 167 168 def make_cursor_change_on_hover_class(cls): 169 # this can't be a regular class since vanilla TextFields should have it by default 170 def disable_cursor_change_on_hover(self): 171 self.mapEvents({ 172 self.name + '/mouseEntered/cursor': None, 173 self.name + '/mouseExited/cursor': None, 174 }) 175 176 def enable_cursor_change_on_hover(self): 177 self.mapEvents({ 178 self.name + '/mouseEntered/cursor': set_cursor, 179 self.name + '/mouseExited/cursor': unset_cursor, 180 # this changes the cursor if the widget is hidden while the 181 # cursor is still above the textfield 182 self.name + '/ancestorHidden/cursor': unset_cursor 183 }) 184 185 def add_cursor_change_on_hover_init(func): 186 @functools.wraps(func) 187 def wrapper(self, *args, **kwargs): 188 func(self, *args, **kwargs) 189 enable_cursor_change_on_hover(self) 190 return wrapper 191 192 cls.__init__ = add_cursor_change_on_hover_init(cls.__init__) 193 cls.disable_cursor_change_on_hover = disable_cursor_change_on_hover 194 cls.enable_cursor_change_on_hover = enable_cursor_change_on_hover 195 196 make_cursor_change_on_hover_class(pychan.widgets.WIDGETS['TextField']) 197 make_cursor_change_on_hover_class(RenameLabel) 198 make_cursor_change_on_hover_class(RenameImageButton) 199
200 201 -def setup_trigger_signals_on_action():
202 """Make sure that every widget sends a signal when an action event occurs""" 203 def make_action_trigger_a_signal(cls): 204 def add_action_triggers_a_signal(func): 205 @functools.wraps(func) 206 def wrapper(self, *args, **kwargs): 207 func(self, *args, **kwargs) 208 if cls._getName(self) == "cancelButton": 209 self.capture(Callback(GuiCancelAction.broadcast, self), "action", "action_listener") 210 else: 211 self.capture(Callback(GuiAction.broadcast, self), "action", "action_listener")
212 return wrapper 213 214 cls.__init__ = add_action_triggers_a_signal(cls.__init__) 215 216 make_action_trigger_a_signal(pychan.widgets.Widget) 217
218 219 -def setup_trigger_signals_on_hover():
220 """Make sure that the widgets specified below send a signal when a mouseOver event occurs""" 221 def make_hover_trigger_a_signal(cls): 222 def add_hover_triggers_a_signal(func): 223 @functools.wraps(func) 224 def wrapper(self, *args, **kwargs): 225 func(self, *args, **kwargs) 226 self.capture(Callback(GuiHover.broadcast, self), "mouseEntered", "action_listener")
227 return wrapper 228 229 cls.__init__ = add_hover_triggers_a_signal(cls.__init__) 230 231 make_hover_trigger_a_signal(pychan.widgets.WIDGETS['OkButton']) 232 make_hover_trigger_a_signal(pychan.widgets.WIDGETS['CancelButton']) 233 make_hover_trigger_a_signal(pychan.widgets.WIDGETS['DeleteButton']) 234 make_hover_trigger_a_signal(pychan.widgets.WIDGETS['MainmenuButton']) 235 make_hover_trigger_a_signal(pychan.widgets.WIDGETS['ImageButton']) 236