Package horizons :: Package gui :: Package tabs :: Module selectmultitab
[hide private]
[frames] | no frames]

Source Code for Module horizons.gui.tabs.selectmultitab

  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 logging 
 23  from collections import defaultdict 
 24   
 25  from fife import fife 
 26  from fife.extensions.pychan.widgets import Icon 
 27   
 28  from horizons.command.unit import SetStance 
 29  from horizons.component.healthcomponent import HealthComponent 
 30  from horizons.component.selectablecomponent import SelectableComponent 
 31  from horizons.component.stancecomponent import DEFAULT_STANCES 
 32  from horizons.constants import UNITS 
 33  from horizons.gui.tabs.tabinterface import TabInterface 
 34  from horizons.gui.util import load_uh_widget 
 35  from horizons.i18n import gettext_lazy as LazyT 
 36  from horizons.scheduler import Scheduler 
 37  from horizons.util.loaders.actionsetloader import ActionSetLoader 
 38  from horizons.util.python.callback import Callback 
 39   
 40   
41 -class SelectMultiTab(TabInterface):
42 """ 43 Tab shown when multiple units are selected 44 """ 45 widget = 'overview_select_multi.xml' 46 icon_path = 'icons/tabwidget/common/inventory' 47 helptext = LazyT("Selected Units") 48 49 max_row_entry_number = 3 50 max_column_entry_number = 4 51
52 - def __init__(self, selected_instances=None):
53 self.selected_instances = selected_instances or [] 54 55 # keep track of units that have stance 56 self.stance_unit_number = 0 57 # keep local track of selected instances 58 self.instances = [] 59 # keep track of number of instances per type 60 self.type_number = defaultdict(int) 61 62 for i in self.selected_instances: 63 if hasattr(i, 'stance'): 64 self.stance_unit_number += 1 65 self.instances.append(i) 66 if not i.has_remove_listener(Callback(self.on_instance_removed, i)): 67 i.add_remove_listener(Callback(self.on_instance_removed, i)) 68 self.type_number[i.id] += 1 69 70 self._scheduled_refresh = False 71 72 super().__init__()
73
74 - def init_widget(self):
75 if self.stance_unit_number != 0: 76 self.show_stance_widget() 77 78 self.draw_selected_units_widget()
79
80 - def add_entry(self, entry):
81 if self.column_number > self.max_column_entry_number - 1: 82 self.column_number = 0 83 self.row_number += 1 84 if self.row_number >= 3: 85 # TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO 86 # This crashes when more than 2 rows are needed. 87 # There just aren't any hboxes in the xml. 88 # TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO 89 self.row_number = 2 90 return 91 self.column_number += 1 92 self.widget.findChild(name="hbox_{}".format(self.row_number)).addChild(entry.widget) 93 self.entries.append(entry)
94
96 self.entries = [] 97 self.row_number = 0 98 self.column_number = 0 99 # if only one type of units is selected draw individual widgets for selected units 100 if len(self.type_number) == 1: 101 for instance in self.instances: 102 self.add_entry(UnitEntry([instance], False)) 103 else: 104 entry_instances = defaultdict(list) 105 for instance in self.instances: 106 entry_instances[instance.id].append(instance) 107 for instances in entry_instances.values(): 108 self.add_entry(UnitEntry(instances))
109
111 for entry in self.entries: 112 entry.remove() 113 for i in range(0, self.max_row_entry_number): 114 self.widget.findChild(name="hbox_{}".format(i)).removeAllChildren()
115
117 if not self._scheduled_refresh: 118 self._scheduled_refresh = True 119 Scheduler().add_new_object(self.refresh_unit_widget, self, run_in=0)
120
121 - def refresh_unit_widget(self):
122 if self.instances: 123 self._scheduled_refresh = False 124 self.hide_selected_units_widget() 125 self.draw_selected_units_widget() 126 self.toggle_stance() 127 self.widget.adaptLayout() 128 else: 129 # all units were destroyed 130 self.hide_selected_units_widget()
131
132 - def on_instance_removed(self, instance):
133 if hasattr(instance, 'stance'): 134 self.stance_unit_number -= 1 135 136 self.instances.remove(instance) 137 instance.discard_remove_listener(Callback(self.on_instance_removed, instance)) 138 139 if self.widget.isVisible(): 140 if len(self.instances) < 2: 141 # hide the multi-selection tab 142 instance.session.ingame_gui.hide_menu() 143 # if one unit remains, show its menu 144 if len(self.instances) == 1: 145 self.instances[0].get_component(SelectableComponent).show_menu() 146 return 147 148 self.type_number[instance.id] -= 1 149 if self.type_number[instance.id] == 0: 150 del self.type_number[instance.id] 151 # if one type of units dies, schedule refresh 152 self.schedule_unit_widget_refresh() 153 154 # if one type of units is left, any removal would mean refresh 155 if len(self.type_number) == 1: 156 self.schedule_unit_widget_refresh() 157 158 if self.stance_unit_number == 0: 159 self.hide_stance_widget()
160
161 - def show_stance_widget(self):
162 stance_widget = load_uh_widget('stancewidget.xml') 163 self.widget.findChild(name='stance').addChild(stance_widget) 164 self.toggle_stance() 165 events = {i.NAME: Callback(self.set_stance, i) for i in DEFAULT_STANCES} 166 self.widget.mapEvents(events)
167
168 - def hide_stance_widget(self):
169 Scheduler().rem_all_classinst_calls(self) 170 self.widget.findChild(name='stance').removeAllChildren()
171
172 - def set_stance(self, stance):
173 for i in self.instances: 174 if hasattr(i, 'stance'): 175 SetStance(i, stance).execute(i.session) 176 self.toggle_stance()
177
178 - def toggle_stance(self):
179 """ 180 Toggles the stance. Assumes at least one stance unit is selected. 181 """ 182 for stance in DEFAULT_STANCES: 183 self.widget.findChild(name=stance.NAME).set_inactive() 184 # get first unit stance 185 stance_units = [u for u in self.instances if hasattr(u, "stance")] 186 stance = stance_units[0].stance 187 for unit in stance_units[1:]: 188 if unit.stance != stance: 189 # not all have the same stance, toggle none 190 return 191 self.widget.findChild(name=stance.NAME).set_active()
192 193
194 -class UnitEntry:
195 - def __init__(self, instances, show_number=True):
196 self.log = logging.getLogger("gui.tabs") 197 self.instances = instances 198 self.widget = load_uh_widget("unit_entry_widget.xml") 199 # get the icon of the first instance 200 i = instances[0] 201 if i.id < UNITS.DIFFERENCE_BUILDING_UNIT_ID: 202 # A building. Generate dynamic thumbnail from its action set. 203 imgs = list(ActionSetLoader.get_set(i._action_set_id).items())[0][1] 204 thumbnail = list(imgs[45].keys())[0] 205 else: 206 # Units use manually created thumbnails because those need to be 207 # precise and recognizable in combat situations. 208 thumbnail = self.get_unit_thumbnail(i.id) 209 self.widget.findChild(name="unit_button").up_image = thumbnail 210 if show_number: 211 self.widget.findChild(name="instance_number").text = str(len(self.instances)) 212 # only two callbacks are needed so drop unwanted changelistener inheritance 213 for i in instances: 214 if not i.has_remove_listener(Callback(self.on_instance_removed, i)): 215 i.add_remove_listener(Callback(self.on_instance_removed, i)) 216 health_component = i.get_component(HealthComponent) 217 if not health_component.has_damage_dealt_listener(self.draw_health): 218 health_component.add_damage_dealt_listener(self.draw_health) 219 self.draw_health()
220
221 - def get_unit_thumbnail(self, unit_id):
222 """Returns path of the thumbnail icon for unit with id *unit_id*.""" 223 template = "content/gui/icons/thumbnails/{unit_id}.png" 224 path = template.format(unit_id=unit_id) 225 try: 226 Icon(image=path) 227 except fife.NotFound: 228 self.log.warning('Missing unit thumbnail {0}'.format(path)) 229 path = template.format(unit_id='unknown_unit') 230 return path
231
232 - def on_instance_removed(self, instance):
233 self.instances.remove(instance) 234 instance.discard_remove_listener(Callback(self.on_instance_removed, instance)) 235 health_component = instance.get_component(HealthComponent) 236 if health_component.has_damage_dealt_listener(self.draw_health): 237 health_component.remove_damage_dealt_listener(self.draw_health) 238 239 if self.instances: 240 self.widget.findChild(name="instance_number").text = str(len(self.instances))
241
242 - def draw_health(self, caller=None):
243 health = 0 244 max_health = 0 245 for instance in self.instances: 246 health_component = instance.get_component(HealthComponent) 247 health += health_component.health 248 max_health += health_component.max_health 249 health_ratio = float(health) / max_health 250 container = self.widget.findChild(name="main_container") 251 health_bar = self.widget.findChild(name="health") 252 health_bar.position = (health_bar.position[0], int((1 - health_ratio) * container.height))
253
254 - def remove(self):
255 """ 256 Clears all the listeners in instances 257 """ 258 for instance in self.instances: 259 instance.discard_remove_listener(Callback(self.on_instance_removed, instance)) 260 health_component = instance.get_component(HealthComponent) 261 if health_component.has_damage_dealt_listener(self.draw_health): 262 health_component.remove_damage_dealt_listener(self.draw_health)
263