def __init__(self, **kwargs): Builder.load_string(FLYIN_PANEL_LAYOUT) super(FlyinPanel, self).__init__(**kwargs) self.hide_decay = Clock.create_trigger(lambda dt: self.hide(), self.SESSION_HIDE_DELAY) Window.bind(mouse_pos=self.on_mouse_pos) Window.bind(on_motion=self.on_motion) Clock.schedule_once(lambda dt: self.show())
class AnalogScaler(Graph): Builder.load_string(""" <AnalogScaler>: id: graph xlabel: 'Volts' ylabel: 'Scaled' x_ticks_minor: 1 x_ticks_major: 1 y_grid_label: True x_grid_label: True padding: 5 x_grid: True y_grid: True xmin: 0 xmax: 5 """) def __init__(self, **kwargs): super(AnalogScaler, self).__init__(**kwargs)
class AddStreamSelectView(Screen): Builder.load_string(""" <AnalysisFeatureButton@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] disabled_color: [1.0, 1.0, 1.0, 1.0] highlight_color: [0.7, 0.7, 0.7, 1.0] <AddStreamSelectView>: BoxLayout: orientation: 'vertical' GridLayout: size_hint_y: 0.8 padding: [self.height * 0.1, self.height * 0.15] spacing: self.height * 0.1 rows: 1 cols: 3 AnalysisFeatureButton: icon: u'\uf15b' title: 'Import Log File' on_press: root.select_stream('file') AnalysisFeatureButton: icon: u'\uf187' title: 'Saved Session' on_press: root.select_stream('session') FieldLabel: size_hint_y: 0.2 font_size: root.height * 0.1 font_name: 'resource/fonts/ASL_regular.ttf' halign: 'center' text: 'Select a session location' """) def __init__(self, **kwargs): super(AddStreamSelectView, self).__init__(**kwargs) self.register_event_type('on_select_stream') def select_stream(self, stream): self.dispatch('on_select_stream', stream) def on_select_stream(self, stream): pass
class GradientLegend(BoxLayout): ''' Represents a gradient legend, specified by min / max colors ''' Builder.load_string(''' <GradientLegend>: orientation: 'vertical' BoxLayout: size_hint_y: (1.0 - root.height_pct) / 2.0 GradientBox: size_hint_y: root.height_pct color: root.color id: gradient BoxLayout: size_hint_y: (1.0 - root.height_pct) / 2.0 ''') color = ObjectProperty([0.0, 0.0, 0.0, 1.0], allownone=True) height_pct = NumericProperty(0.1)
class GaugeView(DashboardScreen): Builder.load_string(GAUGE_VIEW_KV) def __init__(self, databus, settings, **kwargs): super(GaugeView, self).__init__(**kwargs) self._databus = databus self._settings = settings self._initialized = False def on_meta(self, channelMetas): gauges = self._find_active_gauges() for gauge in gauges: channel = gauge.channel if channel: channelMeta = channelMetas.get(channel) if channelMeta: gauge.precision = channelMeta.precision gauge.min = channelMeta.min gauge.max = channelMeta.max def _find_active_gauges(self): return list(kvFindClass(self, Gauge)) def on_enter(self): if not self._initialized: self._init_view() def _init_view(self): dataBus = self._databus dataBus.addMetaListener(self.on_meta) gauges = self._find_active_gauges() for gauge in gauges: gauge.settings = self._settings gauge.data_bus = dataBus channel = self._settings.userPrefs.get_gauge_config(gauge.rcid) if channel: gauge.channel = channel self._initialized = True
class ItemSelectorView(BoxLayout): Builder.load_string(""" <ItemSelectorView>: orientation: 'vertical' spacing: dp(5) padding: (dp(10), dp(10)) ScrollContainer: id: scroll size_hint_y: 0.6 do_scroll_x:False do_scroll_y:True GridLayout: id: grid padding: (dp(10), dp(10)) spacing: dp(10) row_default_height: root.height * 0.3 size_hint_y: None height: self.minimum_height cols: 1 """) selected_item = ObjectProperty() item_references = ListProperty() def __init__(self, **kwargs): super(ItemSelectorView, self).__init__(**kwargs) self.init_view() def init_view(self): item_references = self.item_references for item in item_references: view = ItemSelectionView(title=item.title, image_source=item.image_source, item_reference=item, selected_item=item) view.bind(on_selected=self._on_item_state) self.ids.grid.add_widget(view) def _on_item_state(self, instance, value): if value: self.selected_item = instance.item_reference
class AlertActionSummaryView(BoxLayout): Builder.load_string(""" <AlertActionSummaryView>: size_hint_y: None height: dp(30) ClickAnchorLayout: id: title size_hint_x: 0.9 on_press: root.dispatch('on_edit', root._alertaction) # TODO re-enable this when we can do drag / drop on alert rules, so # we can save space by removing the up/down arrow buttons. # and add an edit button to the alert rules list # IconButton: # size_hint_x: 0.1 # text: u'\uf044' # on_release: root.dispatch('on_edit', root._alertaction) IconButton: size_hint_x: 0.1 text: u'\uf014' on_release: root.dispatch('on_delete', root._alertaction) """) def __init__(self, alertaction, **kwargs): super(AlertActionSummaryView, self).__init__(**kwargs) self._alertaction = alertaction self.register_event_type('on_edit') self.register_event_type('on_delete') self._refresh_view() def _refresh_view(self): aa = self._alertaction preview = AlertActionPreviewFactory.create_preview(aa) self.ids.title.add_widget(preview) def on_edit(self, alertaction): pass def on_delete(self, alertaction): pass
class LaptimeView(DashboardScreen): Builder.load_string(LAPTIME_VIEW_KV) _databus = None _settings = None def __init__(self, databus, settings, **kwargs): super(LaptimeView, self).__init__(**kwargs) self._databus = databus self._settings = settings self._initialized = False def on_meta(self, channelMetas): gauges = self.findActiveGauges(SingleChannelGauge) for gauge in gauges: channel = gauge.channel if channel: channelMeta = channelMetas.get(channel) if channelMeta: gauge.precision = channelMeta.precision gauge.min = channelMeta.min gauge.max = channelMeta.max def findActiveGauges(self, gauge_type): return list(kvFindClass(self, gauge_type)) def on_enter(self): if not self._initialized: self._init_screen() def _init_screen(self): dataBus = self._databus settings = self._settings dataBus.addMetaListener(self.on_meta) gauges = self.findActiveGauges(Gauge) for gauge in gauges: gauge.settings = settings gauge.data_bus = dataBus self._initialized = True
class ExistingTrackSelectorScreen(Screen): """ Screen to select an existing track map """ Builder.load_string(EXISTING_TRACK_SELECTOR_KV) def __init__(self, track_manager, current_point, **kwargs): super(ExistingTrackSelectorScreen, self).__init__(**kwargs) self.register_event_type('on_track_selected') self.ids.track_select.bind(on_track_selected=self.track_selected) self.ids.track_select.init_view(track_manager, current_point) def track_selected(self, instance, track): self.dispatch('on_track_selected', track) def on_track_selected(self, track): pass def cleanup(self): pass
class ChannelLabel(Label): Builder.load_string(""" <ChannelLabel>: size_hint_y: None padding_x: -10 height: max(dp(30), self.texture_size[1] + dp(5)) text_size: self.width, None valign: 'middle' halign: 'left' font_size: sp(18) canvas.before: Color: rgba: .1, .1, .1, .9 Rectangle: size: self.size pos: self.pos """) def __init__(self, **kwargs): super(ChannelLabel, self).__init__(**kwargs)
class TrackSelectView(StackLayout): Builder.load_string(""" <TrackSelectView>: TracksBrowser: id: track_browser """) def __init__(self, **kwargs): super(TrackSelectView, self).__init__(**kwargs) current_location = kwargs.get('current_location') track_manager = kwargs.get('track_manager') self.selected_track = None self.register_event_type('on_track_selected') self.init_view(track_manager, current_location) def init_view(self, track_manager, current_location): if track_manager is None: # can't init if we don't have track_manager # presumably it will happen later return self.ids.track_browser.multi_select = False self.ids.track_browser.bind(on_track_selected=self.track_selected) self.ids.track_browser.set_trackmanager(track_manager) self._track_manager = track_manager self.ids.track_browser.current_location = current_location self.ids.track_browser.init_view() def on_track_selected(self, track): pass def track_selected(self, instance, tracks): if len(tracks) > 0: # Tracks are a set, we only want 1 but we don't want to modify the original tracks_copy = tracks.copy() track_id = tracks_copy.pop() track = self._track_manager.get_track_by_id(track_id) self.selected_track = track self.dispatch('on_track_selected', track) else: self.selected_track = None
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 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 TrackTypeSelectorScreen(Screen): """ Screen to select different track types """ TRACK_TYPE_CIRCUIT = 'circuit' TRACK_TYPE_POINT_POINT = 'point2point' Builder.load_string(TRACK_TYPE_SELECTOR_KV) def __init__(self, **kwargs): super(TrackTypeSelectorScreen, self).__init__(**kwargs) self.register_event_type('on_track_type_selected') def on_track_type_selected(self, type): pass def cleanup(self): pass def select_track_type(self, type): self.dispatch('on_track_type_selected', type)
class CameraControlView(BaseConfigView): Builder.load_string(CAMERACONTROL_VIEW_KV) config = ObjectProperty() def __init__(self, **kwargs): super(CameraControlView, self).__init__(**kwargs) self.view_loaded = False self.register_event_type('on_modified') settings = self.ids.enabled settings.bind(on_setting=self.on_setting_enabled) settings.setControl(SettingsSwitch()) camera_type = self.ids.makemodel camera_type.bind(on_setting=self._on_setting_makemodel) camera_type.setControl(CameraMakeModelSpinner()) def on_setting_enabled(self, instance, value): if not self.view_loaded: return self.config.enabled = value self.config.stale = True self.dispatch('on_modified') def _on_setting_makemodel(self, instance, value): if not self.view_loaded: return self.config.make_model = value self.config.stale = True self.dispatch('on_modified') def update_config(self, cfg): self.view_loaded = False self.config = cfg self.ids.enabled.setValue(cfg.enabled) self.ids.makemodel.setValue(cfg.make_model) self.view_loaded = True def on_modified(self): pass
class GradientLapLegend(BoxLayout): ''' A compound widget that presents a gradient color legend including session/lap and min/max values ''' Builder.load_string(''' <GradientLapLegend>: orientation: 'horizontal' spacing: sp(5) FieldLabel: id: lap size_hint_x: 0.5 halign: 'right' valign: 'middle' text: '{} :: {}'.format(root.session, root.lap) FieldLabel: id: min_value text: str(root.min_value) size_hint_x: 0.2 halign: 'right' valign: 'middle' font_size: 0.5 * self.height GradientLegend: id: legend size_hint_x: 0.1 height_pct: root.height_pct color: root.color FieldLabel: id: max_value text: str(root.max_value) size_hint_x: 0.2 halign: 'left' valign: 'middle' font_size: 0.5 * self.height ''') color = ObjectProperty([1.0, 1.0, 1.0], allownone=True) min_value = NumericProperty(0.0) max_value = NumericProperty(100.0) session = StringProperty('') lap = StringProperty('') height_pct = NumericProperty(0.3)
class ProgressPopup(GridLayout): Builder.load_string(""" <ProgressPopup>: cols:1 padding: (dp(10), dp(10)) Label: text: root.text size_hint_y: 0.4 ProgressBar: value: root.progress size_hint_y: 0.6 GridLayout: cols: 2 size_hint_y: None height: '44sp' spacing: '5sp' IconButton: id: ok_cancel text: u'\uf00d' on_press: root.dispatch('on_ok_cancel', True) color: ColorScheme.get_primary() """) text = StringProperty() progress = NumericProperty() def __init__(self, **kwargs): self.register_event_type('on_ok_cancel') super(ProgressPopup, self).__init__(**kwargs) def on_ok_cancel(self, *args): pass def update_progress(self, value): self.progress = value 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'
class LapLegend(BoxLayout): ''' A compound widget that presents the the color legend with session/lap information ''' Builder.load_string(''' <LapLegend>: orientation: 'horizontal' spacing: sp(5) FieldLabel: id: lap size_hint_x: 0.8 halign: 'right' valign: 'middle' text: '{} :: {}'.format(root.lap, root.session) ColorLegend: id: legend size_hint_x: 0.2 bar_color: root.color ''') color = ListProperty([1.0, 1.0, 1.0, 1.0]) session = StringProperty('') lap = StringProperty('')
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 CustomizeHeatmapView(BaseOptionsScreen): """ The customization view for customizing the heatmap options """ Builder.load_string(''' <CustomizeHeatmapView>: BoxLayout: orientation: 'vertical' ChannelSelectView: id: heatmap_channel size_hint_y: 0.9 on_channel_selected: root.channel_selected(*args) ''') def __init__(self, params, values, **kwargs): super(CustomizeHeatmapView, self).__init__(params, values, **kwargs) def on_enter(self): if self.initialized == False: channels = self._get_available_channel_names() self.ids.heatmap_channel.selected_channel = self.values.heatmap_channel self.ids.heatmap_channel.available_channels = channels def _get_available_channel_names(self): available_channels = self.params.datastore.channel_list return [str(c) for c in available_channels] def channel_selected(self, instance, value): modified = self.values.heatmap_channel != value self.values.heatmap_channel = value if modified: self.dispatch('on_screen_modified') def channel_cleared(self, *args): modified = self.values.heatmap_channel == None self.values.heatmap_channel = None if modified: self.dispatch('on_screen_modified')
class TrackSelectView(StackLayout): Builder.load_string(TRACK_SELECT_VIEW) def __init__(self, track_manager, **kwargs): super(TrackSelectView, self).__init__(**kwargs) self._track_manager = track_manager self.selected_track = None self.ids.track_browser.multi_select = False self.ids.track_browser.set_trackmanager(self._track_manager) self.ids.track_browser.init_view() self.ids.track_browser.bind(on_track_selected=self.on_track_selected) def on_track_selected(self, instance, tracks): if len(tracks) > 0: # Tracks are a set, we only want 1 but we don't want to modify the original tracks_copy = tracks.copy() id = tracks_copy.pop() track = self._track_manager.get_track_by_id(id) self.selected_track = track else: self.selected_track = None
class ValueField(TextInput): Builder.load_string(VALUE_FIELD_KV) def __init__(self, *args, **kwargs): self.next = kwargs.pop('next', None) super(ValueField, self).__init__(*args, **kwargs) def on_focus(self, instance, value): if value: Window.bind(on_keyboard=self._on_keyboard) else: Window.unbind(on_keyboard=self._on_keyboard) self.dispatch('on_text_validate') def set_next(self, next): self.next = next def _on_keyboard(self, keyboard, keycode, *args): if keycode == 9: # tab if self.next is not None: self.next.focus = True self.dispatch('on_text_validate')
class OkPopup(GridLayout): Builder.load_string(""" <OkPopup>: cols:1 Label: text: root.text GridLayout: cols: 2 size_hint_y: None height: '44sp' spacing: '5sp' IconButton: text: u'\uf00c' on_press: root.dispatch('on_ok', True) """) text = StringProperty() def __init__(self, **kwargs): self.register_event_type('on_ok') super(OkPopup, self).__init__(**kwargs) def on_ok(self, *args): pass
class SelectableChannelLabel(RecycleDataViewBehavior, Label): Builder.load_string(""" <SelectableChannelLabel>: # Draw a background to indicate selection canvas.before: Color: rgba: ColorScheme.get_primary() if self.selected else ColorScheme.get_dark_background_translucent() Rectangle: pos: self.pos size: self.size font_name: 'resource/fonts/ASL_light.ttf' font_size: self.height * 0.5 """) index = NumericProperty(None, allownone=True) selected = BooleanProperty(False) selectable = BooleanProperty(True) def refresh_view_attrs(self, rv, index, data): ''' Catch and handle the view changes ''' self.index = index return super(SelectableChannelLabel, self).refresh_view_attrs(rv, index, data) def on_touch_down(self, touch): ''' Add selection on touch down ''' if super(SelectableChannelLabel, self).on_touch_down(touch): return True if self.collide_point(*touch.pos) and self.selectable: return self.parent.select_with_touch(self.index, touch) def apply_selection(self, rv, index, is_selected): ''' Respond to the selection of items in the view. ''' self.selected = is_selected if is_selected: rv.selected_channel = rv.data[index]['text']
class DashboardScreenPreferences(DashboardPreferenceScreen): """ Provides the interface for selecting screens to enable """ Builder.load_string(DASHBOARD_SCREEN_PREFERENCES_KV) def __init__(self, settings, dashboard_factory, **kwargs): super(DashboardScreenPreferences, self).__init__(**kwargs) self._settings = settings current_screens = self._settings.userPrefs.get_dashboard_screens() screen_keys = dashboard_factory.available_dashboards for key in screen_keys: [name, image] = dashboard_factory.get_dashboard_preview_image_path(key) checkbox = CheckBox() checkbox.active = True if key in current_screens else False checkbox.bind( active=lambda i, v, k=key: self._screen_selected(k, v)) screen_item = DashboardScreenItem() screen_item.add_widget(checkbox) screen_item.add_widget(FieldLabel(text=name)) screen_item.add_widget(Image(source=image)) self.ids.grid.add_widget(screen_item) self._current_screens = current_screens def _screen_selected(self, key, selected): screens = self._current_screens if key in screens and not selected: screens.remove(key) if key not in screens and selected: screens.append(key) @property def selected_screens(self): return self._current_screens
class CANChannelMappingTab(AnchorLayout, AndroidTabsBase): """ Wrapper class to allow customization and styling """ Builder.load_string(""" <CANChannelMappingTab>: canvas.before: Color: rgba: ColorScheme.get_dark_background() Rectangle: pos: self.pos size: self.size tab_font_name: "resource/fonts/ASL_light.ttf" tab_font_size: sp(20) """) tab_font_name = StringProperty() tab_font_size = NumericProperty() def on_tab_font_name(self, instance, value): self.tab_label.font_name = value def on_tab_font_size(self, instance, value): self.tab_label.font_size = value
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
class ColorLegend(BoxLayout): ''' Represents a single color legend. The entire layout is drawn with the color specified. ''' Builder.load_string(''' <ColorLegend>: orientation: 'vertical' BoxLayout: size_hint_y: (1.0 - root.height_pct) / 2.0 BoxLayout: id: legend canvas.before: Color: rgba: root.bar_color Rectangle: pos: self.pos size: self.size id: color_bar size_hint_y: root.height_pct BoxLayout: size_hint_y: (1.0 - root.height_pct) / 2.0 ''') bar_color = ListProperty([1.0, 1.0, 1.0, 1.0]) height_pct = NumericProperty(0.1)
spinner.setValueMap(apn_map, CellSettingsView.CUSTOM_APN) self.cell_provider_info = cell_provider_info except Exception as detail: Logger.error('CellSettingsView: Error loading cell providers ' + str(detail)) Builder.load_string(''' <CellularConfigView>: id: cellular cols: 1 spacing: [0, dp(20)] row_default_height: dp(40) size_hint: [1, None] height: self.minimum_height HSeparator: text: 'Cellular Configuration' CellSettingsView: id: cell_settings on_modified: root.modified() size_hint_y: None height: dp(300) ''') class CellularConfigView(GridLayout): def __init__(self, base_dir, rc_config, **kwargs): super(CellularConfigView, self).__init__(**kwargs) self.connectivityConfig = None self.register_event_type('on_modified')
# this code. If not, see <http://www.gnu.org/licenses/>. import kivy kivy.require('1.10.0') from kivy.app import Builder from kivy.logger import Logger from kivy.uix.boxlayout import BoxLayout from autosportlabs.racecapture.views.configuration.baseconfigview import BaseConfigView from settingsview import SettingsView, SettingsTextField, SettingsSwitch Builder.load_string(''' <BackgroundStreamingView> size_hint_y: None height: dp(80) SettingsView: id: bgStream label_text: 'Background Streaming' help_text: 'Stream real-time telemetry continuously in the background' HelpLabel: text: 'When disabled, telemetry is synchronized with SD logging.' halign: 'left' ''') class BackgroundStreamingView(BaseConfigView): def __init__(self, **kwargs): self.connectivityConfig = None super(BackgroundStreamingView, self).__init__(**kwargs) self.register_event_type('on_config_updated') def on_bg_stream(self, instance, value):
self.channelConfig = channelConfig def on_map_updated(self, *args): self.channelConfig.stale = True self.dispatch('on_modified', self.channelConfig) class AnalogScaler(Graph): def __init__(self, **kwargs): super(AnalogScaler, self).__init__(**kwargs) Builder.load_string(''' <WarnLabel> canvas.before: Color: rgba: (1.0, 0, 0, 0.5) Rectangle: pos: self.pos size: self.size ''') class WarnLabel(Label): pass WARN_DISMISS_TIMEOUT = 3 class AnalogScalingMapEditor(BoxLayout): map_size = SCALING_MAP_POINTS scaling_map = None plot = None
Builder.load_string( """ <ConfirmPopup>: cols:1 Label: text: root.text GridLayout: cols: 2 size_hint_y: None height: '44sp' spacing: '5sp' Button: text: 'Ok' on_release: root.dispatch('on_answer', True) Button: text: 'Cancel' on_release: root.dispatch('on_answer', False) <OkPopup>: cols:1 Label: text: root.text GridLayout: cols: 2 size_hint_y: None height: '44sp' spacing: '5sp' Button: text: 'Ok' on_release: root.dispatch('on_ok') """ )
Builder.load_string(''' <ConfirmPopup>: cols:1 Label: text: root.text GridLayout: cols: 2 size_hint_y: None height: '44sp' spacing: '5sp' IconButton: text: u'\uf00c' on_press: root.dispatch('on_answer', True) IconButton: text: u'\uf00d' color: ColorScheme.get_primary() on_release: root.dispatch('on_answer', False) <OkPopup>: cols:1 Label: text: root.text GridLayout: cols: 2 size_hint_y: None height: '44sp' spacing: '5sp' IconButton: text: u'\uf00c' on_press: root.dispatch('on_answer', True) <EditorPopup>: id: editor_popup cols:1 BoxLayout: id: content GridLayout: cols: 2 size_hint_y: None height: '44sp' spacing: '5sp' IconButton: text: u'\uf00c' on_press: root.dispatch('on_answer', True) IconButton: text: u'\uf00d' color: ColorScheme.get_primary() on_release: root.dispatch('on_answer', False) ''')
from kivy.metrics import dp from settingsview import SettingsView, SettingsSwitch, SettingsButton from autosportlabs.widgets.separator import HLineSeparator from autosportlabs.racecapture.views.util.alertview import editor_popup from autosportlabs.racecapture.views.configuration.rcp.advancedbluetoothconfigview import AdvancedBluetoothConfigView Builder.load_string(''' <BluetoothConfigView> id: bluetooth cols: 1 spacing: [0, dp(20)] size_hint: [1, None] height: self.minimum_height HSeparator: text: 'Bluetooth' size_hint_y: None SettingsView: id: bt_enable label_text: 'Bluetooth' help_text: 'If the Bluetooth module is connected, enable it here' SettingsView: id: btconfig label_text: 'Advanced configuration' help_text: 'Change Bluetooth name and passkey. Firmware version 2.9.0 or greater required.' ''') class BluetoothConfigView(GridLayout): def __init__(self, config, **kwargs): super(BluetoothConfigView, self).__init__(**kwargs)
Builder.load_string(''' <CellularConfigView>: id: cellular cols: 1 spacing: [0, dp(20)] row_default_height: dp(40) size_hint: [1, None] height: self.minimum_height HSeparator: text: 'Cellular Configuration' SettingsView: size_hint_y: 0.20 rcid: 'cellEnable' label_text: 'Cellular Module' help_text: 'Enable if the Real-time telemetry module is installed' SettingsView: size_hint_y: None rcid: 'cellprovider' label_text: 'Cellular Provider' help_text: 'Select the cellular provider, or specify custom APN settings' Label: text: 'Custom Cellular Settings' text_size: self.size halign: 'center' font_size: dp(26) GridLayout: cols: 2 spacing: (dp(30), dp(5)) FieldLabel: text: 'APN Host' halign: 'right' ValueField: rcid: 'apnHost' disabled: True on_text: root.on_apn_host(*args) GridLayout: cols: 2 spacing: (dp(30), dp(5)) FieldLabel: halign: 'right' text: 'APN User Name' ValueField: rcid: 'apnUser' size_hint_y: 1 disabled: True on_text: root.on_apn_user(*args) GridLayout: cols: 2 spacing: (dp(30), dp(5)) FieldLabel: halign: 'right' text: 'APN Password' ValueField: rcid: 'apnPass' disabled: True size_hint_y: 1 on_text: root.on_apn_pass(*args) ''')
from kivy.uix.anchorlayout import AnchorLayout from autosportlabs.racecapture.theme.color import ColorScheme class ClickAnchorLayout(ButtonBehavior, AnchorLayout): pass class SectionBoxLayout(BoxLayout): """ Provides a consistent BoxLayout with some some decoration and padding """ Builder.load_string(""" <SectionBoxLayout>: canvas.before: Color: rgba: ColorScheme.get_dark_background_translucent() Rectangle: pos: self.pos size: self.size padding: (dp(10), dp(10)) spacing: dp(5) """) class HeaderSectionBoxLayout(SectionBoxLayout): """ A header variation of SectionBoxLayout with a highlighted background color """ Builder.load_string(""" <HeaderSectionBoxLayout>: canvas.before: Color: rgba: ColorScheme.get_medium_background()
Builder.load_string(''' <WifiConfigView>: id: wifi cols: 1 spacing: [0, dp(20)] row_default_height: dp(40) size_hint: [1, None] height: self.minimum_height HSeparator: text: 'WiFi' SettingsView: id: wifi_enabled label_text: 'WiFi Module' BaseLabel: text_size: self.size halign: 'center' text: 'Client Mode Configuration' font_size: dp(26) BaseLabel: text: 'Use this mode to setup the wifi module to connect [b]to[/b] an existing wireless network.' markup: True text_size: (self.parent.width, None) padding: [dp(20), 0] SettingsView: id: client_mode label_text: 'Client Mode' GridLayout: spacing: (dp(30), dp(5)) cols: 2 FieldLabel: text: 'SSID' halign: 'right' ValueField: id: client_ssid disabled: False on_text: root.on_client_ssid(*args) GridLayout: spacing: (dp(30), dp(5)) cols: 2 FieldLabel: text: 'Password' halign: 'right' ValueField: id: client_password disabled: False on_text: root.on_client_password(*args) BaseLabel: text: 'Access Point Mode Configuration' text_size: self.size halign: 'center' font_size: dp(26) BaseLabel: text: 'Use this mode to create a wireless network for your phone or table to connect to.' markup: True text_size: (self.parent.width, None) padding: [dp(20), 0] SettingsView: id: ap_mode label_text: 'Access Point Mode' GridLayout: spacing: (dp(30), dp(5)) cols: 2 FieldLabel: text: 'SSID' halign: 'right' BetterTextInput: id: ap_ssid disabled: False on_text: root.on_ap_ssid(*args) max_chars: 24 GridLayout: spacing: (dp(30), dp(5)) cols: 2 FieldLabel: text: 'Password' halign: 'right' BetterTextInput: id: ap_password disabled: False on_text: root.on_ap_password(*args) max_chars: 24 SettingsView: id: ap_channel label_text: 'Channel' SettingsView: id: ap_encryption label_text: 'Encryption' ''')
font_name: root.title_font font_size: root.title_font_size color: root.title_color size_hint_x: 0.75 text: root.title <IconButton>: background_color: [0, 0, 0, 0] background_down: '' font_size: self.height color: [1.0, 1.0, 1.0, 0.2] if self.disabled else [1.0, 1.0, 1.0, 0.8] font_name: 'resource/fonts/fa.ttf' """ Builder.load_string(ICON_BUTTON_KV) class FadeableWidget(EventDispatcher): FADED_ALPHA = 0.1 BRIGHT_ALPHA = 1.0 FADE_STEP = 0.05 FADE_INTERVAL = 0.05 FADE_DELAY = 5.0 fade_color = ObjectProperty([0, 0, 0, 0]) pulsing = BooleanProperty(False) def __init__(self, **kwargs): super(FadeableWidget, self).__init__(**kwargs) self._current_alpha = None self.brighten_mode = True self._schedule_fade = Clock.create_trigger(self._fade_back, FadeableWidget.FADE_DELAY)
class CellSettingsView(BoxLayout): Builder.load_string(""" <CellSettingsView>: orientation: 'vertical' SettingsView: id: cell_enable label_text: 'Enable on-board cellular' help_text: '' size_hint_y: 0.2 SettingsView: id: cell_provider label_text: 'Cellular provider' help_text: 'Select the cellular provider, or specify custom APN' size_hint_y: 0.3 ScreenManager: size_hint_y: 0.5 id: custom_apn_screen Screen: name: 'blank' Widget: Screen: name: 'custom' BoxLayout: orientation: 'vertical' GridLayout: padding: (0, dp(5)) cols: 2 FieldLabel: text: 'APN Host' halign: 'right' padding: (dp(5),0) ValueField: id: apn_host on_text: root.on_apn_host(*args) GridLayout: padding: (0, dp(5)) cols: 2 FieldLabel: halign: 'right' text: 'APN User Name' padding: (dp(5),0) ValueField: id: apn_user size_hint_y: 1 on_text: root.on_apn_user(*args) GridLayout: padding: (0, dp(5)) cols: 2 FieldLabel: halign: 'right' text: 'APN Password' padding: (dp(5),0) ValueField: id: apn_pass size_hint_y: 1 on_text: root.on_apn_pass(*args) """) CUSTOM_APN = 'Custom APN' base_dir = StringProperty(None, noneallowed=True) rc_config = ObjectProperty() def __init__(self, **kwargs): super(CellSettingsView, self).__init__(**kwargs) self.register_event_type('on_modified') self.loaded = False self.cell_provider_info = {} def on_modified(self): pass def on_rc_config(self, instance, value): self.init_view() def on_base_dir(self, instance, value): self.init_view() def init_view(self): if None in [self.base_dir, self.rc_config]: return cell_enable = self.ids.cell_enable cell_enable.setControl( SettingsSwitch(active=self.rc_config.connectivityConfig.cellConfig. cellEnabled)) cell_enable.control.bind(active=self.on_cell_change) cell_provider = self.ids.cell_provider cell_provider.bind(on_setting=self.on_cell_provider) apn_spinner = SettingsMappedSpinner() self.load_apn_settings_spinner(apn_spinner) self.apn_spinner = apn_spinner cell_provider.setControl(apn_spinner) self._update_from_config() self.loaded = True def _update_from_config(self): cellEnable = self.ids.cell_enable cell_config = self.rc_config.connectivityConfig.cellConfig self.ids.apn_host.text = cell_config.apnHost self.ids.apn_user.text = cell_config.apnUser self.ids.apn_pass.text = cell_config.apnPass existing_apn_name = self.update_controls_state() if existing_apn_name: self.apn_spinner.text = existing_apn_name def get_apn_setting_by_name(self, name): providers = self.cell_provider_info['cellProviders'] for apn_name in providers: if apn_name == name: return providers[apn_name] return None def update_controls_state(self): cell_provider_info = self.cell_provider_info existing_apn_name = CellSettingsView.CUSTOM_APN show_custom_fields = True cellConfig = self.rc_config.connectivityConfig.cellConfig providers = cell_provider_info['cellProviders'] for name in providers: apnInfo = providers[name] if apnInfo['apn_host'] == cellConfig.apnHost and apnInfo[ 'apn_user'] == cellConfig.apnUser and apnInfo[ 'apn_pass'] == cellConfig.apnPass: existing_apn_name = name show_custom_fields = False break self.ids.custom_apn_screen.current = 'custom' if show_custom_fields else 'blank' return existing_apn_name def modified(self): if self.loaded: self.dispatch('on_modified') def on_cell_change(self, instance, value): self.rc_config.connectivityConfig.stale = True self.rc_config.connectivityConfig.cellConfig.cellEnabled = value self.modified() def on_cell_provider(self, instance, value): apn_setting = self.get_apn_setting_by_name(value) known_provider = False if apn_setting: self.ids.apn_host.text = apn_setting['apn_host'] self.ids.apn_user.text = apn_setting['apn_user'] self.ids.apn_pass.text = apn_setting['apn_pass'] known_provider = True self.ids.custom_apn_screen.current = 'blank' if known_provider else 'custom' self.modified() def on_apn_host(self, instance, value): self.rc_config.connectivityConfig.cellConfig.apnHost = value self.rc_config.connectivityConfig.stale = True self.modified() def on_apn_user(self, instance, value): self.rc_config.connectivityConfig.cellConfig.apnUser = value self.rc_config.connectivityConfig.stale = True self.modified() def on_apn_pass(self, instance, value): self.rc_config.connectivityConfig.cellConfig.apnPass = value self.rc_config.connectivityConfig.stale = True self.modified() def load_apn_settings_spinner(self, spinner): try: json_data = open( os.path.join(self.base_dir, 'resource', 'settings', 'cell_providers.json')) cell_provider_info = json.load(json_data) apn_map = {} apn_map['custom'] = CellSettingsView.CUSTOM_APN for name in cell_provider_info['cellProviders']: apn_map[name] = name spinner.setValueMap(apn_map, CellSettingsView.CUSTOM_APN) self.cell_provider_info = cell_provider_info except Exception as detail: Logger.error('CellSettingsView: Error loading cell providers ' + str(detail))
DEFAULT_NORMAL_COLOR = [1.0, 1.0 , 1.0, 1.0] DEFAULT_VALUE = None DEFAULT_MIN = 0 DEFAULT_MAX = 100 DEFAULT_PRECISION = 0 DEFAULT_TYPE = CHANNEL_TYPE_SENSOR MENU_ITEM_RADIUS = 100 POPUP_DISMISS_TIMEOUT_SHORT = 10.0 POPUP_DISMISS_TIMEOUT_LONG = 60.0 Builder.load_string(''' <CustomizeGaugeBubble> orientation: 'vertical' size_hint: (None, None) #pos_hint: {'center_x': .5, 'y': .5} #arrow_pos: 'bottom_mid' #background_color: (1, 0, 0, 1.0) #50% translucent red #border: [0, 0, 0, 0] ''') class CustomizeGaugeBubble(CenteredBubble): pass NULL_LAP_TIME='--:--.---' class Gauge(AnchorLayout): rcid = None settings = ObjectProperty(None) value_size = NumericProperty(0) title_size = NumericProperty(0)
class SimpleTrackConfigView(BaseConfigView): Builder.load_string(SIMPLE_TRACK_CONFIG_VIEW) def __init__(self, rc_api, databus, settings, track_manager, **kwargs): super(SimpleTrackConfigView, self).__init__(**kwargs) self._databus = databus self._rc_api = rc_api self._settings = settings self._track_manager = track_manager self._track_config = None self.register_event_type('on_config_updated') button = Button(text='Set Track') button.bind(on_press=self.on_set_track_press) self.ids.current_track.setControl(button) self.ids.custom_start_finish.bind( on_setting=self.on_custom_start_finish) self.ids.custom_start_finish.setControl(SettingsSwitch()) self._track_select_view = None self._popup = None self._manual_track_config_view = None self._old_track_id = None start_line = SectorPointView(databus=self._databus) start_line.set_point(GeoPoint()) start_line.setTitle("Start/Finish") start_line.bind(on_config_changed=self._on_custom_change) self.ids.custom.add_widget(start_line) self._start_line = start_line def on_set_track_press(self, instance): content = TrackSelectView(self._track_manager) self._track_select_view = content self._popup = editor_popup("Select your track", content, self._on_track_select_close) self._popup.open() def _on_track_select_close(self, instance, answer): if answer: if self._track_config: selected_track = self._track_select_view.selected_track Logger.info("TempTrackConfigView: setting track: {}".format( selected_track)) track_config = Track.fromTrackMap(selected_track) self._track_config.track = track_config self._track_config.stale = True self.dispatch('on_modified') track_name = selected_track.name configuration_name = selected_track.configuration if configuration_name and len(configuration_name): track_name += ' (' + configuration_name + ')' self.ids.current_track.label_text = 'Track: ' + track_name self._popup.dismiss() def on_custom_start_finish(self, instance, value): # Show or hide custom start/finish points if value: if self._track_config: if self._track_config.track.trackId > 0: self._old_track_id = self._track_config.track.trackId track = Track() track.trackId = 0 track.startLine = self._start_line.point self._track_config.track = track self._track_config.stale = True self.dispatch('on_modified') self.ids.current_track.control.disabled = True self.ids.current_track.label_text = 'Track: Custom' else: # Revert if self._old_track_id: track_map = self._track_manager.find_track_by_short_id( self._old_track_id) track = Track.fromTrackMap(track_map) if track_map is None: track_name = 'Track not found' else: track_name = track_map.name configuration_name = track_map.configuration if configuration_name and len(configuration_name): track_name += ' (' + configuration_name + ')' self.ids.current_track.label_text = 'Track: ' + track_name else: self.ids.current_track.label_text = 'Track: Unknown' track = Track() track.trackId = -1 track.startLine = GeoPoint() self._track_config.track = track self._track_config.stale = True self.dispatch('on_modified') self.ids.current_track.control.disabled = False def _on_custom_change(self, *args): Logger.info("TempTrackConfig: on_custom_modified: {}".format(args)) if self._track_config: # Get point, use it to set custom start/finish track = Track() track.trackId = 0 track.startLine = self._start_line.point self._track_config.track = track self._track_config.stale = True self.dispatch('on_modified') def on_config_updated(self, rcp_config): self._track_config = rcp_config.trackConfig if rcp_config.trackConfig.track.trackId > 0 or ( rcp_config.trackConfig.track.startLine.latitude == 0 and rcp_config.trackConfig.track.startLine.latitude == 0): track = self._track_manager.find_track_by_short_id( rcp_config.trackConfig.track.trackId) if track is None: 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 + ')' self.ids.current_track.label_text = 'Track: ' + track_name elif rcp_config.trackConfig.track.trackId == 0: Logger.info("TempTrackConfig: custom start/finish: {}".format( rcp_config.trackConfig.track.startLine.toJson())) self._start_line.set_point(rcp_config.trackConfig.track.startLine) self.ids.custom_start_finish.setValue(True)
apn_map[name] = name spinner.setValueMap(apn_map, CellSettingsView.CUSTOM_APN) self.cell_provider_info = cell_provider_info except Exception as detail: Logger.error('CellSettingsView: Error loading cell providers ' + str(detail)) Builder.load_string(''' <CellularConfigView>: id: cellular cols: 1 spacing: [0, dp(20)] row_default_height: dp(40) size_hint: [1, None] height: self.minimum_height HSeparator: text: 'Cellular Configuration' CellSettingsView: id: cell_settings on_modified: root.modified() size_hint_y: None height: dp(300) ''') class CellularConfigView(GridLayout): def __init__(self, base_dir, rc_config, **kwargs): super(CellularConfigView, self).__init__(**kwargs) self.connectivityConfig = None