class Switch(_button): """ Switch([value, [labels, [options]]]) -> Switch widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is very similar to a button. When clicked, it will cycle through it's list of options. The label displayed will be the the item in the labels list at the same index as the current option (value) in the options list. The labels and options need to be exactly the same size. Arguments --------- value Must be an item in the options list. labels A list of strings that will be used as labels. options A list of items that are cycled through to determin this widgets value. """ _value = widget.ReplaceableCellDescriptor() on_label = widget.ReplaceableCellDescriptor() off_label = widget.ReplaceableCellDescriptor() def __init__(self, value=False, labels=("Off", "On"), options=(False, True), **params): super(Switch, self).__init__(**params) self._value = value self.labels = list(labels) self.options = list(options) if len(self.labels) != len(self.options): raise ValueError( "The number of labels has to equal the number of options!") self.set_value(value) def set_value(self, v): self._value = v if v not in self.options: error = """Could not find value in options. """ error = error + "Value:" + str(v) + " Options:" + str( self.options) raise ValueError(error) self.label = self.labels[self.options.index(v)] value = property(lambda self: self._value, set_value) def click(self): i = self.options.index(self.value) if i == len(self.options) - 1: i = 0 else: i += 1 self.value = self.options[i]
class Image(widget.Widget): """ Image(value) -> Image widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Basic widget for simply displaying an image. value should be a pygame surface. """ value = widget.ReplaceableCellDescriptor() def __init__(self, value, **params): widget.Widget.__init__(self, **params) self.value = value def draw_widget(self): self.blit(self.value, self.pos, clip_rect=self.value.get_rect()) def width(self): return self.value.get_width() width = ComputedCellDescriptor(width) def height(self): return self.value.get_height() height = ComputedCellDescriptor(height)
class _slider(widget.Widget): _value = widget.ReplaceableCellDescriptor() length = widget.ReplaceableCellDescriptor() min_value = widget.ReplaceableCellDescriptor() step = widget.ReplaceableCellDescriptor() def __init__(self, value=0, min_value=0, length=20, step=True, **params): widget.Widget.__init__(self, **params) self.step = step self.length = length self.min_value = min_value self.value = value # Create the marker widget and link it's values to sliders. self.marker = Marker() #widget.Widget() self.marker.parent = self self.marker._cells["hovering"] = self._cells["hovering"] self.marker._cells["focused"] = self._cells["focused"] self.marker._cells["pressing"] = self._cells["pressing"] self.marker._cells["disabled"] = self._cells["disabled"] def set_value(self, v): v = max(v, self.min_value) v = min(v, self.min_value + self.length) if self.step: v = int(v) self._value = v self.send(CHANGE) value = property(lambda self: self._value, set_value) def marker_pos(self): w = self.style_option["width"] - self.marker.width w = float(self.value - self.min_value) / (self.length) * w h = self.style_option["height"] - self.marker.height h = float(self.value - self.min_value) / (self.length) * h return (int(w), int(h)) marker_pos = ComputedCellDescriptor(marker_pos)
class _button(widget.Widget): _label = widget.ReplaceableCellDescriptor() def __init__(self, **params): self._label = None super(_button, self).__init__(**params) def set_label(self, v): if hasattr(v, "value"): l = label.Label(v.value, parent=self) l._cells["value"] = ComputedCell(lambda: str(v.value)) if type(v) == int: v = str(v) if type(v) == str or type(v) == unicode: l = label.Label(v, parent=self) self.send(CHANGE) self._label = l self._label._cells["hovering"] = self._cells["hovering"] self._label._cells["focused"] = self._cells["focused"] self._label._cells["pressing"] = self._cells["pressing"] label = property(lambda self: self._label, set_label) def width(self): if not self.label: return widget.Widget.width.function(self) w = self.label.width + self.style_option[ "padding-left"] + self.style_option["padding-right"] return widget.Widget.width.function(self, w) width = ComputedCellDescriptor(width) def height(self): if not self.label: return widget.Widget.height.function(self) h = self.label.height + self.style_option[ "padding-top"] + self.style_option["padding-bottom"] return widget.Widget.height.function(self, h) height = ComputedCellDescriptor(height) def draw_widget(self): self.label.dirty = True self.label.draw()
class _box(container.Container): expand = widget.ReplaceableCellDescriptor() def __init__(self, expand=True, **params): container.Container.__init__(self, **params) self.expand = expand def add(self, *widgets): super(_box, self).add(*widgets) self.position_widgets() def remove(self, widgets): container.Container.remove(self, widgets) self.position_widgets() def draw(self, drawbg=True): self.dirty = True container.Container.draw(self, drawbg)
class Button(_button): """ Button([value]) -> Button widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A widget resembling a button. Arguments --------- value The text or widget to be displayed on the button. """ _value = widget.ReplaceableCellDescriptor() def __init__(self, value=" ", **params): super(Button, self).__init__(**params) self._value = None self.value = value def set_value(self, v): self._value = v self.label = v value = property(lambda self: self._value, set_value)
class SelectBox(box.VBox): """ SelectBox([multiple, [expand]]) -> SelectBox widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Subclasses from VBox. Like a html select box. You can access SelectBox.values to get a set of all selected values. Arguments --------- multiple If set to True, multiple options can be selected. expand If set to True (default) all widgets will expand to be the same width. """ multiple = widget.ReplaceableCellDescriptor() def __init__(self, multiple=False, **params): box.VBox.__init__(self, **params) self.selected = set() #CellSet() self.options = CellDict() self.multiple = multiple def get_values(self): vs = set() for v in self.selected: vs.add(self.options[v]) return vs values = property(get_values) def get_label(self, v): if hasattr(v, "value"): l = label.Label(v.value, parent=self, container=self) l._cells["value"] = ComputedCell(lambda: str(v.value)) if type(v) == int: v = str(v) if type(v) == str or type(v) == unicode: l = label.Label(v, parent=self) return l def add(self, label, value): """ SelectBox.add(label, value) -> Option widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add an option to SelectBox. Arguments --------- label The text or widget to be displayed as the option. value The value of the option (not displayed). """ label = self.get_label(label) label._cells["disabled"] = self._cells["disabled"] box.VBox.add(self, label) self.options[label] = value self.send(CHANGE) def click(): if label in self.selected: self.selected.remove(label) label.selected = False else: self.selected.add(label) label.selected = True if not self.multiple: for l in self.options.keys(): if l != label: l.selected = False self.selected = set((label, )) #CellSet((label,)) label.click = click return label def remove(self, option): """ SelectBox.remove(option) -> None ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Remove option from SelectBox. Arguments --------- option Can ether be a string representing an option's value or the option widget. """ if type(option) == str: for k, v in self.options.items(): if v == option: box.VBox.remove(self, self.options[k]) del self.options[k] else: box.VBox.remove(self, option) del self.options[option] self.send(CHANGE) def clear(self): """ SelectBox.clear() -> None ^^^^^^^^^^^^^^^^^^^^^^^^^ clears all options from SelectBox. """ rops = set() for v in self.options.keys(): rops.add(v) for v in rops: self.remove(v)
class TextBlock(widget.Widget): """ TextBlock(value) -> TextBlock widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Basic widget for wrapping and displaying some text. Supports line breaks (ether \\n or <br>) """ value = widget.ReplaceableCellDescriptor(restriction=StringRestriction()) def __init__(self, value, **params): widget.Widget.__init__(self, **params) self.value = value def font(self): """ Label.font -> pygame.Font ^^^^^^^^^^^^^^^^^^^^^^^^^ The font that this label is using. """ f = self.get_font(self.style_option["font-family"], self.style_option["font-size"]) if self.style_option["font-weight"] == "bold": f.set_bold(True) return f font = ComputedCellDescriptor(font) def font_x(self): x,_ = self.pos return x + self.style_option["padding-left"] font_x = ComputedCellDescriptor(font_x) def font_y(self): _,y = self.pos return y + self.style_option["padding-top"] font_y = ComputedCellDescriptor(font_y) def lines(self): # First we need to wrap the text (split it into lines). char_w,line_h = self.font.size("e") max_chars = self.style_option["width"]/char_w line_w,_ = self.font.size(max_chars*"e") value = self.value.replace("\n", "<br>") value = value.replace("<br>", " <br> ") words = value.split(" ") lines = [] line_data = "" for word in words: if word != "<br>" and self.font.size(line_data +" "+ word)[0] < line_w: # We can fit this word onto this line. line_data = line_data+" "+word else: # Flush old line and start a new one. lines.append(line_data.strip()) if word != "<br>": line_data = word else: line_data = "" lines.append(line_data.strip()) return lines lines = ComputedCellDescriptor(lines) def height(self): _,line_h = self.font.size("e") h = line_h*len(self.lines) + self.style_option["padding-bottom"]+\ self.style_option["padding-top"] return widget.Widget.height.function(self, h) height = ComputedCellDescriptor(height) def draw_widget(self): linenum = 0 _,line_h = self.font.size("e") for line in self.lines: x = self.font_x y = self.font_y + line_h*linenum self.blit(self.font.render(line, self.style_option["antialias"], self.style_option["color"]), (x,y)) linenum += 1
class Label(widget.Widget): """ Label(value) -> Label widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Basic widget for simply displaying some text. Not really much to it, you can pass it a value which it will display. """ value = widget.ReplaceableCellDescriptor(restriction=StringRestriction()) def __init__(self, value, **params): super(Label, self).__init__(self, **params) self.value = value def font(self): """ Label.font -> pygame.Font ^^^^^^^^^^^^^^^^^^^^^^^^^ The font that this label is using. """ f = self.get_font(self.style_option["font-family"], self.style_option["font-size"]) if self.style_option["font-weight"] == "bold": f.set_bold(True) return f font = ComputedCellDescriptor(font) def width(self): w = self.font.size(self.value)[0] return widget.Widget.width.function(self, w) width = ComputedCellDescriptor(width) def height(self): h = self.font.size(self.value)[1] return widget.Widget.height.function(self, h) height = ComputedCellDescriptor(height) def font_x(self): x, _ = self.pos return x + self.style_option["padding-left"] font_x = ComputedCellDescriptor(font_x) def font_y(self): _, y = self.pos return y + self.style_option["padding-top"] font_y = ComputedCellDescriptor(font_y) #def clip_rect(self): #return pygame.Rect(0,0, #self.width-(self.style_option["padding-left"]+\ #self.style_option["padding-right"]), #self.height-(self.style_option["padding-top"]+\ #self.style_option["padding-bottom"])) #clip_rect = ComputedCellDescriptor(clip_rect) def font_value(self): return self.font.render(self.value, self.style_option["antialias"], self.style_option["color"]) font_value = ComputedCellDescriptor(font_value) def draw_widget(self): # We do this incase we are trying to blit the text into a space smaller # than it can fit into. self.blit(self.font_value, (self.font_x, self.font_y), clip_rect=self.clip_rect)
class Input(widget.Widget): """ Input([value, [max_length, [disallowed_chars]]]) - Input widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Creates an HTML like input field. Arguments --------- value The initial value of the input (defaults to ""). Note that due to the nature of the value, you cannot link to it (but you can from it). If you really want to link it's value you can do so it _value, but it may not always work like you expect! max_length The maximum number of character that can be put into the Input. disallowed_chars A list of characters that are disallowed to enter. """ _value = widget.ReplaceableCellDescriptor() cur_pos = widget.ReplaceableCellDescriptor() max_length = widget.ReplaceableCellDescriptor() def __init__(self, value="", max_length=None, disallowed_chars=[], **params): widget.Widget.__init__(self, **params) self._value = u"" self.cur_pos = 0 self.max_length = max_length self.value = value self.disallowed_chars = disallowed_chars self.last_press = {K_BACKSPACE:0, K_LEFT:0, K_RIGHT:0, K_DELETE:0} def font(self): """ Input.font -> pygame.Font ^^^^^^^^^^^^^^^^^^^^^^^^^ The font that this input is using. """ f = self.get_font(self.style_option["font-family"], self.style_option["font-size"]) if self.style_option["font-weight"] == "bold": f.set_bold(True) return f font = ComputedCellDescriptor(font) def set_value(self,v): if v == None: v = '' v = unicode(v) if self.max_length and len(v) > self.max_length: v = v[0:self.max_length-1] self._value = v if self.cur_pos-1 > len(v): #print self.cur_pos-1, len(v) self.cur_pos = len(v) self.send(CHANGE) value = property(lambda self: self._value, set_value) def size(self): s,_ = self.font.size("e") num = self.style_option["width"]/s return self.font.size("e"*num) size = ComputedCellDescriptor(size) def width(self): w = self.size[0] + self.style_option["padding-left"]+\ self.style_option["padding-right"] return widget.Widget.width.function(self, w) width = ComputedCellDescriptor(width) def height(self): h = self.size[1] + self.style_option["padding-top"]+\ self.style_option["padding-bottom"] return widget.Widget.height.function(self, h) height = ComputedCellDescriptor(height) def font_clip_rect(self): vx = max(self.font.size(self.value)[0] - self.size[0], 0) return pygame.Rect((vx, 0), self.size) font_clip_rect = ComputedCellDescriptor(font_clip_rect) def font_x(self): x,_ = self.pos return x + self.style_option["padding-left"] font_x = ComputedCellDescriptor(font_x) def font_y(self): _,y = self.pos return y + self.style_option["padding-top"] font_y = ComputedCellDescriptor(font_y) def draw_widget(self): self.blit(self.font.render(self.value, self.style_option["antialias"], self.style_option["color"]), (self.font_x,self.font_y), clip_rect=self.font_clip_rect) if self.focused: w,h = self.font.size(self.value[0:self.cur_pos]) r = pygame.Surface((2,self.size[1])) r.fill(self.style_option["color"]) x = min(w, self.size[0]) self.blit(r, (self.font_x+x, self.font_y)) def repetitive_events(self): # Key repeating. Better way to do this? k = pygame.key.get_pressed() if k[K_BACKSPACE] and pygame.time.get_ticks()-self.last_press[K_BACKSPACE] > 300: self.value = self.value[:self.cur_pos-1] + self.value[self.cur_pos:] if self.cur_pos > 0: self.cur_pos -= 1 elif k[K_DELETE] and pygame.time.get_ticks()-self.last_press[K_DELETE] > 300: if len(self.value) > self.cur_pos: self.value = self.value[:self.cur_pos] + self.value[self.cur_pos+1:] elif k[K_LEFT] and pygame.time.get_ticks()-self.last_press[K_LEFT] > 300: if self.cur_pos > 0: self.cur_pos -= 1 elif k[K_RIGHT] and pygame.time.get_ticks()-self.last_press[K_RIGHT] > 300: if self.cur_pos < len(self.value): self.cur_pos += 1 def event(self,e): widget.Widget.event(self, e) if e.type == KEYDOWN: if e.key == K_BACKSPACE: if self.cur_pos: self.value = self.value[:self.cur_pos-1] + self.value[self.cur_pos:] if self.cur_pos > 0: self.cur_pos -= 1 self.last_press[K_BACKSPACE] = pygame.time.get_ticks() elif e.key == K_DELETE: if len(self.value) > self.cur_pos: self.value = self.value[:self.cur_pos] + self.value[self.cur_pos+1:] self.last_press[K_DELETE] = pygame.time.get_ticks() elif e.key == K_HOME: self.cur_pos = 0 elif e.key == K_END: self.cur_pos = len(self.value) elif e.key == K_LEFT: if self.cur_pos > 0: self.cur_pos -= 1 self.last_press[K_LEFT] = pygame.time.get_ticks() elif e.key == K_RIGHT: if self.cur_pos < len(self.value): self.cur_pos += 1 self.last_press[K_RIGHT] = pygame.time.get_ticks() elif e.key == K_ESCAPE: pass elif e.key == K_RETURN: self.next() else: if self.max_length: if len(self.value) == self.max_length: return c = e.unicode #c = (e.unicode).encode('latin-1') if c and c not in self.disallowed_chars: self.value = unicode(self.value)[:self.cur_pos] + c +\ unicode(self.value)[self.cur_pos:] self.cur_pos += 1
class Container(widget.Widget): """ Container([scrollable]) -> Container widget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A base container widget for containing other widgets. (gosh that's descriptive!) scrollable is for wether or not the container is, well, scrollable (this feature isn't complete yet, but still usable)! """ scrollable = widget.ReplaceableCellDescriptor() offset_x = widget.ReplaceableCellDescriptor() offset_y = widget.ReplaceableCellDescriptor() def __init__(self, scrollable=False, **params): super(Container, self).__init__(**params) self.widgets = CellList() self.scrollable = scrollable self.offset_x = 0 self.offset_y = 0 self.vslider = slider.VSlider(length=self.content_height, step=False, active=self.scrollable, height=self.style_option["height"]-\ self.style_option["padding-top"]-\ self.style_option["padding-bottom"]) self.vslider.x = self.width - self.vslider.width-\ self.style_option["padding-left"]-\ self.style_option["padding-right"] #self.vslider.container = self self.vslider.parent = self self.offset_y = self.vslider.link("value") self.vslider._cells["disabled"] = self._cells["disabled"] self.vslider._cells["length"] = self._cells["content_height"] self.vslider.connect(CHANGE, self.draw_widget) #def width(self, width=0): #width = widget.Widget.width.function(self, width) #return width+self.vslider.width #width = ComputedCellDescriptor(width) def content_height(self): return 0 content_height = ComputedCellDescriptor(content_height) def draw_widget(self): if not self.widgets: return for w in self.widgets: w.dirty = True self.vslider.dirty = True def draw(self, drawbg=True): if self.dirty: drawbg = False else: drawbg = True super(Container, self).draw() if not self.widgets: return for w in self.widgets: if w.active: w.draw(drawbg) if self.scrollable: self.vslider.draw(False) def exit(self): self.hovering = False if not self.widgets: return for w in self.widgets: w.exit() def event(self, e): widget.Widget.event(self, e) if not self.widgets: return for w in self.widgets: if w.active: w._event(e) if self.scrollable: self.vslider._event(e) def add(self, *widgets): """ Container.add(widgets) -> None ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add widgets to Container. Arguments --------- widgets can ether be a single widget or a list of widgets. """ for w in widgets: if type(w) == list: raise ValueError( "Got unexpected value. Remember that if you want to add multiple widgets to a container, do c.add(w1,w2,w3)!" ) self.widgets.append(w) w.container = self w.parent = self w.send(OPEN) def remove(self, widgets): """ Container.remove(widgets) -> None ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Remove widgets from Container. Arguments --------- widgets can ether be a single widget or a list of widgets. """ try: iter(widgets) except TypeError: widgets = [widgets] for w in widgets: self.widgets.remove(w) w.send(CLOSE) self.dirty = True def _next(self, orig=None): start = 0 if orig and orig in self.widgets: start = self.widgets.index(orig) + 1 for w in self.widgets[start:]: if not w.disabled and w.focusable: if isinstance(w, Container): if w._next(): return True else: self.focus(w) return True return False def next(self, w=None): if self._next(w): return True if self.container: return self.container.next(self)