Exemplo n.º 1
0
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 = util.ReplaceableCellDescriptor()
    on_label = util.ReplaceableCellDescriptor()
    off_label = util.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]
Exemplo n.º 2
0
class Image(util.widgets.Widget):
    """
    Image(value) -> Image widget
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    Basic widget for simply displaying an image. value should be a pygame
    surface.
    """
    value = util.ReplaceableCellDescriptor()

    def __init__(self, value, **params):
        util.widgets.Widget.__init__(self, **params)
        self.value = value

    def draw_widget(self):
        util.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)
Exemplo n.º 3
0
class _slider(util.widgets.Widget):

    _value = util.ReplaceableCellDescriptor()
    length = util.ReplaceableCellDescriptor()
    min_value = util.ReplaceableCellDescriptor()
    step = util.ReplaceableCellDescriptor()

    def __init__(self, value=0, min_value=0, length=20, step=True, **params):
        util.widgets.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 = util.widgets.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)
Exemplo n.º 4
0
class _box(util.widgets.Container):

    expand = util.ReplaceableCellDescriptor()

    def __init__(self, expand=True, **params):
        util.widgets.Container.__init__(self, **params)

        self.expand = expand

    def add(self, *widgets):
        super(_box, self).add(*widgets)
        self.position_widgets()

    def remove(self, widgets):
        util.widgets.Container.remove(self, widgets)
        self.position_widgets()
Exemplo n.º 5
0
class _button(util.widgets.Widget):

    _label = util.ReplaceableCellDescriptor()

    def __init__(self, **params):
        self._label = None
        super(_button, self).__init__(**params)

    def set_label(self, v):
        if hasattr(v, "value"):
            l = util.widgets.Label(v.value, parent=self)
            l._cells["value"] = ComputedCell(lambda: str(v.value))
        if type(v) == int:
            v = str(v)
        if type(v) == str:
            l = util.widgets.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 util.widgets.Widget.width.function(self)
        w = self.label.width + self.style_option[
            "padding-left"] + self.style_option["padding-right"]
        return util.widgets.Widget.width.function(self, w)

    width = ComputedCellDescriptor(width)

    def height(self):
        if not self.label:
            return util.widgets.Widget.height.function(self)
        h = self.label.height + self.style_option[
            "padding-top"] + self.style_option["padding-bottom"]
        return util.widgets.Widget.height.function(self, h)

    height = ComputedCellDescriptor(height)

    def draw_widget(self):
        self.label.dirty = True
        self.label.draw()
Exemplo n.º 6
0
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 = util.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)
Exemplo n.º 7
0
class Input(util.widgets.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 = util.ReplaceableCellDescriptor()
    cur_pos = util.ReplaceableCellDescriptor()
    max_length = util.ReplaceableCellDescriptor()

    def __init__(self,
                 value="",
                 max_length=None,
                 disallowed_chars=[],
                 **params):
        util.widgets.Widget.__init__(self, **params)
        self._value = ''
        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 = util.app.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 = str(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 util.widgets.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 util.widgets.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):
        util.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])
            util.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 util.get_ticks(
        ) - self.last_press[K_BACKSPACE] > 300:
            self.value = self.value[:self.cur_pos -
                                    1] + self.value[self.cur_pos:]
            self.cur_pos -= 1
        elif k[K_DELETE] and util.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 util.get_ticks() - self.last_press[K_LEFT] > 300:
            if self.cur_pos > 0: self.cur_pos -= 1
        elif k[K_RIGHT] and util.get_ticks() - self.last_press[K_RIGHT] > 300:
            if self.cur_pos < len(self.value): self.cur_pos += 1

    def 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:]
                    self.cur_pos -= 1
                    self.last_press[K_BACKSPACE] = util.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] = util.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] = util.get_ticks()
            elif e.key == K_RIGHT:
                if self.cur_pos < len(self.value): self.cur_pos += 1
                self.last_press[K_RIGHT] = util.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 = str(e.unicode)
                #c = (e.unicode).encode('latin-1')
                if c and c not in self.disallowed_chars:
                    self.value = self.value[:self.cur_pos] + c + self.value[
                        self.cur_pos:]
                    self.cur_pos += 1
Exemplo n.º 8
0
class TextBlock(util.widgets.Widget):
    """
    TextBlock(value) -> TextBlock widget
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    Basic widget for wrapping and displaying some text. Supports line breaks
    (ether \\n or <br>)
    """
    value = util.ReplaceableCellDescriptor(restriction=StringRestriction())

    def __init__(self, value, **params):
        util.widgets.Widget.__init__(self, **params)

        self.value = value

    def font(self):
        """
        Label.font -> pygame.Font
        ^^^^^^^^^^^^^^^^^^^^^^^^^
        The font that this label is using.
        """
        f = util.app.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 util.widgets.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
            util.blit(self.font.render(line, self.style_option["antialias"],
                    self.style_option["color"]), (x,y))
            linenum += 1
        #util.blit(self.font_value, (self.font_x,self.font_y))
Exemplo n.º 9
0
class SelectBox(util.widgets.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 = util.ReplaceableCellDescriptor()

    def __init__(self, multiple=False, **params):
        util.widgets.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 = util.widgets.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:
            l = util.widgets.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"]
        util.widgets.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:
                    util.widgets.VBox.remove(self, self.options[k])
                    del self.options[k]
        else:
            util.widgets.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)
Exemplo n.º 10
0
class Widget(DependantCell):
    """
    Widget([disabled, [active, [parent, [custom_styles]]]]) -> Widget
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    Base widget class.

    Example of use
    --------------
    ::

        mywidget = Widget(x=30, y=100, width=100, height=30)
        mywidget.connect(EVENT, myfunc, [values])

        # One of the cool things about GooeyPy is that you can change any style
        # widget specific options (like 'value' in the input and slider widgets)
        # and the widget will automatically update itself.

        mywidget.value = "GooeyPy is awesome!"

        # One thing to note is that while when initializing a widget you can
        # pass it arguments as custom styles (see below), you can't access those
        # styles directly (i.e. mywidget.width won't give you what you expect,
        # and it will raise an error if you try to assign to it). So if you want
        # to change or read a style option you would have to do something like
        # the following:

        mywidget.stylesets["default"]["width"] = 140

        # You can also do it like this:

        mywidget.style["width"] = 140

        # But be warned, widget.style returns the current styleset that's in
        # use! So if you are hovering over the widget when you change the width,
        # that width will only apply to that widget when you hover over it.
        # So if you want the width to apply to the widget at all times, you
        # can apply it to the "default" styleset (as shown above) in which
        # case it will apply to all stylesets that don't already have a width
        # style set. Or you can loop through mywidget.stylesets and set it for
        # each one, like so:

        for s in mywidget.stylesets.values():
            s["width"] = 140


    Arguments
    ---------
    disabled
        If a widget is disabled, the "disabled" styling will
        apply and it will cease to interact with the user.

    active
        This widget will be ignored all together if it's not active.

    parent
        This is used internally when creating new widget types.

    custom_styles
        When you pass any extra arguments they get passed as a
        custom style; the key being the style option and the
        value passed as the tyle's option.
        Like in our example above, we pass 'x', 'y', 'width',
        and 'height'. Those will automatically get converted
        into styling options specific to that widget. Please
        refer to the documentation on styling for all
        available options and valid values.
    """


    disabled  = util.ReplaceableCellDescriptor()
    hovering  = util.ReplaceableCellDescriptor()
    focused   = util.ReplaceableCellDescriptor()
    stylesets = util.ReplaceableCellDescriptor()
    #parent    = InputCellDescriptor()
    container = util.ReplaceableCellDescriptor()
    pressing  = util.ReplaceableCellDescriptor()
    selected  = util.ReplaceableCellDescriptor()
    active    = util.ReplaceableCellDescriptor()

    def __init__(self, disabled=False, active=True, parent=None, **params):
        DependantCell.__init__(self)

        self.hovering = False
        self.focused = False
        self.active = active

        # There is a subtle difference between parent and container. A widget
        # can have any other widget as a parent while a container must be a
        # Container widget. parent is used for inheriting styles and positioning
        # with padding and aligning. Normally, when a widget has a container,
        # it's parent is the same as it.
        self.parent = parent
        self.container = None

        self.connects = {}

        # Move to widgets that use it?
        self.selected = False

        self._last_blit_rect = None

        self.effect = None

        # Weather or not this widget is being pressed.
        self.pressing = 0

        # This widget is still drawn if disabled is True, it just doesn't work.
        self.disabled = disabled

        def get_style_option(key):
            s = self.style[key]
            if hasattr(s, "values"):
                return s.values
            return s
        self.style_option = ComputedDict(get_style_option)

        # FIXME: Adding one more causes a really wierd bug where sometimes a
        # widget will inherit from Widget styling when not using default style.
        self.stylesets = {"disabled":None, "hover":None, "default":None,
                "focused":None, "down":None}

        # Generate the stylesets.
        surf = None
        style = None
        s = util.app.get_style(self)

        if s:
            #TODO: clear unnecessary spaces.
            for cmd in s.split(";"):
                cmd = cmd.strip()
                t = cmd.split(":")
                if len(t) == 2:
                    option = t[0]
                    values = t[1].split(" ")

                    if option == "event":
                        # A new section!

                        if values[0] != "default" and not self.stylesets["default"]:
                            print "Missing default styling for "+str(self)+" You must have the styling for the default event defined first!"
                            sys.exit()

                        if (self.stylesets.has_key(values[0]) and not\
                                    self.stylesets[values[0]]) or not\
                                    self.stylesets.has_key(values[0]):
                            if values[0] != "default":
                                parent = self.stylesets["default"]
                            else: parent = None
                            style = util.widgets.StyleSet(self, parent)
                            self.stylesets[values[0]] = style
                        else:
                            style = self.stylesets[values[0]]

                    if option != "event":
                        style[option] = values

        # Set any stylesets that are still blank.
        for (e, v) in self.stylesets.items():
            if not v:
                self.stylesets[e] = util.widgets.StyleSet(self, self.stylesets["default"])

        for (option,values) in params.items():
            self.stylesets["default"][option] = values



    def link(self, attr):
        """
        Widget.link(attr) -> cellulose.DependencyCell
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        This is one of those very cool things in GooeyPy where you won't find
        anywhere else. You can have one widget share a value with one or more
        other widgets!
        In the below example, whenever the value of myslider changes, so will
        the fontsize for mylabel and vise-versa (and it automatically redraws
        the stuff too... of course).

        Example of use
        --------------
        ::

            myslider = Slider(value=10, length=20)
            mylabel = Label(value="hi", font_size=myslider.link("value"))

        Linking works with any attribute (that you would want to change) or
        styling option in any widget! Cool eh? Well, actually it's not quite all
        working perfectly, so expect a bug or two with linking certain
        attributes on certain widgets.

        There is one pit-fall with this... if the thing you are trying to link
        isn't an attribute or a Cell (i.e. linkable), it will return None (and
        that could cause rather obscure errors).
        """
        if attr in self._cells:
            return self._cells[attr]
        # Isn't a linkable cell, but perhaps there is a privet attribute that is
        elif attr[0] != "_":
            return self.link("_"+attr)
        else:
            return None



    def connect(self, code, func, *values):
        """
        Widget.connect(code, func, [values]) -> None
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        Connect an event to a callback function.

        Example of use
        --------------
        ::

            def onclick(value):
                print 'clicked', value

            mybutton = Button("GooeyPy")
            mybutton.connect(CLICK, onclick, "button")



        Arguments
        ---------
        code
            The event code to trigger the callback.

        func
            The callback

        values
            Extra values you may want to send on to your callback when
            it is called.
        """
        self.connects[code] = {'fnc':func,'values':values}



    def _set_dirty(self, v):
        self._dirty = v
    def _get_dirty(self):
        self._verify_possibly_changed_dependencies()
        return self._dirty
    dirty = property(_get_dirty, _set_dirty)

    def possibly_changed(self):
        pass


    def set_x(self,v):
        self.stylesets["default"]["x"] = v
    x = property(lambda self:int(self.stylesets["default"]["x"]), set_x)
    def set_y(self,v):
        self.stylesets["default"]["y"] = v
    y = property(lambda self:int(self.stylesets["default"]["y"]), set_y)

    def pos(self, pos=None):
        """
        Widget.pos -> Tuple
        ^^^^^^^^^^^^^^^^^^^
        Get the actual position that the widget should be drawn to and receive
        events at. """
        if pos:
            x,y = pos
        else:
            x = int(self.style_option["x"])
            y = int(self.style_option["y"])
        if self.style_option["position"] == "relative":
            if self.parent:
                px, py = self.parent.pos

                # Padding.
                x += px #+ int(self.parent.stylesets["default"]["padding-left"])
                y += py #+ int(self.parent.stylesets["default"]["padding-top"])

                # Alignment positioning.
                if self.style_option["align"] == "center":
                    x += (self.parent.width - self.width) / 2
                elif self.style_option["align"] == "right":
                    x += (self.parent.width - self.width)
                else:
                    x += int(self.parent.stylesets["default"]["padding-left"])

                if self.style_option["valign"] == "center":
                    y += (self.parent.height - self.height) / 2
                elif self.style_option["valign"] == "bottom":
                    y += (self.parent.height - self.height)
                else:
                    y += int(self.parent.stylesets["default"]["padding-top"])
            if self.container and self.container.scrollable:
                x -= self.container.offset_x
                y -= self.container.offset_y
        return (x,y)
    pos = ComputedCellDescriptor(pos)


    def width(self, w=None):
        """
        Widget.width -> int
        ^^^^^^^^^^^^^^^^^^^
        This returns the actual width of this widget. It takes into account
        padding (for widgets that contain other widgets), min-width and
        max-width styles.
        """
        if not w or self.style_option["width"]:
            w = self.style_option["width"] + self.style_option["padding-left"]+\
                    self.style_option["padding-right"]
            w = max(w,0)
        width = w
        min_width = self.style_option["min-width"]
        max_width = self.style_option["max-width"]
        if min_width and width < min_width:
            w = min_width
        if max_width and width > max_width:
            w = max_width
        return w
    width = ComputedCellDescriptor(width)


    def height(self, h=None):
        """
        Widget.height -> int
        ^^^^^^^^^^^^^^^^^^^^
        This returns the actual height of this widget. It takes into account
        padding (for widgets that contain other widgets), min-height and
        max-height styles.
        """
        if h is None or self.style_option["height"]:
            h = self.style_option["height"] + self.style_option["padding-top"]+\
                    self.style_option["padding-bottom"]
            h = max(h,0)
        height = h
        min_height = self.style_option["min-height"]
        max_height = self.style_option["max-height"]
        if min_height and height < min_height:
            h = min_height
        if max_height and height > max_height:
            h = max_height
        return h
    height = ComputedCellDescriptor(height)


    def rect(self):
        """
        Widget.rect -> pygame.Rect
        ^^^^^^^^^^^^^^^^^^^^^^^^^^
        Gets the rect of the widget's current surface.
        """
        r = self.surface.get_rect()
        r.x, r.y = self.pos
        return r
    rect = ComputedCellDescriptor(rect)

    def focusable(self):
        return self.style_option["focusable"]
    focusable = ComputedCellDescriptor(focusable)

    def style(self):
        """
        Widget.style -> StyleSet
        ^^^^^^^^^^^^^^^^^^^^^^^^
        Returns the current styleset in use (from Widget.stylesets)
        """
        if self.disabled:
            return self.stylesets["disabled"]
        elif self.pressing:
            return self.stylesets["down"]
        elif self.selected is True and self.stylesets.has_key("selected"):
            return self.stylesets["selected"]
        elif self.hovering:
            return self.stylesets["hover"]
        elif self.focused and self.stylesets["focused"]:
            return self.stylesets["focused"]
        else:
            return self.stylesets["default"]
    style = ComputedCellDescriptor(style)

    def surface(self):
        return self.style.surf
    surface = ComputedCellDescriptor(surface)


    def draw(self, drawbg=True):
        """
        Widget.draw() -> None
        ^^^^^^^^^^^^^^^^^^^^^
        draws the widget if necessary.
        """

        #HACK: Better place to put this? Here because draw is run every frame.
        if self.focused:
            self.repetitive_events()

        if self.dirty or self.effect or self.style_option["effect"] != 'none':
            # We don't want to keep around old dependencies. We gotta clear them
            # before adding the new ones.
            for dep in self.dependencies.values():
                dep.unregister_dependant(self)
            self.dependencies = {}

            self.push()
            try:
                surf = self.style.surf
                if self.effect:
                    # An effect is running.
                    r = self.effect.run(surf)
                    if not r:
                        self.effect = None
                    else:
                        surf = r
                elif self.style_option["effect"][0] == "pulsate":
                    i = int(self.style_option["effect"][1])
                    self.effect = effects.Pulsate(i)
                    surf = self.effect.run(surf)

                #print self.__class__.__name__

                x,y = self.pos
                # FIXME: won't work if widget doesn't have container... but
                # fixes the x=y=0 display bug.
                if drawbg and self._last_blit_rect and self.container:
                    lbrpos =(self._last_blit_rect.x, self._last_blit_rect.y)
                    self._last_blit_rect.x -= self.container.x
                    self._last_blit_rect.y -= self.container.y
                    util.blit(self.container.surface, lbrpos,
                            clip_rect=self._last_blit_rect)

                clip_rect = surf.get_rect()
                if self.container and self.container.scrollable:
                    #y -= self.container.offset_y
                    if self.container.offset_y > self.y+self.height:
                        return
                    if self.container.offset_y+self.container.height < self.y+\
                            self.container.style_option["padding-bottom"]+\
                            self.container.style_option["padding-top"]+\
                            self.height:
                        # Off the  bottom. Need to figure out how to do clipping
                        return
                
                    #clip_rect = surf.get_rect()#self.container.rect
                    clip_rect.x = 0#min(self.x-self.container.offset_x, self.x)
                    clip_rect.y = min(0, self.y-self.container.offset_y)

                    #y = min(y, self.container.height-self.height)

                    #if self.container.offset_y+self.container.height < self.y+\
                            #self.height+\
                            #self.container.style_option["padding-bottom"]+\
                            #self.container.style_option["padding-top"]:
                        #clip_rect.height = self.container.height-\
                                #self.container.style_option["padding-bottom"] - self.y
                        #print self.y
                self.clip_rect = clip_rect

                self._last_blit_rect = util.blit(surf, (x, y), clip_rect)
                self.draw_widget()
                self.dirty = False
            finally:
                self.pop()


    def draw_widget(self):
        """
        When writing your own widgets, you can overwrite this function
        to have custom processes when drawing.
        """
        pass

    def repetitive_events(self):
        pass

    def event(self, e):
        pass


    def click(self):
        """
        When writing your own widgets, you can overwrite this function which is
        called every time the widget is clicked.
        """
        pass

    def next(self):
        if self.container: self.container.next(self)

    def enter(self):
        self.hovering = True

    def exit(self):
        self.hovering = False


    def send(self,code,event=None):
        """
        Widget.send(code, [event]) -> None
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        Sends the event code 'code' and event 'event' (if suplied) to trigger
        any callbacks.
        """
        if code in self.connects:
            con = self.connects[code]

            fnc = con['fnc']
            values = list(con['values'])

            nargs = fnc.func_code.co_argcount
            names = list(fnc.func_code.co_varnames)[:nargs]
            if hasattr(fnc,'im_class'): names.pop(0)

            args = []
            magic = {'_event':event,'_code':code,'_widget':self}
            for name in names:
                if name in magic.keys():
                    args.append(magic[name])
                elif len(values):
                    args.append(values.pop(0))
                else:
                    break
            args.extend(values)
            fnc(*args)

    def _event(self,e):
        #return
        if self.disabled: return

        send = False
        if e.type == KEYDOWN:
            if self.focused:
                send = True
                if e.key == K_ESCAPE:
                    self.focused = False
        #elif e.type == KEYDOWN and e.key == K_TAB:
            #self.next()
        elif e.type == MOUSEBUTTONDOWN:
            if self.rect.collidepoint(e.pos):
                self.pressing = 1
                self.dirty = True
                self.focused = True
                send = True
            else:
                self.focused = False
        elif e.type == MOUSEBUTTONUP:
            if self.pressing == 1:
                self.pressing = 0
                self.dirty = True
                if self.rect.collidepoint(e.pos):
                    self.send(CLICK)
                    self.click()
                send = True
        elif e.type == MOUSEMOTION:
            # This takes quite a bit of processing.
            if self.rect.collidepoint(e.pos):
                if not self.hovering:
                    self.enter()

                send = True
            else:
                if self.hovering:
                    self.exit()

            if self.focused: send = True

        if send:
            self.send(e.type, e)
            self.event(e)
Exemplo n.º 11
0
class Label(util.widgets.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 = util.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 = util.app.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 util.widgets.Widget.width.function(self, w)

    width = ComputedCellDescriptor(width)

    def height(self):
        h = self.font.size(self.value)[1]
        return util.widgets.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.

        util.blit(self.font_value, (self.font_x, self.font_y),
                  clip_rect=self.clip_rect)
Exemplo n.º 12
0
class Container(util.widgets.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 = util.ReplaceableCellDescriptor()
    offset_x = util.ReplaceableCellDescriptor()
    offset_y = util.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 = util.widgets.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 = util.widgets.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):
        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)
Exemplo n.º 13
0
class StyleSet(object):
    """
    StyleSet(widget, [parent]) -> StyleSet object
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    A styleset is a collection of style options grouped together for a
    specific event for a widget. Each widget has an dictionary named
    "stylesets" which contains several stylesets. For example::

        {"disabled":styleset, "hover":styleset, "default":styleset,
                "focused":styleset, "down":styleset}

    When the widget is hovered for example it will use stylesets["hover"]
    At any time you can change a styling option for a widget by doing
    something like this::

        mywidget.stylesets["hover"]["color"] = (255,10,10)

    **WARNING:** Do not use the color (0,0,0)! It is used as the surface color
    key. That means that if you use it it won't show up! If you want to use the
    color black, use (5,5,5) or something (which is indistinguishable from pure
    black). This applies to font colors sometimes. :P

    Style options
    -------------

    ===============  =======================================================
    option           value
    ===============  =======================================================
    padding          8 4 4 5 | 4 5 | 10
    padding-top      3
    padding-right    3
    padding-bottom   3
    padding-left     3
    color            (255,255,255)
    font-weight      normal | bold
    font-family      Verra.ttf
    font-size        12
    effect           pulsate 5
    width            300
    height           200
    x                57
    y                102
    position         relative | absolute
    bgcolor          (200,0,0)
    bgimage          image.png repeat|repeat-x|repeat-y|no-repeat|slice
    border           (0,250,20) 3
    spacing          12 # For certain container widgets.
    opacity          25 # Alpha value. Valid otpions between 0 and 255.
    antialias        1 | 0
    align            left | right | center
    valign           top | bottom | center
    ===============  =======================================================

    Something to note about align and valign. When they are changed from their
    default values (top and left), the x and y values act as an offset. So if
    you are aligning a widget center and have an x value of 100, the widget will
    display 100 pixels to the right of the center of it's parent (or container)
    widget.

    Also, aligning only takes effect when positioning is relative (which is
    default).


    Arguments
    ---------
    widget
        The widget this styleset is for.

    parent
        Another styleset that this styleset should inherit from.
    """

    parent = util.ReplaceableCellDescriptor()
    widget = util.ReplaceableCellDescriptor()

    # Setting a default style option here will make it use it at all times
    # unless explicitly set. In other words, inheriting doesn't work for these
    # options.
    default_styles = {
        "padding-top": 0,
        "padding-right": 0,
        "padding-bottom": 0,
        "padding-left": 0,
        "spacing": 0,
        "font-weight": "normal",
        "effect": "none",
        "width": util.screen_width,
        "height": util.screen_height,
        "x": 0,
        "y": 0,
        "position": "relative",
    }

    styles = {
        "opacity": Opacity,
        "bgcolor": BgColor,
        "border": Border,
        "bgimage": BgImage,
        "color": Color,
        "padding": Padding,
        "effect": Effect,
    }

    def __init__(self, widget, parent=None):
        self.widget = widget
        self.parent = parent
        self._applied_styles = CellDict(self.default_styles)

        def __getitem__(v):
            return self.__getitem__(v)

        self._applied_styles.__getitem__ = __getitem__

    # We make this read only so people can only modify the applied_styles and
    # not reassign it.
    applied_styles = property(lambda self: self._applied_styles)

    def surf(self):
        """
        StyleSet.surf -> pygame.Surface
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        The surface generated from the styling.
        """
        if self.parent:
            self.surface = self.parent.surf.copy()
        else:
            size = (int(self.widget.width), int(self.widget.height))
            self.surface = pygame.Surface(size)
            self.surface.set_colorkey((0, 0, 0, 0))

        for s in self.applied_styles.values():
            if hasattr(s, "render"):
                s.render()
        return self.surface

    surf = ComputedCellDescriptor(surf)

    def __getitem__(self, v):
        r = None
        if self.applied_styles.has_key(v):
            r = self.applied_styles[v]
        if not r and self.parent:
            r = self.parent[v]
        if hasattr(r, "value"): r = r.value
        return r

    def __setitem__(self, k, v):
        self.apply(k, v)

    def apply(self, option, values):
        """
        StyleSet.apply(surf, option, values) -> pygame.Surface
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        Applies styling to surf. If you are just wanting to change a style
        option, use::

            StyleSet["option"] = values

        This function is mainly for internal use.
        """
        option = option.replace("_", "-")
        o = option.replace("-", "_")
        if type(values) == str or type(values) == int:
            if type(values) == str:
                values = values.split(" ")
            else:
                values = [values]
        if o in self.styles:
            self.styles[o](self, values)
        else:
            self.generic(option, values)

    def generic(self, n, v):
        if type(v) == list:
            if len(v) == 1:
                v = v[0]
                v = str(v)
                try:
                    v = int(v)
                except ValueError:
                    pass
        self.applied_styles[n] = v