Example #1
0
class SensorView(BaseView, UnitMixin, PropertyObject):
    __gtype_name__ = 'SensorView'

    gproperty('units-widget', object)
    gproperty('update-name', bool, False)

    def __init__(self,
                 pid,
                 index=0,
                 units='Metric',
                 active_widget=None,
                 name_widget=None,
                 value_widget=None,
                 units_widget=None,
                 helper=None):

        self.command = Sensor(pid, index)
        self.command.connect('data-changed', self._data_changed_cb)

        BaseView.__init__(self, active_widget, name_widget, value_widget,
                          helper)
        PropertyObject.__init__(self,
                                active_widget=active_widget,
                                name_widget=name_widget,
                                value_widget=value_widget,
                                units_widget=units_widget,
                                unit_standard=units,
                                helper=helper)

    def __post_init__(self):
        self.connect('notify::unit-standard', self._notify_unit_standard_cb)
        BaseView.__post_init__(self)

    def _notify_unit_standard_cb(self, o, pspec):
        self._update_view()

    def _do_sensitize_widgets(self):
        if self.units_widget:
            self.units_widget.set_sensitive(self.supported and self.active)

    def _do_update_view(self):
        if self.unit_standard == 'Imperial':
            value = self.command.imperial_value
            units = self.command.imperial_units
        else:
            value = self.command.metric_value
            units = self.command.metric_units
        if not units: units = ''
        if not value: value = ''

        if self.name_widget and self.update_name:
            self.name_widget.set_text(self.command.name)
        if self.value_widget:
            self.value_widget.set_text(value)
        if self.units_widget:
            self.units_widget.set_text(units)
Example #2
0
class SensorProgressView(BaseView, PropertyObject):
    __gtype_name__ = 'SensorProgressView'

    gproperty('progress-widget', object)
    gproperty('min-value', float)
    gproperty('max-value', float)
    gproperty('update-name', bool, False)

    def __init__(self,
                 pid,
                 index=0,
                 min_value=0,
                 max_value=100,
                 active_widget=None,
                 name_widget=None,
                 value_widget=None,
                 helper=None,
                 progress_widget=None):

        self.command = Sensor(pid, index)
        self.command.connect('data-changed', self._data_changed_cb)

        BaseView.__init__(self, active_widget, name_widget, value_widget,
                          helper)
        PropertyObject.__init__(self,
                                active_widget=active_widget,
                                name_widget=name_widget,
                                value_widget=value_widget,
                                helper=helper,
                                progress_widget=progress_widget,
                                min_value=min_value,
                                max_value=max_value)

    def _do_sensitize_widgets(self):
        if self.progress_widget:
            self.progress_widget.set_sensitive(self.supported and self.active)

    def _do_update_view(self):
        value = self.command.metric_value
        if not value:
            value = ''
            fraction = 0
        else:
            fraction = eval(value) / (self.max_value - self.min_value)
            if fraction > 1: fraction = 1
            if fraction < 0: fraction = 0

        if self.name_widget and self.update_name:
            self.name_widget.set_text(self.command.name)
        if self.value_widget:
            self.value_widget.set_text(value)
        if self.progress_widget:
            self.progress_widget.set_fraction(fraction)
Example #3
0
class MILWidget(gtk.Entry, StateMixin, PropertyObject):
    __gtype_name__ = 'MILWidget'
    gproperty('on', bool, False)
    gproperty('on-color', str, '#F7D30D')
    gproperty('off-color', str, '#AAAAAA')

    def __init__(self, app):
        gtk.Entry.__init__(self, 3)
        self.command = Sensor('0101', 1)
        PropertyObject.__init__(self)

        self._pref_cbs = []

        self.app = app
        self.set_text('MIL')
        self.set_property('editable', False)
        self.set_property('width-chars', 3)

    def __post_init__(self):
        self.on_color = self.app.prefs.get('mil.on-color')
        self.off_color = self.app.prefs.get('mil.off-color')
        cb_id = self.app.prefs.add_watch('mil.on-color', self._notify_prefs_cb)
        self._pref_cbs.append(('mil.on-color', cb_id))
        cb_id = self.app.prefs.add_watch('mil.off-color',
                                         self._notify_prefs_cb)
        self._pref_cbs.append(('mil.off-color', cb_id))

        self.connect('notify::on', self._notify_cb)
        self.connect('notify::on-color', self._notify_cb)
        self.connect('notify::off-color', self._notify_cb)
        self.notify('on')
        self.command.connect('data-changed', self._data_changed_cb)

    def _notify_cb(self, o, pspec):
        if self.on:
            self.modify_base(gtk.STATE_NORMAL,
                             gtk.gdk.color_parse(self.on_color))
        else:
            self.modify_base(gtk.STATE_NORMAL,
                             gtk.gdk.color_parse(self.off_color))

    def _notify_prefs_cb(self, pname, pvalue, args):
        if pname == 'mil.on-color':
            self.on_color = pvalue
        elif pname == 'mil.off-color':
            self.off_color = pvalue

    def _data_changed_cb(self, command, data):
        on = self.command.metric_value == 'On'
        self.on = on
Example #4
0
class UnitMixin(object):
    gproperty('unit-standard', str, 'Metric')

    def prop_set_unit_standard(self, standard):
        if not standard in ('Metric', 'Imperial'):
            raise ValueError, 'unit-standard should be either Metric or Imperial'
        return standard
Example #5
0
class Command(GObject, PropertyObject):
    __gtype_name__ = 'Command'

    gproperty('command', str, flags=gobject.PARAM_READABLE)
    gproperty('data', object)

    gsignal('data-changed', object)

    def __init__(self, command):
        GObject.__init__(self)
        PropertyObject.__init__(self, command=command)

    def __post_init__(self):
        self.connect('notify::data', self._notify_data_cb)

    def _notify_data_cb(self, o, pspec):
        self.emit('data-changed', self.data)

    def clear(self):
        self.data = None
Example #6
0
class DTCInfo(GObject, PropertyObject):

    gproperty('code', str)
    gproperty('code-class', str)
    gproperty('description', str)
    gproperty('additional', str)
    gproperty('lookup', str)

    def __init__(self, glade):
        GObject.__init__(self)
        PropertyObject.__init__(self)

        self._code_label = glade.get_widget('code_label')
        self._class_label = glade.get_widget('class_label')
        self._description_label = glade.get_widget('description_label')
        self._additional_textview = glade.get_widget('additional_textview')

        self._additional_buffer = gtk.TextBuffer()
        self._additional_textview.set_buffer(self._additional_buffer)
        self._dtc_entry = glade.get_widget('dtc_entry')

    def __post_init__(self):
        self.connect('notify::code', self._notify_cb)
        self.connect('notify::code-class', self._notify_cb)
        self.connect('notify::description', self._notify_cb)
        self.connect('notify::additional', self._notify_cb)
        self.connect('notify::lookup', self._notify_cb)

    def _notify_cb(self, o, pspec):
        if pspec.name == 'code':
            self._code_label.set_text(self.code)
        elif pspec.name == 'code-class':
            self._class_label.set_text(self.code_class)
        elif pspec.name == 'description':
            self._description_label.set_text(self.description)
        elif pspec.name == 'additional':
            self._additional_buffer.set_text(self.additional)
        elif pspec.name == 'lookup':
            self._dtc_entry.set_text(self.lookup)
Example #7
0
class SchedulerTimer(gtk.Label, PropertyObject):

    gproperty('active', bool, False)

    def __init__(self, scheduler):
        GObject.__init__(self)
        PropertyObject.__init__(self)

        self._rate = 0
        self._samples = []
        self.set_text(_('refresh rate: N/A'))

        scheduler.connect('command_executed',
                          self._scheduler_command_executed_cb)
        scheduler.connect('notify::working', self._scheduler_notify_working_cb)

    def _scheduler_notify_working_cb(self, scheduler, working):
        if not working:
            self.set_text(_('refresh rate: N/A'))

    def _scheduler_command_executed_cb(self, scheduler):
        now = datetime.datetime.now()
        if len(self._samples) == 0:
            self._samples.append((now, datetime.timedelta(0, 0, 0)))
        else:
            if len(self._samples) > 20:
                self._samples.pop(0)
            previous = self._samples[len(self._samples) - 1]
            delta = now - previous[0]
            self._samples.append((now, delta))

            total = 0.0
            count = 0
            for item in self._samples:
                count += 1
                u_seconds = item[1].seconds + item[1].microseconds / 1000000.0
                total += u_seconds

            rate = round(count / total, 1)

            self.set_text(_('refresh rate: %s Hz') % rate)
Example #8
0
class Gauge(gtk.EventBox, StateMixin, UnitMixin, PropertyObject):
    __gtype_name__ = "Gauge"

    def prop_get_value(self):
        return self._value

    # FIXME: min-angle and max-angle should be float,
    # but set_property does not accept negative values for gdouble or gint.
    # Warning: value "-50" of type `gint' is invalid or out of range for property `min-angle' of type `gint'
    gproperty('min-angle', object)
    gproperty('max-angle', object)
    gproperty('min-value', float)
    gproperty('max-value', float)
    gproperty('idle-value', float)
    gproperty('value', float, flags=gobject.PARAM_READABLE)
    gproperty('needle-color', str, '#FDC62D')
    gproperty('needle-length', int)
    gproperty('needle-width', int)
    gproperty('metric-overlay', object)
    gproperty('imperial-overlay', object)

    def __init__(self, pid, metric, imperial=None, index=0):
        if not imperial:
            imperial = metric
        gtk.EventBox.__init__(self)
        PropertyObject.__init__(self,
                                command=pid,
                                index=index,
                                metric_overlay=metric,
                                imperial_overlay=imperial)
        self.sensor = Sensor(pid, index)

        width = self.metric_overlay.get_width()
        height = self.metric_overlay.get_height()
        self.set_size_request(width, height)

        self._needle_gc = None

        self._set_default_values()

        self._value = self.idle_value
        self._pixmap = None
        self._area = gtk.DrawingArea()
        self.add(self._area)

        self.connect('button-press-event', self._button_press_cb)
        self._area.connect("expose_event", self._expose_event)
        self._area.connect("configure_event", self._configure_event)

    def __post_init__(self):
        self.connect('notify::needle-length', self._notify_must_redraw)
        self.connect('notify::metric-overlay', self._notify_must_redraw)
        self.connect('notify::imperial-overlay', self._notify_must_redraw)
        self.connect('notify::needle-color', self._notify_needle_cb)
        self.connect('notify::needle-width', self._notify_needle_cb)
        self.connect('notify::supported', self._notify_supported_cb)
        self.connect('notify::active', self._notify_active_cb)
        self.sensor.connect('data-changed', self._sensor_data_changed_cb)

    def _notify_must_redraw(self, o, pspec):
        self._draw()

    def _notify_needle_cb(self, o, pspec):
        if pspec.name == 'needle-color':
            if self._needle_gc:
                self._needle_gc.set_rgb_fg_color(
                    gtk.gdk.color_parse(self.needle_color))
        if pspec.name == 'needle-width':
            self._needle_gc.line_width = width
        self._draw()

    def _notify_supported_cb(self, o, pspec):
        self.set_sensitive(self.supported)
        if not self.supported:
            self.active = False

    def _notify_active_cb(self, o, pspec):
        self._area.set_sensitive(self.active)
        self._draw()
        self.emit('active-changed', self.active)

    def _button_press_cb(self, widget, event):
        if event.type == gdk.BUTTON_PRESS:
            self.active = not self.active

    def _sensor_data_changed_cb(self, sensor, data):
        self._value = eval(self.sensor.metric_value)
        self._draw()

    def _set_default_values(self):
        raise NotImplementedError, 'Use one of the subclasses please'

    def _construct_needle(self):
        angle_range = self.max_angle - self.min_angle
        value_range = self.max_value - self.min_value
        value = self._value
        if value < self.min_value:
            value = self.min_value
        if value > self.max_value:
            value = self.max_value
        angle = (value -
                 self.min_value) / value_range * angle_range + self.min_angle

        point_x = int(self._needle_origin_x + self.needle_length *
                      math.cos((angle + 180) * math.pi / 180))
        point_y = int(self._needle_origin_y + self.needle_length *
                      math.sin((angle + 180) * math.pi / 180))

        side1_x = int(self._needle_origin_x + self.needle_width *
                      math.cos((angle + 270) * math.pi / 180))
        side1_y = int(self._needle_origin_y + self.needle_width *
                      math.sin((angle + 270) * math.pi / 180))

        side2_x = int(self._needle_origin_x + self.needle_width *
                      math.cos((angle + 90) * math.pi / 180))
        side2_y = int(self._needle_origin_y + self.needle_width *
                      math.sin((angle + 90) * math.pi / 180))

        return [(self._needle_origin_x, self._needle_origin_y),
                (side1_x, side1_y), (point_x, point_y), (side2_x, side2_y)]

    def _draw(self):
        if self._pixmap is None:
            return
        x, y, width, height = self.get_allocation()
        bg_gc = self.get_style().bg_gc[gtk.STATE_NORMAL]
        self._pixmap.draw_rectangle(bg_gc, True, 0, 0, width, height)

        if self.unit_standard == 'Imperial':
            overlay = self.imperial_overlay
        else:
            overlay = self.metric_overlay
        self._pixmap.draw_pixbuf(gtk.gdk.GC(self.window), overlay, 0, 0, 0, 0)

        needle = self._construct_needle()
        if self.active:
            self._pixmap.draw_polygon(self._needle_gc, True, needle)

        fg_gc = self.get_style().fg_gc[gtk.STATE_NORMAL]
        fg_gc.set_foreground(gtk.gdk.color_parse('#000000'))
        self._pixmap.draw_arc(fg_gc, True,
                              self._needle_origin_x - self._circle_radius,
                              self._needle_origin_y - self._circle_radius,
                              self._circle_radius * 2, self._circle_radius * 2,
                              0, 360 * 64)

        self._area.queue_draw()

    def _expose_event(self, widget, event):
        x, y, width, height = event.area
        widget.window.draw_drawable(widget.get_style().bg_gc[gtk.STATE_NORMAL],
                                    self._pixmap, x, y, x, y, width, height)
        return False

    def _configure_event(self, widget, event):
        x, y, width, height = widget.get_allocation()
        self._pixmap = gtk.gdk.Pixmap(widget.window, width, height)
        self._needle_gc = gtk.gdk.GC(widget.window)
        self._needle_gc.set_rgb_fg_color(gtk.gdk.color_parse(
            self.needle_color))
        self._needle_gc.line_width = self.needle_width
        self.idle()

    def idle(self):
        """Set value to the defined idle value"""
        self._value = self.idle_value
        self._draw()
Example #9
0
class Sensor(Command, PropertyObject):
    __gtype_name__ = 'Sensor'

    gproperty('index', int, 0)
    gproperty('indices', int)
    gproperty('name', str, flags=gobject.PARAM_READABLE)
    gproperty('metric_value', str, flags=gobject.PARAM_READABLE)
    gproperty('metric_units', str, flags=gobject.PARAM_READABLE)
    gproperty('imperial_value', str, flags=gobject.PARAM_READABLE)
    gproperty('imperial_units', str, flags=gobject.PARAM_READABLE)

    def prop_set_index(self, index):
        if self._indices < index + 1:
            raise ValueError, 'index too high'
        else:
            return index

    def prop_get_indices(self):
        return self._num_values

    def prop_get_metric_value(self):
        if self.data:
            return self._decoder(self.data)[0]
        else:
            return None

    def prop_get_imperial_value(self):
        if self.data:
            return self._decoder(self.data)[1]
        else:
            return None

    def prop_get_metric_units(self):
        return self._metric_units

    def prop_get_imperial_units(self):
        return self._imperial_units

    def prop_get_name(self):
        return self._name

    def __init__(self, command, index=0, units='Metric'):
        self._indices = len(SENSORS[command[2:]])
        self._imperial_units = None
        self._metric_units = None
        self._decoder = None
        Command.__init__(self, command)
        PropertyObject.__init__(self, command=command, index=index)

    def __post_init__(self):
        Command.__post_init__(self)
        self.connect('notify::index', self._index_changed_cb)
        self._update_info()

    def _update_info(self):
        self._name = SENSORS[self.command[2:]][self.index][NAME]
        self._metric_units = SENSORS[self.command[2:]][self.index][METRIC]
        self._imperial_units = SENSORS[self.command[2:]][self.index][IMPERIAL]
        self._decoder = SENSORS[self.command[2:]][self.index][FUNC]

    def _index_changed_cb(self, o, pspec):
        self._clear_data()
        self._update_info()
Example #10
0
class StateMixin(object):

    gproperty('active', bool, False)
    gproperty('supported', bool, False)

    gsignal('active-changed', bool)
Example #11
0
class Scheduler(GObject, PropertyObject):
    """ This class receives OBDData objects, puts them in a queue
        and sends them to the OBDDevice
    """
    __gtype_name__ = "Scheduler"

    ################# Properties and signals ###############
    gsignal('command-executed')

    gproperty('working', bool, False)
    gproperty('obd-device', object)

    def prop_set_obd_device(self, device):
        if device and not isinstance(device, OBDDevice):
            raise TypeError, 'obd should be an instance of OBDDevice'
        return device

    def prop_set_working(self, working):
        if working:
            if self.obd_device and self.obd_device.connected:
                return working
            else:
                working = False
        return working

    def __init__(self, obd_device):
        """ @param obd_device: the OBDDevice to send commands
            @param timeout: the time between two commands
        """
        GObject.__init__(self)
        PropertyObject.__init__(self, obd_device=obd_device)
        self._queue = []
        self._os_queue = []

    def __post_init__(self):
        self.connect('notify::working', self._notify_working_cb)
        self.obd_device.connect('connected', self._obd_device_connected_cb)

    def _notify_working_cb(self, o, pspec):
        if self.working:
            self._execute_next_command()

    def _command_success_cb(self, cmd, result, args):
        if self.working:
            self.emit('command_executed')
            # We only care about the first result
            result = result[0]
            for item in cmd.list:
                item.data = result
            self._execute_next_command()

    def _command_error_cb(self, cmd, msg, args):
        debug('Scheduler._command_error_cb: command was: %s' % cmd)
        debug('Scheduler._command_error_cb: msg is %s' % msg)
        if self.working:
            self._execute_next_command()

    def _execute_next_command(self):
        if len(self._os_queue):
            queue_item = self._os_queue.pop(0)
        elif len(self._queue):
            queue_item = self._queue.pop(0)
            self._queue.append(queue_item)
        else:
            print 'nothing in queue'
            self.working = False
            return

        self.obd_device.read_command(queue_item, self._command_success_cb,
                                     self._command_error_cb)

    def _obd_device_connected_cb(self, obd_device, connected):
        if not connected:
            self.working = False

    ####################### Public Interface ###################

    def add(self, cmd, oneshot=False):
        """Add an item to the queue
           @param cmd: the item to add to the queue
           @param oneshot: wether the command should only be 
                           executed once.
        """
        if not isinstance(cmd, Command):
            raise ValueError, 'command should be an instance of Command'
        if oneshot:
            queue = self._os_queue
        else:
            queue = self._queue

        if cmd.command in queue:
            queue_item = queue[queue.index(cmd.command)]
        else:
            queue_item = QueueItem(cmd.command)
            queue.append(queue_item)
        queue_item.list.append(cmd)

    def remove(self, cmd):
        """Remove an item from the queue
           @param cmd: Command instance
        """
        if not isinstance(cmd, Command):
            raise ValueError, 'cmd should be an instance of Command'

        for queue in (self._queue, self._os_queue):
            for queue_item in queue:
                if queue_item == cmd.command:
                    if cmd in queue_item.list:
                        queue_item.list.remove(cmd)
                    if queue_item.list == []:
                        queue.remove(queue_item)
Example #12
0
class BaseView(GObject, StateMixin, PropertyObject):
    __gtype_name__ = 'BaseView'

    gproperty('name-widget', object)
    gproperty('active-widget', object)
    gproperty('value-widget', object)
    gproperty('helper', object)

    def __init__(self,
                 active_widget=None,
                 name_widget=None,
                 value_widget=None,
                 helper=None):

        GObject.__init__(self)
        PropertyObject.__init__(self,
                                active_widget=active_widget,
                                name_widget=name_widget,
                                value_widget=value_widget,
                                helper=helper)

        self._toggleable = False
        if active_widget:
            if isinstance(active_widget, gtk.ToggleButton):
                self._toggleable = True
                self.active = active_widget.get_active()
                active_widget.connect('toggled', self._active_toggled_cb)
            elif isinstance(active_widget, gtk.Button):
                self._togglable = False
                active_widget.connect('clicked', self._active_clicked_cb)
            else:
                raise ValueError, 'active_widget should be gtk.Button or gtk.ToggleButton'

    def __post_init__(self):
        self.connect('notify::supported', self._notify_supported_cb)
        self.connect('notify::active', self._notify_active_cb)
        self._update_view()
        self._sensitize_widgets()

    def _notify_active_cb(self, o, pspec):
        if self._toggleable:
            self.active_widget.set_active(self.active)
        self._sensitize_widgets()
        if not self.active:
            self.command.clear()
        self.emit('active-changed', self.active)

    def _active_toggled_cb(self, togglebutton):
        self.active = togglebutton.get_active()

    def _active_clicked_cb(self, button):
        self.active = not self.active

    def _notify_supported_cb(self, o, pspec):
        if not self.supported:
            self.active = False
        self._sensitize_widgets()

    def _sensitize_widgets(self):
        if self.active_widget:
            self.active_widget.set_sensitive(self.supported)
        for widget in (self.name_widget, self.value_widget):
            if widget:
                widget.set_sensitive(self.supported and self.active)
        self._do_sensitize_widgets()

    def _data_changed_cb(self, command, data):
        self._update_view()

    def _update_view(self):
        if self.helper and callable(self.helper):
            self.helper(self)
        else:
            self._do_update_view()