class PopupLayout(BoxLayout):

    name = StringProperty()
    price = NumericProperty()
    description = StringProperty()
    img_path = StringProperty()
    quantity = BoundedNumericProperty(0, min=0, max=50)

    def __init__(self, **kwargs):
        super(PopupLayout, self).__init__(**kwargs)
        if item_quantity.get(self.name, 0) == 0:
            item_quantity[self.name] = [0, False]
            self.quantity = item_quantity[self.name][0]
        else:
            self.quantity = item_quantity.get(self.name)[0]

    # function called when confirm button in item popup pressed
    def save(self, *args):
        item_quantity[self.name][0] = self.quantity

    # function called when clear button in item popup pressed
    def clear(self, *args):
        item_quantity[self.name][0] = 0
예제 #2
0
파일: tabs.py 프로젝트: caixy6464/test
class MDBottomNavigationHeader(BaseFlatButton, BasePressedButton):
    width = BoundedNumericProperty(dp(0), min=dp(80), max=dp(168), errorhandler=lambda x: small_error_warn(x))
    tab = ObjectProperty(None)
    panel = ObjectProperty(None)
    _label = ObjectProperty()
    _label_font_size = NumericProperty(sp(12))
    _current_color = ListProperty([0.0, 0.0, 0.0, 0.0])
    text = StringProperty('')
    _capitalized_text = StringProperty('')
    active = BooleanProperty(False)

    def on_text(self, instance, value):
        self._capitalized_text = value.upper()

    def __init__(self, panel, height, tab):
        self.panel = panel
        self.height = height
        self.tab = tab
        super(MDBottomNavigationHeader, self).__init__()
        self._current_color = self.theme_cls.disabled_hint_text_color
        self._label = self.ids._label
        self._label_font_size = sp(12)
        self.theme_cls.bind(primary_color=self._update_theme_color,
                            disabled_hint_text_color=self._update_theme_style)
        self.active = False

    def on_press(self):
        Animation(_label_font_size=sp(14), d=0.1).start(self)
        Animation(_current_color=self.theme_cls.primary_color, d=0.1).start(self)

    def _update_theme_color(self, instance, color):
        if self.active:
            self._current_color = self.theme_cls.primary_color

    def _update_theme_style(self, instance, color):
        if not self.active:
            self._current_color = self.theme_cls.disabled_hint_text_color
예제 #3
0
class BaseRectangularButton(RectangularRippleBehavior, BaseButton):
    """
    Abstract base class for all rectangular buttons, bringing in the
    appropriate on-touch behavior. Also maintains the correct minimum width
    as stated in guidelines.
    """

    width = BoundedNumericProperty(
        88, min=88, max=None, errorhandler=lambda x: 88
    )
    text = StringProperty("")
    """Button text.

    :attr:`text` is an :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    increment_width = NumericProperty("32dp")
    """
    Button extra width value.

    :attr:`increment_width` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `'32dp'`.
    """

    button_label = BooleanProperty(True)
    """
    If ``False`` the text on the button will not be displayed.

    :attr:`button_label` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    can_capitalize = BooleanProperty(True)

    _radius = NumericProperty("2dp")
    _height = NumericProperty(0)
예제 #4
0
    def test_bounded_numeric_property_error_handler(self):
        from kivy.properties import BoundedNumericProperty

        bnp = BoundedNumericProperty(0,
                                     min=-5,
                                     max=5,
                                     errorhandler=lambda x: 5 if x > 5 else -5)

        bnp.link(wid, 'bnp')

        bnp.set(wid, 1)
        self.assertEqual(bnp.get(wid), 1)

        bnp.set(wid, 5)
        self.assertEqual(bnp.get(wid), 5)

        bnp.set(wid, 10)
        self.assertEqual(bnp.get(wid), 5)

        bnp.set(wid, -5)
        self.assertEqual(bnp.get(wid), -5)

        bnp.set(wid, -10)
        self.assertEqual(bnp.get(wid), -5)
예제 #5
0
class Timer(Widget):

    delay = BoundedNumericProperty(1, min=.005, max=3600)  # seconds
    oneshot = BooleanProperty(False)
    autoplay = BooleanProperty(False)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.register_event_type('on_timeout')
        self.bind(autoplay=self.start)

        self._clock = None
        self._scheduler = Clock.create_trigger
        self.delta = 0.

    def on_timeout(self):
        pass

    def start(self, *args):
        if self._clock and self._clock.is_triggered:
            return
        if not self._clock:
            self._clock = self._scheduler(self._tick, self.delay)
            self._clock()
        else:
            self._clock()

    def stop(self):
        if self._clock:
            self._clock.cancel()

    def _tick(self, dt):
        self.delta = dt
        self.dispatch('on_timeout')
        if not self.oneshot:
            self._clock()
예제 #6
0
class Layers(Widget):
  r1 = ObjectProperty(None)
  r2 = ObjectProperty(None)

  r_y = BoundedNumericProperty(0,min=-100,max=100)
  inc = 1

  def __init__(self,*args,**kwargs):
    super(Layers,self).__init__(*args,**kwargs)
    Clock.schedule_interval(self.update,0)

  def update(self,t):
    try:
      self.r_y += self.inc
      self.r2.y += self.inc
    except ValueError:
      self.inc *= -1

    children_sorted = sorted(self.children,key=lambda w: w.y)
    children_sorted.reverse()

    for i in children_sorted:
      self.remove_widget(i)
      self.add_widget(i)
예제 #7
0
class TestCar(Widget):

    angle = BoundedNumericProperty(0.)
    rotation = BoundedNumericProperty(0.)
    velocity_x = BoundedNumericProperty(0.)
    velocity_y = BoundedNumericProperty(0.)
    deceleration_x = BoundedNumericProperty(0.)
    deceleration_y = BoundedNumericProperty(0.)

    velocity = ReferenceListProperty(velocity_x, velocity_y)
    deceleration = ReferenceListProperty(deceleration_x, deceleration_y)

    def rotate(self, rotation):
        # print ("Entered rotate method")
        self.pos = Vector(*self.velocity) + self.pos
        self.rotation = float(rotation)
        self.angle = self.angle + self.rotation

    def accelerate(self, acceleration_x):
        # print ("Entered  method: ", inspect.stack()[0][3])
        self.velocity = Vector(*self.velocity) + Vector(acceleration_x, 0)
        if self.velocity_x > 1:
            self.velocity_x = 1
        elif (self.velocity_x < 0):  # To avoid car moving backwards
            self.velocity_x = 0
        self.pos = Vector(*self.velocity) + self.pos  # change property type
        # print ("pos1 :",self.pos , "  vel1 :  " , self.velocity)

    def decelerate(self, deceleration_x):
        # print ("Entered  method: ", inspect.stack()[0][3])
        self.velocity = Vector(*self.velocity) - Vector(deceleration_x, 0)
        if self.velocity_x > 1:
            self.velocity_x = 1
        elif (self.velocity_x < 0):  # To avoid car moving backwards
            self.velocity_x = 0
        self.pos = Vector(*self.velocity) + self.pos
예제 #8
0
class PhotoAlbumScreen(Screen):
    """Base screen to run the photo album."""

    # Reference to the screen manager
    photoscreen = ObjectProperty(None)

    # Value for the screen display time
    photoduration = BoundedNumericProperty(5, min=2, max=60, errorvalue=5)

    def __init__(self, **kwargs):
        #super(PhotoAlbumScreen, self).__init__(**kwargs)
        super(PhotoAlbumScreen, self).__init__()
        self.name = kwargs["name"]

        # Get the user's preferences
        self.folders = kwargs["params"]["folders"]
        self.exts = kwargs["params"]["extensions"]
        self.photoduration = kwargs["params"]["duration"]

        # Initialise some useful variables
        self.running = False
        self.photos = []
        self.timer = None
        self.oldPhoto = None
        self.photoindex = 0

    def on_enter(self):

        if not self.running:

            # The screen hasn't been run before so let's tell the user
            # that we need to get the photos
            self.loading = PhotoLoading(name="loading")
            self.photoscreen.add_widget(self.loading)
            self.photoscreen.current = "loading"

            # Retrieve photos
            Clock.schedule_once(self.getPhotos, 0.5)

        else:
            # We've been here before so just show the photos
            self.timer = Clock.schedule_interval(self.showPhoto,
                                                 self.photoduration)

    def on_leave(self):

        # We can stop looping over photos
        Clock.unschedule(self.timer)

    def getPhotos(self, *args):
        """Method to retrieve list of photos based on user's preferences."""

        # Get a list of extensions. Assumes all caps or all lower case.
        exts = []
        for ext in ([x.upper(), x.lower()] for x in self.exts):
            exts.extend(ext)

        # Loop over the folders
        for folder in self.folders:

            # Look for each extension
            for ext in exts:

                # Get the photos
                photos = glob(os.path.join(folder, "*.{}".format(ext)))

                # Add to our master list
                self.photos.extend(photos)

        # Put the photos in order
        self.photos.sort()

        # We've got the photos so we can set the running flag
        self.running = True

        # and start the timer
        self.timer = Clock.schedule_interval(self.showPhoto,
                                             self.photoduration)

        # Show the first photo
        self.showPhoto()

    def showPhoto(self, *args):
        """Method to update the currently displayed photo."""

        # Get the current photo
        photo = self.photos[self.photoindex]
        fehler = True
        while fehler:
            try:
                print("Foto laden")
                photo = self.photos[self.photoindex]
                fehler = False
            except:
                print("Es gab ein Problem beim Foto laden")
                fehler = True
                self.photoindex = (self.photoindex + 1) % len(self.photos)
        
        if fehler:
            print("Ich lad lieber mal das erste Bild")
            photo = self.photos[0]
        
        print("Photo: ", photo)
        print(type(photo))

        # Create a screen pbject to show that photo
        scr = Photo(name=photo)

        # Add it to our screenmanager and display it
        self.photoscreen.add_widget(scr)
        self.photoscreen.current = photo

        # If we've got an old photo
        if self.oldPhoto:

            # We can unload it
            self.photoscreen.remove_widget(self.oldPhoto)

        # Create reference to the new photo
        self.oldPhoto = scr

        # Increase our index for the next photo
        self.photoindex = (self.photoindex + 1) % len(self.photos)
예제 #9
0
class ProgressSpinnerBase(Widget):
    '''ProgressSpinnerBase - base class for progress spinner widgets
	'''

    color = ListProperty([1, 1, 1, 1])
    '''Color to render the spinner.

	:attr:`color` is a :class:`~kivy.properties.ListProperty` and defaults
	to [1, 1, 1, 1] (white, full opacity).
	'''

    speed = BoundedNumericProperty(1, min=0.1)
    '''Speed coefficient of the spinner. This value is multiplied by the
	base speed of 90 degrees per second.

	:attr:`speed` is a :class:`~kivy.properties.BoundedNumericProperty` and
	defaults to 1.
	'''

    stroke_length = BoundedNumericProperty(25., min=1, max=180)
    '''Base length of the stroke in degrees.

	:attr:`stroke_length` is a :class:`~kivy.properties.BoundedNumericProperty`
	and defaults to 25.
	'''

    stroke_width = NumericProperty(None, allownone=True)
    '''Width of the stroke in pixels. If set to None, the width will be
	calculated automatically as 1/20th of the radius.

	:attr:`stroke_width` is a :class:`~kivy.properties.NumericProperty` and
	defaults to None.
	'''

    auto_start = BooleanProperty(False)
    '''Whether to automatically start spinning.

	:attr:`auto_start` is a :class:`~kivy.properties.BooleanProperty` and
	defaults to True.
	'''

    # internal properties
    _angle_center = NumericProperty(0)
    _angle_start = NumericProperty()
    _angle_end = NumericProperty()
    _size = NumericProperty()
    _rsize = NumericProperty()
    _stroke = NumericProperty(1)
    _radius = NumericProperty(50)

    def __init__(self, **kwargs):
        super(ProgressSpinnerBase, self).__init__(**kwargs)
        self._state = 'wait1'
        self._next = None
        self._spinning = False

        if self.auto_start:
            self.start_spinning()

    def start_spinning(self, *args):
        '''Start spinning the progress spinner. Ignores all positional args
		for easy binding.
		'''
        if not self._spinning:
            self._state = 'wait1'
            self._next = None
            self._angle_center = 0.
            self._angle_start = 360.
            self._angle_end = 360. + self.stroke_length
            Clock.schedule_interval(self._update, 0)
            Clock.schedule_once(self._rotate, 0.3)
            self._spinning = True

    def stop_spinning(self, *args):
        '''Stop spinning the progress spinner. Ignores all positional args
		for easy binding.

		If you intend to keep the spinner around, you should stop it when
		not using it and restart it when needed again.
		'''
        if self._spinning:
            if self._next:
                if isinstance(self._next, Animation):
                    self._next.cancel(self)
                else:
                    self._next.cancel()
            Clock.unschedule(self._update)
            Clock.unschedule(self._rotate)
            self._angle_start = self._angle_end = 0
            self._spinning = False

    def _update(self, dt):
        angle_speed = 90. * self.speed
        self._angle_center += dt * angle_speed

        if self._angle_center > 360:
            self._angle_center -= 360.

    def _rotate(self, *args):
        if not self._spinning:
            return

        rotate_speed = 0.6 / self.speed
        wait_speed = 0.3 / self.speed
        if self._state == 'wait1':
            self._state = 'rotate1'
            self._next = Animation(_angle_end=self._angle_start + 360. -
                                   self.stroke_length,
                                   d=rotate_speed,
                                   t='in_quad')
            self._next.bind(on_complete=self._rotate)
            self._next.start(self)
        elif self._state == 'rotate1':
            self._state = 'wait2'
            self._next = Clock.schedule_once(self._rotate, wait_speed)
        elif self._state == 'wait2':
            self._state = 'rotate2'
            self._next = Animation(_angle_start=self._angle_end -
                                   self.stroke_length,
                                   d=rotate_speed,
                                   t='in_quad')
            self._next.bind(on_complete=self._rotate)
            self._next.start(self)
        elif self._state == 'rotate2':
            self._state = 'wait1'
            self._next = Clock.schedule_once(self._rotate, wait_speed)
            while self._angle_end > 720.:
                self._angle_start -= 360.
                self._angle_end -= 360.
예제 #10
0
class CircularNumberPicker(CircularLayout):
    """A circular number picker based on CircularLayout. A selector will
    help you pick a number. You can also set :attr:`multiples_of` to make
    it show only some numbers and use the space in between for the other
    numbers.
    """

    min = NumericProperty(0)
    """The first value of the range.
    :attr:`min` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0.
    """

    max = NumericProperty(0)
    """The last value of the range. Note that it behaves like range, so
    the actual last displayed value will be :attr:`max` - 1.
    :attr:`max` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0.
    """

    range = ReferenceListProperty(min, max)
    """Packs :attr:`min` and :attr:`max` into a list for convenience. See
    their documentation for further information.
    :attr:`range` is a :class:`~kivy.properties.ReferenceListProperty`.
    """

    multiples_of = NumericProperty(1)
    """Only show numbers that are multiples of this number. The other numbers
    will be selectable, but won't have their own label.
    :attr:`multiples_of` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 1.
    """

    selector_color = ListProperty([.337, .439, .490])
    """Color of the number selector. RGB.
    :attr:`selector_color` is a :class:`~kivy.properties.ListProperty` and
    defaults to [.337, .439, .490] (material green).
    """

    color = ListProperty([1, 1, 1])
    """Color of the number labels and of the center dot. RGB.
    :attr:`color` is a :class:`~kivy.properties.ListProperty` and
    defaults to [1, 1, 1] (white).
    """

    selector_alpha = BoundedNumericProperty(.3, min=0, max=1)
    """Alpha value for the transparent parts of the selector.
    :attr:`selector_alpha` is a :class:`~kivy.properties.BoundedNumericProperty` and
    defaults to 0.3 (min=0, max=1).
    """

    selected = NumericProperty(None)
    """Currently selected number.
    :attr:`selected` is a :class:`~kivy.properties.NumericProperty` and
    defaults to :attr:`min`.
    """

    number_size_factor = NumericProperty(.5)
    """Font size scale factor fot the :class:`Number`s.
    :attr:`number_size_factor` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0.5.
    """

    number_format_string = StringProperty("{}")
    """String that will be formatted with the selected number as the first argument.
    Can be anything supported by :meth:`str.format` (es. "{:02d}").
    :attr:`number_format_string` is a :class:`~kivy.properties.StringProperty` and
    defaults to "{}".
    """

    scale = NumericProperty(1)
    """Canvas scale factor. Used in :class:`CircularTimePicker` transitions.
    :attr:`scale` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 1.
    """

    _selection_circle = ObjectProperty(None)
    _selection_line = ObjectProperty(None)
    _selection_dot = ObjectProperty(None)
    _selection_dot_color = ObjectProperty(None)
    _selection_color = ObjectProperty(None)
    _center_dot = ObjectProperty(None)
    _center_color = ObjectProperty(None)

    def _get_items(self):
        return self.max - self.min

    items = AliasProperty(_get_items, None)

    def _get_shown_items(self):
        c = 0
        for i in range(*self.range):
            if i % self.multiples_of == 0:
                c += 1
        return c

    shown_items = AliasProperty(_get_shown_items, None)

    def __init__(self, **kw):
        self._trigger_genitems = Clock.create_trigger(self._genitems, -1)
        self.bind(min=self._trigger_genitems,
                  max=self._trigger_genitems,
                  multiples_of=self._trigger_genitems)
        super(CircularNumberPicker, self).__init__(**kw)
        self.selected = self.min
        self.bind(selected=self.on_selected,
                  pos=self.on_selected,
                  size=self.on_selected)

        cx = self.center_x + self.padding[0] - self.padding[2]
        cy = self.center_y + self.padding[3] - self.padding[1]
        sx, sy = self.pos_for_number(self.selected)
        epos = [
            i - (self.delta_radii * self.number_size_factor) for i in (sx, sy)
        ]
        esize = [self.delta_radii * self.number_size_factor * 2] * 2
        dsize = [i * .3 for i in esize]
        dpos = [i + esize[0] / 2. - dsize[0] / 2. for i in epos]
        csize = [i * .05 for i in esize]
        cpos = [i - csize[0] / 2. for i in (cx, cy)]
        dot_alpha = 0 if self.selected % self.multiples_of == 0 else 1
        color = list(self.selector_color)

        with self.canvas:
            self._selection_color = Color(*(color + [self.selector_alpha]))
            self._selection_circle = Ellipse(pos=epos, size=esize)
            self._selection_line = Line(points=[cx, cy, sx, sy])
            self._selection_dot_color = Color(*(color + [dot_alpha]))
            self._selection_dot = Ellipse(pos=dpos, size=dsize)
            self._center_color = Color(*self.color)
            self._center_dot = Ellipse(pos=cpos, size=csize)

        self.bind(selector_color=lambda ign, c: setattr(
            self._selection_color, "rgba", c + [self.selector_alpha]))
        self.bind(selector_color=lambda ign, c: setattr(
            self._selection_dot_color, "rgb", c))
        self.bind(color=lambda ign, c: setattr(self._center_color, "rgb", c))
        Clock.schedule_once(self._genitems)
        Clock.schedule_once(
            self.on_selected)  # Just to make sure pos/size are set

    def _genitems(self, *a):
        self.clear_widgets()
        for i in range(*self.range):
            if i % self.multiples_of != 0:
                continue
            n = Number(text=self.number_format_string.format(i),
                       size_factor=self.number_size_factor,
                       color=self.color)
            self.bind(color=n.setter("color"))
            self.add_widget(n)

    def on_touch_down(self, touch):
        if not self.collide_point(*touch.pos):
            return
        touch.grab(self)
        self.selected = self.number_at_pos(*touch.pos)

    def on_touch_move(self, touch):
        if touch.grab_current is not self:
            return super(CircularNumberPicker, self).on_touch_move(touch)
        self.selected = self.number_at_pos(*touch.pos)

    def on_touch_up(self, touch):
        if touch.grab_current is not self:
            return super(CircularNumberPicker, self).on_touch_up(touch)
        touch.ungrab(self)

    def on_selected(self, *a):
        cx = self.center_x + self.padding[0] - self.padding[2]
        cy = self.center_y + self.padding[3] - self.padding[1]
        sx, sy = self.pos_for_number(self.selected)
        epos = [
            i - (self.delta_radii * self.number_size_factor) for i in (sx, sy)
        ]
        esize = [self.delta_radii * self.number_size_factor * 2] * 2
        dsize = [i * .3 for i in esize]
        dpos = [i + esize[0] / 2. - dsize[0] / 2. for i in epos]
        csize = [i * .05 for i in esize]
        cpos = [i - csize[0] / 2. for i in (cx, cy)]
        dot_alpha = 0 if self.selected % self.multiples_of == 0 else 1

        if self._selection_circle:
            self._selection_circle.pos = epos
            self._selection_circle.size = esize
        if self._selection_line:
            self._selection_line.points = [cx, cy, sx, sy]
        if self._selection_dot:
            self._selection_dot.pos = dpos
            self._selection_dot.size = dsize
        if self._selection_dot_color:
            self._selection_dot_color.a = dot_alpha
        if self._center_dot:
            self._center_dot.pos = cpos
            self._center_dot.size = csize

        # print self.selected

    def pos_for_number(self, n):
        """Returns the center x, y coordinates for a given number.
        """

        if self.items == 0:
            return 0, 0
        radius = min(self.width - self.padding[0] - self.padding[2],
                     self.height - self.padding[1] - self.padding[3]) / 2.
        middle_r = radius * sum(self.radius_hint) / 2.
        cx = self.center_x + self.padding[0] - self.padding[2]
        cy = self.center_y + self.padding[3] - self.padding[1]
        sign = +1.
        angle_offset = radians(self.start_angle)
        if self.direction == 'cw':
            angle_offset = 2 * pi - angle_offset
            sign = -1.
        quota = 2 * pi / self.items
        mult_quota = 2 * pi / self.shown_items
        angle = angle_offset + n * sign * quota

        if self.items == self.shown_items:
            angle += quota / 2
        else:
            angle -= mult_quota / 2

        # kived: looking it up, yes. x = cos(angle) * radius + centerx; y = sin(angle) * radius + centery
        x = cos(angle) * middle_r + cx
        y = sin(angle) * middle_r + cy

        return x, y

    def number_at_pos(self, x, y):
        """Returns the number at a given x, y position. The number is found
        using the widget's center as a starting point for angle calculations.
        Not thoroughly tested, may yield wrong results.
        """
        if self.items == 0:
            return self.min
        cx = self.center_x + self.padding[0] - self.padding[2]
        cy = self.center_y + self.padding[3] - self.padding[1]
        lx = x - cx
        ly = y - cy
        quota = 2 * pi / self.items
        mult_quota = 2 * pi / self.shown_items
        if lx == 0 and ly > 0:
            angle = pi / 2
        elif lx == 0 and ly < 0:
            angle = 3 * pi / 2
        else:
            angle = atan(ly / lx)
            if lx < 0 and ly > 0:
                angle += pi
            if lx > 0 and ly < 0:
                angle += 2 * pi
            if lx < 0 and ly < 0:
                angle += pi
        angle += radians(self.start_angle)
        if self.direction == "cw":
            angle = 2 * pi - angle
        if mult_quota != quota:
            angle -= mult_quota / 2
        if angle < 0:
            angle += 2 * pi
        elif angle > 2 * pi:
            angle -= 2 * pi

        return int(angle / quota) + self.min
예제 #11
0
class CircularLayout(Layout):
    '''Circular layout class. See module documentation for more information.
    '''

    padding = VariableListProperty([0, 0, 0, 0])
    '''Padding between the layout box and it's children: [padding_left,
    padding_top, padding_right, padding_bottom].

    padding also accepts a two argument form [padding_horizontal,
    padding_vertical] and a one argument form [padding].

    .. versionchanged:: 1.7.0
        Replaced NumericProperty with VariableListProperty.

    :attr:`padding` is a :class:`~kivy.properties.VariableListProperty` and
    defaults to [0, 0, 0, 0].
    '''

    start_angle = NumericProperty(0)
    '''Angle (in degrees) at which the first widget will be placed.
    Start counting angles from the X axis, going counterclockwise.

    :attr:`start_angle` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0 (start from the right).
    '''

    circle_quota = BoundedNumericProperty(360, min=0, max=360)
    '''Size (in degrees) of the part of the circumference that will actually
    be used to place widgets.

    :attr:`circle_quota` is a :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to 360 (all the circumference).
    '''

    direction = OptionProperty("ccw", options=("cw", "ccw"))
    '''Direction of widgets in the circle.

    :attr:`direction` is an :class:`~kivy.properties.OptionProperty` and
    defaults to 'ccw'. Can be 'ccw' (counterclockwise) or 'cw' (clockwise).
    '''

    outer_radius_hint = NumericProperty(1)
    '''Sets the size of the outer circle. A number greater than 1 will make the
    widgets larger than the actual widget, a number smaller than 1 will leave
    a gap.

    :attr:`outer_radius_hint` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 1.
    '''

    inner_radius_hint = NumericProperty(.6)
    '''Sets the size of the inner circle. A number greater than
    :attr:`outer_radius_hint` will cause glitches. The closest it is to
    :attr:`outer_radius_hint`, the smallest will be the widget in the layout.

    :attr:`outer_radius_hint` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 1.
    '''

    radius_hint = ReferenceListProperty(inner_radius_hint, outer_radius_hint)
    '''Combined :attr:`outer_radius_hint` and :attr:`inner_radius_hint` in a list
    for convenience. See their documentation for more details.

    :attr:`radius_hint` is a :class:`~kivy.properties.ReferenceListProperty`.
    '''
    def _get_delta_radii(self):
        radius = min(self.width - self.padding[0] - self.padding[2],
                     self.height - self.padding[1] - self.padding[3]) / 2.
        outer_r = radius * self.outer_radius_hint
        inner_r = radius * self.inner_radius_hint
        return outer_r - inner_r

    delta_radii = AliasProperty(_get_delta_radii,
                                None,
                                bind=("radius_hint", "padding", "size"))

    def __init__(self, **kwargs):
        super(CircularLayout, self).__init__(**kwargs)

        self.bind(
            start_angle=self._trigger_layout,
            parent=self._trigger_layout,
            # padding=self._trigger_layout,
            children=self._trigger_layout,
            size=self._trigger_layout,
            radius_hint=self._trigger_layout,
            pos=self._trigger_layout)

    def do_layout(self, *largs):
        # optimize layout by preventing looking at the same attribute in a loop
        len_children = len(self.children)
        if len_children == 0:
            return
        selfcx = self.center_x
        selfcy = self.center_y
        direction = self.direction
        cquota = radians(self.circle_quota)
        # selfw = self.width
        # selfh = self.height
        start_angle_r = radians(self.start_angle)
        padding_left = self.padding[0]
        padding_top = self.padding[1]
        padding_right = self.padding[2]
        padding_bottom = self.padding[3]
        padding_x = padding_left + padding_right
        padding_y = padding_top + padding_bottom

        radius = min(self.width - padding_x, self.height - padding_y) / 2.
        outer_r = radius * self.outer_radius_hint
        inner_r = radius * self.inner_radius_hint
        middle_r = radius * sum(self.radius_hint) / 2.
        delta_r = outer_r - inner_r

        # calculate maximum space used by size_hint
        stretch_weight_angle = 0.
        for w in self.children:
            sha = w.size_hint_x
            if sha is None:
                raise ValueError(
                    "size_hint_x cannot be None in a CircularLayout")
            else:
                stretch_weight_angle += sha

        sign = +1.
        angle_offset = start_angle_r
        if direction == 'cw':
            angle_offset = 2 * pi - start_angle_r
            sign = -1.

        for c in reversed(self.children):
            sha = c.size_hint_x
            shs = c.size_hint_y

            angle_quota = cquota / stretch_weight_angle * sha
            angle = angle_offset + (sign * angle_quota / 2)
            angle_offset += sign * angle_quota

            # kived: looking it up, yes. x = cos(angle) * radius + centerx; y = sin(angle) * radius + centery
            ccx = cos(angle) * middle_r + selfcx + padding_left - padding_right
            ccy = sin(angle) * middle_r + selfcy + padding_bottom - padding_top

            c.center_x = ccx
            c.center_y = ccy
            if shs:
                s = delta_r * shs
                c.width = s
                c.height = s
예제 #12
0
class Scatter(Widget):
    '''Scatter class. See module documentation for more information.

    :Events:
        `on_transform_with_touch`:
            Fired when the scatter has been transformed by user touch
            or multitouch, such as panning or zooming.
        `on_bring_to_front`:
            Fired when the scatter is brought to the front.

    .. versionchanged:: 1.9.0
        Event `on_bring_to_front` added.

    .. versionchanged:: 1.8.0
        Event `on_transform_with_touch` added.
    '''

    __events__ = ('on_transform_with_touch', 'on_bring_to_front')

    auto_bring_to_front = BooleanProperty(True)
    '''If True, the widget will be automatically pushed on the top of parent
    widget list for drawing.

    :attr:`auto_bring_to_front` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to True.
    '''

    do_translation_x = BooleanProperty(True)
    '''Allow translation on the X axis.

    :attr:`do_translation_x` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to True.
    '''

    do_translation_y = BooleanProperty(True)
    '''Allow translation on Y axis.

    :attr:`do_translation_y` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to True.
    '''
    def _get_do_translation(self):
        return (self.do_translation_x, self.do_translation_y)

    def _set_do_translation(self, value):
        if type(value) in (list, tuple):
            self.do_translation_x, self.do_translation_y = value
        else:
            self.do_translation_x = self.do_translation_y = bool(value)

    do_translation = AliasProperty(_get_do_translation,
                                   _set_do_translation,
                                   bind=('do_translation_x',
                                         'do_translation_y'),
                                   cache=True)
    '''Allow translation on the X or Y axis.

    :attr:`do_translation` is an :class:`~kivy.properties.AliasProperty` of
    (:attr:`do_translation_x` + :attr:`do_translation_y`)
    '''

    translation_touches = BoundedNumericProperty(1, min=1)
    '''Determine whether translation was triggered by a single or multiple
    touches. This only has effect when :attr:`do_translation` = True.

    :attr:`translation_touches` is a :class:`~kivy.properties.NumericProperty`
    and defaults to 1.

    .. versionadded:: 1.7.0
    '''

    do_rotation = BooleanProperty(True)
    '''Allow rotation.

    :attr:`do_rotation` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to True.
    '''

    do_scale = BooleanProperty(True)
    '''Allow scaling.

    :attr:`do_scale` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to True.
    '''

    do_collide_after_children = BooleanProperty(False)
    '''If True, the collision detection for limiting the touch inside the
    scatter will be done after dispaching the touch to the children.
    You can put children outside the bounding box of the scatter and still be
    able to touch them.

    :attr:`do_collide_after_children` is a
    :class:`~kivy.properties.BooleanProperty` and defaults to False.

    .. versionadded:: 1.3.0
    '''

    scale_min = NumericProperty(0.01)
    '''Minimum scaling factor allowed.

    :attr:`scale_min` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0.01.
    '''

    scale_max = NumericProperty(1e20)
    '''Maximum scaling factor allowed.

    :attr:`scale_max` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 1e20.
    '''

    transform = ObjectProperty(Matrix())
    '''Transformation matrix.

    :attr:`transform` is an :class:`~kivy.properties.ObjectProperty` and
    defaults to the identity matrix.

    .. note::

        This matrix reflects the current state of the transformation matrix
        but setting it directly will erase previously applied
        transformations. To apply a transformation considering context,
        please use the :attr:`~Scatter.apply_transform` method.

    '''

    transform_inv = ObjectProperty(Matrix())
    '''Inverse of the transformation matrix.

    :attr:`transform_inv` is an :class:`~kivy.properties.ObjectProperty` and
    defaults to the identity matrix.
    '''

    def _get_bbox(self):
        xmin, ymin = xmax, ymax = self.to_parent(0, 0)
        for point in [(self.width, 0), (0, self.height), self.size]:
            x, y = self.to_parent(*point)
            if x < xmin:
                xmin = x
            if y < ymin:
                ymin = y
            if x > xmax:
                xmax = x
            if y > ymax:
                ymax = y
        return (xmin, ymin), (xmax - xmin, ymax - ymin)

    bbox = AliasProperty(_get_bbox, bind=('transform', 'width', 'height'))
    '''Bounding box of the widget in parent space::

        ((x, y), (w, h))
        # x, y = lower left corner

    :attr:`bbox` is an :class:`~kivy.properties.AliasProperty`.
    '''

    def _get_rotation(self):
        v1 = Vector(0, 10)
        tp = self.to_parent
        v2 = Vector(*tp(*self.pos)) - tp(self.x, self.y + 10)
        return -1.0 * (v1.angle(v2) + 180) % 360

    def _set_rotation(self, rotation):
        angle_change = self.rotation - rotation
        r = Matrix().rotate(-radians(angle_change), 0, 0, 1)
        self.apply_transform(r,
                             post_multiply=True,
                             anchor=self.to_local(*self.center))

    rotation = AliasProperty(_get_rotation,
                             _set_rotation,
                             bind=('x', 'y', 'transform'))
    '''Rotation value of the scatter in degrees moving in a counterclockwise
    direction.

    :attr:`rotation` is an :class:`~kivy.properties.AliasProperty` and defaults
    to 0.0.
    '''

    def _get_scale(self):
        p1 = Vector(*self.to_parent(0, 0))
        p2 = Vector(*self.to_parent(1, 0))
        scale = p1.distance(p2)

        # XXX float calculation are not accurate, and then, scale can be
        # throwed again even with only the position change. So to
        # prevent anything wrong with scale, just avoid to dispatch it
        # if the scale "visually" didn't change. #947
        # Remove this ugly hack when we'll be Python 3 only.
        if hasattr(self, '_scale_p'):
            if str(scale) == str(self._scale_p):
                return self._scale_p

        self._scale_p = scale
        return scale

    def _set_scale(self, scale):
        rescale = scale * 1.0 / self.scale
        self.apply_transform(Matrix().scale(rescale, rescale, rescale),
                             post_multiply=True,
                             anchor=self.to_local(*self.center))

    scale = AliasProperty(_get_scale, _set_scale, bind=('x', 'y', 'transform'))
    '''Scale value of the scatter.

    :attr:`scale` is an :class:`~kivy.properties.AliasProperty` and defaults to
    1.0.
    '''

    def _get_center(self):
        return (self.bbox[0][0] + self.bbox[1][0] / 2.0,
                self.bbox[0][1] + self.bbox[1][1] / 2.0)

    def _set_center(self, center):
        if center == self.center:
            return False
        t = Vector(*center) - self.center
        trans = Matrix().translate(t.x, t.y, 0)
        self.apply_transform(trans)

    center = AliasProperty(_get_center, _set_center, bind=('bbox', ))

    def _get_pos(self):
        return self.bbox[0]

    def _set_pos(self, pos):
        _pos = self.bbox[0]
        if pos == _pos:
            return
        t = Vector(*pos) - _pos
        trans = Matrix().translate(t.x, t.y, 0)
        self.apply_transform(trans)

    pos = AliasProperty(_get_pos, _set_pos, bind=('bbox', ))

    def _get_x(self):
        return self.bbox[0][0]

    def _set_x(self, x):
        if x == self.bbox[0][0]:
            return False
        self.pos = (x, self.y)
        return True

    x = AliasProperty(_get_x, _set_x, bind=('bbox', ))

    def _get_y(self):
        return self.bbox[0][1]

    def _set_y(self, y):
        if y == self.bbox[0][1]:
            return False
        self.pos = (self.x, y)
        return True

    y = AliasProperty(_get_y, _set_y, bind=('bbox', ))

    def get_right(self):
        return self.x + self.bbox[1][0]

    def set_right(self, value):
        self.x = value - self.bbox[1][0]

    right = AliasProperty(get_right, set_right, bind=('x', 'bbox'))

    def get_top(self):
        return self.y + self.bbox[1][1]

    def set_top(self, value):
        self.y = value - self.bbox[1][1]

    top = AliasProperty(get_top, set_top, bind=('y', 'bbox'))

    def get_center_x(self):
        return self.x + self.bbox[1][0] / 2.

    def set_center_x(self, value):
        self.x = value - self.bbox[1][0] / 2.

    center_x = AliasProperty(get_center_x, set_center_x, bind=('x', 'bbox'))

    def get_center_y(self):
        return self.y + self.bbox[1][1] / 2.

    def set_center_y(self, value):
        self.y = value - self.bbox[1][1] / 2.

    center_y = AliasProperty(get_center_y, set_center_y, bind=('y', 'bbox'))

    def __init__(self, **kwargs):
        self._touches = []
        self._last_touch_pos = {}
        super(Scatter, self).__init__(**kwargs)

    def on_transform(self, instance, value):
        self.transform_inv = value.inverse()

    def collide_point(self, x, y):
        x, y = self.to_local(x, y)
        return 0 <= x <= self.width and 0 <= y <= self.height

    def to_parent(self, x, y, **k):
        p = self.transform.transform_point(x, y, 0)
        return (p[0], p[1])

    def to_local(self, x, y, **k):
        p = self.transform_inv.transform_point(x, y, 0)
        return (p[0], p[1])

    def _apply_transform(self, m, pos=None):
        m = self.transform.multiply(m)
        return super(Scatter, self)._apply_transform(m, (0, 0))

    def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)):
        '''
        Transforms the scatter by applying the "trans" transformation
        matrix (on top of its current transformation state). The resultant
        matrix can be found in the :attr:`~Scatter.transform` property.

        :Parameters:
            `trans`: :class:`~kivy.graphics.transformation.Matrix`.
                Transformation matix to be applied to the scatter widget.
            `anchor`: tuple, defaults to (0, 0).
                The point to use as the origin of the transformation
                (uses local widget space).
            `post_multiply`: bool, defaults to False.
                If True, the transform matrix is post multiplied
                (as if applied before the current transform).

        Usage example::

            from kivy.graphics.transformation import Matrix
            mat = Matrix().scale(3, 3, 3)
            scatter_instance.apply_transform(mat)

        '''
        t = Matrix().translate(anchor[0], anchor[1], 0)
        t = t.multiply(trans)
        t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0))

        if post_multiply:
            self.transform = self.transform.multiply(t)
        else:
            self.transform = t.multiply(self.transform)

    def transform_with_touch(self, touch):
        # just do a simple one finger drag
        changed = False
        if len(self._touches) == self.translation_touches:
            # _last_touch_pos has last pos in correct parent space,
            # just like incoming touch
            dx = (touch.x - self._last_touch_pos[touch][0]) \
                 * self.do_translation_x
            dy = (touch.y - self._last_touch_pos[touch][1]) \
                 * self.do_translation_y
            dx = dx / self.translation_touches
            dy = dy / self.translation_touches
            self.apply_transform(Matrix().translate(dx, dy, 0))
            changed = True

        if len(self._touches) == 1:
            return changed

        # we have more than one touch... list of last known pos
        points = [
            Vector(self._last_touch_pos[t]) for t in self._touches
            if t is not touch
        ]
        # add current touch last
        points.append(Vector(touch.pos))

        # we only want to transform if the touch is part of the two touches
        # farthest apart! So first we find anchor, the point to transform
        # around as another touch farthest away from current touch's pos
        anchor = max(points[:-1], key=lambda p: p.distance(touch.pos))

        # now we find the touch farthest away from anchor, if its not the
        # same as touch. Touch is not one of the two touches used to transform
        farthest = max(points, key=anchor.distance)
        if farthest is not points[-1]:
            return changed

        # ok, so we have touch, and anchor, so we can actually compute the
        # transformation
        old_line = Vector(*touch.ppos) - anchor
        new_line = Vector(*touch.pos) - anchor
        if not old_line.length():  # div by zero
            return changed

        angle = radians(new_line.angle(old_line)) * self.do_rotation
        if angle:
            changed = True
        self.apply_transform(Matrix().rotate(angle, 0, 0, 1), anchor=anchor)

        if self.do_scale:
            scale = new_line.length() / old_line.length()
            new_scale = scale * self.scale
            if new_scale < self.scale_min:
                scale = self.scale_min / self.scale
            elif new_scale > self.scale_max:
                scale = self.scale_max / self.scale
            self.apply_transform(Matrix().scale(scale, scale, scale),
                                 anchor=anchor)
            changed = True
        return changed

    def _bring_to_front(self, touch):
        # auto bring to front
        if self.auto_bring_to_front and self.parent:
            parent = self.parent
            if parent.children[0] is self:
                return
            parent.remove_widget(self)
            parent.add_widget(self)
            self.dispatch('on_bring_to_front', touch)

    def on_touch_down(self, touch):
        x, y = touch.x, touch.y

        # if the touch isnt on the widget we do nothing
        if not self.do_collide_after_children:
            if not self.collide_point(x, y):
                return False

        # let the child widgets handle the event if they want
        touch.push()
        touch.apply_transform_2d(self.to_local)
        if super(Scatter, self).on_touch_down(touch):
            touch.pop()
            self._bring_to_front(touch)
            return True
        touch.pop()

        # if our child didn't do anything, and if we don't have any active
        # interaction control, then don't accept the touch.
        if not self.do_translation_x and \
                not self.do_translation_y and \
                not self.do_rotation and \
                not self.do_scale:
            return False

        if self.do_collide_after_children:
            if not self.collide_point(x, y):
                return False

        if 'multitouch_sim' in touch.profile:
            touch.multitouch_sim = True
        # grab the touch so we get all it later move events for sure
        self._bring_to_front(touch)
        touch.grab(self)
        self._touches.append(touch)
        self._last_touch_pos[touch] = touch.pos

        return True

    def on_touch_move(self, touch):
        x, y = touch.x, touch.y
        # let the child widgets handle the event if they want
        if self.collide_point(x, y) and not touch.grab_current == self:
            touch.push()
            touch.apply_transform_2d(self.to_local)
            if super(Scatter, self).on_touch_move(touch):
                touch.pop()
                return True
            touch.pop()

        # rotate/scale/translate
        if touch in self._touches and touch.grab_current == self:
            if self.transform_with_touch(touch):
                self.dispatch('on_transform_with_touch', touch)
            self._last_touch_pos[touch] = touch.pos

        # stop propagating if its within our bounds
        if self.collide_point(x, y):
            return True

    def on_transform_with_touch(self, touch):
        '''
        Called when a touch event has transformed the scatter widget.
        By default this does nothing, but can be overriden by derived
        classes that need to react to transformations caused by user
        input.

        :Parameters:
            `touch`:
                The touch object which triggered the transformation.

        .. versionadded:: 1.8.0
        '''
        pass

    def on_bring_to_front(self, touch):
        '''
        Called when a touch event causes the scatter to be brought to the
        front of the parent (only if :attr:`auto_bring_to_front` is True)

        :Parameters:
            `touch`:
                The touch object which brought the scatter to front.

        .. versionadded:: 1.9.0
        '''
        pass

    def on_touch_up(self, touch):
        x, y = touch.x, touch.y
        # if the touch isnt on the widget we do nothing, just try children
        if not touch.grab_current == self:
            touch.push()
            touch.apply_transform_2d(self.to_local)
            if super(Scatter, self).on_touch_up(touch):
                touch.pop()
                return True
            touch.pop()

        # remove it from our saved touches
        if touch in self._touches and touch.grab_state:
            touch.ungrab(self)
            del self._last_touch_pos[touch]
            self._touches.remove(touch)

        # stop propagating if its within our bounds
        if self.collide_point(x, y):
            return True
예제 #13
0
파일: __init__.py 프로젝트: zarar7576/plyer
class Graph(Widget):
    '''Graph class, see module documentation for more information.
    '''

    # triggers a full reload of graphics
    _trigger = ObjectProperty(None)
    # triggers only a repositioning of objects due to size/pos updates
    _trigger_size = ObjectProperty(None)
    # holds widget with the x-axis label
    _xlabel = ObjectProperty(None)
    # holds widget with the y-axis label
    _ylabel = ObjectProperty(None)
    # holds all the x-axis tick mark labels
    _x_grid_label = ListProperty([])
    # holds all the y-axis tick mark labels
    _y_grid_label = ListProperty([])
    # holds the stencil view that clipse the plots to graph area
    _plot_area = ObjectProperty(None)
    # the mesh drawing all the ticks/grids
    _mesh = ObjectProperty(None)
    # the mesh which draws the surrounding rectangle
    _mesh_rect = ObjectProperty(None)
    # a list of locations of major and minor ticks. The values are not
    # but is in the axis min - max range
    _ticks_majorx = ListProperty([])
    _ticks_minorx = ListProperty([])
    _ticks_majory = ListProperty([])
    _ticks_minory = ListProperty([])

    def __init__(self, **kwargs):
        super(Graph, self).__init__(**kwargs)

        self._mesh = Mesh(mode='lines')
        self._mesh_rect = Mesh(mode='line_strip')
        val = 0.25
        self.canvas.add(Color(1 * val, 1 * val, 1 * val))
        self.canvas.add(self._mesh)
        self.canvas.add(Color(1, 1, 1))
        self.canvas.add(self._mesh_rect)
        mesh = self._mesh_rect
        mesh.vertices = [0] * (5 * 4)
        mesh.indices = [k for k in xrange(5)]

        self._plot_area = StencilView()
        self.add_widget(self._plot_area)

        self._trigger = Clock.create_trigger(self._redraw_all)
        self._trigger_size = Clock.create_trigger(self._redraw_size)

        self.bind(center=self._trigger_size, padding=self._trigger_size,
                  font_size=self._trigger_size, plots=self._trigger_size,
                  x_grid=self._trigger_size, y_grid=self._trigger_size,
                  draw_border=self._trigger_size)
        self.bind(xmin=self._trigger, xmax=self._trigger,
                  xlog=self._trigger, x_ticks_major=self._trigger,
                  x_ticks_minor=self._trigger,
                  xlabel=self._trigger, x_grid_label=self._trigger,
                  ymin=self._trigger, ymax=self._trigger,
                  ylog=self._trigger, y_ticks_major=self._trigger,
                  y_ticks_minor=self._trigger,
                  ylabel=self._trigger, y_grid_label=self._trigger)
        self._trigger()

    def _get_ticks(self, major, minor, log, s_min, s_max):
        if major and s_max > s_min:
            if log:
                s_min = log10(s_min)
                s_max = log10(s_max)
                # count the decades in min - max. This is in actual decades,
                # not logs.
                n_decades = floor(s_max - s_min)
                # for the fractional part of the last decade, we need to
                # convert the log value, x, to 10**x but need to handle
                # differently if the last incomplete decade has a decade
                # boundary in it
                if floor(s_min + n_decades) != floor(s_max):
                    n_decades += 1 - (10 ** (s_min + n_decades + 1) - 10 **
                                      s_max) / 10 ** floor(s_max + 1)
                else:
                    n_decades += ((10 ** s_max - 10 ** (s_min + n_decades)) /
                                  10 ** floor(s_max + 1))
                # this might be larger than what is needed, but we delete
                # excess later
                n_ticks_major = n_decades / float(major)
                n_ticks = int(floor(n_ticks_major * (minor if minor >=
                                                     1. else 1.0))) + 2
                # in decade multiples, e.g. 0.1 of the decade, the distance
                # between ticks
                decade_dist = major / float(minor if minor else 1.0)

                points_minor = [0] * n_ticks
                points_major = [0] * n_ticks
                k = 0  # position in points major
                k2 = 0  # position in points minor
                # because each decade is missing 0.1 of the decade, if a tick
                # falls in < min_pos skip it
                min_pos = 0.1 - 0.00001 * decade_dist
                s_min_low = floor(s_min)
                # first real tick location. value is in fractions of decades
                # from the start we have to use decimals here, otherwise
                # floating point inaccuracies results in bad values
                start_dec = ceil((10 ** Decimal(s_min - s_min_low - 1)) /
                                 Decimal(decade_dist)) * decade_dist
                count_min = (0 if not minor else
                             floor(start_dec / decade_dist) % minor)
                start_dec += s_min_low
                count = 0  # number of ticks we currently have passed start
                while True:
                    # this is the current position in decade that we are.
                    # e.g. -0.9 means that we're at 0.1 of the 10**ceil(-0.9)
                    # decade
                    pos_dec = start_dec + decade_dist * count
                    pos_dec_low = floor(pos_dec)
                    diff = pos_dec - pos_dec_low
                    zero = abs(diff) < 0.001 * decade_dist
                    if zero:
                        # the same value as pos_dec but in log scale
                        pos_log = pos_dec_low
                    else:
                        pos_log = log10((pos_dec - pos_dec_low
                                         ) * 10 ** ceil(pos_dec))
                    if pos_log > s_max:
                        break
                    count += 1
                    if zero or diff >= min_pos:
                        if minor and not count_min % minor:
                            points_major[k] = pos_log
                            k += 1
                        else:
                            points_minor[k2] = pos_log
                            k2 += 1
                    count_min += 1
                # n_ticks = len(points)
            else:
                # distance between each tick
                tick_dist = major / float(minor if minor else 1.0)
                n_ticks = int(floor((s_max - s_min) / tick_dist) + 1)
                points_major = [0] * int(
                    floor((s_max - s_min) / float(major)) + 1
                )
                points_minor = [0] * (n_ticks - len(points_major) + 1)
                k = 0  # position in points major
                k2 = 0  # position in points minor
                for m in xrange(0, n_ticks):
                    if minor and m % minor:
                        points_minor[k2] = m * tick_dist + s_min
                        k2 += 1
                    else:
                        points_major[k] = m * tick_dist + s_min
                        k += 1
            del points_major[k:]
            del points_minor[k2:]
        else:
            points_major = []
            points_minor = []
        return points_major, points_minor

    def _update_labels(self):
        xlabel = self._xlabel
        ylabel = self._ylabel
        x = self.x
        y = self.y
        width = self.width
        height = self.height
        padding = self.padding
        x_next = padding + x
        y_next = padding + y
        xextent = x + width
        yextent = y + height
        ymin = self.ymin
        ymax = self.ymax
        xmin = self.xmin
        precision = self.precision
        x_overlap = False
        y_overlap = False
        # set up x and y axis labels
        if xlabel:
            xlabel.text = self.xlabel
            xlabel.texture_update()
            xlabel.size = xlabel.texture_size
            xlabel.pos = (x + width / 2. - xlabel.width / 2., padding + y)
            y_next += padding + xlabel.height
        if ylabel:
            ylabel.text = self.ylabel
            ylabel.texture_update()
            ylabel.size = ylabel.texture_size
            ylabel.x = padding + x - (ylabel.width / 2. - ylabel.height / 2.)
            x_next += padding + ylabel.height
        xpoints = self._ticks_majorx
        xlabels = self._x_grid_label
        xlabel_grid = self.x_grid_label
        ylabel_grid = self.y_grid_label
        ypoints = self._ticks_majory
        ylabels = self._y_grid_label
        # now x and y tick mark labels
        if len(ylabels) and ylabel_grid:
            # horizontal size of the largest tick label, to have enough room
            ylabels[0].text = precision % ypoints[0]
            ylabels[0].texture_update()
            y1 = ylabels[0].texture_size
            y_start = y_next + (
                padding + y1[1]
                if len(xlabels) and xlabel_grid else 0
            ) + (padding + y1[1] if not y_next else 0)
            yextent = y + height - padding - y1[1] / 2.
            if self.ylog:
                ymax = log10(ymax)
                ymin = log10(ymin)
            ratio = (yextent - y_start) / float(ymax - ymin)
            y_start -= y1[1] / 2.
            func = (lambda x: 10 ** x) if self.ylog else lambda x: x
            y1 = y1[0]
            for k in xrange(len(ylabels)):
                ylabels[k].text = precision % func(ypoints[k])
                ylabels[k].texture_update()
                ylabels[k].size = ylabels[k].texture_size
                y1 = max(y1, ylabels[k].texture_size[0])
                ylabels[k].pos = (x_next, y_start + (ypoints[k] - ymin) *
                                  ratio)
            if len(ylabels) > 1 and ylabels[0].top > ylabels[1].y:
                y_overlap = True
            else:
                x_next += y1 + padding
        if len(xlabels) and xlabel_grid:
            func = log10 if self.xlog else lambda x: x
            # find the distance from the end that'll fit the last tick label
            xlabels[0].text = precision % func(xpoints[-1])
            xlabels[0].texture_update()
            xextent = x + width - xlabels[0].texture_size[0] / 2. - padding
            # find the distance from the start that'll fit the first tick label
            if not x_next:
                xlabels[0].text = precision % func(xpoints[0])
                xlabels[0].texture_update()
                x_next = padding + xlabels[0].texture_size[0] / 2.
            xmin = func(xmin)
            ratio = (xextent - x_next) / float(func(self.xmax) - xmin)
            func = (lambda x: 10 ** x) if self.xlog else lambda x: x
            right = -1
            for k in xrange(len(xlabels)):
                xlabels[k].text = precision % func(xpoints[k])
                # update the size so we can center the labels on ticks
                xlabels[k].texture_update()
                xlabels[k].size = xlabels[k].texture_size
                xlabels[k].pos = (x_next + (xpoints[k] - xmin) * ratio -
                                  xlabels[k].texture_size[0] / 2., y_next)
                if xlabels[k].x < right:
                    x_overlap = True
                    break
                right = xlabels[k].right
            if not x_overlap:
                y_next += padding + xlabels[0].texture_size[1]
        # now re-center the x and y axis labels
        if xlabel:
            xlabel.x = x_next + (xextent - x_next) / 2. - xlabel.width / 2.
        if ylabel:
            ylabel.y = y_next + (yextent - y_next) / 2. - ylabel.height / 2.
            t = Matrix().translate(ylabel.center[0], ylabel.center[1], 0)
            t = t.multiply(Matrix().rotate(-radians(270), 0, 0, 1))
            ylabel.transform = t.multiply(Matrix().translate(-ylabel.center[0],
                                                             -ylabel.center[1],
                                                             0))
        if x_overlap:
            for k in xrange(len(xlabels)):
                xlabels[k].text = ''
        if y_overlap:
            for k in xrange(len(ylabels)):
                ylabels[k].text = ''
        return x_next, y_next, xextent, yextent

    def _update_ticks(self, size):
        # re-compute the positions of the bounding rectangle
        mesh = self._mesh_rect
        vert = mesh.vertices
        if self.draw_border:
            vert[0] = size[0]
            vert[1] = size[1]
            vert[4] = size[2]
            vert[5] = size[1]
            vert[8] = size[2]
            vert[9] = size[3]
            vert[12] = size[0]
            vert[13] = size[3]
            vert[16] = size[0]
            vert[17] = size[1]
        else:
            vert[0:18] = [0 for k in xrange(18)]
        mesh.vertices = vert
        # re-compute the positions of the x/y axis ticks
        mesh = self._mesh
        vert = mesh.vertices
        start = 0
        xpoints = self._ticks_majorx
        ypoints = self._ticks_majory
        ylog = self.ylog
        xlog = self.xlog
        xmin = self.xmin
        xmax = self.xmax
        if xlog:
            xmin = log10(xmin)
            xmax = log10(xmax)
        ymin = self.ymin
        ymax = self.ymax
        if ylog:
            xmin = log10(ymin)
            ymax = log10(ymax)
        if len(xpoints):
            top = size[3] if self.x_grid else metrics.dp(12) + size[1]
            ratio = (size[2] - size[0]) / float(xmax - xmin)
            for k in xrange(start, len(xpoints) + start):
                vert[k * 8] = size[0] + (xpoints[k - start] - xmin) * ratio
                vert[k * 8 + 1] = size[1]
                vert[k * 8 + 4] = vert[k * 8]
                vert[k * 8 + 5] = top
            start += len(xpoints)
        if len(ypoints):
            top = size[2] if self.y_grid else metrics.dp(12) + size[0]
            ratio = (size[3] - size[1]) / float(ymax - ymin)
            for k in xrange(start, len(ypoints) + start):
                vert[k * 8 + 1] = size[1] + (ypoints[k - start] - ymin) * ratio
                vert[k * 8 + 5] = vert[k * 8 + 1]
                vert[k * 8] = size[0]
                vert[k * 8 + 4] = top
        mesh.vertices = vert

    def _update_plots(self, size):
        ylog = self.ylog
        xlog = self.xlog
        xmin = self.xmin
        xmax = self.xmax
        ymin = self.ymin
        ymax = self.ymax
        for plot in self.plots:
            plot._update(xlog, xmin, xmax, ylog, ymin, ymax, size)

    def _redraw_all(self, *args):
        # add/remove all the required labels
        font_size = self.font_size
        if self.xlabel:
            if not self._xlabel:
                xlabel = Label(font_size=font_size)
                self.add_widget(xlabel)
                self._xlabel = xlabel
        else:
            xlabel = self._xlabel
            if xlabel:
                self.remove_widget(xlabel)
                self._xlabel = None
        grids = self._x_grid_label
        xpoints_major, xpoints_minor = self._get_ticks(self.x_ticks_major,
                                                       self.x_ticks_minor,
                                                       self.xlog, self.xmin,
                                                       self.xmax)
        self._ticks_majorx = xpoints_major
        self._ticks_minorx = xpoints_minor
        if not self.x_grid_label:
            n_labels = 0
        else:
            n_labels = len(xpoints_major)
        for k in xrange(n_labels, len(grids)):
            self.remove_widget(grids[k])
        del grids[n_labels:]
        grid_len = len(grids)
        grids.extend([None] * (n_labels - len(grids)))
        for k in xrange(grid_len, n_labels):
            grids[k] = Label(font_size=font_size)
            self.add_widget(grids[k])

        if self.ylabel:
            if not self._ylabel:
                ylabel = RotateLabel(font_size=font_size)
                self.add_widget(ylabel)
                self._ylabel = ylabel
        else:
            ylabel = self._ylabel
            if ylabel:
                self.remove_widget(ylabel)
                self._ylabel = None
        grids = self._y_grid_label
        ypoints_major, ypoints_minor = self._get_ticks(self.y_ticks_major,
                                                       self.y_ticks_minor,
                                                       self.ylog, self.ymin,
                                                       self.ymax)
        self._ticks_majory = ypoints_major
        self._ticks_minory = ypoints_minor
        if not self.y_grid_label:
            n_labels = 0
        else:
            n_labels = len(ypoints_major)
        for k in xrange(n_labels, len(grids)):
            self.remove_widget(grids[k])
        del grids[n_labels:]
        grid_len = len(grids)
        grids.extend([None] * (n_labels - len(grids)))
        for k in xrange(grid_len, n_labels):
            grids[k] = Label(font_size=font_size)
            self.add_widget(grids[k])

        mesh = self._mesh
        n_points = (len(xpoints_major) + len(xpoints_minor) +
                    len(ypoints_major) + len(ypoints_minor))
        mesh.vertices = [0] * (n_points * 8)
        mesh.indices = [k for k in xrange(n_points * 2)]
        self._redraw_size()

    def _redraw_size(self, *args):
        # size a 4-tuple describing the bounding box in which we can draw
        # graphs, it's (x0, y0, x1, y1), which correspond with the bottom left
        # and top right corner locations, respectively
        size = self._update_labels()
        self._plot_area.pos = (size[0], size[1])
        self._plot_area.size = (size[2] - size[0], size[3] - size[1])
        self._update_ticks(size)
        self._update_plots(size)

    def add_plot(self, plot):
        '''Add a new plot to this graph.

        :Parameters:
            `plot`:
                Plot to add to this graph.

        >>> graph = Graph()
        >>> plot = MeshLinePlot(mode='line_strip', color=[1, 0, 0, 1])
        >>> plot.points = [(x / 10., sin(x / 50.)) for x in xrange(-0, 101)]
        >>> graph.add_plot(plot)
        '''
        area = self._plot_area
        for group in plot._get_drawings():
            area.canvas.add(group)
        self.plots = self.plots + [plot]

    def remove_plot(self, plot):
        '''Remove a plot from this graph.

        :Parameters:
            `plot`:
                Plot to remove from this graph.

        >>> graph = Graph()
        >>> plot = MeshLinePlot(mode='line_strip', color=[1, 0, 0, 1])
        >>> plot.points = [(x / 10., sin(x / 50.)) for x in xrange(-0, 101)]
        >>> graph.add_plot(plot)
        >>> graph.remove_plot(plot)
        '''
        self._plot_area.canvas.remove_group(plot._get_group())
        self.plots.remove(plot)

    xmin = NumericProperty(0.)
    '''The x-axis minimum value.

    If :data:`xlog` is True, xmin must be larger than zero.

    :data:`xmin` is a :class:`~kivy.properties.NumericProperty`, defaults to 0.
    '''

    xmax = NumericProperty(100.)
    '''The x-axis maximum value, larger than xmin.

    :data:`xmax` is a :class:`~kivy.properties.NumericProperty`, defaults to 0.
    '''

    xlog = BooleanProperty(False)
    '''Determines whether the x-axis should be displayed logarithmically (True)
    or linearly (False).

    :data:`xlog` is a :class:`~kivy.properties.BooleanProperty`, defaults
    to False.
    '''

    x_ticks_major = BoundedNumericProperty(0, min=0)
    '''Distance between major tick marks on the x-axis.

    Determines the distance between the major tick marks. Major tick marks
    start from min and re-occur at every ticks_major until :data:`xmax`.
    If :data:`xmax` doesn't overlap with a integer multiple of ticks_major,
    no tick will occur at :data:`xmax`. Zero indicates no tick marks.

    If :data:`xlog` is true, then this indicates the distance between ticks
    in multiples of current decade. E.g. if :data:`xmin` is 0.1 and
    ticks_major is 0.1, it means there will be a tick at every 10th of the
    decade, i.e. 0.1 ... 0.9, 1, 2... If it is 0.3, the ticks will occur at
    0.1, 0.3, 0.6, 0.9, 2, 5, 8, 10. You'll notice that it went from 8 to 10
    instead of to 20, that's so that we can say 0.5 and have ticks at every
    half decade, e.g. 0.1, 0.5, 1, 5, 10, 50... Similarly, if ticks_major is
    1.5, there will be ticks at 0.1, 5, 100, 5,000... Also notice, that there's
    always a major tick at the start. Finally, if e.g. :data:`xmin` is 0.6
    and this 0.5 there will be ticks at 0.6, 1, 5...

    :data:`x_ticks_major` is a
    :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0.
    '''

    x_ticks_minor = BoundedNumericProperty(0, min=0)
    '''The number of sub-intervals that divide x_ticks_major.

    Determines the number of sub-intervals into which ticks_major is divided,
    if non-zero. The actual number of minor ticks between the major ticks is
    ticks_minor - 1. Only used if ticks_major is non-zero. If there's no major
    tick at xmax then the number of minor ticks after the last major
    tick will be however many ticks fit until xmax.

    If self.xlog is true, then this indicates the number of intervals the
    distance between major ticks is divided. The result is the number of
    multiples of decades between ticks. I.e. if ticks_minor is 10, then if
    ticks_major is 1, there will be ticks at 0.1, 0.2...0.9, 1, 2, 3... If
    ticks_major is 0.3, ticks will occur at 0.1, 0.12, 0.15, 0.18... Finally,
    as is common, if ticks major is 1, and ticks minor is 5, there will be
    ticks at 0.1, 0.2, 0.4... 0.8, 1, 2...

    :data:`x_ticks_minor` is a
    :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0.
    '''

    x_grid = BooleanProperty(False)
    '''Determines whether the x-axis has tick marks or a full grid.

    If :data:`x_ticks_major` is non-zero, then if x_grid is False tick marks
    will be displayed at every major tick. If x_grid is True, instead of ticks,
    a vertical line will be displayed at every major tick.

    :data:`x_grid` is a :class:`~kivy.properties.BooleanProperty`, defaults
    to False.
    '''

    x_grid_label = BooleanProperty(False)
    '''Whether labels should be displayed beneath each major tick. If true,
    each major tick will have a label containing the axis value.

    :data:`x_grid_label` is a :class:`~kivy.properties.BooleanProperty`,
    defaults to False.
    '''

    xlabel = StringProperty('')
    '''The label for the x-axis. If not empty it is displayed in the center of
    the axis.

    :data:`xlabel` is a :class:`~kivy.properties.StringProperty`,
    defaults to ''.
    '''

    ymin = NumericProperty(0.)
    '''The y-axis minimum value.

    If :data:`ylog` is True, ymin must be larger than zero.

    :data:`ymin` is a :class:`~kivy.properties.NumericProperty`, defaults to 0.
    '''

    ymax = NumericProperty(100.)
    '''The y-axis maximum value, larger than ymin.

    :data:`ymax` is a :class:`~kivy.properties.NumericProperty`, defaults to 0.
    '''

    ylog = BooleanProperty(False)
    '''Determines whether the y-axis should be displayed logarithmically (True)
    or linearly (False).

    :data:`ylog` is a :class:`~kivy.properties.BooleanProperty`, defaults
    to False.
    '''

    y_ticks_major = BoundedNumericProperty(0, min=0)
    '''Distance between major tick marks. See :data:`x_ticks_major`.

    :data:`y_ticks_major` is a
    :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0.
    '''

    y_ticks_minor = BoundedNumericProperty(0, min=0)
    '''The number of sub-intervals that divide ticks_major.
    See :data:`x_ticks_minor`.

    :data:`y_ticks_minor` is a
    :class:`~kivy.properties.BoundedNumericProperty`, defaults to 0.
    '''

    y_grid = BooleanProperty(False)
    '''Determines whether the y-axis has tick marks or a full grid. See
    :data:`x_grid`.

    :data:`y_grid` is a :class:`~kivy.properties.BooleanProperty`, defaults
    to False.
    '''

    y_grid_label = BooleanProperty(False)
    '''Whether labels should be displayed beneath each major tick. If true,
    each major tick will have a label containing the axis value.

    :data:`y_grid_label` is a :class:`~kivy.properties.BooleanProperty`,
    defaults to False.
    '''

    ylabel = StringProperty('')
    '''The label for the y-axis. If not empty it is displayed in the center of
    the axis.

    :data:`ylabel` is a :class:`~kivy.properties.StringProperty`,
    defaults to ''.
    '''

    padding = NumericProperty('5dp')
    '''Padding distances between the labels, titles and graph, as well between
    the widget and the objects near the boundaries.

    :data:`padding` is a :class:`~kivy.properties.NumericProperty`, defaults
    to 5dp.
    '''

    font_size = NumericProperty('15sp')
    '''Font size of the labels.

    :data:`font_size` is a :class:`~kivy.properties.NumericProperty`, defaults
    to 15sp.
    '''

    precision = StringProperty('%g')
    '''Determines the numerical precision of the tick mark labels. This value
    governs how the numbers are converted into string representation. Accepted
    values are those listed in Python's manual in the
    "String Formatting Operations" section.

    :data:`precision` is a :class:`~kivy.properties.StringProperty`, defaults
    to '%g'.
    '''

    draw_border = BooleanProperty(True)
    '''Whether a border is drawn around the canvas of the graph where the
    plots are displayed.

    :data:`draw_border` is a :class:`~kivy.properties.BooleanProperty`,
    defaults to True.
    '''

    plots = ListProperty([])
    '''Holds a list of all the plots in the graph. To add and remove plots
예제 #14
0
class AndroidTabs(AnchorLayout):
    '''
    The AndroidTabs class.
    You can use it to create your own custom tabbed panel.
    '''

    default_tab = NumericProperty(0)
    '''
    Index of the default tab. Default to 0.
    '''

    tab_bar_height = NumericProperty('48dp')
    '''
    Height of the tab bar.
    '''

    tab_indicator_anim = BooleanProperty(True)
    '''
    Tab indicator animation. Default to True.
    If you do not want animation set it to False.
    '''

    tab_indicator_height = NumericProperty('2dp')
    '''
    Height of the tab indicator.
    '''

    tab_indicator_color = VariableListProperty([1])
    '''
    Color of the tab indicator.
    '''

    anim_duration = NumericProperty(0.2)
    '''
    Duration of the slide animation. Default to 0.2.
    '''

    anim_threshold = BoundedNumericProperty(0.8,
                                            min=0.0,
                                            max=1.0,
                                            errorhandler=lambda x: 0.0
                                            if x < 0.0 else 1.0)
    '''
    Animation threshold allow you to change
    the tab indicator animation effect.
    Default to 0.8.
    '''
    def on_carousel_index(self, carousel, index):
        # when the index of the carousel change, update
        # tab indicator, select the current tab and reset threshold data.
        current_tab_label = carousel.current_slide.tab_label
        if current_tab_label.state == 'normal':
            current_tab_label._do_press()
        self.tab_bar.update_indicator(current_tab_label.x,
                                      current_tab_label.width)

    def add_widget(self, widget):
        # You can add only subclass of AndroidTabsBase.
        if len(self.children) >= 2:

            if not issubclass(widget.__class__, AndroidTabsBase):
                raise AndroidTabsException(
                    'AndroidTabs accept only subclass of AndroidTabsBase')

            widget.tab_label.tab_bar = self.tab_bar
            self.tab_bar.layout.add_widget(widget.tab_label)
            self.carousel.add_widget(widget)
            return

        return super(AndroidTabs, self).add_widget(widget)

    def remove_widget(self, widget):
        # You can remove only subclass of AndroidTabsBase.
        if not issubclass(widget.__class__, AndroidTabsBase):

            raise AndroidTabsException(
                'AndroidTabs can remove only subclass of AndroidTabBase')

        if widget.parent.parent == self.carousel:

            self.tab_bar.layout.remove_widget(widget.tab_label)
            self.carousel.remove_widget(widget)
예제 #15
0
class BackgroundColorBehavior(CommonElevationBehavior):
    background = StringProperty()
    """
    Background image path.

    :attr:`background` is a :class:`~kivy.properties.StringProperty`
    and defaults to `None`.
    """

    r = BoundedNumericProperty(1.0, min=0.0, max=1.0)
    """
    The value of ``red`` in the ``rgba`` palette.

    :attr:`r` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `1.0`.
    """

    g = BoundedNumericProperty(1.0, min=0.0, max=1.0)
    """
    The value of ``green`` in the ``rgba`` palette.

    :attr:`g` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `1.0`.
    """

    b = BoundedNumericProperty(1.0, min=0.0, max=1.0)
    """
    The value of ``blue`` in the ``rgba`` palette.

    :attr:`b` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `1.0`.
    """

    a = BoundedNumericProperty(0.0, min=0.0, max=1.0)
    """
    The value of ``alpha channel`` in the ``rgba`` palette.

    :attr:`a` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `0.0`.
    """

    radius = VariableListProperty([0], length=4)
    """
    Canvas radius.

    .. code-block:: python

        # Top left corner slice.
        MDBoxLayout:
            md_bg_color: app.theme_cls.primary_color
            radius: [25, 0, 0, 0]

    :attr:`radius` is an :class:`~kivy.properties.VariableListProperty`
    and defaults to `[0, 0, 0, 0]`.
    """

    md_bg_color = ReferenceListProperty(r, g, b, a)
    """
    The background color of the widget (:class:`~kivy.uix.widget.Widget`)
    that will be inherited from the :attr:`BackgroundColorBehavior` class.

    For example:

    .. code-block:: kv

        Widget:
            canvas:
                Color:
                    rgba: 0, 1, 1, 1
                Rectangle:
                    size: self.size
                    pos: self.pos

    similar to code:

    .. code-block:: kv

        <MyWidget@BackgroundColorBehavior>
            md_bg_color: 0, 1, 1, 1

    :attr:`md_bg_color` is an :class:`~kivy.properties.ReferenceListProperty`
    and defaults to :attr:`r`, :attr:`g`, :attr:`b`, :attr:`a`.
    """

    angle = NumericProperty(0)
    background_origin = ListProperty(None)

    _background_x = NumericProperty(0)
    _background_y = NumericProperty(0)
    _background_origin = ReferenceListProperty(
        _background_x,
        _background_y,
    )

    def __init__(self, **kwarg):
        super().__init__(**kwarg)
        self.bind(pos=self.update_background_origin)

    def update_background_origin(self, *args):
        if self.background_origin:
            self._background_origin = self.background_origin
        else:
            self._background_origin = self.center
예제 #16
0
class ProductView(Screen):
    id = None
    amount = BoundedNumericProperty(1, min=1, max=100, errorvalue=1)
    progress = BoundedNumericProperty(0, min=0, max=100, errorvalue=1)

    def on_leave(self):
        self.stop_timer()
        self.ids.product_image.source = ""

    def getProduct(self):
        api = self.manager.api
        barcode = self.manager.scanned
        response = api.search(barcode, self.search_succes, self.search_failed)

    def search_failed(self, err):
        popup = Popup(title="Error",
                      content=Label(text="%s" % (err)),
                      size_hint=(None, None),
                      size=(800, 300))
        popup.open()
        self.manager.go_back("ScannerView")

    def search_succes(self, req, content):
        response = json.loads(content)
        api = self.manager.api
        if api.responseIsSuccess(response):
            barcode = self.manager.scanned
            print "zoeken van product met barcode %s: %s" % (
                barcode, response["status"]["meaning"])

            self.amount = 1
            self.id = productId = response["data"]["searchResults"][0]["list"][
                0]["id"]
            productBrand = response["data"]["searchResults"][0]["list"][0][
                "brand"]
            productDescription = response["data"]["searchResults"][0]["list"][
                0]["description"]
            productImagePath = response["data"]["searchResults"][0]["list"][0][
                "overviewImage"]
            price = response["data"]["searchResults"][0]["list"][0]["price"]
            image = api.get_product_image(productImagePath)

            self.ids.product_image.source = image
            self.ids.product_description.text = "%s - %s : %s euro" % (
                productBrand, productDescription, price)

            print "Product [%s] %s - %s : %s euro" % (
                productId, productBrand, productDescription, price)

            self.progress = 0
            Clock.schedule_interval(
                self.progress_callback,
                float(App.get_running_app().config.getdefaultint(
                    "ColliScanner", "wait_time", 10)) / 100)
        else:
            self.search_failed("Search failed: %s" %
                               (response["status"]["meaning"]))

    def progress_callback(self, dt):
        self.progress += 1
        self.ids.pbProgress.value = self.progress
        if self.progress == 100:
            self.add_product()
            return False
        return True

    def increase(self):
        self.amount += 1
        self.stop_timer()

    def decrease(self):
        self.amount -= 1
        self.stop_timer()

    def stop_timer(self):
        Clock.unschedule(self.progress_callback)
        self.progress = 0
        self.ids.pbProgress.value = self.progress

    def confirm(self):
        self.add_product()

    def add_product(self):
        self.manager.api.add(self.id, self.amount, "S", self.add_success,
                             self.add_failed)

    def add_success(self, req, content):
        response = json.loads(content)
        if self.manager.api.responseIsSuccess(response):
            print "%s stuks toevoegen aan de winkelmand: %s" % (
                self.amount, response["status"]["meaning"])
            self.manager.go_back("ScannerView")
        else:
            self.add_failed("Fout bij toevoegen: %s" %
                            (response["status"]["meaning"]))

    def add_failed(self, err):
        popup = Popup(title="Error",
                      content=Label(text="%s" % (err)),
                      size_hint=(None, None),
                      size=(800, 300))
        popup.open()
예제 #17
0
class RangeSlider(Widget):
    """Class for creating a RangeSlider widget.

    Check module documentation for more details.
    """

    connector_color = ListProperty([1, .55, 0, 1])
    '''Connector bar color, in the format (r, g, b, a).
    for disabling this bar use a = .0 '''
    def _get_value(self):
        return [self.value1, self.value2]

    def _set_value(self, value):
        self.value1, self.value2 = value

    value = AliasProperty(_get_value, _set_value, bind=('value1', 'value2'))
    '''Current value used for the both sliders.


    :attr:`value` is an :class:`~kivy.properties.AliasProperty` and defaults
    to [0, 0].'''

    value1 = NumericProperty(0.)
    '''Current value used for the first slider.

    :attr:`value` is a :class:`~kivy.properties.NumericProperty` and defaults
    to 0.'''

    value2 = NumericProperty(100.)
    '''Current value used for the second slider.

    :attr:`value` is a :class:`~kivy.properties.NumericProperty` and defaults
    to 0.'''

    min = NumericProperty(0.)
    '''Minimum value allowed for :attr:`value`.

    :attr:`min` is a :class:`~kivy.properties.NumericProperty` and defaults to
    0.'''

    max = NumericProperty(100.)
    '''Maximum value allowed for :attr:`value`.

    :attr:`max` is a :class:`~kivy.properties.NumericProperty` and defaults to
    100.'''

    padding = NumericProperty(sp(16))
    '''Padding of the slider. The padding is used for graphical representation
    and interaction. It prevents the cursor from going out of the bounds of the
    slider bounding box.

    By default, padding is sp(16). The range of the slider is reduced from
    padding \*2 on the screen. It allows drawing the default cursor of sp(32)
    width without having the cursor go out of the widget.

    :attr:`padding` is a :class:`~kivy.properties.NumericProperty` and defaults
    to sp(16).'''

    orientation = OptionProperty('horizontal',
                                 options=('vertical', 'horizontal'))
    '''Orientation of the slider.

    :attr:`orientation` is an :class:`~kivy.properties.OptionProperty` and
    defaults to 'horizontal'. Can take a value of 'vertical' or 'horizontal'.
    '''

    range = ReferenceListProperty(min, max)
    '''Range of the slider in the format (minimum value, maximum value)::

        >>> slider = Slider(min=10, max=80)
        >>> slider.range
        [10, 80]
        >>> slider.range = (20, 100)
        >>> slider.min
        20
        >>> slider.max
        100

    :attr:`range` is a :class:`~kivy.properties.ReferenceListProperty` of
    (:attr:`min`, :attr:`max`) properties.
    '''

    step = BoundedNumericProperty(0, min=0)
    '''Step size of the slider.

    .. versionadded:: 1.4.0

    Determines the size of each interval or step the slider takes between
    min and max. If the value range can't be evenly divisible by step the
    last step will be capped by slider.max

    :attr:`step` is a :class:`~kivy.properties.NumericProperty` and defaults
    to 1.'''

    # The following two methods constrain the slider's value
    # to range(min,max). Otherwise it may happen that self.value < self.min
    # at init.

    def on_min(self, *largs):
        self.value1 = min(self.max, max(self.min, self.value1))
        self.value2 = min(self.max, max(self.min, self.value2))

    def on_max(self, *largs):
        self.value1 = min(self.max, max(self.min, self.value1))
        self.value2 = min(self.max, max(self.min, self.value2))

    def get_norm_value1(self):
        vmin = self.min
        d = self.max - vmin
        if d == 0:
            return 0
        return (self.value1 - vmin) / float(d)

    def get_norm_value2(self):
        vmin = self.min
        d = self.max - vmin
        if d == 0:
            return 0
        return (self.value2 - vmin) / float(d)

    def set_norm_value1(self, value):
        vmin = self.min
        step = self.step
        val = value * (self.max - vmin) + vmin
        if step == 0:
            self.value1 = val
        else:
            self.value1 = min(
                round((val - vmin) / step) * step + vmin, self.max)

    def set_norm_value2(self, value):
        vmin = self.min
        step = self.step
        val = value * (self.max - vmin) + vmin
        if step == 0:
            self.value2 = val
        else:
            self.value2 = min(
                round((val - vmin) / step) * step + vmin, self.max)

    value1_normalized = AliasProperty(get_norm_value1,
                                      set_norm_value1,
                                      bind=('value1', 'min', 'max', 'step'))
    value2_normalized = AliasProperty(get_norm_value2,
                                      set_norm_value2,
                                      bind=('value2', 'min', 'max', 'step'))
    '''Normalized value inside the :attr:`range` (min/max) to 0-1 range::

        >>> slider = Slider(value=50, min=0, max=100)
        >>> slider.value
        50
        >>> slider.value_normalized
        0.5
        >>> slider.value = 0
        >>> slider.value_normalized
        0
        >>> slider.value = 100
        >>> slider.value_normalized
        1

    You can also use it for setting the real value without knowing the minimum
    and maximum::

        >>> slider = Slider(min=0, max=200)
        >>> slider.value_normalized = .5
        >>> slider.value
        100
        >>> slider.value_normalized = 1.
        >>> slider.value
        200

    :attr:`value_normalized` is an :class:`~kivy.properties.AliasProperty`.
    '''

    def get_value1_pos(self):
        padding = self.padding
        x = self.x
        y = self.y
        nval = self.value1_normalized
        if self.orientation == 'horizontal':
            return (x + padding + nval * (self.width - 2 * padding), y)
        else:
            return (x, y + padding + nval * (self.height - 2 * padding))

    def get_value2_pos(self):
        padding = self.padding
        x = self.x
        y = self.y
        nval = self.value2_normalized
        if self.orientation == 'horizontal':
            return (x + padding + nval * (self.width - 2 * padding), y)
        else:
            return (x, y + padding + nval * (self.height - 2 * padding))

    def set_value1_pos(self, pos):
        padding = self.padding
        x = min(self.right - padding, max(pos[0], self.x + padding))
        y = min(self.top - padding, max(pos[1], self.y + padding))
        if self.orientation == 'horizontal':
            if self.width == 0:
                self.value1_normalized = 0
            else:
                self.value1_normalized = (
                    x - self.x - padding) / float(self.width - 2 * padding)
        else:
            if self.height == 0:
                self.value1_normalized = 0
            else:
                self.value1_normalized = (
                    y - self.y - padding) / float(self.height - 2 * padding)

    def set_value2_pos(self, pos):
        padding = self.padding
        x = min(self.right - padding, max(pos[0], self.x + padding))
        y = min(self.top - padding, max(pos[1], self.y + padding))
        if self.orientation == 'horizontal':
            if self.width == 0:
                self.value2_normalized = 0
            else:
                self.value2_normalized = (
                    x - self.x - padding) / float(self.width - 2 * padding)
        else:
            if self.height == 0:
                self.value2_normalized = 0
            else:
                self.value2_normalized = (
                    y - self.y - padding) / float(self.height - 2 * padding)

    value1_pos = AliasProperty(get_value1_pos,
                               set_value1_pos,
                               bind=('x', 'y', 'width', 'height', 'min', 'max',
                                     'value1_normalized', 'orientation'))
    value2_pos = AliasProperty(get_value2_pos,
                               set_value2_pos,
                               bind=('x', 'y', 'width', 'height', 'min', 'max',
                                     'value2_normalized', 'orientation'))
    '''Position of the internal cursor, based on the normalized value.

    :attr:`value_pos` is an :class:`~kivy.properties.AliasProperty`.
    '''

    def _touch_normalized_value(self, touch):
        pos = touch.pos
        padding = self.padding
        x = min(self.right - padding, max(pos[0], self.x + padding))
        y = min(self.top - padding, max(pos[1], self.y + padding))
        if self.orientation == 'horizontal':
            value = (x - self.x - padding) / float(self.width - 2 * padding)
        else:
            value = (y - self.y - padding) / float(self.height - 2 * padding)
        return value

    def on_touch_down(self, touch):
        if self.disabled or not self.collide_point(*touch.pos):
            return
        touch.grab(self)
        t_value = self._touch_normalized_value(touch)
        if abs(self.value1_normalized - t_value) < abs(self.value2_normalized -
                                                       t_value):
            self.value1_pos = touch.pos
            touch.ud['cursorid'] = 1
        else:
            self.value2_pos = touch.pos
            touch.ud['cursorid'] = 2
        return True

    def on_touch_move(self, touch):
        if touch.grab_current == self:
            if 'cursorid' in touch.ud:
                if touch.ud['cursorid'] == 1:
                    self.value1_pos = touch.pos
                    if self.value1 > self.value2:
                        self.value1_pos = self.value2_pos
                elif touch.ud['cursorid'] == 2:
                    self.value2_pos = touch.pos
                    if self.value2 < self.value1:
                        self.value2_pos = self.value1_pos
                return True

    def on_touch_up(self, touch):
        if touch.grab_current == self:
            touch.ungrab(self)
            return True
예제 #18
0
파일: tab.py 프로젝트: LYU-Daliang/KivyMD
class MDTabs(ThemableBehavior, SpecificBackgroundColorBehavior, AnchorLayout):
    """
    You can use this class to create your own tabbed panel.

    :Events:
        `on_tab_switch`
            Called when switching tabs.
        `on_slide_progress`
            Called while the slide is scrolling.
        `on_ref_press`
            The method will be called when the ``on_ref_press`` event
            occurs when you, for example, use markup text for tabs.
    """

    default_tab = NumericProperty(0)
    """
    Index of the default tab.

    :attr:`default_tab` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `0`.
    """

    tab_bar_height = NumericProperty("48dp")
    """
    Height of the tab bar.

    :attr:`tab_bar_height` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `'48dp'`.
    """

    tab_indicator_anim = BooleanProperty(False)
    """
    Tab indicator animation. If you want use animation set it to ``True``.

    :attr:`tab_indicator_anim` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    tab_indicator_height = NumericProperty("2dp")
    """
    Height of the tab indicator.

    :attr:`tab_indicator_height` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `'2dp'`.
    """

    tab_indicator_type = OptionProperty(
        "line", options=["line", "fill", "round", "line-round", "line-rect"])
    """
    Type of tab indicator. Available options are: `'line'`, `'fill'`,
    `'round'`, `'line-rect'` and `'line-round'`.

    :attr:`tab_indicator_type` is an :class:`~kivy.properties.OptionProperty`
    and defaults to `'line'`.
    """

    anim_duration = NumericProperty(0.2)
    """
    Duration of the slide animation.

    :attr:`anim_duration` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `0.2`.
    """

    anim_threshold = BoundedNumericProperty(0.8,
                                            min=0.0,
                                            max=1.0,
                                            errorhandler=lambda x: 0.0
                                            if x < 0.0 else 1.0)
    """
    Animation threshold allow you to change the tab indicator animation effect.

    :attr:`anim_threshold` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `0.8`.
    """

    allow_stretch = BooleanProperty(True)
    """
    If False - tabs will not stretch to full screen.

    :attr:`allow_stretch` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """

    background_color = ColorProperty(None)
    """
    Background color of tabs in ``rgba`` format.

    :attr:`background_color` is an :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    text_color_normal = ColorProperty(None)
    """
    Text color of the label when it is not selected.

    :attr:`text_color_normal` is an :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    text_color_active = ColorProperty(None)
    """
    Text color of the label when it is selected.

    :attr:`text_color_active` is an :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    elevation = NumericProperty(0)
    """
    Tab value elevation.

    .. seealso::

        `Behaviors/Elevation <https://kivymd.readthedocs.io/en/latest/behaviors/elevation/index.html>`_

    :attr:`elevation` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `0`.
    """

    indicator_color = ColorProperty(None)
    """
    Color indicator in ``rgba`` format.

    :attr:`indicator_color` is an :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    lock_swiping = BooleanProperty(False)
    """
    If True - disable switching tabs by swipe.

    :attr:`lock_swiping` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `False`.
    """

    font_name = StringProperty("Roboto")
    """
    Font name for tab text.

    :attr:`font_name` is an :class:`~kivy.properties.StringProperty`
    and defaults to `'Roboto'`.
    """

    ripple_duration = NumericProperty(2)
    """
    Ripple duration when long touching to tab.

    :attr:`ripple_duration` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `2`.
    """

    no_ripple_effect = BooleanProperty(True)
    """
    Whether to use the ripple effect when tapping on a tab.

    :attr:`no_ripple_effect` is an :class:`~kivy.properties.BooleanProperty`
    and defaults to `True`.
    """
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.register_event_type("on_tab_switch")
        self.register_event_type("on_ref_press")
        self.register_event_type("on_slide_progress")
        Clock.schedule_once(self._carousel_bind, 1)
        self.theme_cls.bind(primary_palette=self.update_icon_color)
        self.theme_cls.bind(theme_style=self.update_icon_color)

    def update_icon_color(self, instance, value):
        for tab_label in self.get_tab_list():
            if not self.text_color_normal:
                tab_label.text_color_normal = self.theme_cls.text_color
            if not self.text_color_active:
                tab_label.text_color_active = self.specific_secondary_text_color

    def switch_tab(self, name_tab):
        """Switching the tab by name."""

        for instance_tab in self.tab_bar.parent.carousel.slides:
            if instance_tab.text == name_tab:
                self.tab_bar.parent.carousel.load_slide(instance_tab)
                break
        for tab_label in self.get_tab_list():
            if name_tab == tab_label.text:
                self.ids.scrollview.scroll_to(tab_label)
                break

    def get_tab_list(self):
        """Returns a list of tab objects."""

        return self.tab_bar.layout.children

    def add_widget(self, widget, index=0, canvas=None):
        # You can add only subclass of MDTabsBase.
        if issubclass(widget.__class__, MDTabsBase):
            try:
                # FIXME: Can't set the value of the `no_ripple_effect`
                #  and `ripple_duration` properties for widget.tab_label.
                widget.tab_label._no_ripple_effect = self.no_ripple_effect
                widget.tab_label.ripple_duration_in_slow = self.ripple_duration
                widget.tab_label.group = str(self)
                widget.tab_label.tab_bar = self.tab_bar
                widget.tab_label.text_color_normal = (
                    self.text_color_normal
                    if self.text_color_normal else self.theme_cls.text_color)
                widget.tab_label.text_color_active = (
                    self.text_color_active if self.text_color_active else
                    self.specific_secondary_text_color)
                self.bind(font_name=widget.tab_label.setter("font_name"))
                self.bind(text_color_active=widget.tab_label.setter(
                    "text_color_active"))
                self.bind(text_color_normal=widget.tab_label.setter(
                    "text_color_normal"))
                self.tab_bar.layout.add_widget(widget.tab_label)
                self.carousel.add_widget(widget)
                return
            except AttributeError:
                pass
        if isinstance(widget, (MDTabsMain, MDTabsBar)):
            return super().add_widget(widget)

    def remove_widget(self, widget):
        # You can remove only subclass of MDTabsLabel.
        if not issubclass(widget.__class__, MDTabsLabel):
            raise MDTabsException(
                "MDTabs can remove only subclass of MDTabsLabel")
        # The last tab is not deleted.
        if len(self.tab_bar.layout.children) == 1:
            return

        # Search object next tab.
        next_tab = None
        for i, tab in enumerate(self.tab_bar.layout.children):
            if tab == widget:
                next_tab = self.tab_bar.layout.children[i - 1]
                break

        self.tab_bar.layout.remove_widget(widget)
        # After deleting the tab, it updates the indicator width
        # on the next tab.
        if next_tab:
            self._update_indicator(next_tab)

        for slide in self.carousel.slides:
            if slide.text == widget.text:
                self.carousel.remove_widget(slide)
                break

    def on_slide_progress(self, *args):
        """Called while the slide is scrolling."""

    def on_carousel_index(self, carousel, index):
        """Called when the carousel index changes."""

        # When the index of the carousel change, update tab indicator,
        # select the current tab and reset threshold data.
        if carousel.current_slide:
            current_tab_label = carousel.current_slide.tab_label
            if current_tab_label.state == "normal":
                # current_tab_label._do_press()
                current_tab_label.dispatch("on_release")

            if self.tab_indicator_type == "round":
                self.tab_indicator_height = self.tab_bar_height
                if index == 0:
                    radius = [
                        0,
                        self.tab_bar_height / 2,
                        self.tab_bar_height / 2,
                        0,
                    ]
                    self.tab_bar.update_indicator(current_tab_label.x,
                                                  current_tab_label.width,
                                                  radius)
                elif index == len(self.get_tab_list()) - 1:
                    radius = [
                        self.tab_bar_height / 2,
                        0,
                        0,
                        self.tab_bar_height / 2,
                    ]
                    self.tab_bar.update_indicator(current_tab_label.x,
                                                  current_tab_label.width,
                                                  radius)
                else:
                    radius = [
                        self.tab_bar_height / 2,
                    ]
                    self.tab_bar.update_indicator(current_tab_label.x,
                                                  current_tab_label.width,
                                                  radius)
            elif (self.tab_indicator_type == "fill"
                  or self.tab_indicator_type == "line-round"
                  or self.tab_indicator_type == "line-rect"):
                self.tab_indicator_height = self.tab_bar_height
                self.tab_bar.update_indicator(current_tab_label.x,
                                              current_tab_label.width)
            else:
                self.tab_bar.update_indicator(current_tab_label.x,
                                              current_tab_label.width)

    def on_ref_press(self, *args):
        """The method will be called when the ``on_ref_press`` event
        occurs when you, for example, use markup text for tabs."""

    def on_tab_switch(self, *args):
        """Called when switching tabs."""

    def on_size(self, *args):
        if self.carousel.current_slide:
            self._update_indicator(self.carousel.current_slide.tab_label)

    def _carousel_bind(self, interval):
        self.carousel.bind(on_slide_progress=self._on_slide_progress)

    def _on_slide_progress(self, *args):
        self.dispatch("on_slide_progress", args)

    def _update_indicator(self, current_tab_label):
        def update_indicator(interval):
            self.tab_bar.update_indicator(current_tab_label.x,
                                          current_tab_label.width)

        if not current_tab_label:
            current_tab_label = self.tab_bar.layout.children[-1]
        Clock.schedule_once(update_indicator)
예제 #19
0
class Sound(EventDispatcher):
    '''Represents a sound to play. This class is abstract, and cannot be used
    directly.

    Use SoundLoader to load a sound.

    :Events:
        `on_play`: None
            Fired when the sound is played.
        `on_stop`: None
            Fired when the sound is stopped.
    '''

    source = StringProperty(None)
    '''Filename / source of your audio file.

    .. versionadded:: 1.3.0

    :attr:`source` is a :class:`~kivy.properties.StringProperty` that defaults
    to None and is read-only. Use the :meth:`SoundLoader.load` for loading
    audio.
    '''

    volume = NumericProperty(1.)
    '''Volume, in the range 0-1. 1 means full volume, 0 means mute.

    .. versionadded:: 1.3.0

    :attr:`volume` is a :class:`~kivy.properties.NumericProperty` and defaults
    to 1.
    '''

    pitch = BoundedNumericProperty(1., min=float_info.epsilon)
    '''Pitch of a sound. 2 is an octave higher, .5 one below. This is only
    implemented for SDL2 audio provider yet.

    .. versionadded:: 1.10.0

    :attr:`pitch` is a :class:`~kivy.properties.NumericProperty` and defaults
    to 1.
    '''

    state = OptionProperty('stop', options=('stop', 'play'))
    '''State of the sound, one of 'stop' or 'play'.

    .. versionadded:: 1.3.0

    :attr:`state` is a read-only :class:`~kivy.properties.OptionProperty`.'''

    loop = BooleanProperty(False)
    '''Set to True if the sound should automatically loop when it finishes.

    .. versionadded:: 1.8.0

    :attr:`loop` is a :class:`~kivy.properties.BooleanProperty` and defaults to
    False.'''

    #
    # deprecated
    #
    def _get_status(self):
        return self.state

    status = AliasProperty(
        _get_status, None, bind=('state',), deprecated=True)
    '''
    .. deprecated:: 1.3.0
        Use :attr:`state` instead.
    '''

    def _get_filename(self):
        return self.source

    filename = AliasProperty(
        _get_filename, None, bind=('source',), deprecated=True)
    '''
    .. deprecated:: 1.3.0
        Use :attr:`source` instead.
    '''

    __events__ = ('on_play', 'on_stop')

    def on_source(self, instance, filename):
        self.unload()
        if filename is None:
            return
        self.load()

    def get_pos(self):
        '''
        Returns the current position of the audio file.
        Returns 0 if not playing.

        .. versionadded:: 1.4.1
        '''
        return 0

    def _get_length(self):
        return 0

    length = property(lambda self: self._get_length(),
                      doc='Get length of the sound (in seconds).')

    def load(self):
        '''Load the file into memory.'''
        pass

    def unload(self):
        '''Unload the file from memory.'''
        pass

    def play(self):
        '''Play the file.'''
        self.state = 'play'
        self.dispatch('on_play')

    def stop(self):
        '''Stop playback.'''
        self.state = 'stop'
        self.dispatch('on_stop')

    def seek(self, position):
        '''Go to the <position> (in seconds).

        .. note::
            Most sound providers cannot seek when the audio is stopped.
            Play then seek.
        '''
        pass

    def on_play(self):
        pass

    def on_stop(self):
        pass
예제 #20
0
class GridLayout(Layout):
    '''Grid layout class. See module documentation for more information.
    '''

    spacing = NumericProperty(0)
    '''Spacing between widget box and children, in pixels.

    :data:`spacing` is a :class:`~kivy.properties.NumericProperty`, default to
    0.
    '''

    padding = NumericProperty(0)
    '''Padding between widget box and children, in pixels.

    :data:`padding` is a :class:`~kivy.properties.NumericProperty`, default to
    0.
    '''

    cols = BoundedNumericProperty(None, min=0, allow_none=True)
    '''Number of columns in the grid

    .. versionadded:: 1.0.8
        Change from NumericProperty to BoundedNumericProperty. You cannot set a
        negative value anymore.

    :data:`cols` is a :class:`~kivy.properties.NumericProperty`, default to 0.
    '''

    rows = BoundedNumericProperty(None, min=0, allow_none=True)
    '''Number of rows in the grid

    .. versionadded:: 1.0.8
        Change from NumericProperty to BoundedNumericProperty. You cannot set a
        negative value anymore.

    :data:`rows` is a :class:`~kivy.properties.NumericProperty`, default to 0.
    '''

    col_default_width = NumericProperty(0)
    '''Default minimum size to use for column

    .. versionadded:: 1.0.7

    :data:`col_default_width` is a :class:`~kivy.properties.NumericProperty`,
    default to 0.
    '''

    row_default_height = NumericProperty(0)
    '''Default minimum size to use for row

    .. versionadded:: 1.0.7

    :data:`row_default_height` is a :class:`~kivy.properties.NumericProperty`,
    default to 0.
    '''

    col_force_default = BooleanProperty(False)
    '''If True, ignore the width and size_hint_x of the child, and use the
    default column width.

    .. versionadded:: 1.0.7

    :data:`col_force_default` is a :class:`~kivy.properties.BooleanProperty`,
    default to False.
    '''

    row_force_default = BooleanProperty(False)
    '''If True, ignore the height and size_hint_y of the child, and use the
    default row height.

    .. versionadded:: 1.0.7

    :data:`row_force_default` is a :class:`~kivy.properties.BooleanProperty`,
    default to False.
    '''

    cols_minimum = DictProperty({})
    '''List of minimum size for each column.

    .. versionadded:: 1.0.7

    :data:`cols_minimum` is a :class:`~kivy.properties.DictProperty`, default
    to {}
    '''

    rows_minimum = DictProperty({})
    '''List of minimum size for each row.

    .. versionadded:: 1.0.7

    :data:`rows_minimum` is a :class:`~kivy.properties.DictProperty`, default
    to {}
    '''

    minimum_width = NumericProperty(0)
    '''Minimum width needed to contain all childrens.

    .. versionadded:: 1.0.8

    :data:`minimum_width` is a :class:`kivy.properties.NumericProperty`, default
    to 0.
    '''

    minimum_height = NumericProperty(0)
    '''Minimum height needed to contain all childrens.

    .. versionadded:: 1.0.8

    :data:`minimum_height` is a :class:`kivy.properties.NumericProperty`,
    default to 0.
    '''

    minimum_size = ReferenceListProperty(minimum_width, minimum_height)
    '''Minimum size needed to contain all childrens.

    .. versionadded:: 1.0.8

    :data:`minimum_size` is a :class:`~kivy.properties.ReferenceListProperty` of
    (:data:`minimum_width`, :data:`minimum_height`) properties.
    '''

    def __init__(self, **kwargs):
        self._cols = self._rows = None
        super(GridLayout, self).__init__(**kwargs)

        self.bind(
            col_default_width = self._trigger_layout,
            row_default_height = self._trigger_layout,
            col_force_default = self._trigger_layout,
            row_force_default = self._trigger_layout,
            cols = self._trigger_layout,
            rows = self._trigger_layout,
            parent = self._trigger_layout,
            spacing = self._trigger_layout,
            padding = self._trigger_layout,
            children = self._trigger_layout,
            size = self._trigger_layout,
            pos = self._trigger_layout)

    def add_widget(self, widget, index=0):
        widget.bind(
            size = self._trigger_layout,
            size_hint = self._trigger_layout)
        return super(Layout, self).add_widget(widget, index)

    def remove_widget(self, widget):
        widget.unbind(
            size = self._trigger_layout,
            size_hint = self._trigger_layout)
        return super(Layout, self).remove_widget(widget)

    def get_max_widgets(self):
        if self.cols and not self.rows:
            return None
        if self.rows and not self.cols:
            return None
        if not self.cols and not self.rows:
            return None
        return self.rows * self.cols

    def on_children(self, instance, value):
        # if that makes impossible to construct things with deffered method,
        # migrate this test in do_layout, and/or issue a warning.
        smax = self.get_max_widgets()
        if smax and len(value) > smax:
            raise GridLayoutException(
                    'Too much children in GridLayout. Increase your rows/cols!')

    def update_minimum_size(self, *largs):
        # the goal here is to calculate the minimum size of every cols/rows
        # and determine if they have stretch or not
        current_cols = self.cols
        current_rows = self.rows
        children = self.children
        len_children = len(children)

        # if no cols or rows are set, we can't calculate minimum size.
        # the grid must be contrained at least on one side
        if not current_cols and not current_rows:
            Logger.warning('%r have no cols or rows set, '
                'layout are not triggered.' % self)
            return None
        if current_cols is None:
            current_cols = int(ceil(len_children / float(current_rows)))
        elif current_rows is None:
            current_rows = int(ceil(len_children / float(current_cols)))

        current_cols = max(1, current_cols)
        current_rows = max(1, current_rows)

        cols = [self.col_default_width] * current_cols
        cols_sh = [None] * current_cols
        rows = [self.row_default_height] * current_rows
        rows_sh = [None] * current_rows

        # update minimum size from the dicts
        # FIXME index might be outside the bounds ?
        for index, value in self.cols_minimum.iteritems():
            cols[index] = value
        for index, value in self.rows_minimum.iteritems():
            rows[index] = value

        # calculate minimum size for each columns and rows
        i = len_children - 1
        for row in xrange(current_rows):
            for col in xrange(current_cols):

                # don't go further is we don't have child left
                if i < 0:
                    break

                # get initial information from the child
                c = children[i]
                shw = c.size_hint_x
                shh = c.size_hint_y
                w = c.width
                h = c.height

                # compute minimum size / maximum stretch needed
                if shw is None:
                    cols[col] = max(cols[col], w)
                else:
                    cols_sh[col] = max(cols_sh[col], shw)
                if shh is None:
                    rows[row] = max(rows[row], h)
                else:
                    rows_sh[row] = max(rows_sh[row], shh)

                # next child
                i = i - 1

        # calculate minimum width/height needed, starting from padding + spacing
        padding2 = self.padding * 2
        spacing = self.spacing
        width = padding2 + spacing * (current_cols - 1)
        height = padding2 + spacing * (current_rows - 1)
        # then add the cell size
        width += sum(cols)
        height += sum(rows)

        # remember for layout
        self._cols = cols
        self._rows = rows
        self._cols_sh = cols_sh
        self._rows_sh = rows_sh

        # finally, set the minimum size
        self.minimum_size = (width, height)

    def do_layout(self, *largs):
        self.update_minimum_size()
        if self._cols is None:
            return
        if self.cols is None and self.rows is None:
            raise GridLayoutException('Need at least cols or rows restriction.')

        children = self.children
        len_children = len(children)
        if len_children == 0:
            return

        # speedup
        padding = self.padding
        spacing = self.spacing
        selfx = self.x
        selfw = self.width
        selfh = self.height

        # resolve size for each column
        if self.col_force_default:
            cols = [self.col_default_width] * len(self._cols)
            for index, value in self.cols_minimum.iteritems():
                cols[index] = value
        else:
            cols = self._cols[:]
            cols_sh = self._cols_sh
            cols_weigth = sum([x for x in cols_sh if x])
            strech_w = max(0, selfw - self.minimum_width)
            for index in xrange(len(cols)):
                # if the col don't have strech information, nothing to do
                col_stretch = cols_sh[index]
                if col_stretch is None:
                    continue
                # calculate the column stretch, and take the maximum from
                # minimum size and the calculated stretch
                col_width = cols[index]
                col_width = max(col_width, strech_w * col_stretch / cols_weigth)
                cols[index] = col_width

        # same algo for rows
        if self.row_force_default:
            rows = [self.row_default_height] * len(self._rows)
            for index, value in self.rows_minimum.iteritems():
                rows[index] = value
        else:
            rows = self._rows[:]
            rows_sh = self._rows_sh
            rows_weigth = sum([x for x in rows_sh if x])
            strech_h = max(0, selfh - self.minimum_height)
            for index in xrange(len(rows)):
                # if the row don't have strech information, nothing to do
                row_stretch = rows_sh[index]
                if row_stretch is None:
                    continue
                # calculate the row stretch, and take the maximum from minimum
                # size and the calculated stretch
                row_height = rows[index]
                row_height = max(row_height,
                                 strech_h * row_stretch / rows_weigth)
                rows[index] = row_height

        # reposition every child
        i = len_children - 1
        y = self.top - padding
        reposition_child = self.reposition_child
        for row_height in rows:
            x = selfx + padding
            for col_width in cols:
                if i < 0:
                    break
                c = children[i]
                c_pos = x, y - row_height
                c_size = (col_width, row_height)
                reposition_child(c, pos=c_pos, size=c_size)
                i = i - 1
                x = x + col_width + spacing
            y -= row_height + spacing
예제 #21
0
class FloatingActionButton(ThemeBehaviour, CircularRippleBehavior,
                           ElevationBehaviour, ButtonBehavior, AnchorLayout):

    _bg_color_down = ListProperty([])

    def _get_bg_color_down(self):
        return self._bg_color_down

    def _set_bg_color_down(self, color, alpha=None):
        if len(color) == 2:
            self._bg_color_down = get_rgba_color(color, control_alpha=alpha)
        elif len(color) == 4:
            self._bg_color_down = color

    background_color_down = AliasProperty(_get_bg_color_down,
                                          _set_bg_color_down,
                                          bind=('_bg_color_down', ))

    _bg_color_disabled = ListProperty([])

    def _get_bg_color_disabled(self):
        return self._bg_color_disabled

    def _set_bg_color_disabled(self, color, alpha=None):
        if len(color) == 2:
            self._bg_color_disabled = get_rgba_color(color,
                                                     control_alpha=alpha)
        elif len(color) == 4:
            self._bg_color_disabled = color

    background_color_disabled = AliasProperty(_get_bg_color_disabled,
                                              _set_bg_color_disabled,
                                              bind=('_bg_color_disabled', ))

    theme_style = OptionProperty(None,
                                 options=['Light', 'Dark', 'Custom'],
                                 allownone=True)

    icon_color = ListProperty(None, allownone=True)

    elevation_normal = BoundedNumericProperty(2, min=0, max=12)
    elevation_raised = BoundedNumericProperty(0, min=0, max=12)

    icon = StringProperty('md-android')

    def __init__(self, **kwargs):
        self.elevation = self.elevation_normal

        if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12:
            self.elevation_raised = self.elevation_normal + 6
        elif self.elevation_raised == 0:
            self.elevation_raised = 12

        super(FloatingActionButton, self).__init__(**kwargs)
        self.background_color = self._theme_cls.primary_color
        self.background_color_down = self._theme_cls.primary_dark
        self.background_color_disabled = self._theme_cls.disabled_bg_color()

        self.elevation_press_anim = Animation(elevation=self.elevation_raised,
                                              duration=.2,
                                              t='out_quad')
        self.elevation_release_anim = Animation(
            elevation=self.elevation_normal, duration=.2, t='out_quad')

    def _set_ellipse(self, instance, value):
        ellipse = self.ellipse
        ripple_rad = self.ripple_rad

        ellipse.size = (ripple_rad, ripple_rad)
        ellipse.pos = (self.center_x - ripple_rad / 2.,
                       self.center_y - ripple_rad / 2.)

    def on_disabled(self, instance, value):
        super(FloatingActionButton, self).on_disabled(instance, value)
        if self.disabled:
            self.elevation = 0
        else:
            self.elevation = self.elevation_normal

    def on_touch_down(self, touch):
        if not self.disabled:
            if touch.is_mouse_scrolling:
                return False
            if not self.collide_point(touch.x, touch.y):
                return False
            if self in touch.ud:
                return False
            self.elevation_press_anim.stop(self)
            self.elevation_press_anim.start(self)
        return super(FloatingActionButton, self).on_touch_down(touch)

    def on_touch_up(self, touch):
        if not self.disabled:
            if touch.grab_current is not self:
                return super(ButtonBehavior, self).on_touch_up(touch)
            self.elevation_release_anim.stop(self)
            self.elevation_release_anim.start(self)
        return super(FloatingActionButton, self).on_touch_up(touch)

    def on_elevation_normal(self, instance, value):
        self.elevation = value

    def on_elevation_raised(self, instance, value):
        if self.elevation_raised == 0 and self.elevation_normal + 6 <= 12:
            self.elevation_raised = self.elevation_normal + 6
        elif self.elevation_raised == 0:
            self.elevation_raised = 12
예제 #22
0
class Player(Widget):
    """Player class."""

    partisans = BoundedNumericProperty(0, min=0, rebind=True)
    swing = BoundedNumericProperty(0, min=0)
    media = BoundedNumericProperty(1, min=1)
    news = BoundedNumericProperty(1, min=0)
    mojo = BoundedNumericProperty(1, min=1)
    hype = BoundedNumericProperty(1, min=0)
    money = BoundedNumericProperty(1, min=1)
    cash = BoundedNumericProperty(1, min=0)
    state_area = ObjectProperty('')

    cards_actions = ListProperty([])

    def __init__(self, **kwargs):
        """Init player."""
        super(Player, self).__init__(**kwargs)
        self.player_id = None
        self.player_name = None
        self.stats = None

    def late_init(self, **kwargs):
        """Init player resources."""
        self.player_id = kwargs.pop('player_id')
        self.card_factory = kwargs.pop('card_factory')
        is_bot = kwargs.pop('is_bot')
        self.human = False if is_bot else True
        self.bot = True if is_bot else False
        self.player_name = PLAYERS[self.player_id]
        self.stats = kwargs
        for prop_name, value in self.stats.items():
            self.property(prop_name).set(self, value)

        self.RESOURSES = {1: 'news', 2: 'cash', 3: 'hype'}
        self.ACTIONS = {
            1: ['swing'],
            2: ['partisans'],
            3: ['news'],
            4: ['hype'],
            5: ['cash'],
            6: ['media'],
            7: ['mojo'],
            8: ['money'],
            9: ['news', 'hype', 'cash'],
            10: ['media', 'mojo', 'money']
        }
        self.active = False
        self.winner = None

        self.deck = Deck(self, self.card_factory)
        self.hand = Hand(self.deck)

    def set_opponent(self, opponent):
        """Set player's opponent."""
        self.opponent = opponent

    def set_active(self, active):
        """Set player as active."""
        self.active = active

    def get_active(self):
        """See if player is active."""
        return self.active

    def is_bot(self):
        """See if player is bot."""
        return self.bot

    def set_winner(self, winner):
        """Make player winner or loser."""
        self.winner = winner

    def get_deck(self):
        """Get deck."""
        return self.deck

    def get_hand(self):
        """Get player hand."""
        return self.hand

    def get_player_id(self):
        """Get player id."""
        return self.player_id

    def get_voters(self):
        """Get number of partisans."""
        return self.partisans

    def play(self):
        pass

    def pay_for_card(self, card_color, card_value):
        """If possible pay for card."""
        if card_color != 0 and card_color != 4:
            property = self.property(self.RESOURSES[card_color])
            property_value = property.get(self)
            if (property_value - card_value) >= 0:
                property.set(self, property_value - card_value)
            else:
                return False
        else:
            for color, prop_name in self.RESOURSES.items():
                property = self.property(prop_name)
                property_value = property.get(self)
                if (property_value - card_value) >= 0:
                    property.set(self, property_value - card_value)
                else:
                    return False
        return True

    def apply_card(self, type, value):
        """
        Apply card actions.

        Return True if after applying this card the turn doesn't change.
        """
        if type == 0:
            if value >= 0:
                self.swing += value
            # lose voters branch
            elif value < 0:
                diff = self.swing + value
                self.swing = max(0, self.swing + value)
                if diff < 0:
                    self.partisans = max(0, self.partisans + diff)
        elif type == 11:
            return True
        else:
            for res in self.ACTIONS[type]:
                # TODO:
                # check with -value, it seems that it doesn't work((
                old_value = self.property(res).get(self)
                min_value = self.property(res).get_min(self)
                # print self.player_id, res, type, value, old_value, min_value
                self.property(res).set(self, max(min_value, old_value + value))
        return False

    def update_resources(self):
        """Update resources of players at the end of the turn."""
        for increment, resource in (('media', 'news'), ('mojo', 'hype'),
                                    ('money', 'cash')):
            increment_property = self.property(increment)
            increment_property_value = increment_property.get(self)
            resource_property = self.property(resource)
            resource_property_value = resource_property.get(self)
            resource_property.set(
                self, increment_property_value + resource_property_value)
예제 #23
0
class GridLayout(Layout):
    '''Grid layout class. See module documentation for more information.
    '''

    spacing = VariableListProperty([0, 0], length=2)
    '''Spacing between children: [spacing_horizontal, spacing_vertical].

    spacing also accepts a one argument form [spacing].

    :attr:`spacing` is a
    :class:`~kivy.properties.VariableListProperty` and defaults to [0, 0].
    '''

    padding = VariableListProperty([0, 0, 0, 0])
    '''Padding between the layout box and its children: [padding_left,
    padding_top, padding_right, padding_bottom].

    padding also accepts a two argument form [padding_horizontal,
    padding_vertical] and a one argument form [padding].

    .. versionchanged:: 1.7.0
        Replaced NumericProperty with VariableListProperty.

    :attr:`padding` is a :class:`~kivy.properties.VariableListProperty` and
    defaults to [0, 0, 0, 0].
    '''

    cols = BoundedNumericProperty(None, min=0, allownone=True)
    '''Number of columns in the grid.

    .. versionchanged:: 1.0.8
        Changed from a NumericProperty to BoundedNumericProperty. You can no
        longer set this to a negative value.

    :attr:`cols` is a :class:`~kivy.properties.NumericProperty` and defaults to
    0.
    '''

    rows = BoundedNumericProperty(None, min=0, allownone=True)
    '''Number of rows in the grid.

    .. versionchanged:: 1.0.8
        Changed from a NumericProperty to a BoundedNumericProperty. You can no
        longer set this to a negative value.

    :attr:`rows` is a :class:`~kivy.properties.NumericProperty` and defaults to
    0.
    '''

    col_default_width = NumericProperty(0)
    '''Default minimum size to use for a column.

    .. versionadded:: 1.0.7

    :attr:`col_default_width` is a :class:`~kivy.properties.NumericProperty`
    and defaults to 0.
    '''

    row_default_height = NumericProperty(0)
    '''Default minimum size to use for row.

    .. versionadded:: 1.0.7

    :attr:`row_default_height` is a :class:`~kivy.properties.NumericProperty`
    and defaults to 0.
    '''

    col_force_default = BooleanProperty(False)
    '''If True, ignore the width and size_hint_x of the child and use the
    default column width.

    .. versionadded:: 1.0.7

    :attr:`col_force_default` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to False.
    '''

    row_force_default = BooleanProperty(False)
    '''If True, ignore the height and size_hint_y of the child and use the
    default row height.

    .. versionadded:: 1.0.7

    :attr:`row_force_default` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to False.
    '''

    cols_minimum = DictProperty({})
    '''Dict of minimum width for each column. The dictionary keys are the
    column numbers, e.g. 0, 1, 2...

    .. versionadded:: 1.0.7

    :attr:`cols_minimum` is a :class:`~kivy.properties.DictProperty` and
    defaults to {}.
    '''

    rows_minimum = DictProperty({})
    '''Dict of minimum height for each row. The dictionary keys are the
    row numbers, e.g. 0, 1, 2...

    .. versionadded:: 1.0.7

    :attr:`rows_minimum` is a :class:`~kivy.properties.DictProperty` and
    defaults to {}.
    '''

    minimum_width = NumericProperty(0)
    '''Automatically computed minimum width needed to contain all children.

    .. versionadded:: 1.0.8

    :attr:`minimum_width` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0. It is read only.
    '''

    minimum_height = NumericProperty(0)
    '''Automatically computed minimum height needed to contain all children.

    .. versionadded:: 1.0.8

    :attr:`minimum_height` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0. It is read only.
    '''

    minimum_size = ReferenceListProperty(minimum_width, minimum_height)
    '''Automatically computed minimum size needed to contain all children.

    .. versionadded:: 1.0.8

    :attr:`minimum_size` is a
    :class:`~kivy.properties.ReferenceListProperty` of
    (:attr:`minimum_width`, :attr:`minimum_height`) properties. It is read
    only.
    '''
    def __init__(self, **kwargs):
        self._cols = self._rows = None
        super(GridLayout, self).__init__(**kwargs)
        fbind = self.fbind
        update = self._trigger_layout
        fbind('col_default_width', update)
        fbind('row_default_height', update)
        fbind('col_force_default', update)
        fbind('row_force_default', update)
        fbind('cols', update)
        fbind('rows', update)
        fbind('parent', update)
        fbind('spacing', update)
        fbind('padding', update)
        fbind('children', update)
        fbind('size', update)
        fbind('pos', update)

    def get_max_widgets(self):
        if self.cols and self.rows:
            return self.rows * self.cols
        else:
            return None

    def on_children(self, instance, value):
        # if that makes impossible to construct things with deffered method,
        # migrate this test in do_layout, and/or issue a warning.
        smax = self.get_max_widgets()
        if smax and len(value) > smax:
            raise GridLayoutException(
                'Too many children in GridLayout. Increase rows/cols!')

    def _init_rows_cols_sizes(self, count):
        # the goal here is to calculate the minimum size of every cols/rows
        # and determine if they have stretch or not
        current_cols = self.cols
        current_rows = self.rows

        # if no cols or rows are set, we can't calculate minimum size.
        # the grid must be contrained at least on one side
        if not current_cols and not current_rows:
            Logger.warning('%r have no cols or rows set, '
                           'layout is not triggered.' % self)
            return
        if current_cols is None:
            current_cols = int(ceil(count / float(current_rows)))
        elif current_rows is None:
            current_rows = int(ceil(count / float(current_cols)))

        current_cols = max(1, current_cols)
        current_rows = max(1, current_rows)

        self._has_hint_bound_x = False
        self._has_hint_bound_y = False
        self._cols_min_size_none = 0.  # min size from all the None hint
        self._rows_min_size_none = 0.  # min size from all the None hint
        self._cols = cols = [self.col_default_width] * current_cols
        self._cols_sh = [None] * current_cols
        self._cols_sh_min = [None] * current_cols
        self._cols_sh_max = [None] * current_cols
        self._rows = rows = [self.row_default_height] * current_rows
        self._rows_sh = [None] * current_rows
        self._rows_sh_min = [None] * current_rows
        self._rows_sh_max = [None] * current_rows

        # update minimum size from the dicts
        items = (i for i in self.cols_minimum.items() if i[0] < len(cols))
        for index, value in items:
            cols[index] = max(value, cols[index])

        items = (i for i in self.rows_minimum.items() if i[0] < len(rows))
        for index, value in items:
            rows[index] = max(value, rows[index])
        return True

    def _fill_rows_cols_sizes(self):
        cols, rows = self._cols, self._rows
        cols_sh, rows_sh = self._cols_sh, self._rows_sh
        cols_sh_min, rows_sh_min = self._cols_sh_min, self._rows_sh_min
        cols_sh_max, rows_sh_max = self._cols_sh_max, self._rows_sh_max

        # calculate minimum size for each columns and rows
        n_cols = len(cols)
        has_bound_y = has_bound_x = False
        for i, child in enumerate(reversed(self.children)):
            (shw, shh), (w, h) = child.size_hint, child.size
            shw_min, shh_min = child.size_hint_min
            shw_max, shh_max = child.size_hint_max
            row, col = divmod(i, n_cols)

            # compute minimum size / maximum stretch needed
            if shw is None:
                cols[col] = nmax(cols[col], w)
            else:
                cols_sh[col] = nmax(cols_sh[col], shw)
                if shw_min is not None:
                    has_bound_x = True
                    cols_sh_min[col] = nmax(cols_sh_min[col], shw_min)
                if shw_max is not None:
                    has_bound_x = True
                    cols_sh_max[col] = nmin(cols_sh_max[col], shw_max)

            if shh is None:
                rows[row] = nmax(rows[row], h)
            else:
                rows_sh[row] = nmax(rows_sh[row], shh)
                if shh_min is not None:
                    has_bound_y = True
                    rows_sh_min[row] = nmax(rows_sh_min[row], shh_min)
                if shh_max is not None:
                    has_bound_y = True
                    rows_sh_max[row] = nmin(rows_sh_max[row], shh_max)
        self._has_hint_bound_x = has_bound_x
        self._has_hint_bound_y = has_bound_y

    def _update_minimum_size(self):
        # calculate minimum width/height needed, starting from padding +
        # spacing
        l, t, r, b = self.padding
        spacing_x, spacing_y = self.spacing
        cols, rows = self._cols, self._rows

        width = l + r + spacing_x * (len(cols) - 1)
        self._cols_min_size_none = sum(cols) + width
        # we need to subtract for the sh_max/min the already guaranteed size
        # due to having a None in the col. So sh_min gets smaller by that size
        # since it's already covered. Similarly for sh_max, because if we
        # already exceeded the max, the subtracted max will be zero, so
        # it won't get larger
        if self._has_hint_bound_x:
            cols_sh_min = self._cols_sh_min
            cols_sh_max = self._cols_sh_max

            for i, (c, sh_min,
                    sh_max) in enumerate(zip(cols, cols_sh_min, cols_sh_max)):
                if sh_min is not None:
                    width += max(c, sh_min)
                    cols_sh_min[i] = max(0., sh_min - c)
                else:
                    width += c

                if sh_max is not None:
                    cols_sh_max[i] = max(0., sh_max - c)
        else:
            width = self._cols_min_size_none

        height = t + b + spacing_y * (len(rows) - 1)
        self._rows_min_size_none = sum(rows) + height
        if self._has_hint_bound_y:
            rows_sh_min = self._rows_sh_min
            rows_sh_max = self._rows_sh_max

            for i, (r, sh_min,
                    sh_max) in enumerate(zip(rows, rows_sh_min, rows_sh_max)):
                if sh_min is not None:
                    height += max(r, sh_min)
                    rows_sh_min[i] = max(0., sh_min - r)
                else:
                    height += r

                if sh_max is not None:
                    rows_sh_max[i] = max(0., sh_max - r)
        else:
            height = self._rows_min_size_none

        # finally, set the minimum size
        self.minimum_size = (width, height)

    def _finalize_rows_cols_sizes(self):
        selfw = self.width
        selfh = self.height

        # resolve size for each column
        if self.col_force_default:
            cols = [self.col_default_width] * len(self._cols)
            for index, value in self.cols_minimum.items():
                cols[index] = value
            self._cols = cols
        else:
            cols = self._cols
            cols_sh = self._cols_sh
            cols_sh_min = self._cols_sh_min
            cols_weight = float(sum((x for x in cols_sh if x is not None)))
            stretch_w = max(0., selfw - self._cols_min_size_none)

            if stretch_w > 1e-9:
                if self._has_hint_bound_x:
                    # fix the hints to be within bounds
                    self.layout_hint_with_bounds(
                        cols_weight, stretch_w,
                        sum((c for c in cols_sh_min if c is not None)),
                        cols_sh_min, self._cols_sh_max, cols_sh)

                for index, col_stretch in enumerate(cols_sh):
                    # if the col don't have stretch information, nothing to do
                    if not col_stretch:
                        continue
                    # add to the min width whatever remains from size_hint
                    cols[index] += stretch_w * col_stretch / cols_weight

        # same algo for rows
        if self.row_force_default:
            rows = [self.row_default_height] * len(self._rows)
            for index, value in self.rows_minimum.items():
                rows[index] = value
            self._rows = rows
        else:
            rows = self._rows
            rows_sh = self._rows_sh
            rows_sh_min = self._rows_sh_min
            rows_weight = float(sum((x for x in rows_sh if x is not None)))
            stretch_h = max(0., selfh - self._rows_min_size_none)

            if stretch_h > 1e-9:
                if self._has_hint_bound_y:
                    # fix the hints to be within bounds
                    self.layout_hint_with_bounds(
                        rows_weight, stretch_h,
                        sum((r for r in rows_sh_min if r is not None)),
                        rows_sh_min, self._rows_sh_max, rows_sh)

                for index, row_stretch in enumerate(rows_sh):
                    # if the row don't have stretch information, nothing to do
                    if not row_stretch:
                        continue
                    # add to the min height whatever remains from size_hint
                    rows[index] += stretch_h * row_stretch / rows_weight

    def _iterate_layout(self, count):
        selfx = self.x
        padding_left = self.padding[0]
        padding_top = self.padding[1]
        spacing_x, spacing_y = self.spacing

        i = count - 1
        y = self.top - padding_top
        cols = self._cols
        for row_height in self._rows:
            x = selfx + padding_left
            for col_width in cols:
                if i < 0:
                    break

                yield i, x, y - row_height, col_width, row_height
                i = i - 1
                x = x + col_width + spacing_x
            y -= row_height + spacing_y

    def do_layout(self, *largs):
        children = self.children
        if not children or not self._init_rows_cols_sizes(len(children)):
            l, t, r, b = self.padding
            self.minimum_size = l + r, t + b
            return
        self._fill_rows_cols_sizes()
        self._update_minimum_size()
        self._finalize_rows_cols_sizes()

        for i, x, y, w, h in self._iterate_layout(len(children)):
            c = children[i]
            c.pos = x, y
            shw, shh = c.size_hint
            shw_min, shh_min = c.size_hint_min
            shw_max, shh_max = c.size_hint_max

            if shw_min is not None:
                if shw_max is not None:
                    w = max(min(w, shw_max), shw_min)
                else:
                    w = max(w, shw_min)
            else:
                if shw_max is not None:
                    w = min(w, shw_max)

            if shh_min is not None:
                if shh_max is not None:
                    h = max(min(h, shh_max), shh_min)
                else:
                    h = max(h, shh_min)
            else:
                if shh_max is not None:
                    h = min(h, shh_max)

            if shw is None:
                if shh is not None:
                    c.height = h
            else:
                if shh is None:
                    c.width = w
                else:
                    c.size = (w, h)
예제 #24
0
class MainScreen(Screen):
    """A master layout that contains one board and some menus.

    This contains three elements: a scrollview (containing the board),
    a menu, and the time control panel. This class has some support methods
    for handling interactions with the menu and the character sheet,
    but if neither of those happen, the scrollview handles touches on its
    own.

    """
    manager = ObjectProperty()
    boards = DictProperty()
    boardview = ObjectProperty()
    charmenu = ObjectProperty()
    statlist = ObjectProperty()
    statpanel = ObjectProperty()
    timepanel = ObjectProperty()
    kv = StringProperty()
    use_kv = BooleanProperty()
    play_speed = NumericProperty()
    playbut = ObjectProperty()
    portaladdbut = ObjectProperty()
    portaldirbut = ObjectProperty()
    dummyplace = ObjectProperty()
    dummything = ObjectProperty()
    dummies = ReferenceListProperty(dummyplace, dummything)
    dialoglayout = ObjectProperty()
    visible = BooleanProperty()
    _touch = ObjectProperty(None, allownone=True)
    rules_per_frame = BoundedNumericProperty(10, min=1)
    app = ObjectProperty()

    def on_statpanel(self, *args):
        if not self.app:
            Clock.schedule_once(self.on_statpanel, 0)
            return
        self.app.bind(selected_proxy=self.statpanel.setter('proxy'))

    def pull_visibility(self, *args):
        self.visible = self.manager.current == 'main'

    def on_manager(self, *args):
        self.pull_visibility()
        self.manager.bind(current=self.pull_visibility)

    def on_play_speed(self, *args):
        """Change the interval at which ``self.play`` is called to match my
        current ``play_speed``.

        """
        Clock.unschedule(self.play)
        Clock.schedule_interval(self.play, 1.0 / self.play_speed)

    def remake_display(self, *args):
        """Remake any affected widgets after a change in my ``kv``.

        """
        Builder.load_string(self.kv)
        if hasattr(self, '_kv_layout'):
            self.remove_widget(self._kv_layout)
            del self._kv_layout
        self._kv_layout = KvLayout()
        self.add_widget(self._kv_layout)

    _trigger_remake_display = trigger(remake_display)

    def on_touch_down(self, touch):
        if self.visible:
            touch.grab(self)
        for interceptor in (self.timepanel, self.charmenu, self.statpanel,
                            self.dummyplace, self.dummything):
            if interceptor.collide_point(*touch.pos):
                interceptor.dispatch('on_touch_down', touch)
                self.boardview.keep_selection = True
                return True
        if self.dialoglayout.dispatch('on_touch_down', touch):
            return True
        return self.boardview.dispatch('on_touch_down', touch)

    def on_touch_up(self, touch):
        if self.timepanel.collide_point(*touch.pos):
            return self.timepanel.dispatch('on_touch_up', touch)
        elif self.charmenu.collide_point(*touch.pos):
            return self.charmenu.dispatch('on_touch_up', touch)
        elif self.statpanel.collide_point(*touch.pos):
            return self.statpanel.dispatch('on_touch_up', touch)
        return self.boardview.dispatch('on_touch_up', touch)

    def on_dummies(self, *args):
        """Give the dummies numbers such that, when appended to their names,
        they give a unique name for the resulting new
        :class:`board.Pawn` or :class:`board.Spot`.

        """
        def renum_dummy(dummy, *args):
            dummy.num = dummynum(self.app.character, dummy.prefix) + 1

        for dummy in self.dummies:
            if dummy is None or hasattr(dummy, '_numbered'):
                continue
            if dummy == self.dummything:
                self.app.pawncfg.bind(imgpaths=self._propagate_thing_paths)
            if dummy == self.dummyplace:
                self.app.spotcfg.bind(imgpaths=self._propagate_place_paths)
            dummy.num = dummynum(self.app.character, dummy.prefix) + 1
            Logger.debug("MainScreen: dummy #{}".format(dummy.num))
            dummy.bind(prefix=partial(renum_dummy, dummy))
            dummy._numbered = True

    def _propagate_thing_paths(self, *args):
        # horrible hack
        self.dummything.paths = self.app.pawncfg.imgpaths

    def _propagate_place_paths(self, *args):
        # horrible hack
        self.dummyplace.paths = self.app.spotcfg.imgpaths

    def _update_from_time_travel(self, command, branch, turn, tick, result,
                                 **kwargs):
        self._update_from_delta(command, branch, turn, tick, result[-1])

    def _update_from_delta(self, cmd, branch, turn, tick, delta, **kwargs):
        self.app.branch = branch
        self.app.turn = turn
        self.app.tick = tick
        chardelta = delta.get(self.boardview.board.character.name, {})
        for unwanted in ('character_rulebook', 'avatar_rulebook',
                         'character_thing_rulebook',
                         'character_place_rulebook',
                         'character_portal_rulebook'):
            if unwanted in chardelta:
                del chardelta[unwanted]
        self.boardview.board.trigger_update_from_delta(chardelta)
        self.statpanel.statlist.mirror = dict(self.app.selected_proxy)

    def play(self, *args):
        """If the 'play' button is pressed, advance a turn.

        If you want to disable this, set ``engine.universal['block'] = True``

        """
        if self.playbut.state == 'normal':
            return
        self.next_turn()

    def _update_from_next_turn(self, command, branch, turn, tick, result):
        todo, deltas = result
        if isinstance(todo, list):
            self.dialoglayout.todo = todo
            self.dialoglayout.idx = 0
        self._update_from_delta(command, branch, turn, tick, deltas)
        self.dialoglayout.advance_dialog()

    def next_turn(self, *args):
        """Advance time by one turn, if it's not blocked.

        Block time by setting ``engine.universal['block'] = True``"""
        eng = self.app.engine
        dial = self.dialoglayout
        if eng.universal.get('block'):
            Logger.info(
                "MainScreen: next_turn blocked, delete universal['block'] to unblock"
            )
            return
        if dial.idx < len(dial.todo):
            Logger.info(
                "MainScreen: not advancing time while there's a dialog")
            return
        eng.next_turn(cb=self._update_from_next_turn)
예제 #25
0
class CircularTimePicker(BoxLayout):
    """Widget that makes use of :class:`CircularHourPicker` and
    :class:`CircularMinutePicker` to create a user-friendly, animated
    time picker like the one seen on Android.
    See module documentation for more details.
    """

    hours = NumericProperty(0)
    """The hours, in military format (0-23).
    :attr:`hours` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0 (12am).
    """

    minutes = NumericProperty(0)
    """The minutes.
    :attr:`minutes` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0.
    """

    time_list = ReferenceListProperty(hours, minutes)
    """Packs :attr:`hours` and :attr:`minutes` in a list for convenience.
    :attr:`time_list` is a :class:`~kivy.properties.ReferenceListProperty`.
    """

    # military = BooleanProperty(False)
    time_format = StringProperty(
        "[color={hours_color}][ref=hours]{hours}[/ref][/color]:[color={minutes_color}][ref=minutes]{minutes:02d}[/ref][/color]"
    )
    """String that will be formatted with the time and shown in the time label.
    Can be anything supported by :meth:`str.format`. Make sure you don't
    remove the refs. See the default for the arguments passed to format.
    :attr:`time_format` is a :class:`~kivy.properties.StringProperty` and
    defaults to "[color={hours_color}][ref=hours]{hours}[/ref][/color]:[color={minutes_color}][ref=minutes]{minutes:02d}[/ref][/color]".
    """

    ampm_format = StringProperty(
        "[color={am_color}][ref=am]AM[/ref][/color]\n[color={pm_color}][ref=pm]PM[/ref][/color]"
    )
    """String that will be formatted and shown in the AM/PM label.
    Can be anything supported by :meth:`str.format`. Make sure you don't
    remove the refs. See the default for the arguments passed to format.
    :attr:`ampm_format` is a :class:`~kivy.properties.StringProperty` and
    defaults to "[color={am_color}][ref=am]AM[/ref][/color]\n[color={pm_color}][ref=pm]PM[/ref][/color]".
    """

    picker = OptionProperty("hours", options=("minutes", "hours"))
    """Currently shown time picker. Can be one of "minutes", "hours".
    :attr:`picker` is a :class:`~kivy.properties.OptionProperty` and
    defaults to "hours".
    """

    selector_color = ListProperty([.337, .439, .490])
    """Color of the number selector and of the highlighted text. RGB.
    :attr:`selector_color` is a :class:`~kivy.properties.ListProperty` and
    defaults to [.337, .439, .490] (material green).
    """

    color = ListProperty([1, 1, 1])
    """Color of the number labels and of the center dot. RGB.
    :attr:`color` is a :class:`~kivy.properties.ListProperty` and
    defaults to [1, 1, 1] (white).
    """

    selector_alpha = BoundedNumericProperty(.3, min=0, max=1)
    """Alpha value for the transparent parts of the selector.
    :attr:`selector_alpha` is a :class:`~kivy.properties.BoundedNumericProperty` and
    defaults to 0.3 (min=0, max=1).
    """

    _am = BooleanProperty(True)
    _h_picker = ObjectProperty(None)
    _m_picker = ObjectProperty(None)
    _bound = DictProperty({})

    def _get_time(self):
        return datetime.time(*self.time_list)

    def _set_time(self, dt):
        self.time_list = [dt.hour, dt.minute]

    time = AliasProperty(_get_time, _set_time, bind=("time_list", ))
    """Selected time as a datetime.time object.
    :attr:`time` is an :class:`~kivy.properties.AliasProperty`.
    """

    def _get_picker(self):
        if self.picker == "hours":
            return self._h_picker
        return self._m_picker

    _picker = AliasProperty(_get_picker, None)

    def _get_time_text(self):
        hc = rgb_to_hex(
            *self.selector_color) if self.picker == "hours" else rgb_to_hex(
                *self.color)
        mc = rgb_to_hex(
            *self.selector_color) if self.picker == "minutes" else rgb_to_hex(
                *self.color)
        h = self.hours == 0 and 12 or self.hours <= 12 and self.hours or self.hours - 12
        m = self.minutes
        return self.time_format.format(hours_color=hc,
                                       minutes_color=mc,
                                       hours=h,
                                       minutes=m)

    time_text = AliasProperty(_get_time_text,
                              None,
                              bind=("hours", "minutes", "time_format",
                                    "picker"))

    def _get_ampm_text(self):
        amc = rgb_to_hex(*self.selector_color) if self._am else rgb_to_hex(
            *self.color)
        pmc = rgb_to_hex(*self.selector_color) if not self._am else rgb_to_hex(
            *self.color)
        return self.ampm_format.format(am_color=amc, pm_color=pmc)

    ampm_text = AliasProperty(_get_ampm_text,
                              None,
                              bind=("hours", "ampm_format", "_am"))

    def __init__(self, as_popup=False, touch_switch=False, *args, **kwargs):
        super(CircularTimePicker, self).__init__(*args, **kwargs)
        if self.hours >= 12:
            self._am = False
        self.bind(time_list=self.on_time_list,
                  picker=self._switch_picker,
                  _am=self.on_ampm)
        self._h_picker = CircularHourPicker()
        self._m_picker = CircularMinutePicker()
        Clock.schedule_once(self.on_selected)
        Clock.schedule_once(self.on_time_list)
        Clock.schedule_once(self._init_later)
        Clock.schedule_once(lambda *a: self._switch_picker(noanim=True))
        #print "TIMEee", self.time

    def _init_later(self, *args):
        self.ids.timelabel.bind(on_ref_press=self.on_ref_press)
        self.ids.ampmlabel.bind(on_ref_press=self.on_ref_press)

    def on_ref_press(self, ign, ref):
        if ref == "hours":
            self.picker = "hours"
        elif ref == "minutes":
            self.picker = "minutes"
        elif ref == "am":
            self._am = True
        elif ref == "pm":
            self._am = False

    def on_selected(self, *a):
        if not self._picker:
            return
        if self.picker == "hours":
            hours = self._picker.selected if self._am else self._picker.selected + 12
            if hours == 24 and not self._am:
                hours = 12
            elif hours == 12 and self._am:
                hours = 0
            self.hours = hours
        elif self.picker == "minutes":
            self.minutes = self._picker.selected

    def on_time_list(self, *a):
        #print "TIME", self.time
        if not self._picker:
            return
        if self.picker == "hours":
            self._picker.selected = self.hours == 0 and 12 or self._am and self.hours or self.hours - 12
        elif self.picker == "minutes":
            self._picker.selected = self.minutes

    def on_ampm(self, *a):
        if self._am:
            self.hours = self.hours if self.hours < 12 else self.hours - 12
        else:
            self.hours = self.hours if self.hours >= 12 else self.hours + 12

    def _switch_picker(self, *a, **kw):
        noanim = "noanim" in kw
        if noanim:
            noanim = kw["noanim"]

        try:
            container = self.ids.picker_container
        except (AttributeError, NameError):
            Clock.schedule_once(lambda *a: self._switch_picker(noanim=noanim))

        if self.picker == "hours":
            picker = self._h_picker
            prevpicker = self._m_picker
        elif self.picker == "minutes":
            picker = self._m_picker
            prevpicker = self._h_picker

        if len(self._bound) > 0:
            prevpicker.unbind(selected=self.on_selected)
            self.unbind(**self._bound)
        picker.bind(selected=self.on_selected)
        self._bound = {
            "selector_color": picker.setter("selector_color"),
            "color": picker.setter("color"),
            "selector_alpha": picker.setter("selector_alpha")
        }
        self.bind(**self._bound)

        if len(container._bound) > 0:
            container.unbind(**container._bound)
        container._bound = {
            "size": picker.setter("size"),
            "pos": picker.setter("pos")
        }
        container.bind(**container._bound)

        picker.pos = container.pos
        picker.size = container.size
        picker.selector_color = self.selector_color
        picker.color = self.color
        picker.selector_alpha = self.selector_alpha

        if noanim:
            # print "noanim"
            if prevpicker in container.children:
                container.remove_widget(prevpicker)
            if picker.parent:
                picker.parent.remove_widget(picker)
            container.add_widget(picker)
        else:
            if prevpicker in container.children:
                anim = Animation(scale=1.5, d=.5, t="in_back") & Animation(
                    opacity=0, d=.5, t="in_cubic")
                anim.start(prevpicker)
                Clock.schedule_once(
                    lambda *a: container.remove_widget(prevpicker), .5)  #.31)
            picker.scale = 1.5
            picker.opacity = 0
            if picker.parent:
                picker.parent.remove_widget(picker)
            container.add_widget(picker)
            anim = Animation(scale=1, d=.5, t="out_back") & Animation(
                opacity=1, d=.5, t="out_cubic")
            Clock.schedule_once(lambda *a: anim.start(picker), .3)
예제 #26
0
class PlayScreen(Screen):
    game_mode = ""
    current_level = {}
    rows = 3
    cols = 3
    moves_made = BoundedNumericProperty(0)
    max_moves = BoundedNumericProperty(15)
    time_limit = BoundedNumericProperty(15)
    time_elapsed = BoundedNumericProperty(0)
    timer = None
    gridlayout = None
    answerlayout = None
    button_ids = {}
    random = False
    resume = False
    game_tile_sound = None
    filename = ''
    level = None
    level_stars = 0
    stars = [0] * 20
    is_paused = False
    hint_count = 0

    def on_enter(self):
        self.set_mode()
        self.set_level()
        if not self.resume:
            self.gridlayout = GridLayout(rows=self.rows, cols=self.cols)
            self.answerlayout = GridLayout(rows=self.rows, cols=self.cols)
            # generate answer key
            self.generate_answer()
            if self.game_mode != "Expert":
                self.answerlayout.size_hint = [0.3, 0.17]
                self.answerlayout.pos = (0.35 * self.width,
                                         0.695 * self.height)
                self.add_widget(self.answerlayout)

            # generate game board
            self.generate_grid()
            self.gridlayout.size_hint = [0.75, 0.43]  # height, width
            self.gridlayout.pos = (0.13 * self.width, 0.25 * self.height
                                   )  # x, y
            self.add_widget(self.gridlayout)
            self.user_key = "0" * self.rows * self.cols

            self.resume = True
        if self.game_mode == "Expert":
            self.open_answer("init")
            self.answer_button = Button(
                background_normal="../Art/SHOWANS.png",
                background_down='../Art/SHOWANS_DOWN.png',
                size=(99.8, 67),
                size_hint=(None, None),
                pos=(430, 843))
            self.answer_button.bind(on_release=self.open_answer)
            self.add_widget(self.answer_button)
        else:
            self.hint_button = Button(background_normal="../Art/HINT.png",
                                      background_down='../Art/HINT_DOWN.png',
                                      size=(99.8, 67),
                                      size_hint=(None, None),
                                      pos=(430, 845))
            self.hint_button.bind(on_release=self.get_hint)
            self.add_widget(self.hint_button)

        self.game_tile_sound = SoundLoader.load('../Audio/GAME_TILE_PRESS.wav')

    def generate_grid(self):
        for i in range(self.rows):
            for j in range(self.cols):
                button = Button(background_normal="../Art/TILE.png",
                                background_down="../Art/TILE.png")
                button.bind(on_release=self.move_made)
                self.button_ids[button] = "{},{}".format(i, j)
                self.gridlayout.add_widget(button,
                                           len(self.gridlayout.children))

    def generate_answer(self):
        for _ in range(self.rows * self.cols):
            button = Button(background_normal="../Art/TILE.png",
                            background_down="../Art/TILE.png")
            self.answerlayout.add_widget(button,
                                         len(self.answerlayout.children))

        # if random, generate new answer_key
        if self.random:
            # while loop to make sure at least one tile is pressed
            while True:
                self.answer_key = ""
                for _ in range(self.rows * self.cols):
                    self.answer_key = self.answer_key + str(
                        random.randint(0, 1))
                if "1" in self.answer_key:
                    break

        self.minimum_moves = self.answer_key.count("1")
        for index in range(len(self.answerlayout.children)):
            if self.answer_key[index] == "1":
                row, col = self.get_row_col_by_index(index)
                self.change_surrounding_tiles(index,
                                              row,
                                              col,
                                              is_answer_grid=True)

    def get_index_by_tile_id(self, col, row):
        return row * self.cols + col

    def get_row_col_by_index(self, index):
        return (index // self.cols, index % self.cols)

    def move_made(self, instance):
        self.game_tile_sound.play()

        row, col = (int(d) for d in self.button_ids[instance].split(','))
        index = self.get_index_by_tile_id(col, row)
        self.moves_made += 1
        self.user_key = self.user_key[:index] + (
            "1" if self.user_key[index] == "0" else
            "0") + self.user_key[index + 1:]
        print("Pressed button row: {}, col: {}".format(row, col))

        self.change_surrounding_tiles(index, row, col)
        self.goal_reached()

    def change_surrounding_tiles(self, index, row, col, is_answer_grid=False):
        self.change_tile_color(index, is_answer_grid)
        # check if NOT top row
        if (row < self.rows - 1):
            top_index = self.get_index_by_tile_id(col, row + 1)
            self.change_tile_color(top_index, is_answer_grid)
        # check if NOT bottom row
        if (row > 0):
            bottom_index = self.get_index_by_tile_id(col, row - 1)
            self.change_tile_color(bottom_index, is_answer_grid)
        # check if NOT left column
        if (col < self.cols - 1):
            left_index = self.get_index_by_tile_id(col + 1, row)
            self.change_tile_color(left_index, is_answer_grid)
        # check if NOT right column
        if (col > 0):
            right_index = self.get_index_by_tile_id(col - 1, row)
            self.change_tile_color(right_index, is_answer_grid)

    def change_tile_color(self, index, is_answer_grid=False):
        grid = self.gridlayout if not is_answer_grid else self.answerlayout
        if grid.children[index].background_normal == "../Art/TILE_HINT.png":
            if grid.children[index].background_down == "../Art/TILE_DOWN.png":
                grid.children[index].background_normal = "../Art/TILE.png"
            else:
                grid.children[index].background_normal = "../Art/TILE_DOWN.png"
        elif grid.children[
                index].background_normal == "../Art/TILE.png" or grid.children[
                    index].background_normal == "tile":
            grid.children[index].background_normal = "../Art/TILE_DOWN.png"
            grid.children[index].background_down = "../Art/TILE_DOWN.png"
        else:
            grid.children[index].background_normal = "../Art/TILE.png"
            grid.children[index].background_down = "../Art/TILE.png"

    def goal_reached(self):
        if self.user_key == self.answer_key:
            print("Yay, you won!")
            self.number_stars()
            self.open_won()
            self.clear_game()
        else:
            if self.game_mode != "Classic":
                self.ids.moves.text = "Moves Left: " + str(self.max_moves -
                                                           self.moves_made)
                if self.moves_made == self.max_moves:
                    print("Oops, you lost!")
                    self.open_lost()
                    self.clear_game()
            else:
                self.ids.moves.text = "Moves Made: " + str(self.moves_made)

    def number_stars(self):
        intervals = 5
        hint_weight = self.hint_count / self.minimum_moves * 3 * intervals
        print("Hints:", self.hint_count)
        if self.moves_made + hint_weight < self.minimum_moves + intervals:
            self.level_stars = 3
        elif self.moves_made + hint_weight <= self.minimum_moves + intervals * 2:
            self.level_stars = 2
        else:
            self.level_stars = 1
        if self.game_mode == 'Classic':
            if self.stars[self.current_level[self.game_mode] -
                          1] < self.level_stars:
                self.stars[self.current_level[self.game_mode] -
                           1] = self.level_stars
            print("Number of stars:", sum(self.stars))

    def reset_board(self):
        self.moves_made = 0
        self.time_elapsed = 0
        self.hint_count = 0
        self.is_paused = False
        if self.game_mode == "Expert":
            if self.timer:
                self.timer.cancel()
                self.start_timer()
        self.user_key = "0" * self.rows * self.cols
        if self.game_mode == "Classic":
            self.ids.moves.text = "Moves Made: " + str(self.moves_made)
        else:
            self.ids.moves.text = "Moves Left: " + str(self.max_moves -
                                                       self.moves_made)

        for tile in self.gridlayout.children:
            tile.background_normal = "../Art/TILE.png"
            tile.background_down = "../Art/TILE_DOWN.png"

    def clear_game(self):
        self.is_paused = False
        if self.game_mode == "Expert":
            if self.timer:
                self.timer.cancel()
                self.remove_widget(self.answer_button)
            if self.answertimer:
                self.answertimer.cancel()
        if self.resume:
            if self.game_mode != "Expert":
                self.remove_widget(self.hint_button)
            self.ids.extra_settings.text = ""  # to clear up numbers from timer
            self.ids.moves.text = ""  # clear up move counters (slight glitch in which user can see 'Moves Made' changed to "Moves Left" after switching from Classic to Challenger/Expert)
            self.moves_made = 0
            self.time_elapsed = 0
            self.hint_count = 0
            self.gridlayout.clear_widgets()
            self.answerlayout.clear_widgets()
            self.clear_widgets([self.gridlayout, self.answerlayout])
            self.resume = False

    def open_pause(self):
        self.is_paused = True
        if self.game_mode == "Expert":
            self.timer.cancel()
        popup = Pause()
        popup.open()

    def open_won(self):
        if self.game_mode == "Expert":
            self.timer.cancel()
        #self.current_level[self.game_mode] = self.current_level[self.game_mode] + 1
        popup = GameWin()
        popup.set_stars(self.level_stars)
        popup.open()
        self.clear_game()

    def open_lost(self):
        if self.game_mode == "Expert":
            self.timer.cancel()
        popup = GameLose()
        popup.open()
        self.clear_game()

    # shows the answer for 5 seconds
    # need to change the color of the background or the tiles
    def open_answer(self, instance):
        self.answer = ExpertAnswer()
        if instance == "init":
            self.answer.init(self.answerlayout, True)
        else:
            self.answer.init(self.answerlayout, False)
        self.answer.open()

        if instance == "init":
            self.answertimer = Timer(5.0, self.answer.clean)
            self.answertimer.start()

    def get_hint(self, instance=0):
        self.hint_count += 1
        #compare the user and answer key and the first place with a difference changes color for 2 seconds
        for i in range(self.rows * self.cols):
            if self.user_key[i] != self.answer_key[i]:
                self.hintloc = i
                break

        self.gridlayout.children[
            self.hintloc].background_normal = "../Art/TILE_HINT.png"
        timer = Timer(2.0, self.reverse_hint)
        timer.start()

    def reverse_hint(self):
        if self.gridlayout.children[
                self.hintloc].background_normal == "../Art/TILE_HINT.png":
            if self.gridlayout.children[
                    self.hintloc].background_down == "../Art/TILE_DOWN.png":
                self.gridlayout.children[
                    self.hintloc].background_normal = "../Art/TILE_DOWN.png"
            elif self.gridlayout.children[
                    self.hintloc].background_down == "../Art/TILE.png":
                self.gridlayout.children[
                    self.hintloc].background_normal = "../Art/TILE.png"

    def set_mode(self):
        app = App.get_running_app()
        self.game_mode = app.DIFFICULTY

        if app.STARTLEVEL:
            self.current_level[self.game_mode] = app.STARTLEVEL

        # Initialize what level we are on for each difficulty level
        if self.game_mode not in self.current_level:
            self.current_level[self.game_mode] = 1
        self.ids.moves.text = ""
        if self.game_mode == "Classic":
            self.filename = os.path.join(dirname, '../Levels/Classic.txt')
            self.ids.moves.text = "Moves Made: " + str(self.moves_made)
        elif self.game_mode == "Challenge":
            self.filename = os.path.join(dirname, '../Levels/Challenge.txt')
            self.ids.moves.text = "Moves Left: " + str(self.max_moves -
                                                       self.moves_made)
        elif self.game_mode == "Expert":
            self.filename = os.path.join(dirname, '../Levels/Expert.txt')
            self.ids.moves.text = "Moves Left: " + str(self.max_moves -
                                                       self.moves_made)
        else:
            self.random = True

        with open(self.filename) as f:
            level_info = ""
            for i, line in enumerate(f):
                if i + 1 == self.current_level[self.game_mode]:
                    level_info = line
            # level data in the format col row answerkey
            level_info = level_info.rstrip('\n').split(' ')
            # if no more pre-determined level data, then set to randomized level generation
            self.random = level_info == ['']
            if self.random:
                # if randomized, set defaults (including time limit)
                rows, cols, time_limit = 3, 3, 15
                return
            elif self.game_mode == 'Expert':
                # if expert, set time limit too
                rows, cols, self.answer_key, time_limit = level_info
                self.time_limit = int(time_limit)
            else:
                rows, cols, self.answer_key = level_info
            self.rows = int(rows)
            self.cols = int(cols)

    def set_level(self):
        app = App.get_running_app()
        level_number = app.STARTLEVEL
        self.current_level[self.game_mode] = level_number

    def start_timer(self):
        if self.game_mode == 'Expert':  #MUST have this if statement here
            if self.timer:  # make sure only one timer is running at a time
                self.timer.cancel()
            self.ids.extra_settings.text = "Time Left: " + str(
                self.time_limit - self.time_elapsed)
            self.timer = Clock.schedule_interval(partial(self.timer_tick), 1)

    #update the timer every sec
    def timer_tick(self, *largs):
        if not self.is_paused:
            self.time_elapsed += 1
            self.ids.extra_settings.text = "Time Left: " + str(
                self.time_limit - self.time_elapsed)
            if self.time_limit - self.time_elapsed <= 0:
                self.timer.cancel()
                self.open_lost()
예제 #27
0
class Gauge(Widget):
    '''
    Gauge class

    '''

    unit = NumericProperty(1.8)
    file_gauge = StringProperty("cadran.png")
    file_needle = StringProperty("needle.png")
    size_text = NumericProperty(10)
    value = BoundedNumericProperty(0, min_value=0, max_value=100, errorvalue=0)

    def __init__(self,
                 curent_value=0,
                 min_value=0,
                 max_value=100,
                 half_widget=True,
                 show_progress=True,
                 **kwargs):

        super(Gauge, self).__init__(**kwargs)

        self.property('value').set_min(self, min_value)
        self.property('value').set_max(self, max_value)
        self.value = curent_value

        self.midle_value = (max_value - min_value) / 2
        self._show_progress = show_progress

        self._gauge = Scatter(
            size=self.size,
            do_rotation=False,
        )

        # _img_gauge = Image(source=get_module_resource_path(self.file_gauge), size=(self.size_gauge, self.size_gauge))
        self._img_gauge = get_module_resource_path(self.file_gauge,
                                                   size=self.size,
                                                   resource_package=__name__)

        self._needle = Scatter(
            size=self.size,
            do_rotation=False,
        )

        self._img_needle = get_module_resource_path(self.file_needle,
                                                    size=self.size,
                                                    resource_package=__name__)

        self._glab = Label(font_size=self.size_text, markup=True)

        self._gauge.add_widget(self._img_gauge)
        self._needle.add_widget(self._img_needle)

        self.add_widget(self._gauge)
        self.add_widget(self._needle)
        self.add_widget(self._glab)

        if show_progress:
            self._progress = ProgressBar(max=max_value,
                                         height=20,
                                         value=curent_value)
            self.add_widget(self._progress)

        self.bind(pos=self._update)
        self.bind(size=self._update)
        self.bind(value=self._turn)
        self.unit = 90 / self.midle_value if half_widget else 180 / self.midle_value
        self._turn()

    def _update(self, *args):
        '''
        Update gauge and needle positions after sizing or positioning.

        '''
        self._img_gauge.size = self.size
        self._img_needle.size = self._img_gauge.size
        self._needle.size = self.size
        self._gauge.size = self.size
        self._gauge.pos = self.pos
        self._gauge.center = self.center
        self._needle.pos = (self.x, self.y)
        self._needle.center = self._gauge.center
        self._glab.center_x = self._gauge.center_x
        self._glab.center_y = self.center_y + (self.height / 4)

        if self._show_progress:
            element_size = min(self.height, self.width)
            self._progress.x = self._gauge.center_x - element_size / 2
            self._progress.y = self._gauge.y + (self.height / 4)
            self._progress.width = element_size

    def _turn(self, *args):
        '''
        Turn needle, 1 degree = 1 unit, 0 degree point start on 50 value.

        '''
        self._needle.center_x = self._gauge.center_x
        self._needle.center_y = self._gauge.center_y
        self._needle.rotation = (self.midle_value * self.unit) - (self.value *
                                                                  self.unit)
        self._glab.text = "[b]{0:.0f}[/b]".format(self.value)
        if self._show_progress:
            self._progress.value = self.value

    def set_animate(self, value, easing='in_out_quad', speed=1):
        from kivy.animation import Animation
        Animation(value=value, duration=speed, t=easing).start(self)
예제 #28
0
class Slider(Widget):
    '''Class for creating Slider widget.

    Check module documentation for more details.
    '''

    value = NumericProperty(0.)
    '''Current value used for the slider.

    :data:`value` is a :class:`~kivy.properties.NumericProperty`, default to 0.
    '''

    min = NumericProperty(0.)
    '''Minimum value allowed for :data:`value`.

    :data:`min` is a :class:`~kivy.properties.NumericProperty`, default to 0.
    '''

    max = NumericProperty(100.)
    '''Maximum value allowed for :data:`value`.

    :data:`max` is a :class:`~kivy.properties.NumericProperty`, default to 100.
    '''

    padding = NumericProperty(10)
    '''Padding of the slider. The padding is used for graphical representation
    and interaction. It prevents the cursor from going out of the bounds of the
    slider bounding box.

    By default, padding is 10. The range of the slider is reduced from padding *
    2 on the screen. It allows drawing a cursor of 20px width, without having
    the cursor going out of the widget.

    :data:`padding` is a :class:`~kivy.properties.NumericProperty`, default to
    10.
    '''

    orientation = OptionProperty('horizontal', options=(
        'vertical', 'horizontal'))
    '''Orientation of the slider.

    :data:`orientation` is an :class:`~kivy.properties.OptionProperty`, default
    to 'horizontal'. Can take a value of 'vertical' or 'horizontal'.
    '''

    range = ReferenceListProperty(min, max)
    '''Range of the slider, in the format (minimum value, maximum value)::

        >>> slider = Slider(min=10, max=80)
        >>> slider.range
        [10, 80]
        >>> slider.range = (20, 100)
        >>> slider.min
        20
        >>> slider.max
        100

    :data:`range` is a :class:`~kivy.properties.ReferenceListProperty` of
    (:data:`min`, :data:`max`)
    '''

    step = BoundedNumericProperty(0, min=0)
    '''Step size of the slider.

    .. versionadded:: 1.4.0

    Determines the size of each interval or step the slider takes between
    min and max. If the value range can't be evenly divisible by step the
    last step will be capped by slider.max

    :data:`step` is a :class:`~kivy.properties.NumericProperty`, default to 1.
    '''

    def get_norm_value(self):
        vmin = self.min
        d = self.max - vmin
        if d == 0:
            return 0
        return (self.value - vmin) / float(d)

    def set_norm_value(self, value):
        vmin = self.min
        step = self.step
        val = value * (self.max - vmin) + vmin
        if step == 0:
            self.value = val
        else:
            self.value = min(round((val - vmin) / step) * step, self.max)
    value_normalized = AliasProperty(get_norm_value, set_norm_value,
                                     bind=('value', 'min', 'max', 'step'))
    '''Normalized value inside the :data:`range` (min/max) to 0-1 range::

        >>> slider = Slider(value=50, min=0, max=100)
        >>> slider.value
        50
        >>> slider.value_normalized
        0.5
        >>> slider.value = 0
        >>> slider.value_normalized
        0
        >>> slider.value = 1
        >>> slider.value_normalized
        1

    You can also use it for setting the real value without knowing the minimum
    and maximum::

        >>> slider = Slider(min=0, max=200)
        >>> slider.value_normalized = .5
        >>> slider.value
        100
        >>> slider.value_normalized = 1.
        >>> slider.value
        200

    :data:`value_normalized` is an :class:`~kivy.properties.AliasProperty`.
    '''

    def get_value_pos(self):
        padding = self.padding
        x = self.x
        y = self.y
        nval = self.value_normalized
        if self.orientation == 'horizontal':
            return (x + padding + nval * (self.width - 2 * padding), y)
        else:
            return (x, y + padding + nval * (self.height - 2 * padding))

    def set_value_pos(self, pos):
        x = min(self.right, max(pos[0], self.x))
        y = min(self.top, max(pos[1], self.y))
        if self.orientation == 'horizontal':
            if self.width == 0:
                self.value_normalized = 0
            else:
                self.value_normalized = (x - self.x) / float(self.width)
        else:
            if self.height == 0:
                self.value_normalized = 0
            else:
                self.value_normalized = (y - self.y) / float(self.height)
    value_pos = AliasProperty(get_value_pos, set_value_pos,
                              bind=('x', 'y', 'width', 'height', 'min',
                                    'max', 'value_normalized', 'orientation'))
    '''Position of the internal cursor, based on the normalized value.

    :data:`value_pos` is an :class:`~kivy.properties.AliasProperty`.
    '''

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            touch.grab(self)
            self.value_pos = touch.pos
            return True

    def on_touch_move(self, touch):
        if touch.grab_current == self:
            self.value_pos = touch.pos
            return True

    def on_touch_up(self, touch):
        if touch.grab_current == self:
            self.value_pos = touch.pos
            return True
예제 #29
0
class Gauge(Widget):
    '''
    Gauge class

    '''

    unit        = NumericProperty(225/10) # 1 needle tick = 180+45 degrees divided by range
    hrpm        = BoundedNumericProperty(0, min = 0, max = 10, errorvalue = 10)
    speed       = BoundedNumericProperty(0, min = 0, max = 15, errorvalue = 15)
    distance    = BoundedNumericProperty(0, min = 0, max = 50000, errorvalue = 0)
    elapsed     = StringProperty(str(datetime.now()))
    path        = dirname(abspath(__file__))
    file_gauge  = StringProperty(join(path, "lm_tacho_dial_768.png"))
    file_needle = StringProperty(join(path, "lm_tacho_needle_768.png"))
    size_gauge  = BoundedNumericProperty(128, min = 128, max = 768, errorvalue = 128)
    size_text   = NumericProperty(24)

    def __init__(self, **kwargs):
        super(Gauge, self).__init__(**kwargs)

        self._gauge = Scatter(
            size=(self.size_gauge, self.size_gauge),
            do_rotation=False,
            do_scale=False,
            do_translation=False
        )

        _img_gauge = Image(
            source=self.file_gauge,
            size=(self.size_gauge, self.size_gauge)
        )

        self._needle = Scatter(
            size=(self.size_gauge, self.size_gauge),
            do_rotation=False,
            do_scale=False,
            do_translation=False
        )

        _img_needle = Image(
            source=self.file_needle,
            size=(self.size_gauge, self.size_gauge)
        )

        self._speed    = Label(font_size=self.size_text*3, markup=True)
        self._distance = Label(font_size=self.size_text*1.5, markup=True)
        self._elapsed  = Label(font_size=self.size_text*1.5, markup=True)

        self._gauge.add_widget(_img_gauge)
        self._needle.add_widget(_img_needle)

        self.add_widget(self._gauge)
        self.add_widget(self._needle)
        self.add_widget(self._speed)
        self.add_widget(self._distance)
        self.add_widget(self._elapsed)

        self.bind(pos=self._update)
        self.bind(size=self._update)
        self.bind(hrpm=self._turn)
        self.bind(speed=self._turn)
        self.bind(distance=self._turn)
        self.bind(elapsed=self._turn)

    def _update(self, *args):
        '''
        Update gauge and needle positions after sizing or positioning.

        '''
        self._gauge.pos         = self.pos
        self._needle.pos        = (self.x, self.y)
        self._needle.center     = self._gauge.center
        self._speed.center_x    = self._gauge.center_x
        self._speed.center_y    = self._gauge.center_y - (self.size_gauge / 7)
        self._distance.center_x = self._gauge.center_x
        self._distance.center_y = self._gauge.center_y - (self.size_gauge / 4.4)
        self._elapsed.center_x  = self._gauge.center_x
        self._elapsed.center_y  = self._gauge.center_y - (self.size_gauge / 3.4)

    def _turn(self, *args):
        '''
        Turn needle, 1 degree = 1 unit, 0 degree point start on 50 value.

        '''
        self._needle.center_x = self._gauge.center_x
        self._needle.center_y = self._gauge.center_y
        self._needle.rotation = -self.hrpm * self.unit
        self._speed.text      = "[color=5599ff][b]{0:.1f}[/b][/color]".format(self.speed)
        self._distance.text   = "[color=99ff55][b]{0:.0f}m[/b][/color]".format(self.distance)
        self._elapsed.text    = "[b]{}[/b]".format(self.elapsed)
예제 #30
0
class Position(EventDispatcher):
    """
    Position manager for fish, hooks, boat, etc. Enables a wrapped X axis and a bounded Y axis.
    """
    pos_x = BoundedNumericProperty(0, min=0, max=1)
    pos_y = BoundedNumericProperty(0, min=0, max=1)

    def __init__(self, parent, space_subdivisions):
        super().__init__()
        self.parent = parent
        self.space_subdivisions = space_subdivisions
        self.unit = 0.5 / self.space_subdivisions
        self.bind(pos_x=parent.on_state)
        self.bind(pos_y=parent.on_state)

    @property
    def x(self):
        """X axis"""
        cur_pos = self.pos_x
        state_centering = (cur_pos - self.unit + 1.0) % 1.0
        state = self.space_subdivisions * state_centering
        return int(round(state)) % self.space_subdivisions

    def increase_x(self, state_amount):
        """
        Increase the x axis by given (small) amount
        :param state_amount: double. amount to increase in the x axis
        :return:
        """
        pos_amount = state_amount / self.space_subdivisions
        self.pos_x = (self.pos_x + pos_amount) % 1.0

    @property
    def y(self):
        """Y axis"""
        cur_pos = self.pos_y
        state_centering = (cur_pos - self.unit + 1.0) % 1.0
        state = self.space_subdivisions * state_centering
        return int(round(state)) % self.space_subdivisions

    def increase_y(self, state_amount):
        """
        Increase the y axis by given (small) amount
        :param state_amount: double. amount to increase in the y axis
        :return:
        """
        pos_amount = state_amount / self.space_subdivisions
        if self.pos_y + pos_amount < self.unit:
            self.pos_y = self.unit
        elif self.pos_y + pos_amount > 1.0 - self.unit:
            self.pos_y = 1.0 - self.unit
        else:
            self.pos_y = self.pos_y + pos_amount

    def set_x(self, state_value):
        """
        Set the x axis decimal position
        :param state_value: decimal position in range [0, 1]
        :return:
        """
        val = state_value / self.space_subdivisions + self.unit
        if not self.unit <= val <= 1.0 - self.unit:
            raise AttributeError("Value out of bounds")
        self.pos_x = val

    def set_y(self, state_value):
        """
        Set the y axis decimal position
        :param state_value: decimal position in range [0, 1]
        :return:
        """
        val = state_value / self.space_subdivisions + self.unit
        if not self.unit <= val <= 1.0 - self.unit:
            raise AttributeError("Value out of bounds")
        self.pos_y = val

    def __str__(self):
        return self.__repr__()

    def __eq__(self, other):
        """Equivalent states in order to check fish and hooks in same position (caught fish)"""
        return self.x == other.x and self.y == other.y

    def copy(self):
        """
        Copy the current positions
        :return: new position instance
        """
        s = Position(self.parent, self.space_subdivisions)
        s.pos_x = self.pos_x
        s.pos_y = self.pos_y
        return s