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
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
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 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
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 _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()
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
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
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()
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
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
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
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)
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
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
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)
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()
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()
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)
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)
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)
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))
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)
def on_disabled(self, instance, value): super(LabelIconButton, self).on_disabled(instance, value) self.tile_color = ColorScheme.get_medium_accent()
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 on_active(self, instance, value): self.title_color = ColorScheme.get_light_primary_text( ) if value else ColorScheme.get_secondary_text()
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)
def on_release(self, *args): super(LabelIconButton, self).on_release(*args) if not self.pulsing: self.tile_color = ColorScheme.get_dark_accent()
def on_pulsing(self, instance, value): self.tile_color = ColorScheme.get_medium_accent() super(LabelIconButton, self).on_pulsing(instance, value)
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
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
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))
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'
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
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))
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_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)