def create_grid(self): """ Setup the grid. After this function has has been called no modifications to the grid are possible. @todo: make it possible to change the layout during runtime """ # do some calculations padding_x, padding_y = self.item_padding if padding_x is None: # no padding is given. Get the number fitting items # and devide the remaining space as paddingand border self.num_items_x = int(self.width / self.cell_size[0]) padding_x = self.width / self.num_items_x - self.cell_size[0] self.item_width = self.cell_size[0] + padding_x else: self.item_width = self.cell_size[0] + padding_x # now that we know the sizes check how much items fit self.num_items_x = int(self.width / self.item_width) if padding_y is None: # no padding is given. Get the number fitting items # and devide the remaining space as padding and border self.num_items_y = int(self.height / self.cell_size[1]) padding_y = self.height / self.num_items_y - self.cell_size[1] self.item_height = self.cell_size[1] + padding_y else: self.item_height = self.cell_size[1] + padding_y # now that we know the sizes check how much items fit self.num_items_y = int(self.height / self.item_height) self.item_padding = padding_x, padding_y # we now center the grid by default x0 = (self.width - self.num_items_x * self.item_width + padding_x) / 2 y0 = (self.height - self.num_items_y * self.item_height + padding_y) / 2 self.clip = (x0 - padding_x, y0 - padding_y), \ (self.num_items_x * self.item_width + padding_x, self.num_items_y * self.item_height + padding_y) self.location = (0, 0) # list of rendered items self.item_widgets = {} # group of items self.item_group = AbstractGroup((x0, y0)) self.add(self.item_group) self.create_grid = None
class Grid(AbstractGroup): """ Grid holding several widgets based on the given items. The grid supports scrolling. @note: see C{test/flickr.py} for an example """ candyxml_name = 'grid' HORIZONTAL, VERTICAL = range(2) __items = None def __init__(self, pos, size, cell_size, cell_item, items, template, orientation, xpadding=None, ypadding=None, context=None): """ Simple grid widget to show the items based on the template. @param pos: (x,y) position of the widget or None @param size: (width,height) geometry of the widget. @param cell_size: (width,height) of each cell @param cell_item: string how the cell item should be added to the context @param items: list of objects or object name in the context @param template: child template for each cell @param orientation: how to arange the grid: Grid.HORIZONTAL or Grid.VERTICAL @param xpadding: x value of space between two items. If set to None the padding will be calculated based on cell size and widget size @param ypadding: y value of space between two items. If set to None the padding will be calculated based on cell size and widget size @param context: the context the widget is created in """ super(Grid, self).__init__(pos, size, context=context) # store arguments for later public use self.cell_size = cell_size # store arguments for later private use self.__orientation = orientation if isinstance(items, (str, unicode)): # items is a string, get it from the context items = self.context.get(items) self.items = items self.cell_item = cell_item self.template = template self.item_padding = xpadding, ypadding def create_grid(self): """ Setup the grid. After this function has has been called no modifications to the grid are possible. @todo: make it possible to change the layout during runtime """ # do some calculations padding_x, padding_y = self.item_padding if padding_x is None: # no padding is given. Get the number fitting items # and devide the remaining space as paddingand border self.num_items_x = int(self.width / self.cell_size[0]) padding_x = self.width / self.num_items_x - self.cell_size[0] self.item_width = self.cell_size[0] + padding_x else: self.item_width = self.cell_size[0] + padding_x # now that we know the sizes check how much items fit self.num_items_x = int(self.width / self.item_width) if padding_y is None: # no padding is given. Get the number fitting items # and devide the remaining space as padding and border self.num_items_y = int(self.height / self.cell_size[1]) padding_y = self.height / self.num_items_y - self.cell_size[1] self.item_height = self.cell_size[1] + padding_y else: self.item_height = self.cell_size[1] + padding_y # now that we know the sizes check how much items fit self.num_items_y = int(self.height / self.item_height) self.item_padding = padding_x, padding_y # we now center the grid by default x0 = (self.width - self.num_items_x * self.item_width + padding_x) / 2 y0 = (self.height - self.num_items_y * self.item_height + padding_y) / 2 self.clip = (x0 - padding_x, y0 - padding_y), \ (self.num_items_x * self.item_width + padding_x, self.num_items_y * self.item_height + padding_y) self.location = (0, 0) # list of rendered items self.item_widgets = {} # group of items self.item_group = AbstractGroup((x0, y0)) self.add(self.item_group) self.create_grid = None def create_item(self, item_num, pos_x, pos_y): """ Render one child """ if item_num < 0 or item_num >= len(self.items): self.item_widgets[(pos_x, pos_y)] = None return # calculate the size where the child should be child_x = pos_x * self.item_width child_y = pos_y * self.item_height context = self.context.copy() context[self.cell_item] = self.items[item_num] child = self.template(context=context) child.x = child_x child.y = child_y child.width, child.height = self.cell_size self.item_group.add(child) self.item_widgets[(pos_x, pos_y)] = child return child def clear(self): """ Clear the grid """ self.item_group.clear() self.item_widgets = {} self.queue_rendering() def sync_prepare(self): """ Prepare widget for the next sync with the backend """ if self.create_grid: self.create_grid() if not super(Grid, self).sync_prepare(): return False if self.__orientation == Grid.VERTICAL: max_x, max_y = self.location for y in range(0, max_y + self.num_items_y): for x in range(0, max_x + self.num_items_x): item_num = x + y * self.num_items_x if not (x, y) in self.item_widgets: self.create_item(item_num, x, y) if self.__orientation == Grid.HORIZONTAL: max_x, max_y = self.location for x in range(0, max_x + self.num_items_x): for y in range(0, max_y + self.num_items_y): item_num = x * self.num_items_y + y if not (x, y) in self.item_widgets: self.create_item(item_num, x, y) return super(Grid, self).sync_prepare() def sync_context(self): """ Adjust to a new context. Note: the context of the children is not synced. If required the grid must be cleared first. This may be fixed in future versions of this widget. """ self.items = self.__items_provided @property def items(self): """ Get list of items """ return self.__items @items.setter def items(self, items): """ Set list of items """ self.__items_provided = items if isinstance(items, (str, unicode)): # items is a string, get it from the context items = self.context.get(items) if self.__items != items: if self.__items: # we already had a valid list of items. Set the new # list of items before clearing in case an inherting # class already needs it on clear self.__items = items self.clear() self.__items = items @kaa.synchronized() def scroll_by(self, (x, y), secs, force=False):