예제 #1
0
class ProgressFieldLabel(AnchorLayout):
    minval = NumericProperty(0)
    maxval = NumericProperty(100)
    value = NumericProperty(0)
    color = ColorScheme.get_accent()
    text_color = ColorScheme.get_light_primary_text()
    text = StringProperty('')

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

    def on_minval(self, instance, value):
        self._refresh_value()

    def on_maxval(self, instance, value):
        self._refresh_value()

    def on_value(self, instance, value):
        self._refresh_value()

    def on_text(self, instance, value):
        self.ids.value.text = str(value)

    def _refresh_value(self):
        stencil = self.ids.stencil
        value = self.value
        minval = self.minval
        maxval = self.maxval
        pct = ((value - minval) / (maxval - minval))
        width = self.width * pct
        stencil.width = width
예제 #2
0
 def _on_option(self, option):
     self.ids.screens.current = option
     for name in self.buttons.keys():
         button = self.buttons[name]
         if name == option:
             button.tile_color = ColorScheme.get_accent()
         else:
             button.tile_color = ColorScheme.get_dark_accent()
예제 #3
0
 def _on_option(self, option):
     self.ids.screens.current = option
     for name in self.buttons.keys():
         button = self.buttons[name]
         if name == option:
             button.tile_color = ColorScheme.get_accent()
         else:
             button.tile_color = ColorScheme.get_dark_accent()
 def on_current_channel(self, instance, value):
     icon = ''
     if value == True:
         icon = '\357\200\224'
         self.highlight_color = ColorScheme.get_primary()
     else:
         self.highlight_color = ColorScheme.get_dark_background_translucent()
     self.ids.action_button.text = icon
예제 #5
0
 def on_current_channel(self, instance, value):
     icon = ''
     if value == True:
         icon = '\357\200\224'
         self.highlight_color = ColorScheme.get_primary()
     else:
         self.highlight_color = ColorScheme.get_dark_background_translucent(
         )
     self.ids.action_button.text = icon
예제 #6
0
 def add_options_screen(self, screen, button):
     screen.bind(on_screen_modified=self.on_modified)
     self.ids.screens.add_widget(screen)
     button.bind(on_press=lambda x: self._on_option(screen.name))
     self.ids.options.add_widget(button)
     button.tile_color = ColorScheme.get_dark_accent() \
         if len(self.buttons.values()) > 0 else \
         ColorScheme.get_accent()
     self.buttons[screen.name] = button
예제 #7
0
 def add_options_screen(self, screen, button):
     screen.bind(on_screen_modified=self.on_modified)
     self.ids.screens.add_widget(screen)
     button.bind(on_press=lambda x: self._on_option(screen.name))
     self.ids.options.add_widget(button)
     button.tile_color = ColorScheme.get_dark_accent() \
         if len(self.buttons.values()) > 0 else \
         ColorScheme.get_accent()
     self.buttons[screen.name] = button
예제 #8
0
    def on_mouse_pos(self, *args):
        pos = args[1]
        if self.pulsing:
            return

        if self.collide_point(*self.to_widget(*pos)):
            if not self.disabled:
                self.tile_color = ColorScheme.get_medium_accent()
        else:
            self.tile_color = ColorScheme.get_dark_accent()
예제 #9
0
    def on_mouse_pos(self, *args):
        pos = args[1]
        if self.pulsing:
            return

        if self.collide_point(*self.to_widget(*pos)):
            if not self.disabled:
                self.tile_color = ColorScheme.get_medium_accent()
        else:
            self.tile_color = ColorScheme.get_dark_accent()
예제 #10
0
 def _finish_stopwatch(self):
     '''
     Finish the current stopwatch
     '''
     self._flash_count = 0
     self.ids.speed_rect.rect_color = ColorScheme.get_dark_background_translucent()
     self.exit_speed_color = ColorScheme.get_dark_background_translucent()
     self._set_exit_speed_frame_visible(False)
     Clock.schedule_once(self._flash_pit_stop_time)
     self._stop_stopwatch()
예제 #11
0
class LapItemButton(ToggleButton):
    background_color_normal = ColorScheme.get_dark_background()
    background_color_down = ColorScheme.get_primary()

    def __init__(self, session, lap, laptime, **kwargs):
        super(LapItemButton, self).__init__(**kwargs)
        self.background_normal = ""
        self.background_down = ""
        self.background_color = self.background_color_normal
        self.session = session
        self.lap = lap
        self.laptime = laptime

    def on_state(self, instance, value):
        self.background_color = self.background_color_down if value == 'down' else self.background_color_normal
예제 #12
0
class FeatureButton(TileIconButton):
    Builder.load_string("""
<FeatureButton>
    title_font: 'resource/fonts/ASL_regular.ttf'
    icon_color: (0.0, 0.0, 0.0, 1.0)
    title_color: (0.2, 0.2, 0.2, 1.0)
    line_width: 5
    """)

    disabled_color = ListProperty(ColorScheme.get_dark_accent())
    highlight_color = ListProperty(ColorScheme.get_medium_accent())

    def __init__(self, **kwargs):
        super(FeatureButton, self).__init__(**kwargs)
        self.tile_color = (1.0, 1.0, 1.0, 1.0)
        Window.bind(mouse_pos=self.on_mouse_pos)

    def on_mouse_pos(self, *args):
        pos = args[1]
        if self.pulsing:
            return

        if self.collide_point(*self.to_widget(*pos)):
            if not self.disabled:
                self.tile_color = self.highlight_color
        else:
            self.tile_color = self.disabled_color

    def on_press(self, *args):
        super(FeatureButton, self).on_press(*args)
        if not self.disabled and not self.pulsing:
            self.tile_color = self.highlight_color

    def on_release(self, *args):
        super(FeatureButton, self).on_release(*args)
        if not self.pulsing:
            self.tile_color = self.disabled_color

    def on_fade_color(self, instance, value):
        self.tile_color = value

    def on_pulsing(self, instance, value):
        self.tile_color = self.highlight_color
        super(FeatureButton, self).on_pulsing(instance, value)

    def on_disabled(self, instance, value):
        super(FeatureButton, self).on_disabled(instance, value)
        self.tile_color = self.disabled_color
예제 #13
0
class LabelIconButton(FadeableWidget, ButtonBehavior, AnchorLayout):
    title_font = StringProperty('resource/fonts/ASL_regular.ttf')
    title_font_size = NumericProperty(sp(20))
    tile_color = ObjectProperty(ColorScheme.get_dark_accent())
    icon_color = ObjectProperty(ColorScheme.get_accent())
    title_color = ObjectProperty(ColorScheme.get_accent())
    icon = StringProperty('')
    icon_size = NumericProperty(sp(25))
    title = StringProperty('')

    def __init__(self, **kwargs):
        super(LabelIconButton, self).__init__(**kwargs)
        Window.bind(mouse_pos=self.on_mouse_pos)

    def on_tile_color(self, instance, value):
        self.fade_color = value

    def on_mouse_pos(self, *args):
        pos = args[1]
        if self.pulsing:
            return

        if self.collide_point(*self.to_widget(*pos)):
            if not self.disabled:
                self.tile_color = ColorScheme.get_medium_accent()
        else:
            self.tile_color = ColorScheme.get_dark_accent()

    def on_press(self, *args):
        super(LabelIconButton, self).on_press(*args)
        if not self.disabled and not self.pulsing:
            self.tile_color = ColorScheme.get_medium_accent()

    def on_release(self, *args):
        super(LabelIconButton, self).on_release(*args)
        if not self.pulsing:
            self.tile_color = ColorScheme.get_dark_accent()

    def on_fade_color(self, instance, value):
        self.tile_color = value

    def on_pulsing(self, instance, value):
        self.tile_color = ColorScheme.get_medium_accent()
        super(LabelIconButton, self).on_pulsing(instance, value)

    def on_disabled(self, instance, value):
        super(LabelIconButton, self).on_disabled(instance, value)
        self.tile_color = ColorScheme.get_medium_accent()
예제 #14
0
    def _on_value_updated(self, *args):
        value_text = self.ids.value.text
        value = 0 if len(value_text) == 0 else float(value_text)
        min_value = self.min_value
        max_value = self.max_value
        is_valid = (min_value is None
                    or value >= min_value) and (max_value is None
                                                or value <= max_value)
        self.value = value

        self.ids.value.background_color = ColorScheme.get_normal_background(
        ) if is_valid else ColorScheme.get_error_background()
        self.ids.msg.text = self.msg if is_valid else 'Valid range is {} - {}'.format(
            autoformat_number(self.min_value), autoformat_number(
                self.max_value))
        self.ids.ok.disabled = not is_valid
예제 #15
0
class ModelSelectorItemView(ToggleButtonBehavior, BoxLayout):
    Builder.load_string("""
<ModelSelectorItemView>:
    canvas.before:
        Color:
            rgba: root.selected_color
        Rectangle:
            pos: self.pos
            size: self.size
    group: 'model'
    padding: (dp(5), dp(5))
    FieldLabel:
        text: root.label
        halign: 'center'
    Image:
        source: root.image_source
    """)

    label = StringProperty()
    image_source = StringProperty()
    obj_source = StringProperty()
    selected_color = ListProperty(ColorScheme.get_dark_background())

    def on_state(self, instance, value):
        selected = value == 'down'
        self.selected_color = ColorScheme.get_medium_background() if selected else ColorScheme.get_dark_background()
        self.dispatch('on_selected', selected)

    def __init__(self, **kwargs):
        super(ModelSelectorItemView, self).__init__(**kwargs)
        self.register_event_type('on_selected')
        self.state = 'down' if kwargs.get('selected') == True else 'normal'

    def on_selected(self, state):
        pass
예제 #16
0
 def _toggle_exit_speed_alert(self, alert_color):
     '''
     Toggles the background color for the exit speed frame
     '''
     if self.exit_speed_color == alert_color:
         self.exit_speed_color = ColorScheme.get_dark_background_translucent()
     else:
         self.exit_speed_color = alert_color
예제 #17
0
class SingleTrackItemView(ToggleButtonBehavior, BaseTrackItemView):
    selected_color = ListProperty(ColorScheme.get_dark_background())

    def on_state(self, instance, value):
        selected = value == 'down'
        self.selected_color = ColorScheme.get_medium_background(
        ) if selected else ColorScheme.get_dark_background()
        self.dispatch('on_track_selected', selected, self.track.track_id)
예제 #18
0
    def _on_value_updated(self, *args):
        value_text = self.ids.value.text
        value = 0 if len(value_text) == 0 else float(value_text)
        min_value = self.min_value
        max_value = self.max_value
        is_valid = (min_value is None or value >= min_value) and (max_value is None or value <= max_value)
        self.value = value

        self.ids.value.background_color = ColorScheme.get_normal_background() if is_valid else ColorScheme.get_error_background()
        self.ids.msg.text = self.msg if is_valid else 'Valid range is {} - {}'.format(autoformat_number(self.min_value), autoformat_number(self.max_value))
        self.ids.ok.disabled = not is_valid
예제 #19
0
class LapItemButton(ToggleButton):
    Builder.load_string("""
<LapItemButton>:
    #background_down: ''
    font_size: self.height * 0.5
    font_name: 'resource/fonts/ASL_light.ttf'    
    """)
    background_color_normal = ColorScheme.get_dark_background()
    background_color_down = ColorScheme.get_primary()

    def __init__(self, session, lap, laptime, **kwargs):
        super(LapItemButton, self).__init__(**kwargs)
        self.background_normal = ""
        self.background_down = ""
        self.background_color = self.background_color_normal
        self.session = session
        self.lap = lap
        self.laptime = laptime

    def on_state(self, instance, value):
        self.background_color = self.background_color_down if value == 'down' else self.background_color_normal
예제 #20
0
class LabelIconButton(ButtonBehavior, AnchorLayout):
    title_font = StringProperty('resource/fonts/ASL_regular.ttf')
    title_font_size = NumericProperty(sp(20))
    tile_color = ObjectProperty(ColorScheme.get_accent())
    icon_color = ObjectProperty((0.0, 0.0, 0.0, 1.0))
    title_color = ObjectProperty((0.0, 0.0, 0.0, 1.9))
    icon = StringProperty('')
    icon_size = NumericProperty(sp(25))
    title = StringProperty('')

    def __init__(self, **kwargs):
        super(LabelIconButton, self).__init__(**kwargs)
예제 #21
0
class SetupItem(BoxLayout):
    """
    An individual setup item view that shows the state of the current step
    """
    title = StringProperty('')
    complete = BooleanProperty(False)
    active = BooleanProperty(False)
    title_color = ListProperty(ColorScheme.get_secondary_text())

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

    def on_complete(self, instance, value):
        self.ids.complete.text = u'\uf00c' if value else ''

    def on_active(self, instance, value):
        self.title_color = ColorScheme.get_light_primary_text(
        ) if value else ColorScheme.get_secondary_text()
예제 #22
0
 def _update_speed_color(self):
     '''
     Selects the appropriate color for the exit speed indicator based on 
     configured speed threshold
     '''
     if self.current_speed > self._pitstop_alert_speed:
         self.ids.speed_rect.rect_color = ColorScheme.get_error()
         self._toggle_exit_speed_alert(ColorScheme.get_error())
     elif self.current_speed > self._pitstop_alert_speed * self._SPEED_ALERT_THRESHOLD:
         self.ids.speed_rect.rect_color = ColorScheme.get_alert()
         self.exit_speed_color = ColorScheme.get_dark_background_translucent()
     else:
         self.ids.speed_rect.rect_color = ColorScheme.get_happy()
         self.exit_speed_color = ColorScheme.get_dark_background_translucent()
예제 #23
0
class SingleTrackItemView(ToggleButtonBehavior, BaseTrackItemView):
    Builder.load_string("""
<SingleTrackItemView>:
    group: 'track'
    TrackInfoView:
        canvas.before:
            Color:
                rgba: root.selected_color
            Rectangle:
                pos: self.pos
                size: self.size
        id: trackinfo    
    """)

    selected_color = ListProperty(ColorScheme.get_dark_background())

    def on_state(self, instance, value):
        selected = value == 'down'
        self.selected_color = ColorScheme.get_medium_background(
        ) if selected else ColorScheme.get_dark_background()
        self.dispatch('on_track_selected', selected, self.track.track_id)
예제 #24
0
class RaceTrackView(BoxLayout):
    Builder.load_string(RACETRACK_VIEW_KV)
    track_color = ListProperty(ColorScheme.get_primary())
    
    def __init__(self, **kwargs):
        super(RaceTrackView, self).__init__(**kwargs)

    def loadTrack(self, track):
        self.initMap(track)

    def initMap(self, track):
        self.ids.trackmap.setTrackPoints(track.map_points)
        self.ids.trackmap.sector_points = track.sector_points
        self.ids.trackmap.start_point = track.start_finish_point
        self.ids.trackmap.finish_point = track.finish_point

    def remove_reference_mark(self, key):
        self.ids.trackmap.remove_marker(key)

    def add_reference_mark(self, key, color):
        trackmap = self.ids.trackmap
        if trackmap.get_marker(key) is None:
            trackmap.add_marker(key, color)

    def update_reference_mark(self, key, geo_point):
        self.ids.trackmap.update_marker(key, geo_point)

    def add_map_path(self, key, path, color):
        self.ids.trackmap.add_path(key, path, color)

    def remove_map_path(self, key):
        self.ids.trackmap.remove_path(key)

    def add_heat_values(self, key, heat_values):
        self.ids.trackmap.add_heat_values(key, heat_values)

    def remove_heat_values(self, key):
        self.ids.trackmap.remove_heat_values(key)
예제 #25
0
class ChannelSelection(BoxLayout):
    highlight_color = ListProperty(
        ColorScheme.get_dark_background_translucent())
    current_channel = BooleanProperty(False)

    def __init__(self, **kwargs):
        super(ChannelSelection, self).__init__(**kwargs)
        self.channel = kwargs.get('channel')
        self.register_event_type('on_delete_channel')
        self.register_event_type('on_add_channel')
        self.ids.name.text = self.channel

    def on_label_add(self, *args):
        if self.current_channel == False:
            self.dispatch('on_add_channel', self.channel)

    def on_delete_channel(self, *args):
        pass

    def on_add_channel(self, *args):
        pass

    def on_current_channel(self, instance, value):
        icon = ''
        if value == True:
            icon = '\357\200\224'
            self.highlight_color = ColorScheme.get_primary()
        else:
            self.highlight_color = ColorScheme.get_dark_background_translucent(
            )
        self.ids.action_button.text = icon

    def on_action(self, *args):
        if self.current_channel == True:
            self.dispatch('on_delete_channel', self.channel)
        else:
            self.dispatch('on_add_channel', self.channel)
예제 #26
0
class ToolbarView(BoxLayout):
    """
    The Toolbar view provides various status indicators for the running application. 
    """
    status_pump = ObjectProperty(None)
    track_manager = ObjectProperty(None)

    TELEMETRY_IDLE = 0
    TELEMETRY_ACTIVE = 1
    TELEMETRY_CONNECTING = 2
    TELEMETRY_ERROR = 3

    telemetry_color = {
        TELEMETRY_IDLE: [0.0, 1.0, 0.0, 0.2],
        TELEMETRY_ACTIVE: [0.0, 1.0, 0.0, 1.0],
        TELEMETRY_CONNECTING: [1.0, 1.0, 0.0, 1.0],
        TELEMETRY_ERROR: [1.0, 0.0, 0.0, 1.0]
    }

    DATA_NO_RX = 0
    DATA_RX = 1
    data_rx_color = {
        DATA_NO_RX: [0.0, 0.8, 1.0, 0.2],
        DATA_RX: [0.0, 0.8, 1.0, 1.0]
    }
    TOOLBAR_DATA_RX_DURATION = 0.1

    GPS_NO_DATA = 0
    GPS_NO_LOCK = 1
    GPS_MARGINAL = 2
    GPS_HIGH_QUALITY = 3
    gps_color = {
        GPS_NO_DATA: [0.3, 0.3, 0.3, 0.2],
        GPS_NO_LOCK: [1.0, 0.0, 0.0, 1.0],
        GPS_MARGINAL: [1.0, 1.0, 0.0, 1.0],
        GPS_HIGH_QUALITY: [0.0, 1.0, 0.0, 1.0]
    }

    STATUS_LINGER_DURATION = 2.0
    ACTIVITY_MESSAGE_LINGER_DURATION = 7.5
    PROGRESS_COMPLETE_LINGER_DURATION = 7.0
    normal_status_color = ColorScheme.get_light_primary_text()
    alert_status_color = ColorScheme.get_alert()

    Builder.load_string(TOOLBAR_VIEW_KV)

    def __init__(self, **kwargs):
        super(ToolbarView, self).__init__(**kwargs)
        self.current_status = ''
        self.register_event_type('on_main_menu')
        self.register_event_type('on_progress')
        self.register_event_type('on_data_rx')
        self.register_event_type('on_tele_status')
        self.register_event_type('on_status')
        self.register_event_type('on_activity')
        self._data_rx_decay = Clock.create_trigger(
            self._on_data_rx_decay, ToolbarView.TOOLBAR_DATA_RX_DURATION)
        self._activity_decay = Clock.create_trigger(
            self._on_activity_decay,
            ToolbarView.ACTIVITY_MESSAGE_LINGER_DURATION)
        self._progress_decay = Clock.create_trigger(
            self._on_progress_decay,
            ToolbarView.PROGRESS_COMPLETE_LINGER_DURATION)
        self._gps_decay = Clock.create_trigger(
            self._on_gps_decay, ToolbarView.STATUS_LINGER_DURATION)

    def on_status_pump(self, instance, value):
        value.add_listener(self.on_rc_status_updated)

    def on_rc_status_updated(self, status_data):
        self._update_track_status(status_data)
        self._update_gps_status(status_data)

    def on_activity(self, msg):
        self._set_activity_message(msg)
        self._activity_decay()

    def on_main_menu(self, instance, *args):
        pass

    def mainMenu(self):
        self.dispatch('on_main_menu', None)

    def _set_state_message(self, msg):
        self.ids.state.text = msg

    def _set_activity_message(self, msg):
        prog_status = self.ids.prog_status
        prog_status.text = msg

    def on_status(self, msg, isAlert):
        status_label = self.ids.prog_status
        status_label.text = msg
        self.current_status = msg
        if isAlert == True:
            status_label.text_color = self.alert_status_color
        else:
            status_label.text_color = self.normal_status_color

    def _update_progress(self, value):
        self.ids.prog_status.value = value
        if value == 100:
            self._progress_decay()

    def on_progress(self, value):
        self._update_progress(value)

    def _on_progress_decay(self, dt):
        self._update_progress(0)
        self.ids.prog_status.text = self.current_status

    def _on_activity_decay(self, dt):
        self._set_activity_message(self.current_status)

    def _on_data_rx_decay(self, dt):
        self.ids.data_rx_status.color = ToolbarView.data_rx_color[int(False)]

    def on_data_rx(self, value):
        self._data_rx_decay.cancel()
        self.ids.data_rx_status.color = ToolbarView.data_rx_color[int(value)]
        self._data_rx_decay()

    def on_tele_status(self, status):
        try:
            self.ids.tele_status.color = self.telemetry_color[status]
        except:
            Logger.error("ToolbarView: Invalid telemetry status: " +
                         str(status))

    def _on_gps_decay(self, dt):
        self.ids.gps_status.color = ToolbarView.gps_color[
            ToolbarView.GPS_NO_DATA]

    def _update_gps_status(self, status_data):
        self._gps_decay.cancel()
        gps_status = status_data['status']['GPS']
        gps_quality_code = gps_status['qual']
        gps_quality = ToolbarView.GPS_NO_DATA
        if gps_quality_code == 0:
            gps_quality = ToolbarView.GPS_NO_LOCK
        elif gps_quality_code == 1:
            gps_quality = ToolbarView.GPS_MARGINAL
        elif gps_quality_code >= 2:
            gps_quality = ToolbarView.GPS_HIGH_QUALITY

        gps_color = ToolbarView.gps_color[gps_quality]
        self.ids.gps_status.color = gps_color
        self._gps_decay()

    def _update_track_status(self, status_data):
        try:
            track_status = status_data['status']['track']
            detection_status = track_status['status']
            if detection_status == 0:
                track_status_msg = 'Searching for Track'
            elif detection_status == 1 and status_data['status']['track'][
                    'trackId'] == 0:
                track_status_msg = 'User defined Track'
            else:
                if track_status['trackId'] != 0:
                    track = self.track_manager.find_track_by_short_id(
                        track_status['trackId'])
                    if track is None:
                        track_status_msg = '(Unknown Track)'
                    else:
                        track_status_msg = track.full_name
                else:
                    track_status_msg = 'No track detected'
            self._set_state_message(track_status_msg)
        except Exception as e:
            Logger.warn(
                "ToolbarView: Could not retrieve track detection status " +
                str(e))
예제 #27
0
 def attach_node(text, n, view_builder):
     tree = self.ids.menu
     label = LinkedTreeViewLabel(text=text)
     label.view_builder = view_builder
     label.color_selected = ColorScheme.get_dark_primary()
     return tree.add_node(label, n)
예제 #28
0
 def on_disabled(self, instance, value):
     super(LabelIconButton, self).on_disabled(instance, value)
     self.tile_color = ColorScheme.get_medium_accent()
예제 #29
0
 def on_disabled(self, instance, value):
     super(LabelIconButton, self).on_disabled(instance, value)
     self.tile_color = ColorScheme.get_medium_accent()
예제 #30
0
 def on_state(self, instance, value):
     selected = value == 'down'
     self.selected_color = ColorScheme.get_medium_background(
     ) if selected else ColorScheme.get_dark_background()
     self.dispatch('on_selected', selected)
예제 #31
0
 def on_active(self, instance, value):
     self.title_color = ColorScheme.get_light_primary_text(
     ) if value else ColorScheme.get_secondary_text()
예제 #32
0
 def attach_node(text, n, view_builder):
     label = LinkedTreeViewLabel(text=text)
     label.view_builder = view_builder
     label.color_selected = ColorScheme.get_dark_primary()
     return tree.add_node(label, n)
예제 #33
0
 def on_release(self, *args):
     super(LabelIconButton, self).on_release(*args)
     if not self.pulsing:
         self.tile_color = ColorScheme.get_dark_accent()
예제 #34
0
 def on_pulsing(self, instance, value):
     self.tile_color = ColorScheme.get_medium_accent()
     super(LabelIconButton, self).on_pulsing(instance, value)
예제 #35
0
 def on_active(self, instance, value):
     self.title_color = ColorScheme.get_light_primary_text(
     ) if value else ColorScheme.get_secondary_text()
예제 #36
0
from kivy.uix.screenmanager import Screen
from kivy.uix.treeview import TreeView, TreeViewLabel
from kivy.uix.label import Label
from kivy.properties import ObjectProperty, StringProperty, NumericProperty
from datetime import timedelta
from utils import *
from fieldlabel import AutoShrinkFieldLabel
from kivy.logger import LoggerHistory, Logger
from autosportlabs.racecapture.theme.color import ColorScheme
from autosportlabs.uix.toast.kivytoast import toast
from main import RaceCaptureApp
from autosportlabs.widgets.scrollcontainer import ScrollContainer

STATUS_KV_FILE = 'autosportlabs/racecapture/views/status/statusview.kv'

RAW_STATUS_BGCOLOR_1 = ColorScheme.get_background()
RAW_STATUS_BGCOLOR_2 = ColorScheme.get_dark_background()


class StatusLabel(AutoShrinkFieldLabel):
    backgroundColor = ObjectProperty(RAW_STATUS_BGCOLOR_1)


class StatusTitle(StatusLabel):
    pass


class StatusValue(StatusLabel):
    def __init__(self, **kwargs):
        super(StatusLabel, self).__init__(**kwargs)
        self.shorten = False
예제 #37
0
class StatusView(Screen):

    _bg_current = RAW_STATUS_BGCOLOR_1

    # Dict object that contains the status of RCP
    status = ObjectProperty(None)

    # Currently selected menu item
    _selected_item = None

    _menu_built = False

    # Track manager for getting track name
    track_manager = None

    # Connection to status pump
    _status_pump = None

    # Used for building the left side menu
    _menu_keys = {
        "app": "Application",
        "system": "Device",
        "GPS": "GPS",
        "cell": "Cellular",
        "bt": "Bluetooth",
        "logging": "Logging",
        "track": "Track",
        "telemetry": "Telemetry",
        "wifi": "WiFi",
        "imu": "Accel/Gyro",
    }

    # Dict for getting English text for status enums
    _enum_keys = {
        'GPS': {
            'init': ['Not initialized', 'Initialized', 'Error initializing'],
            'qual': ['No fix', 'Weak', 'Acceptable', 'Strong']
        },
        'cell': {
            'init': [
                'Not initialized', 'Initialized', 'Searching', 'Denied',
                'Registered'
            ],
            'sig_str': [
                'Unknown', 'Marginal', 'Marginal', 'Marginal', 'Marginal',
                'Marginal', 'Marginal', 'Marginal', 'Marginal', 'Marginal',
                'OK', 'OK', 'OK', 'OK', 'OK', 'Good', 'Good', 'Good', 'Good',
                'Good', 'Excellent', 'Excellent', 'Excellent', 'Excellent',
                'Excellent', 'Excellent', 'Excellent', 'Excellent',
                'Excellent', 'Excellent', 'Excellent'
            ]
        },
        'bt': {
            'init': ['Not initialized', 'Initialized', 'Error initializing']
        },
        'wifi': {
            'init': ['Not initialized', 'Initialized']
        },
        'logging': {
            'status': ['Not logging', 'Logging', 'Error logging']
        },
        'track': {
            'status':
            ['Searching', 'Fixed start/finish', 'Detected', 'Manually Set']
        },
        'telemetry': {
            'status': [
                'Idle', 'Connected', 'Connection terminated',
                'Device ID rejected',
                'Data connection failed. SIM card is valid, either no data plan is associated or the plan has expired.',
                'Failed to connect to server',
                'Data connection failed. APN settings possibly wrong.',
                'Unable to join network'
            ]
        }
    }

    _menu_node = None
    menu_select_color = ColorScheme.get_primary()

    def __init__(self, track_manager, status_pump, **kwargs):
        Builder.load_file(STATUS_KV_FILE)
        super(StatusView, self).__init__(**kwargs)
        self.track_manager = track_manager
        self.register_event_type('on_tracks_updated')
        self._menu_node = self.ids.menu
        self._menu_node.bind(selected_node=self._on_menu_select)
        status_pump.add_listener(self.status_updated)
        self._build_core_menu()

    def _build_core_menu(self):
        # build application status node
        self._append_menu_node('Application', 'app')

        # select the first node in the tree.
        self._menu_node.select_node(self._menu_node.root.nodes[0])

    def _build_menu(self):
        if self._menu_built:
            return

        for item in self.status.iterkeys():
            text = self._menu_keys[item] if item in self._menu_keys else item
            self._append_menu_node(text, item)

        self._menu_built = True

    def _append_menu_node(self, text, item):
        label = LinkedTreeViewLabel(text=text)
        label.id = item
        label.color_selected = self.menu_select_color
        return self._menu_node.add_node(label)

    def _on_menu_select(self, instance, value):
        self._selected_item = value.id
        self.update()

    def status_updated(self, status):
        self.status = status['status']

    def update(self):
        _bg_current = RAW_STATUS_BGCOLOR_1

        if self._selected_item in self._menu_keys:
            text = self._menu_keys[self._selected_item]
        else:
            text = self._selected_item

        self.ids.name.text = text
        self.ids.status_grid.clear_widgets()

        function_name = ('render_' + self._selected_item).lower()

        # Generic way of not having to create a long switch or if/else block
        # to call each render function
        if function_name in dir(self):
            getattr(self, function_name)()
        else:
            self.render_generic(self._selected_item)

    def render_generic(self, section):
        status = self.status[section]

        for item, value in status.iteritems():
            self._add_item(item, value)

    def render_app(self):
        label_widget = StatusTitle(text='Application Log')
        self.ids.status_grid.add_widget(label_widget)
        self.ids.status_grid.add_widget(ApplicationLogView())
        self._add_item('Application Version', RaceCaptureApp.get_app_version())

    def render_system(self):
        if 'git_info' in self.status['system']:
            version = self.status['system']['git_info']
        else:
            version = '.'.join([
                str(self.status['system']['ver_major']),
                str(self.status['system']['ver_minor']),
                str(self.status['system']['ver_bugfix'])
            ])

        self._add_item('Version', version)
        self._add_item('Serial Number', self.status['system']['serial'])

        uptime = timedelta(seconds=(self.status['system']['uptime'] / 1000))
        self._add_item('Uptime', uptime)

    def render_gps(self):
        status = self.status['GPS']

        init_status = self._get_enum_definition('GPS', 'init', status['init'])
        quality = self._get_enum_definition('GPS', 'qual', status['qual'])
        location = str(status['lat']) + ', ' + str(status['lon'])
        satellites = status['sats']
        dop = status['DOP']

        self._add_item('Status', init_status)
        self._add_item('GPS Quality', quality)
        self._add_item('Location', location)
        self._add_item('Satellites', satellites)
        self._add_item('Dilution of precision', dop)

    def render_cell(self):
        status = self.status['cell']

        init_status = self._get_enum_definition('cell', 'init', status['init'])
        imei = status['IMEI']
        signal_strength = self._get_enum_definition('cell', 'sig_str',
                                                    status['sig_str'],
                                                    'Unknown')
        number = status['number']

        self._add_item('Status', init_status)
        self._add_item('IMEI', imei)
        self._add_item('Signal strength', signal_strength)
        self._add_item('Phone Number', number)
        self._add_item('Network Status', status.get('state', '').capitalize())

    def render_bt(self):
        status = self.status['bt']

        init_status = self._get_enum_definition('bt', 'init', status['init'])
        self._add_item('Status', init_status)

    def render_wifi(self):
        status = self.status['wifi']
        initialized = status['initialized']
        ap_enabled = status['ap']['active']
        self._add_item(
            'Status',
            self._get_enum_definition('wifi', 'init',
                                      int(status['initialized'])))
        self._add_item('Access Point', 'Enabled' if ap_enabled else 'Disabled')
        client_enabled = status['client']['active']
        client_connected = status['client']['connected']
        connected_msg = '' if not client_enabled else '({})'.format(
            'Connected' if client_connected else 'Disconnected')
        client_status_msg = '{} {}'.format(
            'Enabled' if client_enabled else 'Disabled', connected_msg)
        self._add_item('Client', client_status_msg)

    def render_imu(self):
        status = self.status['imu']
        self._add_item('Status',
                       'Initialized' if status['init'] else 'Not initialized')

    def render_logging(self):
        status = self.status['logging']

        init_status = self._get_enum_definition('logging', 'status',
                                                status['status'])
        duration = timedelta(seconds=(status['dur'] / 1000))

        self._add_item('Status', init_status)
        self._add_item('Logging for', duration)

    def render_telemetry(self):
        status = self.status['telemetry']

        init_status = self._get_enum_definition('telemetry', 'status',
                                                status['status'])
        duration = timedelta(seconds=(status['dur'] / 1000))

        self._add_item('Status', init_status)
        self._add_item('Logging for', duration)

    def render_track(self):
        status = self.status['track']

        init_status = self._get_enum_definition('track', 'status',
                                                status['status'])

        if status['status'] == 1:
            track_name = 'User defined'
        else:
            if status['trackId'] != 0:
                track = self.track_manager.find_track_by_short_id(
                    status['trackId'])

                if track is None:
                    if status['status'] == 1:
                        track_name = 'Fixed'
                    else:
                        track_name = 'Track not found'
                else:
                    track_name = track.name
                    configuration_name = track.configuration
                    if configuration_name and len(configuration_name):
                        track_name += ' (' + configuration_name + ')'

            else:
                track_name = 'No track detected'

        in_lap = 'Yes' if status['inLap'] == 1 else 'No'
        armed = 'Yes' if status['armed'] == 1 else 'No'

        self._add_item('Status', init_status)
        self._add_item('Track', track_name)
        self._add_item('In lap', in_lap)
        self._add_item('Armed', armed)

    def _add_item(self, label, data):
        label_widget = StatusTitle(text=label)
        data_widget = StatusValue(text=str(data))
        self.ids.status_grid.add_widget(label_widget)
        self.ids.status_grid.add_widget(data_widget)
        if len(self.ids.status_grid.children) / 2 % 2 == 0:
            bg_color = RAW_STATUS_BGCOLOR_2
        else:
            bg_color = RAW_STATUS_BGCOLOR_1

        label_widget.backgroundColor = bg_color
        data_widget.backgroundColor = bg_color

    def on_status(self, instance, value):
        self._build_menu()
        self.update()

    # Generalized function for getting an enum's English
    # equivalent. If the value is not found, the enum is returned
    def _get_enum_definition(self, section, subsection, value, default=None):
        val = default if default is not None else value

        if section in self._enum_keys and subsection in self._enum_keys[
                section]:
            enum_data = self._enum_keys[section][subsection]
            if len(enum_data) > value:
                val = enum_data[value]

        return val

    def on_tracks_updated(self, track_manager):
        pass
예제 #38
0
class ToolbarView(BoxLayout):
    status_pump = ObjectProperty(None)
    track_manager = ObjectProperty(None)

    TELEMETRY_IDLE = 0
    TELEMETRY_ACTIVE = 1
    TELEMETRY_CONNECTING = 2
    TELEMETRY_ERROR = 3

    telemetry_color = {
        TELEMETRY_IDLE: [0.0, 1.0, 0.0, 0.2],
        TELEMETRY_ACTIVE: [0.0, 1.0, 0.0, 1.0],
        TELEMETRY_CONNECTING: [1.0, 1.0, 0.0, 1.0],
        TELEMETRY_ERROR: [1.0, 0.0, 0.0, 1.0]
    }
    txOffColor = [0.0, 1.0, 0.0, 0.2]
    rxOffColor = [0.0, 0.8, 1.0, 0.2]
    txOnColor = [0.0, 1.0, 0.0, 1.0]
    rxOnColor = [0.0, 0.8, 1.0, 1.0]

    normalStatusColor = ColorScheme.get_light_primary_text()
    alertStatusColor = ColorScheme.get_alert()

    teleStatus = None
    rcTxStatus = None
    rcRxStatus = None

    def __init__(self, **kwargs):
        super(ToolbarView, self).__init__(**kwargs)
        self.current_status = ''
        self.register_event_type('on_main_menu')
        self.register_event_type('on_progress')
        self.register_event_type('on_rc_tx')
        self.register_event_type('on_rc_rx')
        self.register_event_type('on_tele_status')
        self.register_event_type('on_status')
        self.register_event_type('on_activity')

        self._rcTxDecay = Clock.create_trigger(self.on_rc_tx_decay,
                                               TOOLBAR_LED_DURATION)
        self._rcRxDecay = Clock.create_trigger(self.on_rc_rx_decay,
                                               TOOLBAR_LED_DURATION)
        self._activityDecay = Clock.create_trigger(
            self.on_activity_decay, ACTIVITY_MESSAGE_LINGER_DURATION)
        self._progressDecay = Clock.create_trigger(
            self.on_progress_decay, PROGRESS_COMPLETE_LINGER_DURATION)

    def on_status_pump(self, instance, value):
        value.add_listener(self.on_rc_status_updated)

    def on_rc_status_updated(self, status_data):
        self._update_track_status(status_data)

    def on_activity(self, msg):
        self.setActivityMessage(msg)
        self._activityDecay()

    def set_state_message(self, msg):
        self.ids.state.text = msg

    def setActivityMessage(self, msg):
        prog_status = self.ids.prog_status
        prog_status.text = msg

    def on_status(self, msg, isAlert):
        statusLabel = self.ids.prog_status
        statusLabel.text = msg
        self.current_status = msg
        if isAlert == True:
            statusLabel.text_color = self.alertStatusColor
        else:
            statusLabel.text_color = self.normalStatusColor

    def update_progress(self, value):
        self.ids.prog_status.value = value
        if value == 100:
            self._progressDecay()

    def on_progress(self, value):
        self.update_progress(value)

    def on_main_menu(self, instance, *args):
        pass

    def mainMenu(self):
        self.dispatch('on_main_menu', None)

    def on_progress_decay(self, dt):
        self.update_progress(0)
        self.ids.prog_status.text = self.current_status

    def on_activity_decay(self, dt):
        self.setActivityMessage(self.current_status)

    def on_rc_tx_decay(self, dt):
        self.on_rc_tx(False)

    def on_rc_tx(self, value):
        if not self.rcTxStatus:
            self.rcTxStatus = self.ids.rcTxStatus
        self.rcTxStatus.color = self.txOnColor if value else self.txOffColor
        self._rcTxDecay()

    def on_rc_rx_decay(self, dt):
        self.on_rc_rx(False)

    def on_rc_rx(self, value):
        if not self.rcRxStatus:
            self.rcRxStatus = self.ids.rcRxStatus
        self.rcRxStatus.color = self.rxOnColor if value else self.rxOffColor
        self._rcRxDecay()

    def on_tele_status(self, status):
        if not self.teleStatus:
            self.teleStatus = self.ids.teleStatus
        try:
            self.teleStatus.color = self.telemetry_color[status]
        except:
            Logger.error("ToolbarView: Invalid telemetry status: " +
                         str(status))

    def _update_track_status(self, status_data):
        try:
            track_status = status_data['status']['track']
            detection_status = track_status['status']
            if detection_status == 0:
                track_status_msg = 'Searching for Track'
            elif detection_status == 1:
                track_status_msg = 'User defined Track'
            else:
                if track_status['trackId'] != 0:
                    track = self.track_manager.find_track_by_short_id(
                        track_status['trackId'])
                    if track is None:
                        track_status_msg = '(Unknown Track)'
                    else:
                        track_status_msg = track.name
                        configuration_name = track.configuration
                        if configuration_name and len(configuration_name):
                            track_status_msg += ' (' + configuration_name + ')'
                else:
                    track_status_msg = 'No track detected'
            self.set_state_message(track_status_msg)
        except Exception as e:
            Logger.warn(
                "ToolbarView: Could not retrieve track detection status " +
                str(e))
예제 #39
0
 def on_progress(self, instance, value):
     if value == 100:
         self.ids.ok_cancel.color = ColorScheme.get_light_primary_text()
         self.ids.ok_cancel.text = u'\uf00c'
예제 #40
0
from kivy.app import Builder
from kivy.uix.screenmanager import Screen
from kivy.uix.treeview import TreeView, TreeViewLabel
from kivy.uix.label import Label
from kivy.properties import ObjectProperty, StringProperty, NumericProperty
from datetime import timedelta
from utils import *
from fieldlabel import FieldLabel
from kivy.logger import LoggerHistory, Logger
from autosportlabs.racecapture.theme.color import ColorScheme
from autosportlabs.uix.toast.kivytoast import toast
from main import RaceCaptureApp
from autosportlabs.widgets.scrollcontainer import ScrollContainer
STATUS_KV_FILE = 'autosportlabs/racecapture/views/status/statusview.kv'

RAW_STATUS_BGCOLOR_1 = ColorScheme.get_background()
RAW_STATUS_BGCOLOR_2 = ColorScheme.get_dark_background()

class StatusLabel(FieldLabel):
    backgroundColor = ObjectProperty(RAW_STATUS_BGCOLOR_1)

class StatusTitle(StatusLabel):
    pass

class StatusValue(StatusLabel):

    def __init__(self, **kwargs):
        super(StatusLabel, self).__init__(**kwargs)
        self.shorten = False

# Simple extension of Kivy's TreeViewLabel so we can add on our own properties
예제 #41
0
class PitstopTimerView(BoxLayout, SettingsListener):
    '''
    Provides a pit stop timer that automatically activates when certain speed
    thresholds are met. 
    '''
    _STOPWATCH_TICK = 0.1
    _FLASH_INTERVAL = 0.25
    _FLASH_COUNT = 10
    _EXIT_SPEED_CONTAINER_SIZE_HINT = 0.3
    _TIMER_WIDGET_SIZE_HINT = 0.4
    _SPEED_ALERT_THRESHOLD = 0.85
    _POPUP_SIZE_HINT = (0.75, 0.8)

    exit_speed_color = ListProperty(ColorScheme.get_dark_background_translucent())
    title = StringProperty('')
    current_time = StringProperty(STOPWATCH_NULL_TIME)
    exit_speed = StringProperty('')
    current_speed = NumericProperty(None)
    current_lap = NumericProperty(None)

    Builder.load_string(STOPWATCH_LAYOUT)

    def __init__(self, databus, title='Pit Stop', **kwargs):
        super(PitstopTimerView, self).__init__(**kwargs)
        self.title = title
        self.databus = databus
        self._popup = None
        self._flash_count = 0
        self._start_time = 0.0
        self._currently_racing = False
        self._pitstop_trigger_speed = 0
        self._pitstop_exit_speed = 0
        self._pitstop_alert_speed = 0
        databus.addChannelListener(STOPWATCH_SPEED_CHANNEL, self.set_speed)
        databus.addChannelListener(STOPWATCH_CURRENT_LAP, self.set_current_lap)

    def _set_exit_speed_frame_visible(self, visible):
        '''
        Shows or hides the frame containing the exit speed indicator
        :param visible true if exit speed frame should be visible
        :type visible
        '''
        container = self.ids.exit_speed_frame_container
        frame_visible = self.exit_speed_frame in container.children
        if visible:
            if not frame_visible:
                container.add_widget(self.exit_speed_frame)
        else:
            if frame_visible:
                container.remove_widget(self.exit_speed_frame)

    def _format_stopwatch_time(self):
        '''
        Format current laptime for display
        '''
        return format_laptime(self._current_time / 60.0)

    def _toggle_exit_speed_alert(self, alert_color):
        '''
        Toggles the background color for the exit speed frame
        '''
        if self.exit_speed_color == alert_color:
            self.exit_speed_color = ColorScheme.get_dark_background_translucent()
        else:
            self.exit_speed_color = alert_color

    def _update_speed_color(self):
        '''
        Selects the appropriate color for the exit speed indicator based on 
        configured speed threshold
        '''
        if self.current_speed > self._pitstop_alert_speed:
            self.ids.speed_rect.rect_color = ColorScheme.get_error()
            self._toggle_exit_speed_alert(ColorScheme.get_error())
        elif self.current_speed > self._pitstop_alert_speed * self._SPEED_ALERT_THRESHOLD:
            self.ids.speed_rect.rect_color = ColorScheme.get_alert()
            self.exit_speed_color = ColorScheme.get_dark_background_translucent()
        else:
            self.ids.speed_rect.rect_color = ColorScheme.get_happy()
            self.exit_speed_color = ColorScheme.get_dark_background_translucent()

    def _tick_stopwatch(self, *args):
        '''
        Increment the current stopwatch time
        '''
        self._current_time = time() - self._start_time
        self.current_time = self._format_stopwatch_time()
        self.exit_speed = '{}'.format(int(self.current_speed))
        if self.current_speed > self._pitstop_trigger_speed:
            self._set_exit_speed_frame_visible(True)
        self._update_speed_color()

    def _stop_stopwatch(self):
        '''
        Stops the stopwatch timer
        '''
        Clock.unschedule(self._tick_stopwatch)
        self.current_time = STOPWATCH_NULL_TIME

    def _in_race_mode(self):
        '''
        Return True if we think we're in 'racing mode'
        '''
        return self.current_speed > self._pitstop_exit_speed and\
            self.current_lap > 0

    def user_preferences_updated(self, user_preferences):
        '''
        Update runtime values from user preferences
        '''
        self._pitstop_timer_enabled = user_preferences.get_pref_bool('dashboard_preferences', 'pitstoptimer_enabled')
        self._pitstop_trigger_speed = user_preferences.get_pref_int('dashboard_preferences', 'pitstoptimer_trigger_speed')
        self._pitstop_exit_speed = user_preferences.get_pref_int('dashboard_preferences', 'pitstoptimer_exit_speed')
        self._pitstop_alert_speed = user_preferences.get_pref_int('dashboard_preferences', 'pitstoptimer_alert_speed')

    def _start_stopwatch(self):
        '''
        Starts the stopwatch timer
        '''

        if not self._popup:
            self._popup = ModalView(size_hint=self._POPUP_SIZE_HINT, auto_dismiss=False)
            self._popup.add_widget(self)
        self._set_exit_speed_frame_visible(False)
        self._popup.open()
        self._current_time = 0.0
        self._start_time = time()
        self._flash_count = 0
        self._currently_racing = False
        Clock.schedule_interval(self._tick_stopwatch, self._STOPWATCH_TICK)

    def _is_popup_open(self):
        '''
        Indicates if current popup is open
        :return True if popup is currently open
        '''
        return self._popup and self._popup._window is not None

    def _flash_pit_stop_time(self, *args):
        '''
        Flashes the final pit stop time when complete
        '''
        self.current_time = self._format_stopwatch_time() if self._flash_count % 2 == 0 else ''
        self._flash_count += 1
        if self._flash_count < self._FLASH_COUNT * 2:
            Clock.schedule_once(self._flash_pit_stop_time, self._FLASH_INTERVAL)
        else:
            self._popup.dismiss()

    def _finish_stopwatch(self):
        '''
        Finish the current stopwatch
        '''
        self._flash_count = 0
        self.ids.speed_rect.rect_color = ColorScheme.get_dark_background_translucent()
        self.exit_speed_color = ColorScheme.get_dark_background_translucent()
        self._set_exit_speed_frame_visible(False)
        Clock.schedule_once(self._flash_pit_stop_time)
        self._stop_stopwatch()

    def check_popup(self):
        '''
        Check if we should pop-up the timer
        '''
        if not self._pitstop_timer_enabled:
            return

        if self._in_race_mode():
            self._currently_racing = True
        if self.current_speed < self._pitstop_trigger_speed and\
                self._currently_racing and\
                not self._is_popup_open():
            self._start_stopwatch()

        if self.current_speed > self._pitstop_exit_speed and\
                self._is_popup_open() and\
                self._flash_count == 0:
            self._finish_stopwatch()

    def on_current_speed(self, instance, value):
        self.check_popup()

    def on_current_lap(self, instance, value):
        self.check_popup()

    def set_speed(self, value):
        self.current_speed = value

    def set_current_lap(self, value):
        self.current_lap = value

    def dismiss(self):
        self._popup.dismiss()
        self._stop_stopwatch()

    def change_font_size(self):
        '''
        Auto resizes the timer widget if it spills over the edge of the bounding box
        '''
        widget = self.ids.timer
        try:
            if widget.texture_size[0] > widget.width:
                widget.font_size -= 1
        except Exception as e:
            Logger.warn('[PitstopTimerView] Failed to change font size: {}'.format(e))
예제 #42
0
 def on_press(self, *args):
     super(LabelIconButton, self).on_press(*args)
     if not self.disabled and not self.pulsing:
         self.tile_color = ColorScheme.get_medium_accent()
예제 #43
0
 def on_state(self, instance, value):
     selected = value == 'down'
     self.selected_color = ColorScheme.get_medium_background() if selected else ColorScheme.get_dark_background()
     self.dispatch('on_selected', selected)
예제 #44
0
 def on_pulsing(self, instance, value):
     self.tile_color = ColorScheme.get_medium_accent()
     super(LabelIconButton, self).on_pulsing(instance, value)