Beispiel #1
0
class MainMenu(GridLayout):
    padding = VariableListProperty(100)
    spacing = VariableListProperty(10, length=2)

    learn_button = Button(text=_learn, size_hint=(.3, .3))
    test_button = Button(text=_test, size_hint=(.3, .3))
    exit_button = Button(text=_exit, size_hint=(.3, .3))

    def __init__(self, **kwargs):
        super(MainMenu, self).__init__(**kwargs)
        self.show_menu()

    def show_menu(self):
        self.add_widget(self.learn_button)
        self.add_widget(self.test_button)
        self.add_widget(self.exit_button)

        self.learn_button.bind(on_press=self.begin_learning)
        self.test_button.bind(on_press=self.begin_test)
        self.exit_button.bind(on_press=self.exit_app)

    def begin_learning(self, learn_button):
        kodownik_window = self.parent
        kodownik_window.show_learning_screen()
        pass

    def begin_test(self, test_button):
        kodownik_window = self.parent
        kodownik_window.show_testing_screen()

    def exit_app(self, exit_button):
        App.get_running_app().stop()
Beispiel #2
0
class MDTabsLabel(ToggleButtonBehavior, Label):
    """MDTabsLabel it represent the label of each tab."""

    text_color_normal = VariableListProperty([1, 1, 1, 0.6])
    """Text color of the label when it is not selected."""

    text_color_active = VariableListProperty([1])
    """Text color of the label when it is selected."""

    tab = ObjectProperty()
    tab_bar = ObjectProperty()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.min_space = 0

    def on_release(self):
        # if the label is selected load the relative tab from carousel
        if self.state == "down":
            self.tab_bar.parent.carousel.load_slide(self.tab)

    def on_texture(self, widget, texture):
        # just save the minimum width of the label based of the content
        if texture:
            self.width = texture.width
            self.min_space = self.width

    def _trigger_update_tab_indicator(self):
        # update the position and size of the indicator
        # when the label changes size or position
        if self.state == "down":
            self.tab_bar.update_indicator(self.x, self.width)
Beispiel #3
0
class CodeScreen(GridLayout):
    padding = VariableListProperty(100)
    spacing = VariableListProperty(10, length=2)
    code_manager = CodeManager()

    product_name = ProductName()
    product_code = ProductCode()
    screen_keyboard = ScreenKeyboard(cols=1, rows=1)
    submit_buttons = SubmitButtons(cols=2, rows=1, size_hint=(1, .3))
    app_buttons = AppButtons(cols=2, rows=1, size_hint=(1, .3))

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

        self.add_widget(self.product_code)
        self.add_widget(self.screen_keyboard)
        self.add_widget(self.submit_buttons)
        self.add_widget(self.product_name)
        self.add_widget(self.app_buttons)

        self.learning = LearningWorkflow(
            cm=self.code_manager,
            product_code=self.product_code,
            product_name=self.product_name,
            screen_keyboard=self.screen_keyboard,
            submit_buttons=self.submit_buttons
        )
        self.begin_learning()

    def begin_learning(self):
        kodlog.info("I started showing codes")
        event_dispatcher.do_pick_a_product()
Beispiel #4
0
class Box(Widget):
    padding = VariableListProperty(6)
    border = VariableListProperty(4)
    font_size = StringProperty('15sp')
    font_name = StringProperty(DEFAULT_FONT)
    background = StringProperty()
    background_color = VariableListProperty([1, 1, 1, 1])
    foreground_color = VariableListProperty([0, 0, 0, 1])
Beispiel #5
0
class PlaybackSlider(RelativeLayout):

    # Playback state data
    duration = NumericProperty(None, allownone=True)
    position = NumericProperty(None, allownone=True)
    seekable = BooleanProperty(True)

    # Resolution of the data in seconds. Defaults to 1ms
    resolution = NumericProperty(0.001)
    # Text to display when no time is availabe
    default_text = StringProperty('--:--')
    # Seconds on -Xs and +Xs controls
    shortcut_secs = VariableListProperty(0, length=2)

    available = AliasProperty(
        lambda self: self.position is not None \
            and self.duration is not None,
        None,
        bind=['position', 'duration']
        )

    def __init__(self, **kwargs):
        super(PlaybackSlider, self).__init__(**kwargs)
        self.register_event_type('on_seek')

    def on_seek(self, value):
        pass
Beispiel #6
0
class MDNavigationDrawerDivider(MDBoxLayout):
    """
    Implements a divider for a menu for :class:`~MDNavigationDrawer` class.

    .. versionadded:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

                MDNavigationDrawerLabel:
                    text: "Mail"

                MDNavigationDrawerDivider:

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-divider.png
        :align: center
    """

    padding = VariableListProperty(["20dp", "12dp", 0, "12dp"])
    """
    Padding between layout box and 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].

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

    color = ColorProperty(None)
    """
Beispiel #7
0
class MDNavigationDrawerLabel(MDBoxLayout):
    """
    Implements a label for a menu for :class:`~MDNavigationDrawer` class.

    .. versionadded:: 1.0.0

    .. code-block:: kv

        MDNavigationDrawer:

            MDNavigationDrawerMenu:

                MDNavigationDrawerLabel:
                    text: "Mail"

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/navigation-drawer-label.png
        :align: center
    """

    text = StringProperty()
    """
    Text label.

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

    padding = VariableListProperty(["20dp", 0, 0, "8dp"])
    """
Beispiel #8
0
class InfoPage(GridLayout):
    """This class contains the widgets for the second screen.
    As well as some more information regarding dates and users"""
    padding = VariableListProperty([50, 50, 50, 50])

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

        today = str(datetime.date.today())

        if os.path.isfile("prev_details.txt"):  # Checks to see if the file exists.
            with open("prev_details.txt", "r") as f:
                d = f.read().split(",")
                prev_date = d[0]

                if prev_date != today:  # Checks if a day has passed.
                    days = int(d[1]) + 1
                else:
                    days = int(d[1])

                try:
                    users = app('ca.gc.hcsc.canada.covid19').get("installs")  # Finds how many people have the app.
                except ConnectionError:  # For now just looks at the Canada COVID19 app.
                    users = d[2]
        else:
            days = 0
            users = "100,000+"

        with open("prev_details.txt", "w") as f:  # Writes new information to file.
            f.write(f"{today},{days},{users}")

        self.cols = 1  # This shows text on the screen.
        self.add_widget(Label(text=f"You are on day {days} of quarantine with us. Keep it up!", font_name="DejaVuSans"))
        self.add_widget(Image(source='logo.png'))
        self.add_widget(Label(text=f"You aren't alone! There are {users} people with you!", font_name="DejaVuSans"))
Beispiel #9
0
class ConnectPage(GridLayout):
    """This class contains all the widgets for the first screen.
    """
    padding = VariableListProperty([50, 50, 50, 50])

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.cols = 3

        self.add_widget(Label())
        self.add_widget(Label(text="Do you pledge to do the following:", halign="left", font_name="DejaVuSans"))
        self.add_widget(Label())

        self.add_widget(Image(source="wash.png"))
        self.add_widget(Label(text="Wash your hands often", halign="left", font_name="DejaVuSans"))
        self.wash = CheckBox()
        self.add_widget(self.wash)

        self.add_widget(Image(source="contact.png"))
        self.add_widget(Label(text="Avoid close contact", halign="left", font_name="DejaVuSans"))
        self.contact = CheckBox()
        self.add_widget(self.contact)

        self.add_widget(Image(source="mask.png"))
        self.add_widget(Label(text="Wear a mask properly", halign="left", font_name="DejaVuSans"))
        self.mask = CheckBox()
        self.add_widget(self.mask)

        self.add_widget(Image(source="cover.png"))
        self.add_widget(Label(text="Cover coughs and sneezes", halign="left", font_name="DejaVuSans"))
        self.cover = CheckBox()
        self.add_widget(self.cover)

        self.add_widget(Image(source="clean.png"))
        self.add_widget(Label(text="Clean and disinfect", halign="left", font_name="DejaVuSans"))
        self.clean = CheckBox()
        self.add_widget(self.clean)

        self.add_widget(Image(source="monitor.png"))
        self.add_widget(Label(text="Monitor your health", halign="left", font_name="DejaVuSans"))
        self.monitor = CheckBox()
        self.add_widget(self.monitor)

        self.join = Button(text="Submit", background_color=[0, 0, 1.5, 1.5])
        self.join.bind(on_press=self.submit_button)
        self.add_widget(Label())
        self.add_widget(self.join)

    def submit_button(self, instance):
        """This function ensures the user selects all the options
        before moving forward.
        """
        if self.wash.active \
                and self.contact.active \
                and self.mask.active \
                and self.cover.active \
                and self.clean.active \
                and self.monitor.active:
            todo_app.screen_manager.current = "Info"
Beispiel #10
0
class FlatIconButton(GrabBehavior, LogBehavior, ButtonBehavior,
                     TouchRippleBehavior, ThemeBehavior, AnchorLayout):
    color = ListProperty([1., 1., 1.])
    color_down = ListProperty([.7, .7, .7])
    text = StringProperty('')
    icon = StringProperty('')
    style = StringProperty(None, allownone=True)
    font_size = NumericProperty(12)
    icon_color_tuple = ListProperty(['Grey', '1000'])
    color_tuple = ListProperty(['Blue', '500'])
    font_color_tuple = ListProperty(['Grey', '1000'])
    font_ramp_tuple = ListProperty(None)
    ripple_color_tuple = ListProperty(['Grey', '1000'])
    content_padding = VariableListProperty([0., 0., 0., 0.])
    content_spacing = VariableListProperty([0., 0.], length=2)

    def on_color(self, instance, value):
        self.color_down = [x*.7 for x in value]
Beispiel #11
0
class arc_reacter(Widget):
    circlearc1 = ObjectProperty(None)
    circlearc2 = ObjectProperty(None)
    line1 = ObjectProperty(None)
    color_code = VariableListProperty(None)

    def update(self, color_code):
        self.color_code = random.choice([(.811, .854, .921, 1),
                                         (.933, .968, .964, 1)])
class ScrollableText(ScrollView):
	text = StringProperty("")
	background_color = VariableListProperty([1,1,1,0])
	text_color = VariableListProperty([0,0,0,1])
	padding = VariableListProperty([sp(8), sp(8), sp(8), sp(8)])
	font_size = NumericProperty(sp(18))

	def __init__(self, **kwargs):
		super(ScrollableText, self).__init__(**kwargs)
		self.bind(text = ScrollableText.set_text,
				text_color = ScrollableText.set_text_color,
				padding = ScrollableText.set_padding,
				font_size = ScrollableText.set_font_size
				)
	
	def set_text(self, aText):
		self._text_widget.text = aText
		
	def set_text_color(self, aColor):
		self._text_widget.color = aColor
		
	def set_padding(self, aPadding):
		"""Top, Right, Bottom, Left
		"""
		if isinstance(aPadding, (float,int,long,float)):
			self._padding_top.height = aPadding
			self._padding_right.width = aPadding
			self._padding_bottom.height = aPadding
			self._padding_left.width = aPadding
		if len(aPadding) == 4:
			self._padding_top.height = aPadding[0]
			self._padding_right.width = aPadding[1]
			self._padding_bottom.height = aPadding[2]
			self._padding_left.width = aPadding[3]
		elif len(aPadding) == 2:
			self._padding_top.height = aPadding[0]
			self._padding_right.width = aPadding[1]
			self._padding_bottom.height = aPadding[0]
			self._padding_left.width = aPadding[1]
		
	def set_font_size(self, aSize):
		self._text_widget.font_size = aSize
	
		
Beispiel #13
0
class DexEntry(FloatLayout, Row):
    name = StringProperty('')
    name_lookup = StringProperty('')
    dex_number = StringProperty('')
    type1 = StringProperty('')
    entry = BooleanProperty()
    lucky = BooleanProperty()
    fc = BooleanProperty()
    pic_source = StringProperty('')
    color_regular = VariableListProperty(1)
    color_light = VariableListProperty(1)
    color_dark = VariableListProperty(1)

    def __init__(self, data_row, data_buffer, import_data, **kwargs):
        super(DexEntry, self).__init__(data_row=data_row,
                                       data_buffer=data_buffer,
                                       import_data=import_data,
                                       **kwargs)
        self.data_row = data_row
        self.name_lookup = str(data_row['Name'].lower())
        self.pic_source = PIC_DIR + str(self.dex_number).zfill(3) + '.png'
        self.color_regular = ColorPicker(self.type1, 'regular')
        self.color_light = ColorPicker(self.type1, 'light')
        self.color_dark = ColorPicker(self.type1, 'dark')
class FixedProperties(object):

    fixed_screen_size = VariableListProperty([1920, 1080], limit=2)
    #
    # initial sizing somewhat inspired by Google Material
    font_title_size_fixed = NumericProperty(80.0)
    font_subheading_size_fixed = NumericProperty(64.0)
    font_size_fixed = NumericProperty(56.0)
    #
    pos_fixed = ObjectProperty((0, 0))
    size_fixed = ObjectProperty((100, 100))
    spot_fixed = ObjectProperty((0, 0))
    #
    active_pos_fixed = ObjectProperty((0, 0))

    def apply_fixed_properties(self, widget):
        if not hasattr(widget, "fixed_screen_size"):
            x = self.fixed_screen_size[X]
            y = self.fixed_screen_size[Y]
            widget.apply_property(fixed_screen_size=ObjectProperty([x, y]))
        #
        if not hasattr(widget, "font_title_size_fixed"):
            widget.apply_property(font_title_size_fixed=ObjectProperty((80.0)))
        if not hasattr(widget, "font_subheading_size_fixed"):
            widget.apply_property(
                font_subheading_size_fixed=ObjectProperty((56.0)))
        if not hasattr(widget, "font_size_fixed"):
            widget.apply_property(font_size_fixed=ObjectProperty((56.0)))
        #
        if not hasattr(widget, "pos_fixed"):
            widget.apply_property(pos_fixed=ObjectProperty((0, 0)))
        if not hasattr(widget, "size_fixed"):
            widget.apply_property(size_fixed=ObjectProperty((100, 100)))
        if not hasattr(widget, "spot_fixed"):
            widget.apply_property(spot_fixed=ObjectProperty((0, 0)))
        #
        if not hasattr(widget, "active_pos_fixed"):
            widget.apply_property(active_pos_fixed=ObjectProperty((0, 0)))
        # we force stretching to True
        widget.allow_stretch = True
Beispiel #15
0
class FitImage(BoxLayout):
    source = ObjectProperty()
    """
    Filename/source of your image.

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

    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]`.
    """

    _container = ObjectProperty()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Clock.schedule_once(self._late_init)

    def _late_init(self, *args):
        self._container = Container(self.source)
        self.bind(source=self._container.setter("source"))
        self.add_widget(self._container)

    def reload(self):
        self._container.image.reload()
Beispiel #16
0
class IconText(BoxLayout):
    text = StringProperty("")
    icon = StringProperty("")
    icon_size = NumericProperty(sp(80))
    font_size = NumericProperty(sp(18))
    text_color = VariableListProperty([0, 0, 0, 1])
    forced_width = NumericProperty(sp(80))

    def __init__(self, **kwargs):
        super(IconText, self).__init__(**kwargs)
        self.orientation = 'vertical'
        self.bind(text=IconText.set_text,
                  icon=IconText.set_icon,
                  icon_size=IconText.set_icon_size,
                  font_size=IconText.set_font_size,
                  text_color=IconText.set_text_color,
                  forced_width=IconText.set_forced_width)

    def set_text(self, aText):
        self._text_widget.text = aText

    def set_icon(self, aSourceImage):
        self._icon_widget.canvas.before.children[1].source = aSourceImage

    def set_icon_size(self, aWidthHeight):
        self._icon_widget.size = (aWidthHeight, aWidthHeight)

    def set_font_size(self, aFontSize):
        self._text_widget.font_size = aFontSize

    def set_text_color(self, aColor):
        self._text_widget.color = aColor

    def set_forced_width(self, aWidth):
        if aWidth is not None:
            self.size_hint_x = None
            self.width = aWidth
Beispiel #17
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)
Beispiel #18
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
Beispiel #19
0
class PrinterStatusContent(BoxLayout):
    """
    """
    filename = StringProperty("")
    status = StringProperty("None")
    extruder_one_temp = NumericProperty(0)
    extruder_one_max_temp = NumericProperty(200)
    extruder_two_max_temp = NumericProperty(200)
    extruder_two_temp = NumericProperty(0)
    bed_max_temp = NumericProperty(200)
    bed_temp = NumericProperty(0)
    progress = StringProperty('')
    progress_number = NumericProperty(0)
    startup = False
    safety_counter = 0
    update_lock = False
    etr = StringProperty("Error")
    te = StringProperty("Error")
    etr_start_time = -999
    te_start_time = -999
    first_round = True
    progress_width = NumericProperty(200)
    printing = True

    tae_x = NumericProperty(0)
    tae_y = NumericProperty(0)
    triangle_y = -150
    grey_color = [0.50390625, 0.49609375, 0.49609375, 1]
    black_color = [0.0, 0.0, 0.0, 1.0]
    white_color = [1, 1, 1, 1]
    green_color = [0, 1, 0, 1]
    error_color = VariableListProperty([0.0, 0.0, 0.0, 1.0], length=4)
    temp_color = VariableListProperty([0.0, 0.0, 0.0, 1.0], length=4)

    #temp and error message icons
    temp_icon = StringProperty('Icons/Icon_Buttons/Temperature.png')
    error_icon = StringProperty('Icons/failure_icon.png')

    def __init__(self, *args, **kwargs):
        super(PrinterStatusContent, self).__init__(*args, **kwargs)
        #get the model
        self.model = roboprinter.printer_instance._settings.get(['Model'])
        #Logger.info('===================> model: {}'.format(self.model))

        self.splash_event = Clock.schedule_interval(self.turn_off_splash, .1)
        self.extruder = Temp_Control_Button()
        self.manual = Motor_Control_Button()
        Clock.schedule_interval(self.monitor_errors, 0.2)
        self.update_lock = False
        Clock.schedule_interval(self.safety, 1)
        Clock.schedule_interval(self.update, 0.2)

        #add the move tools function to a global space
        session_saver.saved['Move_Tools'] = self.move_tools_to

    def move_tools_to(self, content_space):

        if content_space == "ERROR":
            if self.model == "Robo R2":
                self.tae_x = -800
            else:
                #self.tae_x = -720
                self.tae_x = -800

            self.error_color = self.grey_color
            self.temp_color = self.black_color
            self.canvas.ask_update()
        elif content_space == "TEMP":
            self.tae_x = 0

            self.error_color = self.black_color
            self.temp_color = self.grey_color
            self.canvas.ask_update()

    def add_error_box(self):

        #add the error container
        error_detection = Error_Detection()
        error_content = self.ids.error_box
        error_content.clear_widgets()
        error_content.add_widget(error_detection)

    def update(self, dt):

        current_data = roboprinter.printer_instance._printer.get_current_data()
        is_printing = current_data['state']['flags']['printing']
        is_paused = current_data['state']['flags']['paused']

        if (is_printing or is_paused) and not self.printing:
            self.printing = True
            self.start_print(0)
            self.move_tools_to("TEMP")
            #Add Start and pause buttons
            extruder_buttons = self.ids.status_buttons
            extruder_buttons.clear_widgets()
            extruder_buttons.add_widget(StartPauseButton())
            extruder_buttons.add_widget(CancelButton())
        elif not is_printing and not is_paused and self.printing:
            self.printing = False
            self.end_print(0)
            self.move_tools_to("ERROR")

            #Add Motor Controls and Temp Controls
            extruder_buttons = self.ids.status_buttons
            extruder_buttons.clear_widgets()
            self.extruder = Temp_Control_Button()
            self.manual = Motor_Control_Button()
            extruder_buttons.add_widget(self.extruder)
            extruder_buttons.add_widget(self.manual)
        if self.first_round:
            self.first_round = False
            self.add_error_box()

        #Monitor Temperature
        if self.is_anything_hot():
            #swap icons
            self.temp_icon = "Icons/Printer Status/red temp icon.png"
        else:
            self.temp_icon = "Icons/Icon_Buttons/Temperature.png"

    def is_anything_hot(self):
        temp1 = self.grab_target_and_actual('tool0')
        temp2 = self.grab_target_and_actual('tool1')
        bed = self.grab_target_and_actual('bed')

        temps = [temp1['target'], temp2['target'], bed['target']]

        for temp in temps:
            if temp > 0.0:
                return True

        return False

    def start_print(self, dt):
        try:
            #make new object for the printer
            print_screen = Print_Screen()

            #clear widgets from the screen
            content_space = self.ids.printer_content
            content_space.clear_widgets()
            content_space.clear_widgets()

            #add the print screen
            content_space.add_widget(print_screen)
        except Exception as e:
            Logger.info("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " + str(e))
            traceback.print_exc()

    def end_print(self, dt):
        try:
            #make new object for the printer
            idle_screen = Idle_Screen()

            #clear widgets from the screen
            content_space = self.ids.printer_content
            content_space.clear_widgets()
            content_space.clear_widgets()

            #add Idle Screen
            content_space.add_widget(idle_screen)

        except AttributeError as e:
            Logger.info("Error in End Print")
            Clock.schedule_once(self.end_print, 1)
            Logger.info("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! " + str(e))
            traceback.print_exc()

    def monitor_errors(self, dt):
        #monitor for errors
        if 'current_error' in session_saver.saved:
            error = session_saver.saved['current_error']

            if error == 'MAINBOARD' or error == 'FIRMWARE' or error == 'BED_DISCONNECT':
                #disable features
                self.extruder.button_state = True
                self.manual.button_state = True

                #swap error icon
                self.error_icon = "Icons/Printer Status/failure_icon_red.png"
                self.error_color_adapter = self.white_color

            elif error == 'DEFAULT':
                self.extruder.button_state = False
                self.manual.button_state = False

                self.error_icon = "Icons/check_icon.png"

            else:
                self.extruder.button_state = False
                self.manual.button_state = False

                #swap error icon
                self.error_icon = "Icons/failure_icon.png"
                self.error_color_adapter = self.white_color

    def grab_target_and_actual(self, tool):
        acceptable_tools = {'tool0': 'tool0', 'tool1': 'tool1', 'bed': 'bed'}

        actual = 0
        target = 0

        if tool in acceptable_tools:
            temps = roboprinter.printer_instance._printer.get_current_temperatures(
            )

            if tool in temps:
                if 'actual' in temps[tool] and 'target' in temps[tool]:
                    if temps[tool]['actual'] == None:
                        actual = 0
                        target = 0
                    else:
                        actual = temps[tool]['actual']
                        target = temps[tool]['target']
                else:
                    actual = 0
                    target = 0

        return {'actual': actual, 'target': target}

    def detirmine_layout(self):
        printer_type = roboprinter.printer_instance._settings.global_get(
            ['printerProfiles', 'defaultProfile'])
        model = printer_type['model']

        tool0 = False
        tool1 = False
        bed = False

        if printer_type['extruder']['count'] == 1:
            tool0 = True
        elif printer_type['extruder']['count'] > 1:
            tool0 = True
            tool1 = True
        if printer_type['heatedBed']:
            bed = True

        if tool0 and tool1 and bed:
            tool_0 = Tool_Status(roboprinter.lang.pack['Tool_Status']['Tool1'],
                                 "tool0")
            tool_1 = Tool_Status(roboprinter.lang.pack['Tool_Status']['Tool2'],
                                 "tool1")
            bed = Tool_Status(roboprinter.lang.pack['Tool_Status']['Bed'],
                              "bed")

            self.ids.tools.add_widget(tool_0)
            self.ids.tools.add_widget(tool_1)
            self.ids.tools.add_widget(bed)

        elif tool0 and bed:
            tool_0 = Tool_Status(roboprinter.lang.pack['Tool_Status']['Tool1'],
                                 "tool0")
            bed = Tool_Status(roboprinter.lang.pack['Tool_Status']['Bed'],
                              "bed")
            self.ids.tools.add_widget(tool_0)
            self.ids.tools.add_widget(Label())
            self.ids.tools.add_widget(bed)

        elif tool0:
            tool_0 = Tool_Status(roboprinter.lang.pack['Tool_Status']['Tool1'],
                                 "tool0")
            self.ids.tools.add_widget(Label())
            self.ids.tools.add_widget(tool_0)
            self.ids.tools.add_widget(Label())

        else:
            Logger.info(
                "##################### TOOL STATUS ERROR #######################"
            )

    def turn_off_splash(self, dt):
        temp1 = self.grab_target_and_actual('tool0')
        self.extruder_one_max_temp = temp1['target']
        self.extruder_one_temp = temp1['actual']

        temp2 = self.grab_target_and_actual('tool1')
        self.extruder_two_max_temp = temp2['target']
        self.extruder_two_temp = temp2['actual']

        bed = self.grab_target_and_actual('bed')
        self.bed_max_temp = bed['target']
        self.bed_temp = bed['actual']
        #turn off the splash screen
        if self.extruder_one_temp != 0 and self.startup == False:
            #Logger.info("Turning Off the Splash Screen!")
            self.detirmine_layout()

            #check for updates
            self.check_updates()
            #check for updates every hour
            Clock.schedule_interval(self.update_clock, 3600)
            self.startup = True

            return False

    def update_clock(self, dt):
        current_data = roboprinter.printer_instance._printer.get_current_data()
        is_printing = current_data['state']['flags']['printing']
        is_paused = current_data['state']['flags']['paused']

        if not is_printing and not is_paused:
            self.check_updates()

    def check_updates(self):
        self.updates = UpdateScreen(populate=False)
        self.updates.refresh_versions()
        installed = self.updates.get_installed_version()
        available = self.updates.get_avail_version()

        Logger.info("Available: " + available.encode('utf-8') +
                    " Installed: " + installed.encode('utf-8'))

        if installed < available and available != roboprinter.lang.pack[
                'Update_Printer']['Connection_Error'] and not self.update_lock:
            self.update_lock = True
            #updater popup
            update = Update_Warning_Popup(self.run_update, self.unlock_updater)
            update.open()

    def run_update(self):
        self.updates.run_updater()

    def unlock_updater(self):
        self.update_lock = False

    def safety(self, dt):
        self.safety_counter += 1
        safety_time = 60

        if self.safety_counter == safety_time and self.startup == False:
            self.detirmine_layout()
            self.check_updates()
            Clock.schedule_interval(self.update_clock, 3600)
            Clock.unschedule(self.splash_event)
            return False
        elif self.safety_counter == safety_time and self.startup == True:
            return False
Beispiel #20
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 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].
    '''

    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({})
    '''List of minimum sizes for each column.

    .. versionadded:: 1.0.7

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

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

    .. versionadded:: 1.0.7

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

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

    .. versionadded:: 1.0.8

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

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

    .. versionadded:: 1.0.8

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

    minimum_size = ReferenceListProperty(minimum_width, minimum_height)
    '''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.
    '''
    def __init__(self, **kwargs):
        self._cols = self._rows = None
        super(GridLayout, self).__init__(**kwargs)
        fbind = self.fast_bind
        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 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 is 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.items():
            cols[index] = value
        for index, value in self.rows_minimum.items():
            rows[index] = value

        # calculate minimum size for each columns and rows
        i = len_children - 1
        for row in range(current_rows):
            for col in range(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] = nmax(cols[col], w)
                else:
                    cols_sh[col] = nmax(cols_sh[col], shw)
                if shh is None:
                    rows[row] = nmax(rows[row], h)
                else:
                    rows_sh[row] = nmax(rows_sh[row], shh)

                # next child
                i = i - 1

        # calculate minimum width/height needed, starting from padding +
        # spacing
        padding_x = self.padding[0] + self.padding[2]
        padding_y = self.padding[1] + self.padding[3]
        spacing_x, spacing_y = self.spacing
        width = padding_x + spacing_x * (current_cols - 1)
        height = padding_y + spacing_y * (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 constraint.')

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

        # speedup
        padding_left = self.padding[0]
        padding_top = self.padding[1]
        spacing_x, spacing_y = 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.items():
                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 range(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.items():
                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 range(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_top
        for row_height in rows:
            x = selfx + padding_left
            for col_width in cols:
                if i < 0:
                    break
                c = children[i]
                c.x = x
                c.y = y - row_height
                c.width = col_width
                c.height = row_height
                i = i - 1
                x = x + col_width + spacing_x
            y -= row_height + spacing_y
Beispiel #21
0
class CommonElevationBehavior(Widget):
    """Common base class for rectangular and circular elevation behavior."""

    elevation = BoundedNumericProperty(0, min=0, errorvalue=0)
    """
    Elevation of the widget.

    .. note::
        Although, this value does not represent the current elevation of the
        widget. :attr:`~CommonElevationBehavior._elevation` can be used to
        animate the current elevation and come back using the
        :attr:`~CommonElevationBehavior.elevation` property directly.

        For example:

        .. code-block:: python

            from kivy.lang import Builder
            from kivy.uix.behaviors import ButtonBehavior

            from kivymd.app import MDApp
            from kivymd.uix.behaviors import CircularElevationBehavior, CircularRippleBehavior
            from kivymd.uix.boxlayout import MDBoxLayout

            KV = '''
            #:import Animation kivy.animation.Animation


            <WidgetWithShadow>
                size_hint: [None, None]
                elevation: 6
                animation_: None
                md_bg_color: [1] * 4
                on_size:
                    self.radius = [self.height / 2] * 4
                on_press:
                    if self.animation_: \
                    self.animation_.cancel(self); \
                    self.animation_ = Animation(_elevation=self.elevation + 6, d=0.08); \
                    self.animation_.start(self)
                on_release:
                    if self.animation_: \
                    self.animation_.cancel(self); \
                    self.animation_ = Animation(_elevation = self.elevation, d=0.08); \
                    self.animation_.start(self)

            MDFloatLayout:

                WidgetWithShadow:
                    size: [root.size[1] / 2] * 2
                    pos_hint: {"center": [0.5, 0.5]}
            '''


            class WidgetWithShadow(
                CircularElevationBehavior,
                CircularRippleBehavior,
                ButtonBehavior,
                MDBoxLayout,
            ):
                def __init__(self, **kwargs):
                    # always set the elevation before the super().__init__ call
                    # self.elevation = 6
                    super().__init__(**kwargs)

                def on_size(self, *args):
                    self.radius = [self.size[0] / 2]


            class Example(MDApp):
                def build(self):
                    return Builder.load_string(KV)


            Example().run()
    """

    # Shadow rendering properties.
    # Shadow rotation memory - SHARED ACROSS OTHER CLASSES.
    angle = NumericProperty(0)
    """
    Angle of rotation in degrees of the current shadow.
    This value is shared across different widgets.

    .. note::
        This value will affect both, hard and soft shadows.
        Each shadow has his own origin point that's computed every time the
        elevation changes.

    .. warning::
        Do not add `PushMatrix` inside the canvas before and add `PopMatrix`
        in the next layer, this will cause visual errors, because the stack
        used will clip the push and pop matrix already inside the canvas.before
        canvas layer.

        Incorrect:

        .. code-block:: kv

            <TiltedWidget>
                canvas.before:
                    PushMatrix
                    [...]
                canvas:
                    PopMatrix

        Correct:

        .. code-block:: kv

            <TiltedWidget>
                canvas.before:
                    PushMatrix
                    [...]
                    PopMatrix



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

    radius = VariableListProperty([0])
    """
    Radius of the corners of the shadow.
    This values represents each corner of the shadow, starting from `top-left`
    corner and going clockwise.

    .. code-block:: python

        radius = [
            "top-left",
            "top-right",
            "bottom-right",
            "bottom-left",
        ]

    This value can be expanded thus allowing this settings to be valid:

    .. code-block:: python

        widget.radius=[0]  # Translates to [0, 0, 0, 0]
        widget.radius=[10, 3]  # Translates to [10, 3, 10, 3]
        widget.radius=[7.0, 8.7, 1.5, 3.0]  # Translates to [7, 8, 1, 3]

    .. note::
        This value will affect both, hard and soft shadows.
        This value only affects :class:`~RoundedRectangularElevationBehavior`
        for now, but can be stored and used by custom shadow draw functions.

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

    # Position of the shadow.
    _shadow_origin_x = NumericProperty(0)
    """
    Shadow origin `x` position for the rotation origin.

    Managed by `_shadow_origin`.

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

    .. note::
        This property is automatically processed. by _shadow_origin.
    """

    _shadow_origin_y = NumericProperty(0)
    """
    Shadow origin y position for the rotation origin.

    Managed by :attr:`_shadow_origin`.

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

    .. note::
        This property is automatically processed.
    """

    _shadow_origin = ReferenceListProperty(_shadow_origin_x, _shadow_origin_y)
    """
    Soft shadow rotation origin point.

    :attr:`_shadow_origin` is an :class:`~kivy.properties.ReferenceListProperty`
    and defaults to `[0, 0]`.

    .. note::
        This property is automatically processed and relative to the canvas center.
    """

    _shadow_pos = ListProperty([0, 0])  # custom offset
    """
    Soft shadow origin point.

    :attr:`_shadow_pos` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0, 0]`.

    .. note::
        This property is automatically processed and relative to the widget's
        canvas center.
    """

    shadow_pos = ListProperty([0, 0])  # bottom left corner
    """
    Custom shadow origin point. If this property is set, :attr:`_shadow_pos`
    will be ommited.

    This property allows users to fake light source.

    :attr:`shadow_pos` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0, 0]`.

    .. note::
        this value overwrite the :attr:`_shadow_pos` processing.
    """

    # Shadow Group shared memory
    __shadow_groups = {"global": []}

    shadow_group = StringProperty("global")
    """
    Widget's shadow group.
    By default every widget with a shadow is saved inside the memory
    :attr:`__shadow_groups` as a weakref. This means that you can have multiple
    light sources, one for every shadow group.

    To fake a light source use :attr:`force_shadow_pos`.

    :attr:`shadow_group` is an :class:`~kivy.properties.StringProperty`
    and defaults to `"global"`.
    """

    _elevation = BoundedNumericProperty(0, min=0, errorvalue=0)
    """
    Current elevation of the widget.

    .. warning::
        This property is the current elevation of the widget, do not
        use this property directly, instead, use :class:`~CommonElevationBehavior`
        elevation.

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

    # soft shadow
    _soft_shadow_texture = ObjectProperty()
    """
    Texture of the soft shadow texture for the canvas.

    :attr:`_soft_shadow_texture` is an :class:`~kivy.core.image.Image`
    and defaults to `None`.

    .. note::
        This property is automatically processed.
    """

    soft_shadow_size = ListProperty([0, 0])
    """
    Size of the soft shadow texture over the canvas.

    :attr:`soft_shadow_size` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0, 0]`.

    .. note::
        This property is automatically processed.
    """

    soft_shadow_pos = ListProperty([0, 0])
    """
    Position of the hard shadow texture over the canvas.

    :attr:`soft_shadow_pos` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0, 0]`.

    .. note::
        This property is automatically processed.
    """

    soft_shadow_cl = ListProperty([0, 0, 0, 0.50])
    """
    Color of the soft shadow.

    :attr:`soft_shadow_cl` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0, 0, 0, 0.15]`.
    """

    # hard shadow
    hard_shadow_texture = ObjectProperty()
    """
    Texture of the hard shadow texture for the canvas.

    :attr:`hard_shadow_texture` is an :class:`~kivy.core.image.Image`
    and defaults to `None`.

    .. note::
        This property is automatically processed when elevation is changed.
    """

    hard_shadow_size = ListProperty([0, 0])
    """
    Size of the hard shadow texture over the canvas.

    :attr:`hard_shadow_size` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0, 0]`.

    .. note::
        This property is automatically processed when elevation is changed.
    """

    hard_shadow_pos = ListProperty([0, 0])
    """
    Position of the hard shadow texture over the canvas.

    :attr:`hard_shadow_pos` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[0, 0]`.

    .. note::
        This property is automatically processed when elevation is changed.
    """

    hard_shadow_cl = ListProperty([0, 0, 0, 0.15])
    """
    Color of the hard shadow.

    .. note::
        :attr:`hard_shadow_cl` is an :class:`~kivy.properties.ListProperty`
        and defaults to `[0, 0, 0, 0.15]`.
    """

    # Shared property for some calculations.
    # This values are used to improve the gaussain blur and avoid that
    # the blur goes outside the texture.
    hard_shadow_offset = BoundedNumericProperty(
        2, min=0, errorhandler=lambda x: 0 if x < 0 else x
    )
    """
    This value sets a special offset to the shadow canvas, this offset allows a
    correct draw of the canvas size. allowing the effect to correctly blur the
    image in the given space.

    :attr:`hard_shadow_offset` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `2`.
    """

    soft_shadow_offset = BoundedNumericProperty(
        4, min=0, errorhandler=lambda x: 0 if x < 0 else x
    )
    """
    This value sets a special offset to the shadow canvas, this offset allows a
    correct draw of the canvas size. allowing the effect to correctly blur the
    image in the given space.

    :attr:`soft_shadow_offset` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `4`.
    """

    draw_shadow = ObjectProperty(None)
    """
    This property controls the draw call of the context.

    This property is automatically set to :attr:`__draw_shadow__` inside the
    `super().__init__ call.` unless the property is different of None.

    To set a different drawing instruction function, set this property before the
    `super(),__init__` call inside the `__init__` definition of the new class.

    You can use the source for this classes as example of how to draw over
    with the context:

    Real time shadows:
        #. :class:`~RectangularElevationBehavior`
        #. :class:`~CircularElevationBehavior`
        #. :class:`~RoundedRectangularElevationBehavior`
        #. :class:`~ObservableShadow`


    Fake shadows (d`ont use this property):
        #. :class:`~FakeRectangularElevationBehavior`
        #. :class:`~FakeCircularElevationBehavior`

    :attr:`draw_shadow` is an :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.

    .. note:: If this property is left to `None` the
        :class:`~CommonElevationBehavior` will set to a function that will
        raise a `NotImplementedError` inside `super().__init__`.

    Follow the next example to set a new draw instruction for the class
    inside `__init__`:

    .. code-block:: python

        class RoundedRectangularElevationBehavior(CommonElevationBehavior):
            '''
            Shadow class for the RoundedRectangular shadow behavior.
            Controls the size and position of the shadow.
            '''

            def __init__(self, **kwargs):
                self._draw_shadow = WeakMethod(self.__draw_shadow__)
                super().__init__(**kwargs)

            def __draw_shadow__(self, origin, end, context=None):
                context.draw(...)

    Context is a `Pillow` `ImageDraw` class. For more information check the
    [Pillow official documentation](https://github.com/python-pillow/Pillow/).
    """

    # All classes that uses a fake shadow shall set this value as `True`
    # for performance.
    _fake_elevation = BooleanProperty(False)

    def __init__(self, **kwargs):
        if self.draw_shadow is None:
            self.draw_shadow = WeakMethod(self.__draw_shadow__)
        self.prev_shadow_group = None
        im = BytesIO()
        Image.new("RGBA", (4, 4), color=(0, 0, 0, 0)).save(im, format="png")
        im.seek(0)
        # Setting a empty image as texture, improves performance.
        self._soft_shadow_texture = self.hard_shadow_texture = CoreImage(
            im, ext="png"
        ).texture
        Clock.schedule_once(self.shadow_preset, -1)
        self.on_shadow_group(self, self.shadow_group)

        self.bind(
            pos=self._update_shadow,
            size=self._update_shadow,
            radius=self._update_shadow,
        )
        super().__init__(**kwargs)

    def on_shadow_group(self, instance, value):
        """
        This function controls the shadow group of the widget.
        Do not use Directly to change the group. instead, use the shadow_group
        :attr:`property`.
        """

        groups = CommonElevationBehavior.__shadow_groups
        if self.prev_shadow_group:
            group = groups[self.prev_shadow_group]
            for widget in group[:]:
                if widget() is self:
                    group.remove(widget)
        group = self.prev_shadow_group = self.shadow_group
        if group not in groups:
            groups[group] = []
        r = ref(self, CommonElevationBehavior._clear_shadow_groups)
        groups[group].append(r)

    @staticmethod
    def _clear_shadow_groups(wk):
        # auto flush the element when the weak reference have been deleted
        groups = CommonElevationBehavior.__shadow_groups
        for group in list(groups.values()):
            if not group:
                break
            if wk in group:
                group.remove(wk)
                break

    def force_shadow_pos(self, shadow_pos):
        """
        This property forces the shadow position in every widget inside the
        widget. The argument :attr:`shadow_pos` is expected as a <class 'list'>
        or <class 'tuple'>.
        """

        if self.shadow_group is None:
            return
        group = CommonElevationBehavior.__shadow_groups[self.shadow_group]
        for wk in group[:]:
            widget = wk()
            if widget is None:
                group.remove(wk)
            widget.shadow_pos = shadow_pos
        del group

    def update_group_property(self, property_name, value):
        """
        This functions allows to change properties of every widget inside the
        shadow group.
        """

        if self.shadow_group is None:
            return
        group = CommonElevationBehavior.__shadow_groups[self.shadow_group]
        for wk in group[:]:
            widget = wk()
            if widget is None:
                group.remove(wk)
            setattr(widget, property_name, value)
        del group

    def shadow_preset(self, *args):
        """
        This function is meant to set the default configuration of the
        elevation.

        After a new instance is created, the elevation property will be launched
        and thus this function will update the elevation if the KV lang have not
        done it already.

        Works similar to an `__after_init__` call inside a widget.
        """

        if self.elevation is None:
            self.elevation = 10
        if self._fake_elevation is False:
            self._update_shadow(self, self.elevation)
        self.bind(
            pos=self._update_shadow,
            size=self._update_shadow,
            _elevation=self._update_shadow,
        )

    def on_elevation(self, instance, value):
        """
        Elevation event that sets the current elevation value to `_elevation`.
        """

        if value is not None:
            self._elevation = value

    def _set_soft_shadow_a(self, value):
        value = 0 if value < 0 else (1 if value > 1 else value)
        self.soft_shadow_cl[-1] = value
        return True

    def _set_hard_shadow_a(self, value):
        value = 0 if value < 0 else (1 if value > 1 else value)
        self.hard_shadow_cl[-1] = value
        return True

    def _get_soft_shadow_a(self):
        return self.soft_shadow_cl[-1]

    def _get_hard_shadow_a(self):
        return self.hard_shadow_cl[-1]

    _soft_shadow_a = AliasProperty(
        _get_soft_shadow_a, _set_soft_shadow_a, bind=["soft_shadow_cl"]
    )
    _hard_shadow_a = AliasProperty(
        _get_hard_shadow_a, _set_hard_shadow_a, bind=["hard_shadow_cl"]
    )

    def on_disabled(self, instance, value):
        """
        This function hides the shadow when the widget is disabled.
        It sets the shadow to `0`.
        """

        if self.disabled is True:
            self._elevation = 0
        else:
            self._elevation = 0 if self.elevation is None else self.elevation
        self._update_shadow(self, self._elevation)
        try:
            super().on_disabled(instance, value)
        except Exception:
            pass

    def _update_elevation(self, instance, value):
        self._elevation = value
        self._update_shadow(instance, value)

    def _update_shadow_pos(self, instance, value):
        if self._elevation > 0:
            self.hard_shadow_pos = [
                self.x - dp(self.hard_shadow_offset),  # + self.shadow_pos[0],
                self.y - dp(self.hard_shadow_offset),  # + self.shadow_pos[1],
            ]
            if self.shadow_pos == [0, 0]:
                self.soft_shadow_pos = [
                    self.x
                    + self._shadow_pos[0]
                    - self._elevation
                    - dp(self.soft_shadow_offset),
                    self.y
                    + self._shadow_pos[1]
                    - self._elevation
                    - dp(self.soft_shadow_offset),
                ]
            else:
                self.soft_shadow_pos = [
                    self.x
                    + self.shadow_pos[0]
                    - self._elevation
                    - dp(self.soft_shadow_offset),
                    self.y
                    + self.shadow_pos[1]
                    - self._elevation
                    - dp(self.soft_shadow_offset),
                ]
            self._shadow_origin = [
                self.soft_shadow_pos[0] + self.soft_shadow_size[0] / 2,
                self.soft_shadow_pos[1] + self.soft_shadow_size[1] / 2,
            ]

    def on__shadow_pos(self, ins, val):
        """
        Updates the shadow with the computed value.

        Call this function every time you need to force a shadow update.
        """

        self._update_shadow_pos(ins, val)

    def on_shadow_pos(self, ins, val):
        """
        Updates the shadow with the fixed value.

        Call this function every time you need to force a shadow update.
        """

        self._update_shadow_pos(ins, val)

    def _update_shadow(self, instance, value):
        self._update_shadow_pos(instance, value)
        if self._elevation > 0 and self._fake_elevation is False:
            # dynamic elevation position for the shadow
            if self.shadow_pos == [0, 0]:
                self._shadow_pos = [0, -self._elevation * 0.4]

            # HARD Shadow
            offset = int(dp(self.hard_shadow_offset))
            size = [
                int(self.size[0] + (offset * 2)),
                int(self.size[1] + (offset * 2)),
            ]
            im = BytesIO()
            # context
            img = Image.new("RGBA", tuple(size), color=(0, 0, 0, 0))
            # draw context
            shadow = ImageDraw.Draw(img)
            self.draw_shadow()(
                [offset, offset],
                [
                    int(size[0] - 1 - offset),
                    int(size[1] - 1 - offset),
                ],
                context=shadow
                # context=ref(shadow)
            )
            img = img.filter(
                ImageFilter.GaussianBlur(
                    radius=int(dp(1 + self.hard_shadow_offset / 3))
                )
            )
            img.save(im, format="png")
            im.seek(0)
            self.hard_shadow_size = size
            self.hard_shadow_texture = CoreImage(im, ext="png").texture

            # soft shadow
            if self.soft_shadow_cl[-1] > 0:
                offset = dp(self.soft_shadow_offset)
                size = [
                    int(self.size[0] + dp(self._elevation * 2) + (offset * 2)),
                    int(self.size[1] + dp(self._elevation * 2) + (offset * 2)),
                    # ((self._elevation)*2) + x + (offset*2)) for x in self.size
                ]
                im = BytesIO()
                img = Image.new("RGBA", tuple(size), color=((0,) * 4))
                shadow = ImageDraw.Draw(img)
                _offset = int(dp(self._elevation + offset))
                self.draw_shadow()(
                    [
                        _offset,
                        _offset,
                    ],
                    [int(size[0] - _offset - 1), int(size[1] - _offset - 1)],
                    context=shadow
                    # context=ref(shadow)
                )
                img = img.filter(
                    ImageFilter.GaussianBlur(radius=self._elevation // 2)
                )
                shadow = ImageDraw.Draw(img)
                img.save(im, format="png")
                im.seek(0)
                self.soft_shadow_size = size
                self._soft_shadow_texture = CoreImage(im, ext="png").texture
        else:
            im = BytesIO()
            Image.new("RGBA", (4, 4), color=(0, 0, 0, 0)).save(im, format="png")
            im.seek(0)
            self._soft_shadow_texture = self.hard_shadow_texture = CoreImage(
                im, ext="png"
            ).texture
            return

    def _get_center(self):
        center = [self.pos[0] + self.width / 2, self.pos[1] + self.height / 2]
        return center

    def __draw_shadow__(self, origin, end, context=None):
        raise NotImplementedError(
            "KivyMD:\n"
            "If you see this error, this means that either youre using "
            "`CommonElevationBehavio`r directly or your 'shader' dont have a "
            "`_draw_shadow` instruction, remember to overwrite this function"
            "to draw over the image context. the figure you would like."
        )
Beispiel #22
0
class MDDropdownMenu(ThemableBehavior, FloatLayout):
    """
    :Events:
        `on_release`
            The method that will be called when you click menu items.
    """

    header_cls = ObjectProperty()
    """
    An instance of the class (`Kivy` or `KivyMD` widget) that will be added
    to the menu header.

    .. versionadded:: 0.104.2

    See Header_ for more information.

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-header-cls.png
        :align: center

    :attr:`header_cls` is a :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    items = ListProperty()
    """
    See :attr:`~kivy.uix.recycleview.RecycleView.data`.

    .. code-block:: python

        items = [
            {
                "viewclass": "OneLineListItem",
                "height": dp(56),
                "text": f"Item {i}",
            }
            for i in range(5)
        ]
        self.menu = MDDropdownMenu(
            items=items,
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-items.png
        :align: center

    :attr:`items` is a :class:`~kivy.properties.ListProperty`
    and defaults to `[]`.
    """

    width_mult = NumericProperty(1)
    """
    This number multiplied by the standard increment ('56dp' on mobile, '64dp'
    on desktop), determines the width of the menu items.

    If the resulting number were to be too big for the application Window,
    the multiplier will be adjusted for the biggest possible one.

    .. code-block:: python

        self.menu = MDDropdownMenu(
            width_mult=4,
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-width-mult-4.png
        :align: center

    .. code-block:: python

        self.menu = MDDropdownMenu(
            width_mult=8,
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-width-mult-8.png
        :align: center

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

    max_height = NumericProperty()
    """
    The menu will grow no bigger than this number. Set to 0 for no limit.

    .. code-block:: python

        self.menu = MDDropdownMenu(
            max_height=dp(112),
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-max-height-112.png
        :align: center

    .. code-block:: python

        self.menu = MDDropdownMenu(
            max_height=dp(224),
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-max-height-224.png
        :align: center

    :attr:`max_height` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0`.
    """

    border_margin = NumericProperty("4dp")
    """
    Margin between Window border and menu.

    .. code-block:: python

        self.menu = MDDropdownMenu(
            border_margin=dp(4),
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-border-margin-4.png
        :align: center

    .. code-block:: python

        self.menu = MDDropdownMenu(
            border_margin=dp(24),
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-border-margin-24.png
        :align: center

    :attr:`border_margin` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `4dp`.
    """

    ver_growth = OptionProperty(None, allownone=True, options=["up", "down"])
    """
    Where the menu will grow vertically to when opening. Set to `None` to let
    the widget pick for you. Available options are: `'up'`, `'down'`.

    .. code-block:: python

        self.menu = MDDropdownMenu(
            ver_growth="up",
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-ver-growth-up.gif
        :align: center

    .. code-block:: python

        self.menu = MDDropdownMenu(
            ver_growth="down",
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-ver-growth-down.gif
        :align: center

    :attr:`ver_growth` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `None`.
    """

    hor_growth = OptionProperty(None,
                                allownone=True,
                                options=["left", "right"])
    """
    Where the menu will grow horizontally to when opening. Set to `None` to let
    the widget pick for you. Available options are: `'left'`, `'right'`.

    .. code-block:: python

        self.menu = MDDropdownMenu(
            hor_growth="left",
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-hor-growth-left.gif
        :align: center

    .. code-block:: python

        self.menu = MDDropdownMenu(
            hor_growth="right",
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-hor-growth-right.gif
        :align: center

    :attr:`hor_growth` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `None`.
    """

    background_color = ColorProperty(None)
    """
    Color of the background of the menu.

    .. code-block:: python

        self.menu = MDDropdownMenu(
            background_color=self.theme_cls.primary_light,
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-background-color.png
        :align: center

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

    opening_transition = StringProperty("out_cubic")
    """
    Type of animation for opening a menu window.

    :attr:`opening_transition` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'out_cubic'`.
    """

    opening_time = NumericProperty(0.2)
    """
    Menu window opening animation time and you can set it to 0
    if you don't want animation of menu opening.

    :attr:`opening_time` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0.2`.
    """

    caller = ObjectProperty()
    """
    The widget object that calls the menu window.

    :attr:`caller` is a :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    position = OptionProperty("auto",
                              options=["top", "auto", "center", "bottom"])
    """
    Menu window position relative to parent element.
    Available options are: `'auto'`, `'center'`, `'bottom'`.

    See Position_ for more information.

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-position.png
        :align: center

    :attr:`position` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `'auto'`.
    """

    radius = VariableListProperty([dp(7)])
    """
    Menu radius.

    .. code-block:: python

        self.menu = MDDropdownMenu(
            radius=[24, 0, 24, 0],
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-radius.png
        :align: center

    :attr:`radius` is a :class:`~kivy.properties.VariableListProperty`
    and defaults to `'[dp(7)]'`.
    """

    elevation = NumericProperty(10)
    """
    Elevation value of menu dialog.

    .. versionadded:: 1.0.0

    .. code-block:: python

        self.menu = MDDropdownMenu(
            elevation=16,
            ...,
        )

    .. image:: https://github.com/HeaTTheatR/KivyMD-data/raw/master/gallery/kivymddoc/menu-elevation.png
        :align: center

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

    _start_coords = []
    _calculate_complete = False
    _calculate_process = False

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        Window.bind(on_resize=self.check_position_caller)
        Window.bind(on_maximize=self.set_menu_properties)
        Window.bind(on_restore=self.set_menu_properties)
        Clock.schedule_once(self.ajust_radius)
        self.register_event_type("on_dismiss")
        self.menu = self.ids.md_menu
        self.target_height = 0

    def check_position_caller(self, instance_window: WindowSDL, width: int,
                              height: int) -> None:
        """Called when the application root window is resized."""

        # FIXME: Menu position is not recalculated when changing the size of
        #  the root application window.
        self.set_menu_properties(0)

    def set_menu_properties(self, interval: Union[int, float] = 0) -> None:
        """Sets the size and position for the menu window."""

        if self.caller:
            self.ids.md_menu.data = self.items
            # We need to pick a starting point, see how big we need to be,
            # and where to grow to.
            self._start_coords = self.caller.to_window(self.caller.center_x,
                                                       self.caller.center_y)
            self.target_width = self.width_mult * m_res.STANDARD_INCREMENT

            # If we're wider than the Window...
            if self.target_width > Window.width:
                # ...reduce our multiplier to max allowed.
                self.target_width = (
                    int(Window.width / m_res.STANDARD_INCREMENT) *
                    m_res.STANDARD_INCREMENT)

            # Set the target_height of the menu depending on the size of
            # each MDMenuItem or MDMenuItemIcon.
            self.target_height = 0
            for item in self.ids.md_menu.data:
                self.target_height += item.get("height", dp(72))

            # If we're over max_height...
            if 0 < self.max_height < self.target_height:
                self.target_height = self.max_height

            # Establish vertical growth direction.
            if self.ver_growth is not None:
                ver_growth = self.ver_growth
            else:
                # If there's enough space below us:
                if (self.target_height <=
                        self._start_coords[1] - self.border_margin):
                    ver_growth = "down"
                # if there's enough space above us:
                elif (self.target_height < Window.height -
                      self._start_coords[1] - self.border_margin):
                    ver_growth = "up"
                # Otherwise, let's pick the one with more space and adjust
                # ourselves.
                else:
                    # If there"s more space below us:
                    if (self._start_coords[1] >=
                            Window.height - self._start_coords[1]):
                        ver_growth = "down"
                        self.target_height = (self._start_coords[1] -
                                              self.border_margin)
                    # If there's more space above us:
                    else:
                        ver_growth = "up"
                        self.target_height = (Window.height -
                                              self._start_coords[1] -
                                              self.border_margin)

            if self.hor_growth is not None:
                hor_growth = self.hor_growth
            else:
                # If there's enough space to the right:
                if (self.target_width <= Window.width - self._start_coords[0] -
                        self.border_margin):
                    hor_growth = "right"
                # if there's enough space to the left:
                elif (self.target_width <
                      self._start_coords[0] - self.border_margin):
                    hor_growth = "left"
                # Otherwise, let's pick the one with more space and adjust
                # ourselves.
                else:
                    # if there"s more space to the right:
                    if (Window.width - self._start_coords[0] >=
                            self._start_coords[0]):
                        hor_growth = "right"
                        self.target_width = (Window.width -
                                             self._start_coords[0] -
                                             self.border_margin)
                    # if there"s more space to the left:
                    else:
                        hor_growth = "left"
                        self.target_width = (self._start_coords[0] -
                                             self.border_margin)

            if ver_growth == "down":
                self.tar_y = self._start_coords[1] - self.target_height
            else:  # should always be "up"
                self.tar_y = self._start_coords[1]

            if hor_growth == "right":
                self.tar_x = self._start_coords[0]
            else:  # should always be "left"
                self.tar_x = self._start_coords[0] - self.target_width
            self._calculate_complete = True

    def ajust_radius(self, interval: Union[int, float]) -> None:
        """
        Adjusts the radius of the first and last items in the menu list
        according to the radius that is set for the menu.
        """

        radius_for_firt_item = self.radius[:2]
        radius_for_last_item = self.radius[2:]

        firt_data_item = self.items[0]
        last_data_item = self.items[-1]

        firt_data_item["radius"] = radius_for_firt_item + [0, 0]
        last_data_item["radius"] = [0, 0] + radius_for_last_item
        last_data_item["divider"] = None

        self.items[0] = firt_data_item
        self.items[-1] = last_data_item

        # For all other elements of the list, except for the first and last,
        # we set the value of the radius to `0`.
        for i, data_item in enumerate(self.items):
            if "radius" not in data_item:
                data_item["radius"] = 0
                self.items[i] = data_item

    def adjust_position(self) -> str:
        """
        Returns value 'auto' for the menu position if the menu position is out
        of screen.
        """

        target_width = self.target_width
        target_height = self.target_height
        caller = self.caller
        position = self.position

        if (caller.x < target_width or caller.x + target_width > Window.width
                or caller.y + target_height > Window.height
                or (caller.y < target_height and position == "center")):
            position = "auto"
            if self.hor_growth or self.ver_growth:
                self.hor_growth = None
                self.ver_growth = None
                self.set_menu_properties()
        return position

    def open(self) -> None:
        """Animate the opening of a menu window."""
        def open(interval):
            if not self._calculate_complete:
                return

            position = self.adjust_position()

            if position == "auto":
                self.menu.pos = self._start_coords
                anim = Animation(
                    x=self.tar_x,
                    y=self.tar_y -
                    (self.header_cls.height if self.header_cls else 0),
                    width=self.target_width,
                    height=self.target_height,
                    duration=self.opening_time,
                    opacity=1,
                    transition=self.opening_transition,
                )
                anim.start(self.menu)
            else:
                if position == "center":
                    self.menu.pos = (
                        self._start_coords[0] - self.target_width / 2,
                        self._start_coords[1] - self.target_height / 2,
                    )
                elif position == "bottom":
                    self.menu.pos = (
                        self._start_coords[0] - self.target_width / 2,
                        self.caller.pos[1] - self.target_height,
                    )
                elif position == "top":
                    self.menu.pos = (
                        self._start_coords[0] - self.target_width / 2,
                        self.caller.pos[1] + self.caller.height,
                    )
                anim = Animation(
                    width=self.target_width,
                    height=self.target_height,
                    duration=self.opening_time,
                    opacity=1,
                    transition=self.opening_transition,
                )
            anim.start(self.menu)
            Window.add_widget(self)
            Clock.unschedule(open)
            self._calculate_process = False

        self.set_menu_properties()
        if not self._calculate_process:
            self._calculate_process = True
            Clock.schedule_interval(open, 0)

    def on_header_cls(self, instance_dropdown_menu,
                      instance_user_menu_header) -> None:
        """Called when a value is set to the :attr:`header_cls` parameter."""
        def add_content_header_cls(interval):
            self.ids.content_header.clear_widgets()
            self.ids.content_header.add_widget(instance_user_menu_header)

        Clock.schedule_once(add_content_header_cls, 1)

    def on_touch_down(self, touch):
        if not self.menu.collide_point(*touch.pos):
            self.dispatch("on_dismiss")
            return True
        super().on_touch_down(touch)
        return True

    def on_touch_move(self, touch):
        super().on_touch_move(touch)
        return True

    def on_touch_up(self, touch):
        super().on_touch_up(touch)
        return True

    def on_dismiss(self) -> None:
        """Called when the menu is closed."""

        Window.remove_widget(self)
        self.menu.width = 0
        self.menu.height = 0
        self.menu.opacity = 0

    def dismiss(self, *args) -> None:
        """Closes the menu."""

        self.on_dismiss()
Beispiel #23
0
class StackLayout(Layout):
    '''Stack 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 single 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 it's children: [padding_left,
    padding_top, padding_right, padding_bottom].

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

    .. versionchanged:: 1.7.0
        Replaced the NumericProperty with a VariableListProperty.

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

    '''

    orientation = OptionProperty('lr-tb',
                                 options=('lr-tb', 'tb-lr', 'rl-tb', 'tb-rl',
                                          'lr-bt', 'bt-lr', 'rl-bt', 'bt-rl'))
    '''Orientation of the layout.

    :attr:`orientation` is an :class:`~kivy.properties.OptionProperty` and
    defaults to 'lr-tb'.

    Valid orientations are 'lr-tb', 'tb-lr', 'rl-tb', 'tb-rl', 'lr-bt',
    'bt-lr', 'rl-bt' and 'bt-rl'.

    .. versionchanged:: 1.5.0
        :attr:`orientation` now correctly handles all valid combinations of
        'lr','rl','tb','bt'. Before this version only 'lr-tb' and
        'tb-lr' were supported, and 'tb-lr' was misnamed and placed
        widgets from bottom to top and from right to left (reversed compared
        to what was expected).

    .. note::

        'lr' means Left to Right.
        'rl' means Right to Left.
        'tb' means Top to Bottom.
        'bt' means Bottom to Top.
    '''

    minimum_width = NumericProperty(0)
    '''Minimum width needed to contain all children. It is automatically set
    by the layout.

    .. versionadded:: 1.0.8

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

    minimum_height = NumericProperty(0)
    '''Minimum height needed to contain all children. It is automatically set
    by the layout.

    .. versionadded:: 1.0.8

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

    minimum_size = ReferenceListProperty(minimum_width, minimum_height)
    '''Minimum size needed to contain all children. It is automatically set
    by the layout.

    .. versionadded:: 1.0.8

    :attr:`minimum_size` is a
    :class:`~kivy.properties.ReferenceListProperty` of
    (:attr:`minimum_width`, :attr:`minimum_height`) properties.
    '''
    def __init__(self, **kwargs):
        super(StackLayout, self).__init__(**kwargs)
        trigger = self._trigger_layout
        fbind = self.fbind
        fbind('padding', trigger)
        fbind('spacing', trigger)
        fbind('children', trigger)
        fbind('orientation', trigger)
        fbind('size', trigger)
        fbind('pos', trigger)

    def do_layout(self, *largs):
        if not self.children:
            return

        # optimize layout by preventing looking at the same attribute in a loop
        selfpos = self.pos
        selfsize = self.size
        orientation = self.orientation.split('-')
        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
        spacing_x, spacing_y = self.spacing

        lc = []

        # Determine which direction and in what order to place the widgets
        posattr = [0] * 2
        posdelta = [0] * 2
        posstart = [0] * 2
        for i in (0, 1):
            posattr[i] = 1 * (orientation[i] in ('tb', 'bt'))
            k = posattr[i]
            if orientation[i] == 'lr':
                # left to right
                posdelta[i] = 1
                posstart[i] = selfpos[k] + padding_left
            elif orientation[i] == 'bt':
                # bottom to top
                posdelta[i] = 1
                posstart[i] = selfpos[k] + padding_bottom
            elif orientation[i] == 'rl':
                # right to left
                posdelta[i] = -1
                posstart[i] = selfpos[k] + selfsize[k] - padding_right
            else:
                # top to bottom
                posdelta[i] = -1
                posstart[i] = selfpos[k] + selfsize[k] - padding_top

        innerattr, outerattr = posattr
        ustart, vstart = posstart
        deltau, deltav = posdelta
        del posattr, posdelta, posstart

        u = ustart  # inner loop position variable
        v = vstart  # outer loop position variable

        # space calculation, used for determining when a row or column is full

        if orientation[0] in ('lr', 'rl'):
            sv = padding_y  # size in v-direction, for minimum_size property
            su = padding_x  # size in h-direction
            spacing_u = spacing_x
            spacing_v = spacing_y
            padding_u = padding_x
            padding_v = padding_y
        else:
            sv = padding_x  # size in v-direction, for minimum_size property
            su = padding_y  # size in h-direction
            spacing_u = spacing_y
            spacing_v = spacing_x
            padding_u = padding_y
            padding_v = padding_x

        # space calculation, row height or column width, for arranging widgets
        lv = 0

        urev = (deltau < 0)
        vrev = (deltav < 0)
        firstchild = self.children[0]
        sizes = []
        for c in reversed(self.children):
            if c.size_hint[outerattr]:
                c.size[outerattr] = max(
                    1,
                    c.size_hint[outerattr] * (selfsize[outerattr] - padding_v))

            # does the widget fit in the row/column?
            ccount = len(lc)
            totalsize = availsize = max(
                0, selfsize[innerattr] - padding_u - spacing_u * ccount)
            if not lc:
                if c.size_hint[innerattr]:
                    childsize = max(1, c.size_hint[innerattr] * totalsize)
                else:
                    childsize = max(0, c.size[innerattr])
                availsize = selfsize[innerattr] - padding_u - childsize
                testsizes = [childsize]
            else:
                testsizes = [0] * (ccount + 1)
                for i, child in enumerate(lc):
                    if availsize <= 0:
                        # no space left but we're trying to add another widget.
                        availsize = -1
                        break
                    if child.size_hint[innerattr]:
                        testsizes[i] = childsize = max(
                            1, child.size_hint[innerattr] * totalsize)
                    else:
                        testsizes[i] = childsize = max(0,
                                                       child.size[innerattr])
                    availsize -= childsize
                if c.size_hint[innerattr]:
                    testsizes[-1] = max(1, c.size_hint[innerattr] * totalsize)
                else:
                    testsizes[-1] = max(0, c.size[innerattr])
                availsize -= testsizes[-1]

            if availsize >= 0 or not lc:
                # even if there's no space, we always add one widget to a row
                lc.append(c)
                sizes = testsizes
                lv = max(lv, c.size[outerattr])
                continue

            # apply the sizes
            for i, child in enumerate(lc):
                if child.size_hint[innerattr]:
                    child.size[innerattr] = sizes[i]

            # push the line
            sv += lv + spacing_v
            for c2 in lc:
                if urev:
                    u -= c2.size[innerattr]
                c2.pos[innerattr] = u
                pos_outer = v
                if vrev:
                    # v position is actually the top/right side of the widget
                    # when going from high to low coordinate values,
                    # we need to subtract the height/width from the position.
                    pos_outer -= c2.size[outerattr]
                c2.pos[outerattr] = pos_outer
                if urev:
                    u -= spacing_u
                else:
                    u += c2.size[innerattr] + spacing_u

            v += deltav * lv
            v += deltav * spacing_v
            lc = [c]
            lv = c.size[outerattr]
            if c.size_hint[innerattr]:
                sizes = [
                    max(
                        1, c.size_hint[innerattr] *
                        (selfsize[innerattr] - padding_u))
                ]
            else:
                sizes = [max(0, c.size[innerattr])]
            u = ustart

        if lc:
            # apply the sizes
            for i, child in enumerate(lc):
                if child.size_hint[innerattr]:
                    child.size[innerattr] = sizes[i]

            # push the last (incomplete) line
            sv += lv + spacing_v
            for c2 in lc:
                if urev:
                    u -= c2.size[innerattr]
                c2.pos[innerattr] = u
                pos_outer = v
                if vrev:
                    pos_outer -= c2.size[outerattr]
                c2.pos[outerattr] = pos_outer
                if urev:
                    u -= spacing_u
                else:
                    u += c2.size[innerattr] + spacing_u

        self.minimum_size[outerattr] = sv
Beispiel #24
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)
class BoxLayout(Layout):
    '''Box layout class. See module documentation for more information.
    '''

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

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

    padding = VariableListProperty([0, 0, 0, 0])
    '''Padding between layout box and 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].
    '''

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

    :attr:`orientation` is an :class:`~kivy.properties.OptionProperty` and
    defaults to 'horizontal'. Can be 'vertical' or 'horizontal'.
    '''
    def __init__(self, **kwargs):
        super(BoxLayout, self).__init__(**kwargs)
        self.bind(spacing=self._trigger_layout,
                  padding=self._trigger_layout,
                  children=self._trigger_layout,
                  orientation=self._trigger_layout,
                  parent=self._trigger_layout,
                  size=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
        selfx = self.x
        selfy = self.y
        selfw = self.width
        selfh = self.height
        padding_left = self.padding[0]
        padding_top = self.padding[1]
        padding_right = self.padding[2]
        padding_bottom = self.padding[3]
        spacing = self.spacing
        orientation = self.orientation
        padding_x = padding_left + padding_right
        padding_y = padding_top + padding_bottom

        # calculate maximum space used by size_hint
        stretch_weight_x = 0.
        stretch_weight_y = 0.
        minimum_size_x = padding_x + spacing * (len_children - 1)
        minimum_size_y = padding_y + spacing * (len_children - 1)
        for w in self.children:
            shw = w.size_hint_x
            shh = w.size_hint_y
            if shw is None:
                minimum_size_x += w.width
            else:
                stretch_weight_x += shw
            if shh is None:
                minimum_size_y += w.height
            else:
                stretch_weight_y += shh

        if orientation == 'horizontal':
            x = padding_left
            stretch_space = max(0.0, selfw - minimum_size_x)
            for c in reversed(self.children):
                shw = c.size_hint_x
                shh = c.size_hint_y
                w = c.width
                h = c.height
                cx = selfx + x
                cy = selfy + padding_bottom

                if shw:
                    w = stretch_space * shw / stretch_weight_x
                if shh:
                    h = max(0, shh * (selfh - padding_y))

                for key, value in c.pos_hint.items():
                    posy = value * (selfh - padding_y)
                    if key == 'y':
                        cy += padding_bottom + posy
                    elif key == 'top':
                        cy += padding_bottom + posy - h
                    elif key == 'center_y':
                        cy += padding_bottom - h / 2. + posy

                c.x = cx
                c.y = cy
                c.width = w
                c.height = h
                x += w + spacing

        if orientation == 'vertical':
            y = padding_bottom
            stretch_space = max(0.0, selfh - minimum_size_y)
            for c in self.children:
                shw = c.size_hint_x
                shh = c.size_hint_y
                w = c.width
                h = c.height
                cx = selfx + padding_left
                cy = selfy + y

                if shh:
                    h = stretch_space * shh / stretch_weight_y
                if shw:
                    w = max(0, shw * (selfw - padding_x))

                for key, value in c.pos_hint.items():
                    posx = value * (selfw - padding_x)
                    if key == 'x':
                        cx += padding_left + posx
                    elif key == 'right':
                        cx += padding_left + posx - w
                    elif key == 'center_x':
                        cx += padding_left - w / 2. + posx

                c.x = cx
                c.y = cy
                c.width = w
                c.height = h
                y += h + spacing

    def add_widget(self, widget, index=0):
        widget.bind(pos_hint=self._trigger_layout)
        return super(BoxLayout, self).add_widget(widget, index)

    def remove_widget(self, widget):
        widget.unbind(pos_hint=self._trigger_layout)
        return super(BoxLayout, self).remove_widget(widget)
Beispiel #26
0
class StackLayout(Layout):
    '''Stack 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].

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

    padding = VariableListProperty([0, 0, 0, 0])
    '''Padding between layout box and 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.

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

    orientation = OptionProperty('lr-tb', options=(
        'lr-tb', 'tb-lr', 'rl-tb', 'tb-rl', 'lr-bt', 'bt-lr', 'rl-bt', 'bt-rl'))
    '''Orientation of the layout.

    :data:`orientation` is an :class:`~kivy.properties.OptionProperty`, default
    to 'lr-tb'.

    Valid orientations are: 'lr-tb', 'tb-lr', 'rl-tb', 'tb-rl', 'lr-bt',
    'bt-lr', 'rl-bt', 'bt-rl'

    .. versionchanged:: 1.5.0

        :data:`orientation` now correctly handles all valid combinations of
        'lr','rl','tb','bt'. Before this version only 'lr-tb' and
        'tb-lr' were supported, and 'tb-lr' was misnamed and placed
        widgets from bottom to top and from right to left (reversed compared
        to what was expected).

    .. note::

        lr mean Left to Right.
        rl mean Right to Left.
        tb mean Top to Bottom.
        bt mean Bottom to Top.
    '''

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

    .. 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 children.

    .. 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 children.

    .. 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):
        super(StackLayout, self).__init__(**kwargs)
        self.bind(
            padding=self._trigger_layout,
            spacing=self._trigger_layout,
            children=self._trigger_layout,
            orientation=self._trigger_layout,
            size=self._trigger_layout,
            pos=self._trigger_layout)

    def do_layout(self, *largs):
        # optimize layout by preventing looking at the same attribute in a loop
        selfpos = self.pos
        selfsize = self.size
        orientation = self.orientation.split('-')
        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
        spacing_x, spacing_y = self.spacing

        lc = []

        # Determine which direction and in what order to place the widgets
        posattr = [0] * 2
        posdelta = [0] * 2
        posstart = [0] * 2
        for i in (0, 1):
            posattr[i] = 1 * (orientation[i] in ('tb', 'bt'))
            k = posattr[i]
            if orientation[i] == 'lr':
                # left to right
                posdelta[i] = 1
                posstart[i] = selfpos[k] + padding_left
            elif orientation[i] == 'bt':
                # bottom to top
                posdelta[i] = 1
                posstart[i] = selfpos[k] + padding_bottom
            elif orientation[i] == 'rl':
                # right to left
                posdelta[i] = -1
                posstart[i] = selfpos[k] + selfsize[k] - padding_right
            else:
                # top to bottom
                posdelta[i] = -1
                posstart[i] = selfpos[k] + selfsize[k] - padding_top

        innerattr, outerattr = posattr
        ustart, vstart = posstart
        deltau, deltav = posdelta
        del posattr, posdelta, posstart

        u = ustart  # inner loop position variable
        v = vstart  # outer loop position variable

        # space calculation, used for determining when a row or column is full

        if orientation[0] in ('lr', 'rl'):
            lu = self.size[innerattr] - padding_x
            sv = padding_y  # size in v-direction, for minimum_size property
            su = padding_x  # size in h-direction
            spacing_u = spacing_x
            spacing_v = spacing_y
        else:
            lu = self.size[innerattr] - padding_y
            sv = padding_x  # size in v-direction, for minimum_size property
            su = padding_y  # size in h-direction
            spacing_u = spacing_y
            spacing_v = spacing_x

        # space calculation, row height or column width, for arranging widgets
        lv = 0

        urev = (deltau < 0)
        vrev = (deltav < 0)
        for c in reversed(self.children):
            # Issue#823: ReferenceListProperty doesn't allow changing
            # individual properties.
            # when the above issue is fixed we can remove csize from below and
            # access c.size[i] directly
            csize = c.size[:]  # we need to update the whole tuple at once.
            if c.size_hint[0]:
                # calculate width
                csize[0] = c.size_hint[0] * (selfsize[0] - padding_x)
            if c.size_hint[1]:
                # calculate height
                csize[1] = c.size_hint[1] * (selfsize[1] - padding_y)
            c.size = tuple(csize)

            # does the widget fit in the row/column?
            if lu - c.size[innerattr] >= 0:
                lc.append(c)
                lu -= c.size[innerattr] + spacing_u
                lv = max(lv, c.size[outerattr])
                continue

            # push the line
            sv += lv + spacing_v
            for c2 in lc:
                if urev:
                    u -= c2.size[innerattr]
                p = [0, 0]  # issue #823
                p[innerattr] = u
                p[outerattr] = v
                if vrev:
                    # v position is actually the top/right side of the widget
                    # when going from high to low coordinate values,
                    # we need to subtract the height/width from the position.
                    p[outerattr] -= c2.size[outerattr]
                c2.pos = tuple(p)  # issue #823
                if urev:
                    u -= spacing_u
                else:
                    u += c2.size[innerattr] + spacing_u

            v += deltav * lv
            v += deltav * spacing_v
            lc = [c]
            lv = c.size[outerattr]
            lu = selfsize[innerattr] - su - c.size[innerattr] - spacing_u
            u = ustart

        if lc:
            # push the last (incomplete) line
            sv += lv + spacing_v
            for c2 in lc:
                if urev:
                    u -= c2.size[innerattr]
                p = [0, 0]  # issue #823
                p[innerattr] = u
                p[outerattr] = v
                if vrev:
                    p[outerattr] -= c2.size[outerattr]
                c2.pos = tuple(p)  # issue #823
                if urev:
                    u -= spacing_u
                else:
                    u += c2.size[innerattr] + spacing_u

        minsize = self.minimum_size[:]  # issue #823
        minsize[outerattr] = sv
        self.minimum_size = tuple(minsize)
Beispiel #27
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
Beispiel #28
0
class ListLayoutInt(Layout):
    spacing = VariableListProperty([0, 0], length=2)
    padding = VariableListProperty([0, 0, 0, 0], length=4)
    minimum_width = NumericProperty(0)
    minimum_height = NumericProperty(0)
    minimum_size = ReferenceListProperty(minimum_width, minimum_height)

    def update_minimum_width(self):
        minimum_width = 9999
        for w in self.children:
            minimum_width = min(minimum_width, w.width)
        self.minimum_width = minimum_width

    def add_widget(self, widget, index=0):
        # update widget position relative to our own.
        widget.x = int(self.x + self.padding[0] + widget.x)
        if index != 0:
            widget.y = int(self.y + self.minimum_height)
        else:
            if len(self.children) > 0 and hasattr(
                    self.children[0],
                    'stick_bottom') and self.children[0].stick_bottom:
                widget.y = int(self.children[0].y + self.children[0].height +
                               self.spacing[1])
                index = 1
            else:
                widget.y = int(self.y)
            h = widget.height + self.spacing[1]
            for w in self.children:
                if hasattr(w, 'stick_bottom') and w.stick_bottom:
                    continue
                w.y += h
        Logger.info('ListLayoutInt -- %s %s %s %s' %
                    (self.x, self.y, widget.height, self.minimum_height))
        # udate minimum width / height
        self.minimum_width = min(self.minimum_width, widget.width)
        if len(self.children) == 0:
            self.minimum_height = 2 * self.spacing[1] + widget.height
        else:
            self.minimum_height += self.spacing[1] + widget.height
        index = max(0, min(len(self.children), index))
        # actuall insert into children list and canvas
        #self.children.insert(index, widgetBooleanProperty)
        super(ListLayoutInt, self).add_widget(widget, index)
        self.height = self.minimum_height
        self.width = self.minimum_width
        return True

    def remove_widget(self, widget):
        if widget not in self.children:
            return
        # find out from where we need to update the y location (i.e after the widget)
        #self.children.remove(widget)
        super(ListLayoutInt, self).remove_widget(widget)
        self.update_minimum_width()
        if len(self.children) == 0:
            self.minimum_height = 0
        else:
            self.minimum_height -= self.spacing[1] + widget.height
        self.width = self.minimum_width
        self.height = self.minimum_height
        return True

    def do_layout(self, *largs):
        pass
Beispiel #29
0
class BoxLayout(Layout):
    '''Box layout class. See module documentation for more information.
    '''

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

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

    padding = VariableListProperty([0, 0, 0, 0])
    '''Padding between layout box and 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].
    '''

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

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

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

    .. versionadded:: 1.10.0

    :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.10.0

    :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.10.0

    :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):
        super(BoxLayout, self).__init__(**kwargs)
        update = self._trigger_layout
        fbind = self.fbind
        fbind('spacing', update)
        fbind('padding', update)
        fbind('children', update)
        fbind('orientation', update)
        fbind('parent', update)
        fbind('size', update)
        fbind('pos', update)

    def _iterate_layout(self, sizes):
        # optimize layout by preventing looking at the same attribute in a loop
        len_children = len(sizes)
        padding_left, padding_top, padding_right, padding_bottom = self.padding
        spacing = self.spacing
        orientation = self.orientation
        padding_x = padding_left + padding_right
        padding_y = padding_top + padding_bottom

        # calculate maximum space used by size_hint
        stretch_sum = 0.
        has_bound = False
        hint = [None] * len_children
        # min size from all the None hint, and from those with sh_min
        minimum_size_bounded = 0
        if orientation == 'horizontal':
            minimum_size_y = 0
            minimum_size_none = padding_x + spacing * (len_children - 1)

            for i, ((w, h), (shw, shh), _, (shw_min, shh_min),
                    (shw_max, _)) in enumerate(sizes):
                if shw is None:
                    minimum_size_none += w
                else:
                    hint[i] = shw
                    if shw_min:
                        has_bound = True
                        minimum_size_bounded += shw_min
                    elif shw_max is not None:
                        has_bound = True
                    stretch_sum += shw

                if shh is None:
                    minimum_size_y = max(minimum_size_y, h)
                elif shh_min:
                    minimum_size_y = max(minimum_size_y, shh_min)

            minimum_size_x = minimum_size_bounded + minimum_size_none
            minimum_size_y += padding_y
        else:
            minimum_size_x = 0
            minimum_size_none = padding_y + spacing * (len_children - 1)

            for i, ((w, h), (shw, shh), _, (shw_min, shh_min),
                    (_, shh_max)) in enumerate(sizes):
                if shh is None:
                    minimum_size_none += h
                else:
                    hint[i] = shh
                    if shh_min:
                        has_bound = True
                        minimum_size_bounded += shh_min
                    elif shh_max is not None:
                        has_bound = True
                    stretch_sum += shh

                if shw is None:
                    minimum_size_x = max(minimum_size_x, w)
                elif shw_min:
                    minimum_size_x = max(minimum_size_x, shw_min)

            minimum_size_y = minimum_size_bounded + minimum_size_none
            minimum_size_x += padding_x

        self.minimum_size = minimum_size_x, minimum_size_y
        # do not move the w/h get above, it's likely to change on above line
        selfx = self.x
        selfy = self.y

        if orientation == 'horizontal':
            stretch_space = max(0.0, self.width - minimum_size_none)
            dim = 0
        else:
            stretch_space = max(0.0, self.height - minimum_size_none)
            dim = 1

        if has_bound:
            # make sure the size_hint_min/max are not violated
            if stretch_space < 1e-9:
                # there's no space, so just set to min size or zero
                stretch_sum = stretch_space = 1.

                for i, val in enumerate(sizes):
                    sh = val[1][dim]
                    if sh is None:
                        continue

                    sh_min = val[3][dim]
                    if sh_min is not None:
                        hint[i] = sh_min
                    else:
                        hint[i] = 0.  # everything else is zero
            else:
                # hint gets updated in place
                self.layout_hint_with_bounds(stretch_sum, stretch_space,
                                             minimum_size_bounded,
                                             (val[3][dim] for val in sizes),
                                             (elem[4][dim]
                                              for elem in sizes), hint)

        if orientation == 'horizontal':
            x = padding_left + selfx
            size_y = self.height - padding_y
            for i, (sh, ((w, h), (_, shh), pos_hint, _,
                         _)) in enumerate(zip(reversed(hint),
                                              reversed(sizes))):
                cy = selfy + padding_bottom

                if sh:
                    w = max(0., stretch_space * sh / stretch_sum)
                if shh:
                    h = max(0, shh * size_y)

                for key, value in pos_hint.items():
                    posy = value * size_y
                    if key == 'y':
                        cy += posy
                    elif key == 'top':
                        cy += posy - h
                    elif key == 'center_y':
                        cy += posy - (h / 2.)

                yield len_children - i - 1, x, cy, w, h
                x += w + spacing

        else:
            y = padding_bottom + selfy
            size_x = self.width - padding_x
            for i, (sh, ((w, h), (shw, _), pos_hint, _,
                         _)) in enumerate(zip(hint, sizes)):
                cx = selfx + padding_left

                if sh:
                    h = max(0., stretch_space * sh / stretch_sum)
                if shw:
                    w = max(0, shw * size_x)

                for key, value in pos_hint.items():
                    posx = value * size_x
                    if key == 'x':
                        cx += posx
                    elif key == 'right':
                        cx += posx - w
                    elif key == 'center_x':
                        cx += posx - (w / 2.)

                yield i, cx, y, w, h
                y += h + spacing

    def do_layout(self, *largs):
        children = self.children
        if not children:
            l, t, r, b = self.padding
            self.minimum_size = l + r, t + b
            return

        for i, x, y, w, h in self._iterate_layout([
            (c.size, c.size_hint, c.pos_hint, c.size_hint_min, c.size_hint_max)
                for c in children
        ]):
            try:
                c = children[i]
                c.pos = x, y
                shw, shh = c.size_hint
                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)
            except:
                pass

    def add_widget(self, widget, index=0, canvas=None):
        widget.fbind('pos_hint', self._trigger_layout)
        return super(BoxLayout, self).add_widget(widget, index, canvas)

    def remove_widget(self, widget):
        widget.funbind('pos_hint', self._trigger_layout)
        return super(BoxLayout, self).remove_widget(widget)
Beispiel #30
0
from kivy.clock import Clock
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.settings import SettingsWithSidebar
import pandas as pd
from json_settings import settings_json

import random, os, time, threading

from kivy.config import Config

Config.set('graphics', 'width', '800')
Config.set('graphics', 'height', '480')
Config.set('kivy', 'keyboard_mode', 'dock')
Config.write()

colour = VariableListProperty()
colour = (1, 1, 1, 0)

strain = StringProperty('None')
name = StringProperty('None')
unitGlob = 'None'


#Camera seperate thread class
class useCamera(threading.Thread):
    def __init__(self, filename):
        threading.Thread.__init__(self)
        self.filename = filename

    def run(self):
        try: