Пример #1
0
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)
Пример #2
0
class Inventory(pychan.widgets.Container):
	"""The inventory widget is used to display a stock of items, namely a Storage class instance.
	It makes use of the ImageFillStatusButton to display the icons for resources and the fill bar.
	It can be used like any other widget inside of xmls, 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 the parameters that pychan.widgets.Container can."""
	ATTRIBUTES = pychan.widgets.Container.ATTRIBUTES + [BoolAttr('uncached')]
	# 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.
	ITEMS_PER_LINE = 4 # TODO: make this a xml attribute with a default value
	def __init__(self, uncached=False, **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

	def init(self, db, inventory):
		# this inits the logic of the inventory. @see __init__().
		self.__inited = True
		self.db = db
		self._inventory = inventory
		self.__icon = pychan.widgets.Icon("content/gui/icons/ship/civil_16.png")
		self.update()

	def update(self):
		assert self.__inited
		self._draw()

	def _draw(self):
		"""Draws the inventory."""
		if len(self.children) != 0:
			self.removeChildren(*self.children)
		vbox = pychan.widgets.VBox(padding = 0)
		vbox.width = self.width
		current_hbox = pychan.widgets.HBox(padding = 0)
		index = 0
		for resid, amount in sorted(self._inventory): # sort by resid for unchangeable positions
			# 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 isinstance(self._inventory, TotalStorage):
				filled = 0
			else:
				filled = int(float(amount) / float(self._inventory.get_limit(resid)) * 100.0)
			button = ImageFillStatusButton.init_for_res(self.db, resid, amount, \
			                                            filled=filled, uncached=self.uncached)
			current_hbox.addChild(button)

			# old code to do this, which was bad but kept for reference
			#if index % ((vbox.width/(self.__class__.icon_width + 10))) < 0 and index != 0:
			if index % self.ITEMS_PER_LINE == (self.ITEMS_PER_LINE - 1) and index != 0:
				vbox.addChild(current_hbox)
				current_hbox = pychan.widgets.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)
		if isinstance(self._inventory, TotalStorage):
			# Add total storage indicator
			sum_stored_res = self._inventory.get_sum_of_stored_resources()
			label = pychan.widgets.Label()
			label.text = unicode(sum_stored_res) + u"/" + unicode(self._inventory.get_limit(None))
			label.position = (170, 50)
			self.__icon.position = (150, 50)
			self.addChildren(label, self.__icon)
		elif isinstance(self._inventory, PositiveSizedSlotStorage):
			label = pychan.widgets.Label()
			label.text = _('Limit: %st per slot') % self._inventory.get_limit(None)
			label.position = (110, 150)
			self.__icon.position = (90, 150)
			self.addChildren(label, self.__icon)
		self.adaptLayout()
		self.stylize('menu_black')