def initialize_grid(self): """ Step through all of the calculations to determine the grid size and led size. :return: None """ # Store Grid Size self.grid_size = Vector2(config.get("GRID_SIZE")) if self.draw_borders: self.perimeter_border_size = Vector2( config.get("PERIMETER_BORDER_SIZE")) else: self.perimeter_border_size = Vector2(0, 0) # Calculate cell_size self.calculate_cell_size(self.max_grid_pixel_size()) # Create Background self.create_background() # Calculate LED Size self.calculate_led_size() log.info("LED size calculated %s, with borders %s", self.led_size, self.cell_size) # store led rects, of just the LED rect self.led_rects = [[None for x in range(int(self.grid_size.y))] for y in range(int(self.grid_size.x))] # each grid cells rect, includes led borders self.grid_rects = [[None for x in range(int(self.grid_size.y))] for y in range(int(self.grid_size.x))] for x in range(int(self.grid_size.x)): for y in range(int(self.grid_size.y)): top = self.grid_background_position.y + (self.cell_size.y * y) left = self.grid_background_position.x + (self.cell_size.x * x) top += self.border_size.y left += self.border_size.x top += self.perimeter_border_size.y left += self.perimeter_border_size.x width = self.cell_size.x - (self.border_size.x * 2) height = self.cell_size.y - (self.border_size.y * 2) rect = pygame.Rect(left, top, width, height) self.led_rects[x][y] = rect top = self.grid_background_position.y + ( self.cell_size.y * y) + self.perimeter_border_size.y left = self.grid_background_position.x + ( self.cell_size.x * x) + self.perimeter_border_size.x width = self.cell_size.x height = self.cell_size.y rect = Rect(left, top, width, height) self.grid_rects[x][y] = rect
def __init__(self): """ Manage the widgets layout. :return: """ # Widgets are automatically sized by its parent panel(widget). self.requested_size can influence that # automatic sizing. self.requested_size = Vector2(0, 0) # The size of the drawable widget. Does not include margins. self.size = Vector2(0, 0) # Offset of this widgets location within it's parents panel(widget) surface. # Does not include its own margins. use self.total_offset to include margins self.offset = Vector2(0, 0) # pygame.Rect, ( total_offset.x, total_offset.y, size.x, size.y) self.rect = Rect(0, 0, 0, 0) # base global_offset of the widget in screen space. would still need to add this widgets total_offset to get # correct screen space. self.base_global_offset = Vector2(0, 0) # pygame.Rect, with global_offset + total_offset for position, and self.size for width, height. self.global_rect = Rect(0, 0, 0, 0) # Information about the styles margins. self.margin = Margin() # data used during fit, which can be set by parent panel(widget). Useful so parent panel doesn't have to have # its own way of storing information about the widget for automatic layout. Such as grid x, y position. self.data = {}
def calculate_led_size(self): """ Calculate the led size for each cell. :return: None """ if self.draw_borders: self.border_size = Vector2(config.get("BORDER_SIZE")) else: self.border_size = Vector2(0, 0) width = self.cell_size.x - (self.border_size.x * 2) height = self.cell_size.y - (self.border_size.y * 2) self.led_size = vector2_to_floor(Vector2(width, height)) self.led_surface = pygame.Surface(self.led_size) # Check that the LED size is at least 1px if self.led_size.x <= 1.0 or self.led_size.y <= 1.0: log.error( "LED border size is to large for calculated LED size '(%s, %s) pixels'", self.cell_size.x, self.cell_size.y) raise Exception("LED border size is to large.")
def create_background(self): """ Calculates the background size and position, and creates the surface. :return: None """ # Initial background Size background_size_px = Vector2(self.grid_size.x * self.cell_size.x, self.grid_size.y * self.cell_size.y) # add in perimeter border size background_size_px.x += (self.perimeter_border_size.x * 2) background_size_px.y += (self.perimeter_border_size.y * 2) self.grid_background_surface = pygame.Surface( vector2_to_int(background_size_px)) if self.draw_borders > 0: color = config.get("DRAW_BORDERS_COLORS")[self.draw_borders - 1] self.grid_background_surface.fill(color) grid_background_position_rect = self.grid_background_surface.get_rect() grid_background_position_rect.centerx = self.layout.size.x / 2 grid_background_position_rect.centery = self.layout.size.y / 2 # Center the grid background in its space self.grid_background_position = Vector2( grid_background_position_rect.topleft)
def confirm_customer_size(pixel_mapping): rows = len(pixel_mapping) if rows != int(Vector2(config.get("GRID_SIZE")).y): raise Exception( "Custom PIXEL_MAPPING rows '{}' does not match GRID_SIZE.y '{}'" .format(rows, int(Vector2(config.get("GRID_SIZE")).y))) columns = [] for y in range(len(pixel_mapping)): x = len(pixel_mapping[y]) if x not in columns: columns.append(x) if len(columns) != 1: raise Exception( "Custom PIXEL_MAPPING columns malformed, all are not of equal length." ) if columns[0] != int(Vector2(config.get("GRID_SIZE")).x): raise Exception( "Custom PIXEL_MAPPING columns '{}' does not match GRID_SIZE.x '{}'" .format(columns[0], int(Vector2(config.get("GRID_SIZE")).x))) return columns[0], rows
def calculate_cell_size(self, max_grid_pixel_size): """ Given the max grid pixel size, determine the size of the individual grid cells. :param max_grid_pixel_size: pygame.math.Vector2 of the max size of the whole grid. :return: None """ width = max_grid_pixel_size.x / float(self.grid_size.x) height = max_grid_pixel_size.y / float(self.grid_size.y) # Check that the LED size is at least 1px if width <= 1.0 or height <= 1.0: log.error( "Calculated LED cell size less than 1 pixel. The WINDOW_SIZE is to small to fit " "the configured GRID_SIZE") raise Exception("LED size less than 1 pixel") # Sqaure up the LED if config.get("SQUARE_LED"): if width < height: height = width else: width = height self.cell_size = vector2_to_floor(Vector2(width, height))
def on_mousebuttondown(self, sender, event): global_rect = self.dotgrid.layout.global_rect if global_rect.collidepoint(event.pos): grid_size = self.dotgrid.grid_size grid_rects = self.dotgrid.grid_rects pos = Vector2(event.pos) - Vector2(global_rect.topleft) for x in range(int(grid_size.x)): for y in range(int(grid_size.y)): if grid_rects[x][y].collidepoint(pos): if self.selected and self.x == x and self.y == y: self.clear() else: # self.clear() self.set(x, y)
def __init__(self): """ Data model of a strip of LED's and also handles processes received data packets. :return: """ self.grid_size = Vector2(config.get("GRID_SIZE")) # self.pixel_count = int(self.grid_size.x * self.grid_size.y) self.pixel_count = globals.mapping_data.pixel_count # setup initial pixel data size = self.pixel_count * 4 self.data = bytearray(size) self.clear_data() self.spi_index = 0 # current pixel index of the spi_in function self.updated = None # datetime of last time start frame was received self.packet_length = 0 # count of number of individual bytes received since last start frame was received. self._dirty = True # keep track if data has been changed since last update call # cache blinker signals self._signal_startrecv = blinker.signal("stripdata.startrecv") self._signal_updated = blinker.signal("stripdata.updated") self.header_bytes_found = 0 self.buffer_count = 0 self.buffer = bytearray((0xFF, 0xFF, 0xFF, 0xFF))
def __init__(self): """ Widget to show information about the running screen. :return: """ super(RunningInfo, self).__init__(x=145) self.layout.margin.set(0, 5, 10, 5) # LED information widgets self.led_grid_position_txt = [] # value of grid position self.led_strip_index_txt = [] # value of strip index self.led_value_txt = [] # value of strip index color self.led_color = [] # widget to show color block of strip index color row_size = 14 left = SizedRows(row_size) right = SizedRows(row_size) self.set_left(left) self.set_right(right) left.set(self.hd_txt("Information"), 0) i = 1 left.set(self.lbl_text("Emulator FPS:"), i) self.emulator_fps_txt = self.val_text("") right.set(self.emulator_fps_txt, i) i += 1 left.set(self.lbl_text("Grid size:"), i) grid_size = Vector2(config.get("GRID_SIZE")) pixel_count = globals.mapping_data.pixel_count self.grid_size_txt = self.val_text("({:.0f}, {:.0f}), {:.0f}".format(grid_size.x, grid_size.y, pixel_count)) right.set(self.grid_size_txt, i) i += 1 left.set(self.lbl_text("Packet Updated:"), i) self.packet_updated_txt = self.val_text("") right.set(self.packet_updated_txt, i) i += 1 left.set(self.lbl_text("Packet length:"), i) self.packet_length_txt = self.val_text("") right.set(self.packet_length_txt, i) i += 1 left.set(self.lbl_text("Packet Rate:"), i) self.packet_rate = self.val_text("") right.set(self.packet_rate, i) i += 2 self.pixel_info_count = 4 self.create_pixel_info(left, right, i) blinker.signal("dotgrid.select.set").connect(self.on_dotgrid_select_set) blinker.signal("stripdata.updated").connect(self.on_data_updated) blinker.signal("ratecounter.updated").connect(self.on_ratecounter_updated)
def widthheight(self): """ Return a Vector2 class of the total combined margins for width and height. width is left + right margins. height is top + bottom margins. :return: 'class pygame.math.Vector2' Vector2(width, height) """ return Vector2(self.left + self.right, self.top + self.bottom)
def max_grid_pixel_size(self): """ Calculate the max grid size in pixels. Takes into account the layout of the right panel. :return: Vector2, max grid size in pixels """ max_width = self.layout.rect.width - (self.perimeter_border_size.x * 2) max_height = self.layout.rect.height - (self.perimeter_border_size.y * 2) return Vector2(max_width, max_height)
def __init__(self): """ Provide the mapping from a single series strip of DotStar pixels to an x, y grid :return: """ self.grid_size = Vector2(config.get("GRID_SIZE")) self.pixel_count = 0 self.data = [[None for x in range(int(self.grid_size.y))] for y in range(int(self.grid_size.x))] pixel_mapping = config.get("PIXEL_MAPPING") if pixel_mapping is not None: self.custom_mapping(pixel_mapping) else: self.prebuilt_mappings() self.pixel_count = int(self.grid_size.x * self.grid_size.y)
def __init__(self): super(AboutScene, self).__init__() two_rows = panels.TwoRows(-50) self.set_panel(two_rows) top = self.text_panel() two_rows.set_top(top) bottom = panels.CenterSingle() two_rows.set_bottom(bottom) button = ButtonWidget("[F1] Return", key=pygame.K_F1) button.layout.requested_size = Vector2(100, 21) bottom.set(button) blinker.signal("gui.button.pressed").connect(self.on_return, sender=button) self.fit()
def text_panel(): panel = panels.CenterSingle(25) rows_panel = panels.SizedRows(18, use_surface=True, color=(40, 40, 40)) rows_panel.layout.requested_size = Vector2(500, 400) panel.set(rows_panel) filename = os.path.join(MEDIA_PATH, 'about.txt') with open(filename, 'r') as f: text = f.read() text_split = text.split("\n") widget = TextLabelWidget(config.get("WINDOW_CAPTION"), 18, config.get("BRAND_TITLE")) widget.layout.margin.set(0, 0, 0, 5) rows_panel.set(widget, 0) for i, line in enumerate(text_split): widget = TextLabelWidget(line, 12, (255, 255, 255)) widget.layout.margin.set(0, 0, 0, 5) rows_panel.set(widget, i + 2) return panel
def topleft(self): """ Return a Vector2 class of the top left corner margins :return: 'class pygame.math.Vector2' Vector2(left, top) """ return Vector2(self.left, self.top)
def set_size(self, possible_size, offset, global_offset, flags): """ Automatically calculate the widgets size and position. Size will never exceed possible_size, but may be smaller depending on flags. The calculation is also influenced by the widgets self.requested_size FILLX FILLY CENTERX CENTERY :param possible_size: Vector2 of the maximum size the parent panel(widget) has allotted this widget. :param offset: Vector2 top left corner of where this widget will draw n the parent's surface :param global_offset: total screen global offset of where offset is actually located. This is needed for to maintain screen space rect's of this widget. useful for mouse clicks.. :param flags: Positioning flags to influence the automatic fitting. :return: None """ # Store base global_offset self.base_global_offset = global_offset # Store base offset self.offset = offset size = Vector2(0, 0) if flags & FILLX: if self.requested_size.x == 0 or self.requested_size.x == -1: size.x = possible_size.x else: raise Exception( "can not FILLX, as widget.style.x is already set") else: if self.requested_size.x == 0: raise Exception("widget.style.x is equal to 0") elif self.requested_size.x == -1: # even if FILLX wasn't set by parent, fill out to parents possible_size size.x = possible_size.x else: size.x = self.requested_size.x if flags & FILLY: if self.requested_size.y == 0 or self.requested_size.y == -1: size.y = possible_size.y else: raise Exception( "can not FILLY, as widget.style.y is already set") else: if self.requested_size.y == 0: raise Exception("widget.style.y is equal to 0") elif self.requested_size.y == -1: # even if FILLY wasn't set by parent, fill out to parents possible_size size.y = possible_size.y else: size.y = self.requested_size.y # because size is the size of the widget's drawable surface, remove its own margins. size -= self.margin.widthheight self.size = size # Once widgets size has been determined, we can center it within its parents space by adjusting the margins. # TODO this will not work if the widgets are ever re-sized, as the margins are used to calculate the size in the # step just above this. if flags & CENTERX: space = possible_size.x - size.x self.margin.left = space / 2.0 self.margin.right = space / 2.0 if flags & CENTERY: space = possible_size.y - size.y self.margin.top = space / 2.0 self.margin.bottom = space / 2.0 # Cache Rectangle, topleft is offset + margin, size is size of drawable widget surface. self.rect = Rect(self.total_offset, self.size) # Cache Global Rect. Same as above but globally positioned. Useful for mouse events self.global_rect = Rect(self.base_global_offset + self.total_offset, self.size)