Package horizons :: Package gui :: Package widgets :: Module resourceoverviewbar
[hide private]
[frames] | no frames]

Source Code for Module horizons.gui.widgets.resourceoverviewbar

  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 itertools 
 24  import json 
 25  import weakref 
 26   
 27  from fife import fife 
 28  from fife.extensions.pychan.widgets import HBox, Icon, Label, Spacer 
 29   
 30  import horizons.globals 
 31  from horizons.component.ambientsoundcomponent import AmbientSoundComponent 
 32  from horizons.component.storagecomponent import StorageComponent 
 33  from horizons.constants import RES, TIER 
 34  from horizons.extscheduler import ExtScheduler 
 35  from horizons.gui.mousetools.buildingtool import BuildingTool 
 36  from horizons.gui.mousetools.navigationtool import NavigationTool 
 37  from horizons.gui.util import create_resource_selection_dialog, get_res_icon_path, load_uh_widget 
 38  from horizons.i18n import gettext as T 
 39  from horizons.messaging import NewPlayerSettlementHovered, ResourceBarResize, TabWidgetChanged 
 40  from horizons.util.lastactiveplayersettlementmanager import LastActivePlayerSettlementManager 
 41  from horizons.util.pychanchildfinder import PychanChildFinder 
 42  from horizons.util.python.callback import Callback 
 43  from horizons.util.python.decorators import cachedmethod 
 44  from horizons.world.player import Player 
45 46 47 -class ResourceOverviewBar:
48 """The thing on the top left. 49 50 https://github.com/unknown-horizons/unknown-horizons/wiki/HUD 51 52 Features: 53 - display contents of currently relevant inventory (settlement/ship) 54 - always show gold of local player 55 - show costs of current build 56 - configure the resources to show 57 - per settlement 58 - add new slots 59 - switch displayed resources to construction relevant res on build 60 - res selection consistent with other res selection dlgs 61 62 Invariants: 63 - it should be obvious that the res bar can be configured 64 - it should be obvious that the res bar can be set per settlement 65 66 Has distinguished treatment of gold because it's distinguished by a bigger icon 67 and by being shown always. 68 """ 69 70 GOLD_ENTRY_GUI_FILE = "resource_overview_bar_gold.xml" 71 INITIAL_X_OFFSET = 100 # length of money icon (87px) + padding (10px left, 3px right) 72 73 ENTRY_GUI_FILE = "resource_overview_bar_entry.xml" 74 ENTRY_X_OFFSET = 52 # length of entry icons (49px) + padding (3px right) 75 ENTRY_Y_OFFSET = 17 # only padding (17px top)! 76 ENTRY_Y_HEIGHT = 66 # only height of entry icons (66px)! 77 CONSTRUCTION_LABEL_HEIGHT = 22 # height of extra label shown in build preview mode 78 79 STATS_GUI_FILE = "resource_overview_bar_stats.xml" 80 81 STYLE = "resource_bar" 82 83 DEFAULT_RESOURCES = [RES.TOOLS, 84 RES.BOARDS, 85 RES.BRICKS, 86 RES.FOOD, 87 RES.TEXTILE, 88 RES.SALT] 89 90 # order should match the above, else confuses players when in build mode 91 CONSTRUCTION_RESOURCES = { # per inhabitant tier 92 TIER.SAILORS: [RES.TOOLS, RES.BOARDS], 93 TIER.PIONEERS: [RES.TOOLS, RES.BOARDS, RES.BRICKS], 94 TIER.SETTLERS: [RES.TOOLS, RES.BOARDS, RES.BRICKS], 95 TIER.CITIZENS: [RES.TOOLS, RES.BOARDS, RES.BRICKS], 96 TIER.MERCHANTS: [RES.TOOLS, RES.BOARDS, RES.BRICKS], 97 } 98
99 - def __init__(self, session):
100 from horizons.session import Session 101 assert isinstance(session, Session) 102 self.session = session 103 104 # special slot because of special properties 105 self.gold_gui = load_uh_widget(self.__class__.GOLD_ENTRY_GUI_FILE, style=self.__class__.STYLE) 106 self.gold_gui.balance_visible = False 107 self.gold_gui.child_finder = PychanChildFinder(self.gold_gui) 108 gold_icon = self.gold_gui.child_finder("res_icon") 109 gold_icon.image = get_res_icon_path(RES.GOLD) 110 gold_icon.max_size = gold_icon.min_size = gold_icon.size = (32, 32) 111 self.gold_gui.mapEvents({ 112 "resbar_gold_container/mouseClicked/stats": self._toggle_stats, 113 }) 114 self.gold_gui.helptext = T("Click to show statistics") 115 self.stats_gui = None 116 117 self.gui = [] # list of slots 118 self.resource_configurations = weakref.WeakKeyDictionary() 119 self.current_instance = weakref.ref(self) # can't weakref to None 120 self.construction_mode = False 121 self._last_build_costs = None 122 self._do_show_dummy = False 123 124 self._update_default_configuration() 125 126 NewPlayerSettlementHovered.subscribe(self._on_different_settlement) 127 TabWidgetChanged.subscribe(self._on_tab_widget_changed) 128 129 # set now and then every few sec 130 ExtScheduler().add_new_object(self._update_balance_display, self, run_in=0) 131 ExtScheduler().add_new_object(self._update_balance_display, self, run_in=Player.STATS_UPDATE_INTERVAL, loops=-1)
132
133 - def hide(self):
134 self.gold_gui.hide() 135 for slot in self.gui: 136 slot.hide() 137 if self.stats_gui: 138 self.stats_gui.hide()
139
140 - def end(self):
141 self.set_inventory_instance(None, force_update=True) 142 self.current_instance = weakref.ref(self) 143 ExtScheduler().rem_all_classinst_calls(self) 144 self.resource_configurations.clear() 145 self.hide() 146 self.gold_gui = None 147 self.gui = None 148 self.stats_gui = None 149 self._custom_default_resources = None 150 NewPlayerSettlementHovered.unsubscribe(self._on_different_settlement) 151 TabWidgetChanged.unsubscribe(self._on_tab_widget_changed)
152
154 # user defined variante of DEFAULT_RESOURCES (will be preferred) 155 self._custom_default_resources = None 156 setting = horizons.globals.fife.get_uh_setting("ResourceOverviewBarConfiguration") 157 if setting: # parse it if there is something 158 config = json.loads(setting) 159 if config: # actually use it if it was parseable 160 self._custom_default_resources = config
161
162 - def save(self, db):
163 for obj, config in self.resource_configurations.items(): 164 for position, res in enumerate(config): 165 db("INSERT INTO resource_overview_bar(object, position, resource) VALUES(?, ?, ?)", 166 obj.worldid, position, res)
167
168 - def load(self, db):
169 from horizons.util.worldobject import WorldObject 170 for obj in db("SELECT DISTINCT object FROM resource_overview_bar"): 171 obj = obj[0] 172 l = [] 173 for pos, res in db("SELECT position, resource FROM resource_overview_bar where object=?", obj): 174 l.append((pos, res)) 175 obj = WorldObject.get_object_by_id(obj) 176 self.resource_configurations[obj] = [i[1] for i in sorted(l)] 177 178 # called when any game (also new ones) start 179 # register at player inventory for gold updates 180 inv = self.session.world.player.get_component(StorageComponent).inventory 181 inv.add_change_listener(self._update_gold, call_listener_now=True) 182 self.gold_gui.show() 183 self._update_gold() # call once more to make pychan happy 184 185 self.set_inventory_instance( 186 LastActivePlayerSettlementManager().get(get_current_pos=True))
187
188 - def redraw(self):
189 self.set_inventory_instance(self.current_instance(), force_update=True)
190
191 - def _on_different_settlement(self, message):
193
194 - def set_inventory_instance(self, instance, keep_construction_mode=False, force_update=False):
195 """Display different inventory. May change resources that are displayed""" 196 if self.current_instance() is instance and not self.construction_mode and not force_update: 197 return # caller is drunk yet again 198 if self.construction_mode and not keep_construction_mode: 199 # stop construction mode, immediately update view, which will be a normal view 200 self.close_construction_mode(update_slots=False) 201 202 # reconstruct general gui 203 204 # remove old gui (keep entries for reuse) 205 for i in self.gui: 206 i.hide() 207 self._hide_resource_selection_dialog() 208 209 inv = self._get_current_inventory() 210 if inv is not None: 211 inv.remove_change_listener(self._update_resources) 212 213 if instance in (None, self): # show nothing instead 214 self.current_instance = weakref.ref(self) # can't weakref to None 215 self._do_show_dummy = False # don't save dummy value 216 return 217 218 self.current_instance = weakref.ref(instance) 219 220 # construct new slots (fill values later) 221 load_entry = lambda: load_uh_widget(self.ENTRY_GUI_FILE, style=self.__class__.STYLE) 222 resources = self._get_current_resources() 223 addition = [-1] if self._do_show_dummy or not resources else [] # add dummy at end for adding stuff 224 for i, res in enumerate(resources + addition): 225 if i < len(self.gui): # get old slot 226 entry = self.gui[i] 227 if res == -1: # can't reuse dummy slot, need default data 228 self.gui[i] = entry = load_entry() 229 else: # need new one 230 entry = load_entry() 231 self.gui.append(entry) 232 233 entry.findChild(name="entry").position = (self.INITIAL_X_OFFSET + i * self.ENTRY_X_OFFSET, 234 self.ENTRY_Y_OFFSET) 235 background_icon = entry.findChild(name="entry") 236 background_icon.capture(Callback(self._show_resource_selection_dialog, i), 'mouseClicked', 'resbar') 237 background_icon.capture(self._show_dummy_slot, 'mouseEntered', 'resbar') 238 239 if res != -1: 240 helptext = self.session.db.get_res_name(res) 241 icon = entry.findChild(name="res_icon") 242 icon.num = i 243 icon.image = get_res_icon_path(res) 244 icon.max_size = icon.min_size = icon.size = (24, 24) 245 icon.capture(self._on_res_slot_click, event_name='mouseClicked') 246 else: 247 helptext = T("Click to add a new slot") 248 entry.show() # this will not be filled as the other res 249 background_icon.helptext = helptext 250 251 # show it just when values are entered, this appeases pychan 252 253 # fill values 254 inv = self._get_current_inventory() 255 # update on all changes as well as now 256 inv.add_change_listener(self._update_resources, call_listener_now=True)
257
258 - def set_construction_mode(self, resource_source_instance, build_costs):
259 """Show resources relevant to construction and build costs 260 @param resource_source_instance: object with StorageComponent 261 @param build_costs: dict, { res : amount } 262 """ 263 if resource_source_instance is None: 264 # Build moved out of settlement. This is usually not sane and an interaction error. 265 # Use this heuristically computed settlement to fix preconditions. 266 resource_source_instance = LastActivePlayerSettlementManager().get() 267 if self.construction_mode and \ 268 resource_source_instance == self.current_instance() and \ 269 build_costs == self._last_build_costs: 270 return # now that's not an update 271 272 self._last_build_costs = build_costs 273 274 self.construction_mode = True 275 self.set_inventory_instance(resource_source_instance, keep_construction_mode=True) 276 277 # label background icons 278 cost_icon_gold = "content/gui/images/background/widgets/resbar_stats_bottom.png" 279 cost_icon_res = "content/gui/images/background/widgets/res_extra_bg.png" 280 281 res_list = self._get_current_resources() 282 283 # remove old one before, avoids duplicates 284 self._drop_cost_labels() 285 286 for res, amount in build_costs.items(): 287 assert res in res_list or res == RES.GOLD 288 289 cost_label = Label(text="-" + str(amount)) 290 cost_label.stylize(self.__class__.STYLE) 291 # add icon below end of background icon 292 if res in res_list: 293 entry = res_list.index(res) 294 cur_gui = self.gui[entry] 295 reference_icon = cur_gui.findChild(name="background_icon") 296 below = reference_icon.size[1] 297 cost_icon = Icon(image=cost_icon_res, position=(0, below)) 298 cost_label.position = (15, below) # TODO: centering 299 300 cur_gui.addChild(cost_icon) 301 cur_gui.addChild(cost_label) 302 cur_gui.cost_gui = [cost_label, cost_icon] 303 304 cur_gui.resizeToContent() # container needs to be bigger now 305 else: # must be gold 306 # there is an icon with scales there, use its positioning 307 reference_icon = self.gold_gui.child_finder("balance_background") 308 cost_icon = Icon(image=cost_icon_gold, position=(reference_icon.x, reference_icon.y)) 309 cost_label.position = (23, 74) # TODO: centering 310 311 self.gold_gui.addChild(cost_icon) 312 self.gold_gui.addChild(cost_label) 313 self.gold_gui.cost_gui = [cost_label, cost_icon] 314 315 self.gold_gui.resizeToContent()
316
317 - def close_construction_mode(self, update_slots=True):
318 """Return to normal configuration""" 319 self.construction_mode = False 320 if update_slots: # cleanup 321 self._drop_cost_labels() 322 self.set_inventory_instance(None) 323 #self._update_gold() 324 self.gold_gui.show() 325 self._update_gold(force=True) 326 327 # reshow last settlement 328 self.set_inventory_instance(LastActivePlayerSettlementManager().get(get_current_pos=True))
329
330 - def _drop_cost_labels(self):
331 """Removes all labels below the slots indicating building costs""" 332 for entry in itertools.chain(self.gui, [self.gold_gui]): 333 if hasattr(entry, "cost_gui"): # get rid of possible cost labels 334 for elem in entry.cost_gui: 335 entry.removeChild(elem) 336 del entry.cost_gui
337
338 - def _update_gold(self, force=False):
339 """Changelistener to upate player gold""" 340 # can be called pretty often (e.g. if there's an settlement.inventory.alter() in a loop) 341 # only update every 0.3 sec at most 342 scheduled_attr = "_gold_upate_scheduled" 343 if not hasattr(self, scheduled_attr): 344 setattr(self, scheduled_attr, True) 345 ExtScheduler().add_new_object(Callback(self._update_gold, force=True), self, run_in=0.3) 346 return 347 elif not force: 348 return # these calls we want to suppress, wait for scheduled call 349 350 delattr(self, scheduled_attr) 351 352 # set gold amount 353 gold = self.session.world.player.get_component(StorageComponent).inventory[RES.GOLD] 354 gold_available_lbl = self.gold_gui.child_finder("gold_available") 355 gold_available_lbl.text = str(gold) 356 # reposition according to magic formula passed down from the elders in order to support centering 357 gold_available_lbl.resizeToContent() # this sets new size values 358 gold_available_lbl.position = (42 - (gold_available_lbl.size[0] // 2), 51) 359 360 self.gold_gui.resizeToContent() # update label size
361
362 - def _update_balance_display(self):
363 """Updates balance info below gold icon""" 364 balance = self.session.world.player.get_balance_estimation() 365 balance_lbl = self.gold_gui.child_finder("balance") 366 balance_lbl.text = "{balance:+}".format(balance=balance) 367 balance_lbl.resizeToContent() 368 # 38 369 balance_lbl.position = (70 - balance_lbl.size[0], 74) # see _update_gold 370 371 self.gold_gui.resizeToContent() # update label size
372
373 - def _update_resources(self):
374 """Same as _update_gold but for all other slots""" 375 if self.current_instance() in (None, self): # instance died 376 self.set_inventory_instance(None) 377 return 378 inv = self._get_current_inventory() 379 for i, res in enumerate(self._get_current_resources()): 380 cur_gui = self.gui[i] 381 382 # set amount 383 label = cur_gui.findChild(name="res_available") 384 label.text = str(inv[res]) 385 386 # reposition according to magic formula passed down from the elders in order to support centering 387 cur_gui.adaptLayout() # update size values (e.g. if amount of digits changed) 388 cur_gui.show() 389 label.position = (24 - (label.size[0] // 2), 44)
390
391 - def _get_current_resources(self):
392 """Return list of resources to display now""" 393 if self.construction_mode: 394 tier = self.session.world.player.settler_level 395 res_list = self.__class__.CONSTRUCTION_RESOURCES[tier] 396 # also add additional res that might be needed 397 res_list += [res for res in self._last_build_costs if 398 res not in res_list and res != RES.GOLD] 399 return res_list 400 # prefer user defaults over general defaults 401 default = self._custom_default_resources if self._custom_default_resources else self.__class__.DEFAULT_RESOURCES 402 # prefer specific setting over any defaults 403 return self.resource_configurations.get(self.current_instance(), default)
404
405 - def _get_current_inventory(self):
406 if not (self.current_instance() in (None, self)): # alive and set 407 return self.current_instance().get_component(StorageComponent).inventory 408 else: 409 return None
410
411 - def get_size(self):
412 """Returns (x,y) size tuple. 413 414 Used by the cityinfo to determine how to change its position if the widgets 415 overlap using default positioning (resource bar can get arbitrarily long). 416 Note that the money icon has the same offset effect as all entry icons have 417 (height 73 + padding 10 == height 66 + padding 17), thus the calculation only 418 needs of regular items (ENTRY_Y_*) to determine the maximum widget height. 419 """ 420 item_amount = len(self._get_current_resources()) 421 width = self.INITIAL_X_OFFSET + self.ENTRY_X_OFFSET * item_amount 422 height = self.ENTRY_Y_OFFSET + self.ENTRY_Y_HEIGHT 423 return (width, height)
424 425 ### 426 # Resource slot selection 427
428 - def _show_resource_selection_dialog(self, slot_num):
429 """Shows gui for selecting a resource for slot slot_num""" 430 if isinstance(self.session.ingame_gui.cursor, BuildingTool): 431 return 432 433 self._hide_resource_selection_dialog() 434 inv = self._get_current_inventory() 435 if inv is None: 436 return 437 438 # set mousetool to get notified on clicks outside the resbar area 439 if not isinstance(self.session.ingame_gui.cursor, ResBarMouseTool): 440 self.session.ingame_gui.cursor = ResBarMouseTool(self.session, self.session.ingame_gui.cursor, 441 self.close_resource_selection_mode) 442 443 on_click = functools.partial(self._set_resource_slot, slot_num) 444 cur_res = self._get_current_resources() 445 res_filter = lambda res_id: res_id not in cur_res 446 dlg = create_resource_selection_dialog(on_click, inv, self.session.db, 447 widget='resbar_resource_selection.xml', 448 res_filter=res_filter) 449 450 # position dlg below slot 451 cur_gui = self.gui[slot_num] 452 background_icon = cur_gui.findChild(name="background_icon") 453 dlg.position = (cur_gui.position[0] + background_icon.position[0], 454 cur_gui.position[1] + background_icon.position[1] + background_icon.size[1]) 455 dlg.findChild(name="make_default_btn").capture(self._make_configuration_default) 456 reset_default_btn = dlg.findChild(name="reset_default_btn") 457 # this is a quadruple-use button. 458 # If there is no user set default, restore to factory default 459 # If the current config is different from user default, set to default 460 # If this is the current user set config, remove user set config and fall back to factory default 461 # If there is no user set config and the current config is the system default, 462 # the button should be disabled, but the first case below is shown because 463 # we can't disable it 464 if self._custom_default_resources is None: 465 reset_default_btn.helptext = T("Reset this configuration to the factory default.") 466 reset_default_btn.capture(Callback(self._drop_settlement_resource_configuration)) 467 468 elif self._custom_default_resources != self._get_current_resources(): 469 reset_default_btn.helptext = T("Reset this settlement's displayed resources to the default configuration you have saved.") 470 reset_default_btn.capture(Callback(self._drop_settlement_resource_configuration)) 471 472 else: 473 reset_default_btn.helptext = T("Reset the default configuration (which you see here) to the factory default for all settlements.") 474 cb = Callback.ChainedCallbacks( 475 self._drop_settlement_resource_configuration, # remove specific config 476 Callback(self._make_configuration_default, reset=True) # remove global config 477 ) 478 reset_default_btn.capture(cb) 479 480 dlg.show() 481 self._res_selection_dialog = dlg
482
483 - def _make_configuration_default(self, reset=False):
484 """Saves current resources as default via game settings""" 485 if reset: 486 config = [] # meaning invalid 487 else: 488 config = json.dumps(self._get_current_resources()) 489 horizons.globals.fife.set_uh_setting("ResourceOverviewBarConfiguration", config) 490 horizons.globals.fife.save_settings() 491 self._update_default_configuration() 492 AmbientSoundComponent.play_special("success") 493 if reset: # in the other case, it's already set 494 self.redraw()
495
497 """Forget resource configuration for a settlement""" 498 if self.current_instance() in self.resource_configurations: 499 del self.resource_configurations[self.current_instance()] 500 self.redraw()
501
502 - def _set_resource_slot(self, slot_num, res_id):
503 """Show res_id in slot slot_num 504 @param slot_num: starting at 0, will be added as new slot if greater than no of slots 505 @param res_id: a resource id or 0 for remove slot 506 """ 507 self.close_construction_mode() 508 res_copy = self._get_current_resources()[:] 509 number_of_slots_changed = False 510 if slot_num < len(res_copy): # change existing slot 511 if res_id == 0: # remove it 512 del res_copy[slot_num] 513 number_of_slots_changed = True 514 else: # actual slot change 515 res_copy[slot_num] = res_id 516 else: # addition 517 if res_id == 0: # that would mean adding an empty slot 518 pass 519 else: 520 number_of_slots_changed = True 521 res_copy += [res_id] 522 523 self.resource_configurations[self.current_instance()] = res_copy 524 525 if number_of_slots_changed: 526 ResourceBarResize.broadcast(self) 527 self.redraw() 528 529 if isinstance(self.session.ingame_gui.cursor, ResBarMouseTool): 530 self.session.ingame_gui.cursor.reset()
531
533 if hasattr(self, "_res_selection_dialog"): 534 self._res_selection_dialog.hide() 535 del self._res_selection_dialog
536
538 """Fully disable resource selection mode""" 539 self._hide_resource_selection_dialog() 540 self._hide_dummy_slot()
541
542 - def _on_tab_widget_changed(self, msg=None):
543 if hasattr(self, "_res_selection_dialog"): 544 self.close_resource_selection_mode()
545
546 - def _show_dummy_slot(self):
547 """Show the dummy button at the end to allow for addition of slots""" 548 if self._do_show_dummy: 549 return # already visible 550 self._do_show_dummy = True 551 self.redraw()
552
553 - def _hide_dummy_slot(self):
554 self._do_show_dummy = False 555 self.redraw()
556
557 - def _on_res_slot_click(self, widget, event):
558 """Called when you click on a resource slot in the bar (not the selection dialog)""" 559 #TODO let KeyConfig handle this instead of hardcoding rmb 560 if event.getButton() == fife.MouseEvent.RIGHT: 561 self._set_resource_slot(widget.num, 0)
562
563 - def _toggle_stats(self):
564 if self.stats_gui is None or not self.stats_gui.isVisible(): 565 self._show_stats() 566 else: 567 self._hide_stats()
568
569 - def _show_stats(self):
570 """Show data below gold icon when balance label is clicked""" 571 if self.stats_gui is None: 572 self._init_stats_gui() 573 574 self._update_stats() 575 self.stats_gui.show() 576 577 ExtScheduler().add_new_object(self._update_stats, self, run_in=Player.STATS_UPDATE_INTERVAL, loops=-1)
578
579 - def _update_stats(self):
580 """Fills in (refreshes) numeric values in expanded stats area.""" 581 data = self.session.world.player.get_statistics() 582 # This list must correspond to `images` in _show_stats 583 figures = [ 584 -data.running_costs, 585 data.taxes, 586 -data.buy_expenses, 587 data.sell_income, 588 data.balance 589 ] 590 for (i, numbers) in enumerate(figures): 591 label = self.stats_gui.child_finder("resbar_stats_entry_{}".format(i)) 592 label.text = "{:+d}".format(numbers)
593
594 - def _hide_stats(self):
595 """Inverse of show_stats""" 596 ExtScheduler().rem_call(self, self._update_stats) 597 if self.stats_gui is not None: 598 self.stats_gui.hide()
599
600 - def _init_stats_gui(self):
601 reference_icon = self.gold_gui.child_finder("balance_background") 602 self.stats_gui = load_uh_widget(self.__class__.STATS_GUI_FILE) 603 self.stats_gui.child_finder = PychanChildFinder(self.stats_gui) 604 self.stats_gui.position = (reference_icon.x + self.gold_gui.x, 605 reference_icon.y + self.gold_gui.y) 606 self.stats_gui.mapEvents({ 607 'resbar_stats_container/mouseClicked/stats': self._toggle_stats, 608 }) 609 610 # This list must correspond to `figures` in _update_stats 611 images = [ 612 ("content/gui/images/resbar_stats/expense.png", T("Running costs")), 613 ("content/gui/images/resbar_stats/income.png", T("Taxes")), 614 ("content/gui/images/resbar_stats/buy.png", T("Buy expenses")), 615 ("content/gui/images/resbar_stats/sell.png", T("Sell income")), 616 ("content/gui/images/resbar_stats/scales_icon.png", T("Balance")), 617 ] 618 619 for num, (image, helptext) in enumerate(images): 620 # Keep in sync with comment there until we can use that data: 621 # ./content/gui/xml/ingame/hud/resource_overview_bar_stats.xml 622 box = HBox(padding=0, min_size=(70, 0)) 623 box.name = "resbar_stats_line_{}".format(num) 624 box.helptext = helptext 625 #TODO Fix icon size; looks like not 16x16 a surprising amount of times. 626 box.addChild(Icon(image=image)) 627 box.addChild(Spacer()) 628 box.addChild(Label(name="resbar_stats_entry_{}".format(num))) 629 #TODO This label is a workaround for some fife font bug, 630 # probably http://github.com/fifengine/fifengine/issues/666. 631 templabel = Label(name="resbar_stats_whatever_{}".format(num)) 632 box.addChild(templabel) 633 if num == len(images) - 1: 634 # The balance line (last one) gets bold font. 635 box.stylize('resource_bar') 636 self.stats_gui.child_finder("entries_box").addChild(box)
637 638 ## 639 # CODE FOR REFERENCE 640 ## 641 642 @cachedmethod
644 # currently unused since always 0 645 # can be used for relative positioning of labels 646 # old formula: label.position = (icon.position[0] - label.size[0]/2 + xoffset, yoffset) 647 gui = load_uh_widget(self.__class__.ENTRY_GUI_FILE) 648 icon = gui.findChild(name="background_icon") 649 return icon.position
650 651 @cachedmethod
653 # see above 654 gui = load_uh_widget(self.__class__.GOLD_ENTRY_GUI_FILE) 655 icon = gui.findChild(name="background_icon") 656 return icon.position
657
658 659 -class ResBarMouseTool(NavigationTool):
660 """Temporary mousetool for resource selection. 661 Terminates self on mousePressed and restores old tool"""
662 - def __init__(self, session, old_tool, on_click):
663 super().__init__(session) 664 if old_tool: # can be None in corner cases 665 old_tool.disable() 666 self.old_tool = old_tool 667 self.on_click = on_click
668
669 - def mousePressed(self, evt):
670 self.on_click() 671 self.reset() 672 # this click should still count, especially in case the res 673 # selection dialog has been closed by other means than clicking 674 self.session.ingame_gui.cursor.mousePressed(evt)
675
676 - def reset(self):
677 """Enable old tool again""" 678 if self.old_tool: 679 self.session.ingame_gui.cursor = self.old_tool 680 self.remove() 681 if self.old_tool: 682 self.old_tool.enable() 683 else: 684 self.session.ingame_gui.set_cursor()
685