class ProgressBar(ABox): """The ProgressBar is a pychan widget. It can be used in xml files like this: <ProgressBar /> It is used to display a ProgressBar with a certain progress ;). Set the widgets progress attribute to set the progress. Pretty straight forward. The progress is a value from 0 to 100. Think of it as percent. """ ATTRIBUTES = ABox.ATTRIBUTES + [ IntAttr('progress'), Attr('fill'), Attr('background'), ] def __init__(self, progress=0, fill=None, background=None, **kwargs): super().__init__(**kwargs) if self.max_size == Widget.DEFAULT_MAX_SIZE: self.max_size = (100, 16) self.__progress = progress self.__fill = fill self.__background = background self.tiles = None self.bg_icon = None def _set_progress(self, progress): self.__progress = progress if self.bg_icon is None: self.background = "content/gui/images/background/bar_bg.png" if self.tiles is None: self.fill = "content/gui/images/background/widgets/progressbar_fill.png" self.tiles.size = (int(self.max_size[0] * progress / 100.0), self.max_size[1]) self.adaptLayout() def _get_progress(self): return self.__progress def _set_fill_image(self, image): self.__fill = image self.tiles = Icon(image=image, scale=True) self.addChild(self.tiles) def _get_fill_image(self): return self.__fill def _set_background(self, background): self.__background = background self.bg_icon = Icon(image=background, scale=True) self.bg_icon.min_size = self.bg_icon.size = self.max_size self.addChild(self.bg_icon) def _get_background(self): return self.__background progress = property(_get_progress, _set_progress) fill = property(_get_fill_image, _set_fill_image) background = property(_get_background, _set_background)
class TabBG(VBox, TilingBackground): """Intended to be used for any tab we display. Uses content/gui/images/tabwidget/main_bg_*.png. @param amount: amount of 50px tiles/panels in between top and bottom icon """ ATTRIBUTES = VBox.ATTRIBUTES + [IntAttr('amount')] def __init__(self, **kwargs): VBox.__init__(self, name='tab_background_icons', padding=0) TilingBackground.__init__(self, amount=0, base_path="content/gui/images/tabwidget/main_bg_", start_img="top.png", tiles_img="fill.png", final_img="bottom.png", **kwargs)
class TilingHBox(HBox, TilingBackground): """Currently mostly used by cityinfo, thus using its arguments as defaults. Another use case is the TilingProgressBar. @param amount: amount of 10px tiles/panels in between left and right icon """ ATTRIBUTES = HBox.ATTRIBUTES + [IntAttr('amount')] def __init__(self, **kwargs): HBox.__init__(self, name='city_info_background', padding=0) TilingBackground.__init__(self, amount=0, base_path="content/gui/images/background/widgets/cityinfo_", start_img="left.png", tiles_img="fill.png", final_img="right.png", **kwargs)
class TilingHBox(TilingBackground, HBox): """Currently only used by cityinfo, thus using its arguments as defaults. @param amount: amount of 10px tiles/panels in between left and right icon """ ATTRIBUTES = HBox.ATTRIBUTES + [IntAttr('amount')] def __init__(self, **kwargs): super(TilingHBox, self).__init__( amount=0, name='tab_background_icons', base_path="content/gui/images/background/widgets/cityinfo_", start_img="left.png", tiles_img="fill.png", final_img="right.png", **kwargs)
class TabBG(pychan.widgets.VBox): """The TabBG is a shortcut for several TooltipIcons combined to one group. Intended to be used for any tab we display. Uses content/gui/images/tabwidget/main_bg_*.png. Default attributes are: name="background_icons" amount="0" padding="0" border_size="0" @param amount: amount of 50px tiles in between top and bottom icon """ ATTRIBUTES = pychan.widgets.VBox.ATTRIBUTES + [IntAttr('amount')] def __init__(self, amount=0, **kwargs): super(TabBG, self).__init__( name='background_icons', padding=0, border_size=0, **kwargs) # Note: Don't set the amount in the constructor, # as it will not layout correctly, blame pychan for it :-) self.__amount = amount header_path = "content/gui/images/tabwidget/main_bg_top.png" self.addChild(TooltipIcon(image=header_path, name='background_icon_' + '0')) def _get_amount(self): return self.__amount def _set_amount(self, amount): self.__amount = amount mid_path = "content/gui/images/tabwidget/main_bg_fill.png" footer_path = "content/gui/images/tabwidget/main_bg_bottom.png" for i in xrange(0,self.amount): mid = TooltipIcon(image=mid_path, name='background_icon_' + unicode(i+1)) self.addChild(mid) self.addChild(TooltipIcon(image=footer_path, name='background_icon_' + unicode(self.amount+1))) amount = property(_get_amount, _set_amount)
class Inventory(Container): """The inventory widget displays information about the goods in a Storage. It uses ImageFillStatusButtons to display icons and a fill bar for these resources. It can be used like any other widget in xml files, but for full functionality the inventory has to be manually set, or use the TabWidget, which will autoset it (was made to be done this way). XML use: <Inventory />, can take all parameters of a Container. """ ATTRIBUTES = Container.ATTRIBUTES + [ BoolAttr('uncached'), BoolAttr('display_legend'), IntAttr("items_per_line") ] # uncached: required when resource icons should appear multiple times at any given moment # on the screen. this is usually not the case with single inventories, but e.g. for trading. # display_legend: whether to display a string explanation about slot limits UNUSABLE_SLOT_IMAGE = "content/gui/icons/resources/none_gray.png" def __init__(self, uncached=False, display_legend=True, items_per_line=4, **kwargs): # this inits the gui part of the inventory. @see init(). super(Inventory, self).__init__(**kwargs) self._inventory = None self._inited = False self.uncached = uncached self.display_legend = display_legend self.items_per_line = items_per_line or 1 # negative values are fine, 0 is not def init_needed(self, inventory): return not self._inited or self._inventory is not inventory def init(self, db, inventory, ordinal=None): """This inits the logic of the inventory. @see __init__(). @param ordinal: {res: (min, max)} Display ordinal scale with these boundaries instead of numbers for a particular resource. Currently implemented via ImageFillStatusButton. """ # check if we must init everything anew if self.init_needed(inventory): self._inited = True self.db = db self._inventory = inventory # specific to Inventory self.ordinal = ordinal self._res_order = sorted(self._inventory.iterslots()) self.legend = Label(name="legend") self.__icon = Icon(name="legend_icon") self.__icon.image = "content/gui/icons/ship/civil_16.png" if isinstance(self._inventory, TotalStorage): self.__icon.position = (130, 53) self.legend.position = (150, 53) elif isinstance(self._inventory, PositiveSizedSlotStorage): self.__icon.position = (0, 248) self.legend.position = (20, 248) self.update() def update(self): self.removeAllChildren() if self.display_legend: self.addChildren(self.__icon, self.legend) vbox = VBox(padding=0) vbox.width = self.width current_hbox = HBox(padding=0) # draw the content self._draw(vbox, current_hbox) self.adaptLayout() def _draw(self, vbox, current_hbox, index=0): """Draws the inventory.""" # add res to res order in case there are new ones # (never remove old ones for consistent positioning) new_res = sorted(resid for resid in self._inventory.iterslots() if resid not in self._res_order) if isinstance(self._inventory, PositiveTotalNumSlotsStorage): # limited number of slots. We have to switch unused slots with newly added ones on overflow while len( self._res_order) + len(new_res) > self._inventory.slotnum: for i in xrange(self._inventory.slotnum): if len(self._res_order) <= i or self._inventory[ self._res_order[i]]: # search empty slot continue # insert new res here self._res_order[i] = new_res.pop(0) if not new_res: break # all done # add remaining slots for slotstorage or just add it without consideration for other storage kinds self._res_order += new_res for resid in self._res_order: amount = self._inventory[resid] if amount == 0: index += 1 continue # check if this res should be displayed if not self.db.cached_query( 'SELECT shown_in_inventory FROM resource WHERE id = ?', resid)[0][0]: continue if self.ordinal: lower, upper = self.ordinal.get(resid, (0, 100)) filled = (100 * (amount - lower)) // (upper - lower) amount = "" # do not display exact information for resource deposits elif isinstance(self._inventory, TotalStorage): filled = 0 else: filled = (100 * amount) // self._inventory.get_limit(resid) button = ImageFillStatusButton.init_for_res(self.db, resid, amount, filled=filled, uncached=self.uncached) button.button.name = "inventory_entry_%s" % index # required for gui tests current_hbox.addChild(button) if index % self.items_per_line == self.items_per_line - 1: vbox.addChild(current_hbox) current_hbox = HBox(padding=0) index += 1 if index <= self.items_per_line: # Hide/Remove second line icons = self.parent.findChildren(name='slot') if len(icons) > self.items_per_line: self.parent.removeChildren(icons[self.items_per_line - 1:]) vbox.addChild(current_hbox) self.addChild(vbox) height = ImageFillStatusButton.CELL_SIZE[1] * len( self._res_order) // self.items_per_line self.min_size = (self.min_size[0], height) if isinstance(self._inventory, TotalStorage): # if it's full, the additional slots have to be marked as unusable (#1686) # check for any res, the res type doesn't matter here if not self._inventory.get_free_space_for(0): for i in xrange(index, self.items_per_line): button = Icon(image=self.__class__.UNUSABLE_SLOT_IMAGE) # set min & max_size to prevent pychan to expand this dynamic widget (icon) button.min_size = button.max_size = ImageFillStatusButton.ICON_SIZE current_hbox.addChild(button) if self.display_legend: limit = self._inventory.get_limit(None) if isinstance(self._inventory, TotalStorage): # Add total storage indicator sum_stored = self._inventory.get_sum_of_stored_resources() self.legend.text = _('{stored}/{limit}').format( stored=sum_stored, limit=limit) elif isinstance(self._inventory, PositiveSizedSlotStorage): self.legend.text = _('Limit: {amount}t per slot').format( amount=limit) def apply_to_buttons(self, action, filt=None): """Applies action to all buttons shown in inventory @param action: function called that touches button @param filt: function used to filter the buttons both functions take one parameter which is the button """ if filt: assert callable(filt) assert callable(action) def _find_widget(widget): if isinstance(widget, ImageFillStatusButton): if filt is None or filt(widget): action(widget) self.deepApply(_find_widget)