class SmallProductionOverviewTab(ProductionOverviewTab): """Only display productions for which we have a related 'field' in range. Requires the building class using this tab to implement get_providers(). """ widget = 'overview_farm.xml' helptext = LazyT("Production overview") production_line_gui_xml = "overview_farmproductionline.xml" # the farm uses small buttons ACTIVE_PRODUCTION_ANIM_DIR = "content/gui/images/animations/cogs/small" BUTTON_BACKGROUND = "content/gui/images/buttons/msg_button_small.png" def get_displayed_productions(self): possible_res = { res for field in self.instance.get_providers() for res in field.provided_resources } all_farm_productions = self.instance.get_component( Producer).get_productions() productions = { p for p in all_farm_productions for res in p.get_consumed_resources().keys() if res in possible_res } return sorted(productions, key=operator.methodcaller('get_production_line_id'))
class MainSquareOverviewTab(AccountTab): helptext = LazyT('Main square overview') def init_widget(self): super(MainSquareOverviewTab, self).init_widget() self.widget.child_finder('headline').text = self.settlement.get_component(NamedComponent).name self.widget.child_finder('headline').helptext = T('Click to change the name of your settlement')
def test_gettext_lazy(self): text = LazyT('McAvoy or Stewart? These timelines are confusing.') self.assertEqual(str(text), 'McAvoy or Stewart? These timelines are confusing.') change_language('de') self.assertEqual(str(text), 'McAvoy oder Stewart? Diese Zeitlinien sind verwirrend.')
class InventoryTab(TabInterface): widget = 'island_inventory.xml' icon_path = 'icons/tabwidget/common/inventory' helptext = LazyT("Settlement inventory") def __init__(self, instance=None): self.instance = instance super(InventoryTab, self).__init__() def init_widget(self): self.widget.child_finder('inventory').init( self.instance.session.db, self.instance.get_component(StorageComponent).inventory) def refresh(self): """This function is called by the TabWidget to redraw the widget.""" self.widget.child_finder('inventory').update() def show(self): # run once now ExtScheduler().add_new_object(self.refresh, self, run_in=0, loops=1) # and every sec later ExtScheduler().add_new_object(self.refresh, self, run_in=1, loops=-1) super(InventoryTab, self).show() def hide(self): ExtScheduler().rem_call(self, self.refresh) super(InventoryTab, self).hide()
class TowerOverviewTab(OverviewTab): # defensive tower widget = 'overview_tower.xml' helptext = LazyT("Tower overview") def init_widget(self): super(TowerOverviewTab, self).init_widget() self.widget.findChild(name="headline").text = self.instance.settlement.get_component(NamedComponent).name
class EnemyWarehouseOverviewTab(OverviewTab): widget = 'overview_enemywarehouse.xml' helptext = LazyT("Warehouse overview") def init_widget(self): super().init_widget() self.widget.findChild( name="headline").text = self.instance.settlement.get_component( NamedComponent).name def refresh(self): settlement = self.instance.settlement self.widget.findChild(name="headline").text = settlement.get_component( NamedComponent).name selling_inventory = self.widget.findChild(name='selling_inventory') selling_inventory.init( self.instance.session.db, settlement.get_component(StorageComponent).inventory, settlement.get_component(TradePostComponent).sell_list, True) buying_inventory = self.widget.findChild(name='buying_inventory') buying_inventory.init( self.instance.session.db, settlement.get_component(StorageComponent).inventory, settlement.get_component(TradePostComponent).buy_list, False) super().refresh()
class TradeShipOverviewTab(ShipOverviewTab): widget = 'overview_trade_ship.xml' icon_path = 'icons/tabwidget/ship/ship_inv' helptext = LazyT("Ship overview") def _refresh_discard_resources(self): if self.ship_inv.get_sum_of_stored_resources() == 0: self.widget.findChild(name='discard_res_bg').set_inactive() self.widget.findChild(name='discard_res').set_inactive() else: self.widget.findChild(name='discard_res_bg').set_active() self.widget.findChild(name='discard_res').set_active() def _discard_resources(self): self.ship_inv.reset_all() self.widget.child_finder('inventory').update() def refresh(self): super(TradeShipOverviewTab, self).refresh() events = { 'discard_res/mouseClicked': Callback(self._discard_resources) } self.widget.mapEvents(events) self._refresh_discard_resources() super(TradeShipOverviewTab, self).refresh()
class BarracksSwordmanTab(BarracksSelectTab): icon_path = 'icons/tabwidget/barracks/swordman' helptext = LazyT("Swordman") groundunits = [ (UNITS.SWORDSMAN, PRODUCTIONLINES.SWORDSMAN), ]
def test_gettext_lazy(): text = LazyT('McAvoy or Stewart? These timelines are confusing.') assert str(text) == 'McAvoy or Stewart? These timelines are confusing.' change_language('de') assert str(text) == 'McAvoy oder Stewart? Diese Zeitlinien sind verwirrend.'
class EnemyShipOverviewTab(OverviewTab): widget = 'overview_enemyunit.xml' icon_path = 'icons/tabwidget/ship/ship_inv' helptext = LazyT("Ship overview") def init_widget(self): super(EnemyShipOverviewTab, self).init_widget() self.widget.findChild(name="headline").text = self.instance.owner.name
class BoatbuilderFisherTab(BoatbuilderSelectTab): icon_path = 'icons/tabwidget/boatbuilder/fisher' helptext = LazyT("Fisher boats") ships = [ #(UNITS.FISHER_BOAT, PRODUCTIONLINES.FISHING_BOAT), #(UNITS.CUTTER, PRODUCTIONLINES.xxx), #(UNITS.HERRING_FISHER, PRODUCTIONLINES.xxx), #(UNITS.WHALER, PRODUCTIONLINES.xxx), ] # type: List[Tuple[int, int]]
class SignalFireOverviewTab(OverviewTab): widget = 'overview_signalfire.xml' helptext = LazyT("Overview") def init_widget(self): super(SignalFireOverviewTab, self).init_widget() action_set = ActionSetLoader.get_set(self.instance._action_set_id) action_gfx = list(action_set.items())[0][1] image = list(action_gfx[45].keys())[0] self.widget.findChild(name="building_image").image = image
class BoatbuilderTradeTab(BoatbuilderSelectTab): icon_path = 'icons/tabwidget/boatbuilder/trade' helptext = LazyT("Trade boats") ships = [ (UNITS.HUKER_SHIP, PRODUCTIONLINES.HUKER), #(UNITS.COURIER_BOAT, PRODUCTIONLINES.xxx), #(UNITS.SMALL_MERCHANT, PRODUCTIONLINES.xxx), #(UNITS.BIG_MERCHANT, PRODUCTIONLINES.xxx), ]
class BoatbuilderWar2Tab(BoatbuilderSelectTab): icon_path = 'icons/tabwidget/boatbuilder/war2' helptext = LazyT("War ships") ships = [ #(UNITS.GALLEY, PRODUCTIONLINES.GALLEY), #(UNITS.BIG_GUNBOAT, PRODUCTIONLINES.BIG_GUNBOAT), #(UNITS.CORVETTE, PRODUCTIONLINES.CORVETTE), (UNITS.FRIGATE, PRODUCTIONLINES.FRIGATE), ]
class BoatbuilderWar1Tab(BoatbuilderSelectTab): icon_path = 'icons/tabwidget/boatbuilder/war1' helptext = LazyT("War boats") ships = [ #(UNITS.SMALL_GUNBOAT, PRODUCTIONLINES.SMALL_GUNBOAT), #(UNITS.NAVAL_CUTTER, PRODUCTIONLINES.NAVAL_CUTTER), #(UNITS.BOMBADIERE, PRODUCTIONLINES.BOMBADIERE), #(UNITS.SLOOP_O_WAR, PRODUCTIONLINES.SLOOP_O_WAR), ] # type: List[Tuple[int, int]]
class InventoryFullStatus(StatusIcon): priority = 1200 icon = 'as_inventory_full+idle+45' helptext = LazyT("The inventory of this building is full.") def __init__(self, instance, reslist): """ @param reslist: list of integers describing the resources """ super(InventoryFullStatus, self).__init__(instance) self.reslist = reslist
class BoatbuilderConfirmTab(ProducerOverviewTabBase): widget = 'boatbuilder_confirm.xml' helptext = LazyT("Confirm order") def init_widget(self): super().init_widget() events = {'create_unit': self.start_production} self.widget.mapEvents(events) def start_production(self): AddProduction(self.producer, 15).execute(self.instance.session)
def __init__(self, instance): self.max_inhabitants = instance.session.db.get_tier_inhabitants_max( self.__class__.LEVEL) self.min_inhabitants = instance.session.db.get_tier_inhabitants_min( self.__class__.LEVEL) self.helptext = LazyT( instance.session.db.get_settler_name(self.__class__.LEVEL)) icon_path = 'icons/tabwidget/mainsquare/inhabitants{tier}'.format( tier=self.__class__.LEVEL) super().__init__(instance=instance, icon_path=icon_path)
def _init_settings(self): """Init the settings with the stored values.""" languages = list(find_available_languages().keys()) language_names = [LANGUAGENAMES[x] for x in sorted(languages)] fps = {0: LazyT("Disabled"), 30: 30, 45: 45, 60: 60, 90: 90, 120: 120} FIFE = SETTINGS.FIFE_MODULE UH = SETTINGS.UH_MODULE def get_resolutions(): return get_screen_resolutions(self._settings.get(FIFE, 'ScreenResolution')) self._options = [ # Graphics/Sound/Input Setting(FIFE, 'ScreenResolution', 'screen_resolution', get_resolutions, restart=True), Setting(FIFE, 'FullScreen', 'enable_fullscreen', restart=True), Setting(FIFE, 'FrameLimit', 'fps_rate', fps, restart=True, callback=self._apply_FrameLimit), Setting(UH, 'VolumeMusic', 'volume_music', callback=self._apply_VolumeMusic, on_change=self._on_slider_changed), Setting(UH, 'VolumeEffects', 'volume_effects', callback=self._apply_VolumeEffects, on_change=self._on_slider_changed), Setting(FIFE, 'PlaySounds', 'enable_sound', callback=self._apply_PlaySounds), Setting(UH, 'EdgeScrolling', 'edgescrolling'), Setting(UH, 'CursorCenteredZoom', 'cursor_centered_zoom'), Setting(UH, 'MiddleMousePan', 'middle_mouse_pan'), Setting(FIFE, 'MouseSensitivity', 'mousesensitivity', restart=True, on_change=self._on_slider_changed), # Game Setting(UH, 'AutosaveInterval', 'autosaveinterval', on_change=self._on_slider_changed), Setting(UH, 'AutosaveMaxCount', 'autosavemaxcount', on_change=self._on_slider_changed), Setting(UH, 'QuicksaveMaxCount', 'quicksavemaxcount', on_change=self._on_slider_changed), Setting(UH, 'Language', 'uni_language', language_names, callback=self._apply_Language, on_change=self._on_Language_changed), Setting(UH, 'MinimapRotation', 'minimaprotation'), Setting(UH, 'UninterruptedBuilding', 'uninterrupted_building'), Setting(UH, 'AutoUnload', 'auto_unload'), Setting(UH, 'DebugLog', 'debug_log', callback=self._apply_DebugLog), Setting(UH, 'ShowResourceIcons', 'show_resource_icons'), Setting(UH, 'ScrollSpeed', 'scrollspeed', on_change=self._on_slider_changed), Setting(UH, 'QuotesType', 'quotestype', QUOTES_SETTINGS), Setting(UH, 'NetworkPort', 'network_port', callback=self._apply_NetworkPort), ] self._fill_widgets() # key configuration self.hotkey_interface = HotkeyConfiguration() number = self.sections.index(('hotkeys_settings', T('Hotkeys'))) self.page_widgets[number].removeAllChildren() self.page_widgets[number].addChild(self.hotkey_interface.widget)
class BarrierOverviewTab(OverviewTab): widget = 'overview_barrier.xml' helptext = LazyT("Overview") def init_widget(self): super(BarrierOverviewTab, self).init_widget() action_set = ActionSetLoader.get_sets()[self.instance._action_set_id] action_gfx = action_set.get('single', 'abcd') image = action_gfx[45].keys()[0] self.widget.findChild(name="building_image").image = image health_widget = self.widget.findChild(name='health') health_widget.init(self.instance) self.add_remove_listener(health_widget.remove)
class AccountTab(MainSquareTab): """Display basic income and expenses of a settlement""" widget = 'tab_account.xml' icon_path = 'icons/tabwidget/warehouse/account' helptext = LazyT("Account") def init_widget(self): super(AccountTab, self).init_widget() self.widget.mapEvents({ 'show_production_overview/mouseClicked': self.show_production_overview, }) # FIXME having to access the WindowManager this way is pretty ugly self._windows = self.instance.session.ingame_gui.windows self.prod_overview = ProductionOverview(self._windows, self.settlement) self.widget.child_finder('headline').text = self.settlement.get_component(NamedComponent).name self.widget.child_finder('headline').helptext = T('Click to change the name of your settlement') path = 'icons/widgets/cityinfo/settlement_{}'.format(self.settlement.owner.color.name) self.widget.child_finder('show_production_overview').path = path def show_production_overview(self): self._windows.toggle(self.prod_overview) def refresh(self): super(AccountTab, self).refresh() self.refresh_collector_utilization() taxes = self.settlement.cumulative_taxes running_costs = self.settlement.cumulative_running_costs buy_expenses = self.settlement.get_component(TradePostComponent).buy_expenses sell_income = self.settlement.get_component(TradePostComponent).sell_income balance = self.settlement.balance sign = '+' if balance >= 0 else '-' self.widget.child_finder('taxes').text = str(taxes) self.widget.child_finder('running_costs').text = str(running_costs) self.widget.child_finder('buying').text = str(buy_expenses) self.widget.child_finder('sale').text = str(sell_income) self.widget.child_finder('balance').text = str(sign + ' ' + str(abs(balance))) self.widget.child_finder('headline').text = self.settlement.get_component(NamedComponent).name rename = Callback(self.instance.session.ingame_gui.show_change_name_dialog, self.settlement) self.widget.mapEvents({'headline': rename}) def refresh_collector_utilization(self): if self.instance.has_component(CollectingComponent): utilization = int(round(self.instance.get_collector_utilization() * 100)) utilization = str(utilization) + '%' else: utilization = '---' self.widget.findChild(name="collector_utilization").text = utilization
class GroundUnitOverviewTab(OverviewTab): widget = 'overview_groundunit.xml' helptext = LazyT("Unit overview") has_stance = True def init_widget(self): super(GroundUnitOverviewTab, self).init_widget() health_widget = self.widget.findChild(name='health') health_widget.init(self.instance) self.add_remove_listener(health_widget.remove) weapon_storage_widget = self.widget.findChild(name='weapon_storage') weapon_storage_widget.init(self.instance) self.add_remove_listener(weapon_storage_widget.remove)
def _strip_translation_marks(self, string): """Converts object translation `string` to translated object for in-game display. Object properties supposed to be translated are recognized by the (subsequently stripped) leading `_ `. If `string` is supposed to be translated, returns lazy translation object of `string`. If `string` is not supposed to be translated, returns `string` unchanged. If `string` is None (not defined or empty in yaml), returns empty unicode. """ if not string: return u'' if string.startswith("_ "): return LazyT(string[2:]) else: return string
class LogBook(PickBeltWidget, Window): """Implementation of the logbook as described here: http://wiki.unknown-horizons.org/w/Message_System It displays longer messages, which are essential for scenarios. Headings can be specified for each entry. """ log = logging.getLogger('gui.widgets.logbook') widget_xml = 'captains_log.xml' page_pos = (170, 38) sections = (('logbook', LazyT('Logbook')), ('statistics', LazyT('Statistics')), ('chat_overview', LazyT('Chat'))) def __init__(self, session, windows): self.statistics_index = [i for i, sec in self.sections].index('statistics') self.logbook_index = [i for i, sec in self.sections].index('logbook') self._page_ids = {} # dict mapping self._cur_entry to message.msgcount super().__init__() self.session = session self._windows = windows self._parameters = [] # list of lists of all parameters added to a logbook page self._message_log = [] # list of all messages that have been displayed self._messages_to_display = [] # list messages to display on page close self._displayed_messages = [] # list of messages that were already displayed self._cur_entry = 0 # remember current location; 0 to len(messages)-1 self._hiding_widget = False # True if and only if the widget is currently in the process of being hidden self.stats_visible = None self.last_stats_widget = 'players' self.current_mode = 0 # used to determine if the logbook is on the statistics page self._init_gui() # self.add_captainslog_entry([ # ['Headline', "Heading"], # ['Image', "content/gui/images/background/hr.png"], # ['Label', "Welcome to the Captain's log"], # ['Label', "\n\n"], # ]) # test code def _init_gui(self): """Initial gui setup for all subpages accessible through pickbelts.""" self._gui = self.get_widget() self._gui.mapEvents({ OkButton.DEFAULT_NAME : self._windows.close, 'backwardButton' : Callback(self._scroll, -2), 'forwardButton' : Callback(self._scroll, 2), 'stats_players' : Callback(self.show_statswidget, widget='players'), 'stats_settlements' : Callback(self.show_statswidget, widget='settlements'), 'stats_ships' : Callback(self.show_statswidget, widget='ships'), 'chatTextField' : self._send_chat_message, }) # stuff in the game message / chat history subwidget self.textfield = self._gui.findChild(name="chatTextField") self.textfield.capture(self._chatfield_onfocus, 'mouseReleased', 'default') self.chatbox = self._gui.findChild(name="chatbox") self.messagebox = self._gui.findChild(name="game_messagebox") #self._display_chat_history() # initially print all loaded messages #self._display_message_history() # these buttons flip pages in the captain's log if there are more than two self.backward_button = self._gui.findChild(name="backwardButton") self.forward_button = self._gui.findChild(name="forwardButton") self._redraw_captainslog() def update_view(self, number=0): """ update_view from PickBeltWidget, cleaning up the logbook subwidgets """ self.current_mode = number # self.session might not exist yet during callback setup for pickbelts if hasattr(self, 'session'): self._hide_statswidgets() if self.statistics_index == number: self.show_statswidget(self.last_stats_widget) super().update_view(number) def save(self, db): db("INSERT INTO logbook(widgets) VALUES(?)", json.dumps(self._parameters)) for message in self._message_log: db("INSERT INTO logbook_messages(message) VALUES(?)", message) db("INSERT INTO metadata(name, value) VALUES(?, ?)", "logbook_cur_entry", self._cur_entry) def load(self, db): db_data = db("SELECT widgets FROM logbook") widget_list = json.loads(db_data[0][0] if db_data else "[]") for widgets in widget_list: self.add_captainslog_entry(widgets, show_logbook=False) for msg in db("SELECT message FROM logbook_messages"): self._message_log.append(msg[0]) # each line of the table is one tuple # wipe self._messages_to_display on load, otherwise all previous messages get displayed self._messages_to_display = [] self._displayed_messages = [] value = db('SELECT value FROM metadata WHERE name = "logbook_cur_entry"') if (value and value[0] and value[0][0]): self.set_cur_entry(int(value[0][0])) # this also redraws self.display_messages() def show(self, msg_id=None): if not hasattr(self, '_gui'): self._init_gui() if msg_id: self._cur_entry = self._page_ids[msg_id] if not self.is_visible(): self._gui.show() self._redraw_captainslog() if self.current_mode == self.statistics_index: self.show_statswidget(self.last_stats_widget) def display_messages(self): """Display all messages in self._messages_to_display and map the to the current logbook page""" for message in self._messages_to_display: if message in self._displayed_messages: continue for msg_id in show_message(self.session, "logbook", message): self._page_ids[msg_id] = self._cur_entry self._displayed_messages.append(message) def hide(self): if not self._hiding_widget: self._hiding_widget = True self._hide_statswidgets() self._gui.hide() self._hiding_widget = False self.display_messages() self._message_log.extend(self._messages_to_display) self._messages_to_display = [] # Make sure the game is unpaused always and in any case UnPauseCommand(suggestion=False).execute(self.session) def is_visible(self): return hasattr(self, '_gui') and self._gui.isVisible() def _redraw_captainslog(self): """Redraws gui. Necessary when current message has changed.""" if self._parameters: # there is something to display if this has items self._display_parameters_on_page(self._parameters[self._cur_entry], 'left') if self._cur_entry + 1 < len(self._parameters): # check for content on right page self._display_parameters_on_page(self._parameters[self._cur_entry + 1], 'right') else: self._display_parameters_on_page([], 'right') # display empty page else: self._display_parameters_on_page([ ['Headline', T("Emptiness")], ['Image', "content/gui/images/background/hr.png"], ['Label', "\n\n"], ['Label', T('There is nothing written in your logbook yet!')], ], 'left') self.backward_button.set_active() self.forward_button.set_active() if not self._parameters or self._cur_entry == 0: self.backward_button.set_inactive() if not self._parameters or self._cur_entry >= len(self._parameters) - 2: self.forward_button.set_inactive() self._gui.adaptLayout() ######## # LOGBOOK SUBWIDGET ######## def parse_logbook_item(self, parameter): # Some error checking for widgets that are to be loaded. # This happens, for example, with outdated YAML stored in old # scenario savegames. Instead of crashing, display nothing. def _icon(image): try: # Pychan can only use str objects as file path. # json.loads() however returns unicode. return Icon(image=str(image)) except fife.NotFound: return None def _label(text, font='default'): try: return Label(text=str(text), wrap_text=True, min_size=(325, 0), max_size=(325, 1024), font=font) except InitializationError: return None if parameter and parameter[0]: # allow empty Labels parameter_type = parameter[0] if isinstance(parameter, str): add = _label(parameter) elif parameter_type == 'Label': add = _label(parameter[1]) elif parameter_type == 'Image': add = _icon(parameter[1]) elif parameter_type == 'Gallery': add = HBox() for image in parameter[1]: new_icon = _icon(image) if new_icon is not None: add.addChild(new_icon) elif parameter_type == 'Headline': add = HBox() is_not_last_headline = self._parameters and self._cur_entry is not None and self._cur_entry < (len(self._parameters) - 2) if is_not_last_headline: add.addChild(_icon("content/gui/images/tabwidget/done.png")) add.addChild(_label(parameter[1], font='headline')) elif parameter_type == 'BoldLabel': add = _label(parameter[1], font='default_bold') elif parameter_type == 'Message': add = None # parameters are re-read on page reload. # duplicate_message stops messages from # being duplicated on page reload. message = parameter[1] # message is already going to be displayed or has been displayed # before (e.g. re-opening older logbook pages) duplicate_message = (message in self._messages_to_display or message in self._message_log) if not duplicate_message: self._messages_to_display.append(message) # the new message has not been displayed else: self.log.warning('Unknown parameter type %s in parameter %s', parameter[0], parameter) add = None return add def _display_parameters_on_page(self, parameters, page): """ @param parameters: parameter list, cf. docstring of add_captainslog_entry @param page: 'left' or 'right' """ parameterbox = self._gui.findChild(name="custom_widgets_{page}".format(page=page)) parameterbox.removeAllChildren() for parameter_definition in parameters: add = self.parse_logbook_item(parameter_definition) if add is not None: parameterbox.addChild(add) def add_captainslog_entry(self, parameters, show_logbook=True): """Adds an entry to the logbook VBoxes consisting of a parameter list. Check e.g. content/scenarios/tutorial_en.yaml for real-life usage. @param parameters: Each item in here is a list like the following: [Label, "Awesome text to be displayed as a label"] "Shortcut notation for a Label" [Headline, "Label to be styled as headline (in small caps)"] [BoldLabel, "Like Label but with bold font, use to highlight lines"] [Image, "content/gui/images/path/to/the/file.png"] [Gallery, ["/path/1.png", "/path/file.png", "/file/3.png"]] [Message, "Text to display as a notification on logbook close"] [Pagebreak] """ #TODO last line of message text sometimes get eaten. Ticket #535 def _split_on_pagebreaks(parameters): """This black magic splits the parameter list on each ['Pagebreak'] >> [['a','a'], ['b','b'], ['Pagebreak'], ['c','c'], ['d','d']] >>>> into [[['a', 'a'], ['b', 'b']], [['c', 'c'], ['d', 'd']]] #TODO n successive pagebreaks should insert (n-1) blank pages (currently 0 are inserted) """ return [list(l[1]) for l in groupby(parameters, lambda x: x != ['Pagebreak']) if l[0]] # If a scenario goal has been completed, remove the corresponding message for message in self._displayed_messages: self.session.ingame_gui.message_widget.remove(message) self._displayed_messages = [] # Reset displayed messages for parameter_list in _split_on_pagebreaks(parameters): self._parameters.append(parameter_list) for parameter_definition in parameter_list: self.parse_logbook_item(parameter_definition) # if a new entry contains more than one page, we want to display the first # unread message. note that len(parameters) starts at 1 and _cur_entry at 0. # position always refers to the left page, so only multiples of 2 are valid len_old = len(self._parameters) - len(_split_on_pagebreaks(parameters)) if len_old % 2 == 1: # uneven amount => empty last page, space for 1 new self._cur_entry = len_old - 1 else: # even amount => all pages filled. we could display two new messages self._cur_entry = len_old if show_logbook and hasattr(self, "_gui"): self._redraw_captainslog() self._windows.open(self) self.show_logbookwidget() def clear(self): """Remove all entries""" self._parameters = [] self._cur_entry = 0 def get_cur_entry(self): return self._cur_entry def set_cur_entry(self, cur_entry): if cur_entry < 0 or (cur_entry >= len(self._parameters) and len(self._parameters) != 0): raise ValueError("ERROR: Logbook entry out of Logbook bounds. This should never happen.") self._cur_entry = cur_entry self._redraw_captainslog() def _scroll(self, direction): """Scroll back or forth one message. @param direction: -1 or 1""" if not self._parameters: return new_cur = self._cur_entry + direction if new_cur < 0 or new_cur >= len(self._parameters): return # invalid scroll self._cur_entry = new_cur AmbientSoundComponent.play_special('flippage') self._redraw_captainslog() def show_logbookwidget(self): """Shows logbook with Logbook page selected""" if self.current_mode != self.logbook_index: self.update_view(self.logbook_index) ######## # STATISTICS SUBWIDGET ######## # #TODO list: # [ ] Extract this stuff to extra widget class that properly handles all the # hide and save calls # [ ] fix stats show/hide mess: how is update_view called before self.__init__ # [ ] save last shown stats widget and re-show it when clicking on Statistics # [ ] semantic distinction between general widget and subwidgets (log, stats) # ######## def show_statswidget(self, widget='players'): """Shows logbook with Statistics page selected""" if self.current_mode != self.statistics_index: self.update_view(self.statistics_index) self._hide_statswidgets() if widget: getattr(self, '_show_{widget}'.format(widget=widget))() self.stats_visible = widget self.last_stats_widget = widget def toggle_stats_visibility(self, widget='players'): """ Only hides logbook if hotkey of current stats selection pressed. Otherwise, switch to displaying the new widget instead of hiding. @param widget: 'players' or 'settlements' or 'ships' """ # we're treating every statswidget as a separate window, so if the stats change, # close the logbook and reopen it with a different active widget if self.stats_visible != widget: if self.stats_visible: self._windows.close() self._windows.open(self) self.show_statswidget(widget=widget) else: self._windows.close() def _show_ships(self): self.session.ingame_gui.players_ships.show() def _show_settlements(self): self.session.ingame_gui.players_settlements.show() def _show_players(self): self.session.ingame_gui.players_overview.show() def _hide_statswidgets(self): statswidgets = [ self.session.ingame_gui.players_overview, self.session.ingame_gui.players_ships, self.session.ingame_gui.players_settlements, ] for statswidget in statswidgets: # we don't care which one is shown currently (if any), just hide all of them statswidget.hide() self.stats_visible = None ######## # MESSAGE AND CHAT HISTORY SUBWIDGET ######## # #TODO list: # [ ] use message bus to check for new updates # [ ] only display new message on update, not reload whole history # [x] update message history on new game messages. not on sending a chat line # [ ] implement word wrapping for message history display # ######## def _send_chat_message(self): """Sends a chat message. Called when user presses enter in the input field""" msg = self.textfield.text if msg: Chat(msg).execute(self.session) self.textfield.text = '' self._display_chat_history() def display_message_history(self): self.messagebox.items = [] messages = self.session.ingame_gui.message_widget.active_messages + \ self.session.ingame_gui.message_widget.archive for msg in sorted(messages, key=lambda m: m.created): if msg.id != 'CHAT': # those get displayed in the chat window instead self.messagebox.items.append(msg.message) self.messagebox.selected = len(self.messagebox.items) - 1 # scroll to bottom def _display_chat_history(self): self.chatbox.items = [] messages = self.session.ingame_gui.message_widget.chat for msg in sorted(messages, key=lambda m: m.created): self.chatbox.items.append(msg.message) self.chatbox.selected = len(self.chatbox.items) - 1 # scroll to bottom def _chatfield_onfocus(self): """Removes text in chat input field when it gets focused.""" self.textfield.text = '' self.textfield.capture(None, 'mouseReleased', 'default')
class BuildRelatedTab(OverviewTab): """ Adds a special tab to each production building with at least one entry in the table related_buildings. This tab acts as modified build menu tab and only displays those buildings actually related to the selected building. Examples: tree for lumberjack; pavilion, school, etc. for inhabitants. """ widget = 'related_buildings.xml' icon_path = 'icons/tabwidget/production/related' helptext = LazyT("Build related buildings") template_gui_xml = 'related_buildings_container.xml' def refresh(self): """ This function is called by the TabWidget to redraw the widget. """ # remove old data parent_container = self.widget.child_finder('related_buildings') while parent_container.children: parent_container.removeChild(parent_container.children[0]) # load all related buildings from DB building_ids = self.instance.session.db.get_related_building_ids_for_menu( self.instance.id) sorted_ids = sorted([(b, Entities.buildings[b].settler_level) for b in building_ids], key=lambda x: x[1]) container = self.__get_new_container() for i, (building_id, level) in enumerate(sorted_ids): if level > self.instance.owner.settler_level: break button = self._create_build_buttons(building_id, container) # check whether to start new line (currently only 4 fit per line) if i and i % 4 == 0: parent_container.addChild(container) container = self.__get_new_container() container.findChild(name="build_button_container").addChild(button) button_bg = Icon( image="content/gui/images/buttons/buildmenu_button_bg.png") container.findChild( name="build_button_bg_container").addChild(button_bg) # Still need to add last container parent_container.addChild(container) super(BuildRelatedTab, self).refresh() def __get_new_container(self): """ Loads a background container xml file. Returns the loaded widget. """ gui = load_uh_widget(self.template_gui_xml) return gui.findChild(name="buildings_container") def _create_build_buttons(self, building_id, container): # {{mode}} in double braces because it is replaced as a second step building_type = Entities.buildings[building_id] build_button = ImageButton(name="build{id}".format(id=building_id), helptext=building_type.get_tooltip()) build_button.path = "icons/buildmenu/{id:03d}".format(id=building_id) build_button.capture(Callback(self.build_related, building_id)) return build_button def build_related(self, building_id): self.hide() # deselect all for instance in self.instance.session.selected_instances: instance.get_component(SelectableComponent).deselect() self.instance.session.selected_instances.clear() self.instance.session.ingame_gui.set_cursor( 'building', Entities.buildings[building_id], ship=None, build_related=self.instance)
class SettingsDialog(PickBeltWidget, Window): """Widget for Options dialog with pickbelt style pages""" widget_xml = 'settings.xml' sections = ( ('graphics_settings', LazyT('Graphics')), ('hotkeys_settings', LazyT('Hotkeys')), ('game_settings', LazyT('Game')), ) def __init__(self, windows): Window.__init__(self, windows) PickBeltWidget.__init__(self) self._settings = horizons.globals.fife._setting self.widget.mapEvents({ 'okButton': self.apply_settings, 'defaultButton': self.set_defaults, 'cancelButton': self._windows.close, }) def _init_settings(self): """Init the settings with the stored values.""" languages = list(find_available_languages().keys()) language_names = [LANGUAGENAMES[x] for x in sorted(languages)] fps = {0: LazyT("Disabled"), 30: 30, 45: 45, 60: 60, 90: 90, 120: 120} FIFE = SETTINGS.FIFE_MODULE UH = SETTINGS.UH_MODULE def get_resolutions(): return get_screen_resolutions( self._settings.get(FIFE, 'ScreenResolution')) self._options = [ # Graphics/Sound/Input Setting(FIFE, 'ScreenResolution', 'screen_resolution', get_resolutions, restart=True), Setting(FIFE, 'FullScreen', 'enable_fullscreen', restart=True), Setting(FIFE, 'FrameLimit', 'fps_rate', fps, restart=True, callback=self._apply_FrameLimit), Setting(UH, 'VolumeMusic', 'volume_music', callback=self._apply_VolumeMusic, on_change=self._on_slider_changed), Setting(UH, 'VolumeEffects', 'volume_effects', callback=self._apply_VolumeEffects, on_change=self._on_slider_changed), Setting(FIFE, 'PlaySounds', 'enable_sound', callback=self._apply_PlaySounds), Setting(UH, 'EdgeScrolling', 'edgescrolling'), Setting(UH, 'CursorCenteredZoom', 'cursor_centered_zoom'), Setting(UH, 'MiddleMousePan', 'middle_mouse_pan'), Setting(FIFE, 'MouseSensitivity', 'mousesensitivity', restart=True, on_change=self._on_slider_changed), # Game Setting(UH, 'AutosaveInterval', 'autosaveinterval', on_change=self._on_slider_changed), Setting(UH, 'AutosaveMaxCount', 'autosavemaxcount', on_change=self._on_slider_changed), Setting(UH, 'QuicksaveMaxCount', 'quicksavemaxcount', on_change=self._on_slider_changed), Setting(UH, 'Language', 'uni_language', language_names, callback=self._apply_Language, on_change=self._on_Language_changed), Setting(UH, 'UninterruptedBuilding', 'uninterrupted_building'), Setting(UH, 'AutoUnload', 'auto_unload'), Setting(UH, 'DebugLog', 'debug_log', callback=self._apply_DebugLog), Setting(UH, 'ShowResourceIcons', 'show_resource_icons'), Setting(UH, 'ScrollSpeed', 'scrollspeed', on_change=self._on_slider_changed), Setting(UH, 'QuotesType', 'quotestype', QUOTES_SETTINGS), Setting(UH, 'NetworkPort', 'network_port', callback=self._apply_NetworkPort), ] self._fill_widgets() # key configuration self.hotkey_interface = HotkeyConfiguration() number = self.sections.index(('hotkeys_settings', T('Hotkeys'))) self.page_widgets[number].removeAllChildren() self.page_widgets[number].addChild(self.hotkey_interface.widget) def show(self): self._init_settings() self.widget.show() def hide(self): self.widget.hide() def restart_promt(self): headline = T("Restart required") message = T( "Some of your changes require a restart of Unknown Horizons. Do you want to restart Unknown Horizons now?" ) if self._windows.open_popup(headline, message, show_cancel_button=True): return True def set_defaults(self): title = T("Restore default settings") msg = T("Restoring the default settings will delete all changes to the settings you made so far.") + \ " " + T("Do you want to continue?") if self._windows.open_popup(title, msg, show_cancel_button=True): self.hotkey_interface.reset_to_default() self._settings.set_defaults() self.restart_prompt() self._windows.close() def apply_settings(self): restart_required = False for entry in self._options: widget = self.widget.findChild(name=entry.widget_name) new_value = widget.getData() if callable(entry.initial_data): initial_data = entry.initial_data() else: initial_data = entry.initial_data if isinstance(initial_data, list): new_value = initial_data[new_value] elif isinstance(initial_data, dict): new_value = list(initial_data.keys())[new_value] old_value = self._settings.get(entry.module, entry.name) # Store new setting self._settings.set(entry.module, entry.name, new_value) # If setting changed, allow applying of new value and sanity checks if new_value != old_value: if entry.restart: restart_required = True if entry.callback: entry.callback(old_value, new_value) self.hotkey_interface.save_settings() self._settings.apply() self._settings.save() if restart_required: if self.restart_promt(): horizons.globals.fife.engine.destroy() os.execv(sys.executable, [sys.executable] + sys.argv) self._windows.close() def _fill_widgets(self): for entry in self._options: value = self._settings.get(entry.module, entry.name) widget = self.widget.findChild(name=entry.widget_name) if entry.initial_data: if callable(entry.initial_data): initial_data = entry.initial_data() else: initial_data = entry.initial_data if isinstance(initial_data, dict): widget.setInitialData(list(initial_data.values())) value = list(initial_data.keys()).index(value) elif isinstance(initial_data, list): widget.setInitialData(initial_data) value = initial_data.index(value) else: widget.setInitialData(initial_data) widget.setData(value) if entry.on_change: cb = Callback(entry.on_change, widget) cb() widget.capture(cb) def _on_slider_changed(self, widget): """Callback for updating value label of a slider after dragging it. As the numeric values under the hood often do not represent mental models of what the setting achieves very well, depending on the setting in question we display a modified value, sometimes with a '%' suffix.""" value_label = self.widget.findChild(name=widget.name + '_value') value = { 'volume_music': lambda x: '{:d}%'.format(int(500 * x)), 'volume_effects': lambda x: '{:d}%'.format(int(200 * x)), 'mousesensitivity': lambda x: '{:.1f}x'.format(10 * x), 'autosaveinterval': lambda x: '{:.1f}'.format(x), 'autosavemaxcount': lambda x: '{:d}'.format(int(x)), 'quicksavemaxcount': lambda x: '{:d}'.format(int(x)), 'scrollspeed': lambda x: '{:.1f}'.format(x), }[widget.name](widget.value) value_label.text = value # callbacks for changes of settings def _apply_PlaySounds(self, old, new): horizons.globals.fife.sound.setup_sound() def _apply_VolumeMusic(self, old, new): horizons.globals.fife.sound.set_volume_bgmusic(new) def _apply_VolumeEffects(self, old, new): horizons.globals.fife.sound.set_volume_effects(new) def _apply_FrameLimit(self, old, new): # handling value 0 for framelimit to disable limiter if new == 0: self._settings.set(SETTINGS.FIFE_MODULE, 'FrameLimitEnabled', False) else: self._settings.set(SETTINGS.FIFE_MODULE, 'FrameLimitEnabled', True) def _apply_NetworkPort(self, old, new): """Sets a new value for client network port""" # port is saved as string due to pychan limitations try: # 0 is not a valid port, but a valid value here (used for default) parse_port(new) except ValueError: headline = T("Invalid network port") descr = T( "The port you specified is not valid. It must be a number between 1 and 65535." ) advice = T( "Please check the port you entered and make sure it is in the specified range." ) self._windows.open_error_popup(headline, descr, advice) # reset value and reshow settings dlg self._settings.set(SETTINGS.UH_MODULE, 'NetworkPort', "0") else: # port is valid try: if NetworkInterface() is None: NetworkInterface.create_instance() NetworkInterface().network_data_changed() except Exception as e: headline = T("Failed to apply new network settings.") descr = T( "Network features could not be initialized with the current configuration." ) advice = T( "Check the settings you specified in the network section.") if 0 < parse_port(new) < 1024: #i18n This is advice for players seeing a network error with the current config advice += " " + \ T("Low port numbers sometimes require special access privileges, try 0 or a number greater than 1024.") details = str(e) self._windows.open_error_popup(headline, descr, advice, details) def _apply_Language(self, old, new): language = LANGUAGENAMES.get_by_value(new) change_language(language) def _on_Language_changed(self, widget): value = widget.items[widget.getData()] language_code = LANGUAGENAMES.get_by_value(value) status_label = self.widget.findChild( name='language_translation_status') if not language_code or language_code == 'en': status_label.text = '' else: value = get_language_translation_stats(language_code) if value: status_label.text = T('Translation {percentage}% completed' ).format(percentage=value) else: status_label.text = '' def _apply_DebugLog(self, old, new): horizons.main.set_debug_log(new)
class SettlerOverviewTab(OverviewTab): widget = 'overview_settler.xml' helptext = LazyT("Settler overview") def init_widget(self): super(SettlerOverviewTab, self).init_widget() name = self.instance.settlement.get_component(NamedComponent).name self.widget.findChild(name="headline").text = name setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, self.instance.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = unicode(taxes) action_set = ActionSetLoader.get_set(self.instance._action_set_id) action_gfx = action_set.items()[0][1] image = action_gfx[45].keys()[0] self.widget.findChild(name="building_image").image = image def on_settler_level_change(self, message): assert isinstance(message, SettlerUpdate) setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, message.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = unicode(taxes) imgs = ActionSetLoader.get_set( self.instance._action_set_id).items()[0][1] self.widget.findChild(name="building_image").image = imgs[45].keys()[0] def show(self): super(SettlerOverviewTab, self).show() SettlerUpdate.subscribe(self.on_settler_level_change, sender=self.instance) def hide(self): SettlerUpdate.discard(self.on_settler_level_change, sender=self.instance) super(SettlerOverviewTab, self).hide() def refresh(self): image, helptext = get_happiness_icon_and_helptext( self.instance.happiness, self.instance.session) self.widget.child_finder('happiness_label').image = image self.widget.child_finder('happiness_label').helptext = helptext self.widget.child_finder( 'happiness').progress = self.instance.happiness self.widget.child_finder('inhabitants').text = u"%s/%s" % ( self.instance.inhabitants, self.instance.inhabitants_max) self.widget.child_finder('taxes').text = unicode( self.instance.last_tax_payed) self.update_consumed_res() name = self.instance.settlement.get_component(NamedComponent).name self.widget.findChild(name="headline").text = name events = { 'headline': Callback(self.instance.session.ingame_gui.show_change_name_dialog, self.instance.settlement) } self.widget.mapEvents(events) super(SettlerOverviewTab, self).refresh() def update_consumed_res(self): """Updates the container that displays the needed resources of the settler""" container = self.widget.findChild(name="needed_res") # remove icons from the container container.removeAllChildren() # create new ones resources = self.instance.get_currently_not_consumed_resources() for res in resources: icon = create_resource_icon(res, self.instance.session.db) icon.max_size = icon.min_size = icon.size = (32, 32) container.addChild(icon) container.adaptLayout()
class ProductionOverviewTab(OverviewTab): widget = 'overview_productionbuilding.xml' helptext = LazyT("Production overview") production_line_gui_xml = 'overview_productionline.xml' ACTIVE_PRODUCTION_ANIM_DIR = "content/gui/images/animations/cogs/large" BUTTON_BACKGROUND = "content/gui/images/buttons/msg_button.png" ARROW_TOP = "content/gui/icons/templates/production/production_arrow_top.png" ARROW_MID = "content/gui/icons/templates/production/production_arrow_start.png" ARROW_BOTTOM = "content/gui/icons/templates/production/production_arrow_bottom.png" ARROW_CONNECT_UP = "content/gui/icons/templates/production/production_arrow_connect_up.png" ARROW_CONNECT_DOWN = "content/gui/icons/templates/production/production_arrow_connect_down.png" ARROWHEAD_TOP = "content/gui/icons/templates/production/production_arrowhead_top.png" ARROWHEAD_MID = "content/gui/icons/templates/production/production_arrow_head.png" ARROWHEAD_BOTTOM = "content/gui/icons/templates/production/production_arrowhead_bottom.png" ARROWHEAD_CONNECT_UP = "content/gui/icons/templates/production/production_arrowhead_connect_up.png" ARROWHEAD_CONNECT_DOWN = "content/gui/icons/templates/production/production_arrowhead_connect_down.png" ICON_HEIGHT = ImageFillStatusButton.CELL_SIZE[1] + ImageFillStatusButton.PADDING def __init__(self, instance): self._animations = [] super(ProductionOverviewTab, self).__init__(instance=instance) def get_displayed_productions(self): """List all possible productions of a buildings sorted by production line id. Overwritten in some child classes (e.g. farm tab). """ productions = self.instance.get_component(Producer).get_productions() return sorted(productions, key=operator.methodcaller('get_production_line_id')) def refresh(self): """This function is called by the TabWidget to redraw the widget.""" self._refresh_utilization() # remove old production line data parent_container = self.widget.child_finder('production_lines') while parent_container.children: child = parent_container.children[-1] if hasattr(child, "anim"): child.anim.stop() del child.anim parent_container.removeChild(child) # create a container for each production # sort by production line id to have a consistent (basically arbitrary) order for production in self.get_displayed_productions(): # we need to be notified of small production changes # that aren't passed through the instance production.add_change_listener(self._schedule_refresh, no_duplicates=True) gui = load_uh_widget(self.production_line_gui_xml) # fill in values to gui reflecting the current game state container = gui.findChild(name="production_line_container") centered_container = container.findChild(name='centered_production_icons') center_y = self._center_production_line(container, production) centered_container.position = (centered_container.position[0], center_y - 44 // 2) self._set_resource_amounts(container, production) if production.is_paused(): centered_container.removeChild(centered_container.findChild(name="toggle_active_active")) toggle_icon = centered_container.findChild(name="toggle_active_inactive") toggle_icon.name = "toggle_active" else: centered_container.removeChild(centered_container.findChild(name="toggle_active_inactive")) toggle_icon = centered_container.findChild(name="toggle_active_active") toggle_icon.name = "toggle_active" if production.get_state() == PRODUCTION.STATES.producing: bg = Icon(image=self.__class__.BUTTON_BACKGROUND) bg.position = toggle_icon.position centered_container.addChild(bg) centered_container.removeChild(toggle_icon) # fix z-ordering centered_container.addChild(toggle_icon) anim = PychanAnimation(toggle_icon, self.__class__.ACTIVE_PRODUCTION_ANIM_DIR) centered_container.anim = anim anim.start(1.0/12, -1) # always start anew, people won't notice self._animations.append(weakref.ref(anim)) # fill it with input and output resources in_res_container = container.findChild(name="input_res") self._add_resource_icons(in_res_container, production.get_consumed_resources(), marker=True) out_res_container = container.findChild(name="output_res") self._add_resource_icons(out_res_container, production.get_produced_resources()) # active toggle_active button toggle_active = ToggleActive(self.instance.get_component(Producer), production) centered_container.mapEvents({ 'toggle_active': Callback(toggle_active.execute, self.instance.session) }) # NOTE: this command causes a refresh, so we needn't change the toggle_active-button-image parent_container.addChild(container) super(ProductionOverviewTab, self).refresh() def _center_production_line(self, parent_container, production): """Centers in/out production line display for amount of resources each. @return: y value to center other gui parts (toggle icon etc.) around vertically. """ input_amount = len(production.get_consumed_resources()) output_amount = len(production.get_produced_resources()) # Center of production line arrows (where to place toggle icon). height_diff_y = max(1, output_amount, input_amount) - 1 center_y = (self.ICON_HEIGHT // 2) * height_diff_y # Center input and output boxes of the production line if necessary. if input_amount != output_amount: height_diff_in = max(0, output_amount - max(1, input_amount)) height_diff_out = max(0, input_amount - max(1, output_amount)) center_y_in = (self.ICON_HEIGHT // 2) * height_diff_in center_y_out = (self.ICON_HEIGHT // 2) * height_diff_out input_container = parent_container.findChild(name='input_container') output_container = parent_container.findChild(name='output_container') input_container.position = (input_container.position[0], center_y_in) output_container.position = (output_container.position[0], center_y_out) # Draw and combine arrows for input and output. if input_amount > 0: self._draw_pretty_arrows(parent_container, input_amount, x=58, y=center_y, out=False) if output_amount > 0: self._draw_pretty_arrows(parent_container, output_amount, x=96, y=center_y, out=True) return center_y + self.ICON_HEIGHT // 2 def _draw_pretty_arrows(self, parent_container, amount, x=0, y=0, out=False): """Draws incoming or outgoing arrows for production line container.""" if amount % 2: # Add center arrow for 1, 3, 5, ... but not 2, 4, ... if out: mid_arrow = Icon(image=self.__class__.ARROWHEAD_MID) else: mid_arrow = Icon(image=self.__class__.ARROW_MID) mid_arrow.position = (x, 17 + y) parent_container.insertChild(mid_arrow, 0) for res in xrange(amount // 2): # --\ <= placed for res = 1 # --\| <= place connector <= placed for res = 0 # ---O--> <= placed above (mid_arrow) # --/| <= place connector <= placed for res = 0 # --/ <= placed for res = 1 offset = -17 + (self.ICON_HEIGHT // 2) * (2 * res + (amount % 2) + 1) if out: top_arrow = Icon(image=self.__class__.ARROWHEAD_TOP) else: top_arrow = Icon(image=self.__class__.ARROW_TOP) top_arrow.position = (x, y - offset) parent_container.insertChild(top_arrow, 0) if out: bottom_arrow = Icon(image=self.__class__.ARROWHEAD_BOTTOM) else: bottom_arrow = Icon(image=self.__class__.ARROW_BOTTOM) bottom_arrow.position = (x, y + offset) parent_container.insertChild(bottom_arrow, 0) # Place a connector image (the | in above sketch) that vertically connects # the input resource arrows. We need those if the production line has more # than three input resources. Connectors are placed in the inner loop parts. place_connectors = amount > (3 + 2 * res) if place_connectors: # the connector downwards connects top_arrows if out: down_connector = Icon(image=self.__class__.ARROWHEAD_CONNECT_DOWN) else: down_connector = Icon(image=self.__class__.ARROW_CONNECT_DOWN) down_connector.position = (98, y - offset) parent_container.insertChild(down_connector, 0) # the connector upwards connects up_arrows if out: up_connector = Icon(image=self.__class__.ARROWHEAD_CONNECT_UP) else: up_connector = Icon(image=self.__class__.ARROW_CONNECT_UP) up_connector.position = (98, y + offset) parent_container.insertChild(up_connector, 0) def _set_resource_amounts(self, container, production): for res, amount in production.get_consumed_resources().iteritems(): # consumed resources are negative! label = Label(text=unicode(-amount), margins=(0, 16)) container.findChild(name='input_box').addChild(label) for res, amount in production.get_produced_resources().iteritems(): label = Label(text=unicode(amount).rjust(2), margins=(0, 16)) container.findChild(name='output_box').addChild(label) def destruct_building(self): self.instance.session.ingame_gui.hide_menu() Tear(self.instance).execute(self.instance.session) def _refresh_utilization(self): utilization = 0 if self.instance.has_component(Producer): utilization = int(round(self.instance.get_component(Producer).capacity_utilization * 100)) self.widget.child_finder('capacity_utilization').text = unicode(utilization) + u'%' def _add_resource_icons(self, container, resources, marker=False): calculate_position = lambda amount: (amount * 100) // inventory.get_limit(res) for res in resources: inventory = self.instance.get_component(StorageComponent).inventory filled = calculate_position(inventory[res]) marker_level = calculate_position(-resources[res]) if marker else 0 image_button = ImageFillStatusButton.init_for_res(self.instance.session.db, res, inventory[res], filled, marker=marker_level, use_inactive_icon=False, uncached=True) container.addChild(image_button) def show(self): super(ProductionOverviewTab, self).show() Scheduler().add_new_object(Callback(self._refresh_utilization), self, run_in=GAME_SPEED.TICKS_PER_SECOND, loops=-1) def hide(self): super(ProductionOverviewTab, self).hide() self._cleanup() def on_instance_removed(self): self._cleanup() super(ProductionOverviewTab, self).on_instance_removed() def _cleanup(self): Scheduler().rem_all_classinst_calls(self) for production in self.get_displayed_productions(): production.discard_change_listener(self._schedule_refresh) for anim in self._animations: if anim(): anim().stop() self._animations = []
class OverviewTab(TabInterface): widget = 'overviewtab.xml' icon_path = 'icons/tabwidget/common/building_overview' helptext = LazyT("Overview") has_stance = False def __init__(self, instance, widget=None, icon_path=None): self.instance = instance super(OverviewTab, self).__init__(widget=widget, icon_path=icon_path) def init_widget(self): # set player emblem if self.widget.child_finder('player_emblem'): if self.instance.owner is not None: player_color = self.instance.owner.color.name else: player_color = 'no_player' emblem = 'content/gui/images/tabwidget/emblems/emblem_%s.png' self.widget.child_finder( 'player_emblem').image = emblem % player_color if self.__class__.has_stance: self.init_stance_widget() def refresh(self): if (hasattr(self.instance, 'name') or self.instance.has_component(NamedComponent) ) and self.widget.child_finder('name'): name_widget = self.widget.child_finder('name') # Named objects can't be translated. if self.instance.has_component(NamedComponent): name_widget.text = self.instance.get_component( NamedComponent).name else: name_widget.text = T(self.instance.name) if hasattr(self.instance, 'running_costs') and \ self.widget.child_finder('running_costs'): self.widget.child_finder('running_costs').text = \ unicode(self.instance.running_costs) self.widget.adaptLayout() def show(self): super(OverviewTab, self).show() if not self.instance.has_change_listener(self.refresh): self.instance.add_change_listener(self.refresh) if not self.instance.has_remove_listener(self.on_instance_removed): self.instance.add_remove_listener(self.on_instance_removed) if hasattr(self.instance, 'settlement') and \ self.instance.settlement is not None and \ not self.instance.settlement.has_change_listener(self._schedule_refresh): # listen for settlement name changes displayed as tab headlines self.instance.settlement.add_change_listener( self._schedule_refresh) def hide(self): super(OverviewTab, self).hide() if self.instance is not None: self.instance.discard_change_listener(self.refresh) self.instance.discard_remove_listener(self.on_instance_removed) if hasattr(self.instance, 'settlement') and self.instance.settlement is not None: self.instance.settlement.discard_change_listener( self._schedule_refresh) def on_instance_removed(self): self.on_remove() self.instance = None def init_stance_widget(self): """Call this for tabs with stances.""" stance_widget = self.widget.findChild(name='stance') if stance_widget is not None: stance_widget.init(self.instance) self.add_remove_listener(stance_widget.remove)
class BoatbuilderTab(UnitbuilderTabBase): widget = 'boatbuilder.xml' helptext = LazyT("Boat builder overview") UNIT_THUMBNAIL = "content/gui/icons/thumbnails/{type_id}.png" UNIT_PREVIEW_IMAGE = "content/gui/images/objects/ships/116/{type_id}.png"