示例#1
0
 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())
示例#2
0
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)
示例#3
0
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
示例#4
0
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)
示例#5
0
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
示例#6
0
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
示例#7
0
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
示例#8
0
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
示例#9
0
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
示例#10
0
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)
示例#11
0
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
示例#12
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)
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
示例#14
0
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)
示例#15
0
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
示例#16
0
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)
示例#17
0
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'
示例#18
0
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('')
示例#19
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)
示例#20
0
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')
示例#21
0
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
示例#22
0
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')
示例#23
0
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
示例#24
0
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']
示例#25
0
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
示例#26
0
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
示例#27
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
示例#28
0
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)
示例#29
0
            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')
"""
)
示例#33
0
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)
''')
示例#36
0
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()
示例#37
0
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'
''')
示例#38
0
            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)
示例#39
0
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))
示例#40
0
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