Exemple #1
0
    def setup_icon(self):
        """
        Create icon, create overlay, run the first icon update and start icon
        timer.
        
        """
        self.__icon = SensorIcon(self.applet.settings["theme"],
                                 self.main_sensors, self.applet.get_size(),
                                 self.applet.settings["hand_color"])

        # Create text overlay
        self.__temp_overlay = OverlayText()
        self.change_font_size(self.applet.settings["font_size"])
        self.__temp_overlay.props.active = self.applet.settings[
            "show_value_overlay"]
        self.applet.add_overlay(self.__temp_overlay)

        self.__old_values = None
        # Call the first update and start the updater
        self.update_icon()
        self.__icon_timer = self.applet.timing.register(
            self.update_icon, self.applet.settings["timeout"])
    def setup_icon(self):
        """
        Create icon, create overlay, run the first icon update and start icon
        timer.
        
        """
        self.__icon = SensorIcon(self.applet.settings["theme"],
                                 self.main_sensors,
                                 self.applet.get_size(),
                                 self.applet.settings["hand_color"])

        # Create text overlay
        self.__temp_overlay = OverlayText()
        self.change_font_size(self.applet.settings["font_size"])
        self.__temp_overlay.props.active = self.applet.settings["show_value_overlay"]
        self.applet.add_overlay(self.__temp_overlay)

        self.__old_values = None
        # Call the first update and start the updater
        self.update_icon()
        self.__icon_timer = self.applet.timing.register(
                                    self.update_icon, self.applet.settings["timeout"])
class SensorsApplet:
    """
    Applet to show the hardware sensors readouts.
    
    """

    def __init__(self, applet):
        """
        Initialize the entire applet.
        
        Create applet icon and main dialog, initialize sensors and load
        settings.
        
        """
        self.applet = applet

        # Init sensors
        no_sensors = not self.create_all_sensors()

        # If no sensors were found, display warning massage and icon, then exit
        if no_sensors:
            subject = _("Warning from Hardware Sensors: No sensors found")
            message = _("Install one or more of \
ACPI, HDDTemp, LM-Sensors, nvidia-settings or nvclock and restart the applet.")

            print subject + ".", message

            # Show massage with awn notify
            self.applet.notification.send(subject=subject, body=message, icon=applet_logo)
            # Show "no sensors found" icon
            self.applet.icon.file(no_sensors_icon, size=applet.get_size())
            self.applet.tooltip.set(message)
            return

        self.update_all_sensors()

        # Get a list of themes
        def is_dir(path):
            return os.path.isdir(os.path.join(theme_dir, path))

        self.__themes = filter(is_dir, os.listdir(theme_dir))
        self.__themes = [theme.replace("_", " ") for theme in self.__themes]
        self.__themes.sort()

        # == Settings == #
        # Load settings, setup rightclick menu and create settings dialog
        self.setup_preferences()

        # == Icon == #
        self.setup_icon()

        # Recreate upon awn height change
        self.applet.connect_size_changed(self.height_changed_cb)

        # == Dialog == #
        # Create main applet dialog showing selected sensors.
        main_dialog = self.applet.dialog.new("main", _("Sensors"))
        self.__main_dialog = self.MainDialog(self, main_dialog)


    # == Sensors == #
    def create_all_sensors(self):
        """
        Initialize sensors for all interfaces. Return False if no sensors are
        found.
        
        """
        self.sensors = []
        self.sensors += acpisensors.get_sensors()
        self.sensors += omnibooksensors.get_sensors()
        self.sensors += hddtempsensors.get_sensors()
        self.sensors += lmsensors.get_sensors()
        self.sensors += nvidiasensors.get_sensors()
        self.sensors += nvclocksensors.get_sensors()
        self.sensors += i8ksensors.get_sensors()

        # Check if any sensors were found
        if self.sensors == []:
            return False

        # Connect all the sensors to alarm callback function
        for sensor in self.sensors:
            sensor.connect_to_alarm(self.alarm_cb)

        return True

    def recreate_main_sensors(self):
        """
        Fill self.main_sensors with sensors that should be shown in the applet
        icon.
        
        """
        self.main_sensors = []
        for sensor in self.sensors:
            if sensor.in_icon:
                self.main_sensors.append(sensor)
        self.__icon.set_sensors(self.main_sensors)

    def update_all_sensors(self):
        """Update all of the sensor values."""
        for sensor in self.sensors:
            sensor.read_sensor()


    # == Applet icon == #
    def setup_icon(self):
        """
        Create icon, create overlay, run the first icon update and start icon
        timer.
        
        """
        self.__icon = SensorIcon(self.applet.settings["theme"],
                                 self.main_sensors,
                                 self.applet.get_size(),
                                 self.applet.settings["hand_color"])

        # Create text overlay
        self.__temp_overlay = OverlayText()
        self.change_font_size(self.applet.settings["font_size"])
        self.__temp_overlay.props.active = self.applet.settings["show_value_overlay"]
        self.applet.add_overlay(self.__temp_overlay)

        self.__old_values = None
        # Call the first update and start the updater
        self.update_icon()
        self.__icon_timer = self.applet.timing.register(
                                    self.update_icon, self.applet.settings["timeout"])

    def update_icon_type(self):
        """Updates icon type"""
        if len(self.main_sensors) is 1:
            self.__icon.type("single")
        else:
            self.__icon.type("double")
        self.change_font_size(self.applet.settings["font_size"])
        self.update_icon(True)

    def update_icon(self, force=False):
        """
        Update applet icon to show the updated values.
        
        """
        for main_sensor in self.main_sensors:
            main_sensor.read_sensor()
        values = [sensor.value for sensor in self.main_sensors]
        # Check if values have changed
        if values != self.__old_values or force:
            self.__old_values = values

            # Get updated icon
            context = self.__icon.get_icon()
            # Set updated icon
            self.applet.icon.set(context)

            # Update overlay
            if len(values) is 1:
                overlay_text = str(values[0])
            else:
                overlay_text = str(values[0]) + " " + str(values[1])

            self.__temp_overlay.props.text = overlay_text

            # Update title
            title = "   ".join(["%s: %d %s" % (s.label, s.value, s.unit_str)
                                                   for s in self.main_sensors])
            self.applet.tooltip.set(title)


    # == Settings == #
    def setup_preferences(self):
        """
        Load global and sensor settings and replace them with defaults if
        not set.
        
        """
        # Setup the rightclick context menu.
        self.__pref_dialog = self.applet.dialog.new("preferences")
        self.__pref_dialog.set_resizable(True)

        prefs = gtk.Builder()
        prefs.add_from_file(ui_file)
        prefs.get_object("notebook").reparent(self.__pref_dialog.vbox)

        # = General settings = #

        self.setup_general_preferences(prefs)

        # Bind settings to the gtk+ widgets that control them and callbacks
        # that should be called on setting change.
        binder = self.applet.settings.get_binder(prefs)
        binder.bind("unit", "combobox_unit", key_callback=self.change_unit)
        binder.bind("timeout", "spin_timeout", key_callback=self.change_timeout)
        binder.bind("theme", "combobox_theme", key_callback=self.change_theme)
        binder.bind("show_value_overlay", "checkbutton_show_value_overlay",
                                   key_callback=self.change_show_value_overlay)
        binder.bind("font_size", "combobox_font_size",
                                   key_callback=self.change_font_size)
        binder.bind("hand_color", "colorbutton_hand",
                                   key_callback=self.change_hand_color)
        self.applet.settings.load_bindings(binder)

        # Set the color of the hand color colorbutton
        cb_hand = prefs.get_object("colorbutton_hand")
        cb_hand.set_da_color(self.applet.settings["hand_color"])

        # = Sensor settings = #

        # Copy to local variable for easy and fast access
        sensors = self.sensors

        sensor_default_settings = {
            # Sensor settings
            "ids": [str(sensor.id) for sensor in sensors],
            "labels": [sensor.label for sensor in sensors],
            "show": [sensor.show for sensor in sensors],
            "dialog_row": range(1, len(sensors) + 1),
            "in_icon": [sensor.in_icon for sensor in sensors],
            "high_values": [sensor.high_value for sensor in sensors],
            "low_values": [sensor.low_value for sensor in sensors],
            "high_alarms": [sensor.alarm_on_high for sensor in sensors],
            "low_alarms": [sensor.alarm_on_low for sensor in sensors]
        }

        # Load settings and replace with defaults if not set.
        for key, value in sensor_default_settings.iteritems():
            if key not in self.applet.settings:
                self.applet.settings[key] = value

        # Copy to local variable for easy and fast access
        settings = self.applet.settings

        self.main_sensors = []
        new_sensors = False

        # Apply settings to sensors.
        for sensor in self.sensors:
            sensor.unit = settings["unit"]
            if str(sensor.id) in settings["ids"]:
                idx = settings["ids"].index(str(sensor.id))
                sensor.label = settings["labels"][idx]
                sensor.show = settings["show"][idx]
                sensor.dialog_row = settings["dialog_row"][idx]
                sensor.in_icon = settings["in_icon"][idx]
                sensor.raw_high_value = settings["high_values"][idx]
                sensor.raw_low_value = settings["low_values"][idx]
                sensor.alarm_on_high = settings["high_alarms"][idx]
                sensor.alarm_on_low = settings["low_alarms"][idx]
            else:
                new_sensors = True
            if sensor.in_icon:
                self.main_sensors.append(sensor)
            # Set timeout for updaters.
            if sensor.interface in [lmsensors.interface_name,
                                    nvidiasensors.interface_name]:
                sensor.updater.set_timeout(settings["timeout"])

        # If a sensor was lost, a new one found or if the order was changed
        if new_sensors or \
          len(sensors) != len(settings["ids"]) or \
          [str(sensor.id) for sensor in sensors] != settings["ids"]:

            # Sort sensors by dialog_row and renumber them in that order (to
            # eliminate any 'holes' in row order left by lost sensors and to
            # put the new sensors to the end).
            sorted_sensors = [sensor for sensor in sensors]
            sorted_sensors.sort(key=lambda s: s.dialog_row)
            # Renumber rows
            for row, sensor in enumerate(sorted_sensors):
                sensor.dialog_row = row + 1

            # Save all sensor settings.
            settings = self.applet.settings
            settings["ids"] = [str(sensor.id) for sensor in sensors]
            settings["labels"] = [sensor.label for sensor in sensors]
            settings["show"] = [sensor.show for sensor in sensors]
            settings["dialog_row"] = [sensor.dialog_row for sensor in sensors]
            settings["in_icon"] = [sensor.in_icon for sensor in sensors]
            settings["high_values"] = [s.high_value for s in sensors]
            settings["low_values"] = [s.low_value for s in sensors]
            settings["high_alarms"] = [s.alarm_on_high for s in sensors]
            settings["low_alarms"] = [s.alarm_on_low for s in sensors]

        # If none of the saved sensors has been selected as main, set default.
        if not self.main_sensors:
            # The default for the main sensor is the first sensor.
            sensors[0].in_icon = True
            self.main_sensors.append(sensors[0])
            self.applet.settings["in_icon"] = \
                                         [sensor.in_icon for sensor in sensors]

        if self.applet.settings["theme"] not in self.__themes:
            self.__icon = None
            self.applet.settings["theme"] = self.__themes[0]

        self.setup_sensor_preferences(prefs)

    def setup_general_preferences(self, prefs):
        """Setup the main settings window."""
        # Unit combobox
        unit_combobox = prefs.get_object("combobox_unit")
        awnlib.add_cell_renderer_text(unit_combobox)
        for i in units.UNIT_STR_LONG[:3]:
            unit_combobox.append_text(i)

        # Theme combobox
        theme_combobox = prefs.get_object("combobox_theme")
        awnlib.add_cell_renderer_text(theme_combobox)
        for theme in self.__themes:
            # Add filename with '_' replaced width space
            theme_combobox.append_text(theme)
        # If the set theme is not available, revert to default 
        if self.applet.settings["theme"] not in self.__themes:
            self.__icon = None
            self.applet.settings["theme"] = self.__themes[0]

        # Font size combobox
        font_combobox = prefs.get_object("combobox_font_size")
        awnlib.add_cell_renderer_text(font_combobox)
        for font_size in font_size_names:
            font_combobox.append_text(font_size)

        # Font size combobox should be grayed out when value overlay is
        # disabled
        show_checkbutton = prefs.get_object("checkbutton_show_value_overlay")
        fontsize_hbox = prefs.get_object("hbox_font_size")
        fontsize_hbox.set_sensitive(show_checkbutton.get_active())
        show_checkbutton.connect("toggled", lambda w:
                                   fontsize_hbox.set_sensitive(w.get_active()))

    def setup_sensor_preferences(self, prefs):
        """Setup the sensor settings tab part of window."""
        # All sensors treeview
        treeview_all = prefs.get_object("treeview_sensors")
        # Main sensors treeview
        treeview_main = prefs.get_object("treeview_main_sensors")

        self.setup_sensors_treeview(treeview_all)
        self.setup_main_sensors_treeview(treeview_main)

        # Properties button
        button_properties = prefs.get_object("button_properties")
        button_properties.connect('clicked', self.properties_cb, treeview_all)

        # Add button
        button_add = prefs.get_object("button_add")
        button_add.connect('clicked', self.add_cb, treeview_all)

        # Remove button
        button_remove = prefs.get_object("button_remove")
        button_remove.connect('clicked', self.remove_cb, treeview_main)

        # Up button
        button_up = prefs.get_object("button_up")
        button_up.connect('clicked', self.up_cb, treeview_all)

        # Down button
        button_down = prefs.get_object("button_down")
        button_down.connect('clicked', self.down_cb, treeview_all)

    def setup_sensors_treeview(self, treeview):
        """Create treeview with list of all sensors and their settings."""
        self.__liststore = gtk.ListStore(
                            int, int, str, str, str, 'gboolean', 'gboolean')
        self.__column_idx = 0
        self.__column_row = 1
        self.__column_interface = 2
        self.__column_name = 3
        self.__column_label = 4
        self.__column_show = 5
        self.__column_in_icon = 6

        # Fill liststore with data
        for idx, sensor in enumerate(self.sensors):
            # Add a row to liststore
            last = self.__liststore.append([idx,
                                          sensor.dialog_row,
                                          sensor.interface,
                                          sensor.name,
                                          sensor.label,
                                          sensor.show,
                                          sensor.in_icon])

        # Set TreeView's liststore
        treeview.set_model(self.__liststore)

        # Create a CellRendererText to render the data
        cell_row = gtk.CellRendererText()
        cell_interface = gtk.CellRendererText()
        cell_name = gtk.CellRendererText()
        cell_label = gtk.CellRendererText()
        cell_show = gtk.CellRendererToggle()

        # Set labels to be editable
        cell_label.set_property('editable', True)
        # Make toggle buttons in the show column activatable
        cell_show.set_property('activatable', True)

        # Connect the edited event
        cell_label.connect('edited', self.label_edited_cb)
        # Connect the toggle event
        cell_show.connect('toggled', self.show_toggled_cb)

        # Create the TreeViewColumns to display the data, add the cell renderer
        # and set the cell "text" attribute to correct column (treeview
        # retrieves text from that column in liststore)
        tvcolumn_row = gtk.TreeViewColumn("#", cell_row,
                                          text=self.__column_row,
                                          visible=self.__column_show)
        tvcolumn_interface = gtk.TreeViewColumn(_("Interface"), cell_interface,
                                                  text=self.__column_interface)
        tvcolumn_name = gtk.TreeViewColumn(_("Name"), cell_name,
                                                       text=self.__column_name)
        tvcolumn_label = gtk.TreeViewColumn(_("Label"), cell_label,
                                                      text=self.__column_label)
        # Set the cell "active" attribute to column_show - retrieve the state
        # of the toggle button from that column in liststore
        tvcolumn_show = gtk.TreeViewColumn(_("In dialog"), cell_show,
                                                     active=self.__column_show)

        # Add treeview columns to treeview
        treeview.append_column(tvcolumn_row)
        treeview.append_column(tvcolumn_interface)
        treeview.append_column(tvcolumn_name)
        treeview.append_column(tvcolumn_label)
        treeview.append_column(tvcolumn_show)

        # Make name and label searchable
        treeview.set_search_column(self.__column_name)
        treeview.set_search_column(self.__column_label)

        # Allow sorting on the column
        tvcolumn_row.set_sort_column_id(self.__column_row)
        tvcolumn_interface.set_sort_column_id(self.__column_interface)
        tvcolumn_name.set_sort_column_id(self.__column_name)
        tvcolumn_label.set_sort_column_id(self.__column_label)
        tvcolumn_show.set_sort_column_id(self.__column_show)

        # Sort by dialog_row
        self.__liststore.set_sort_column_id(self.__column_row,
                                          gtk.SORT_ASCENDING)

    def setup_main_sensors_treeview(self, treeview):
        """Create treeview with list of sensors to be shown in applet icon."""
        # List store for main sensors
        model_filter = self.__liststore.filter_new()
        model_filter.set_visible_column(self.__column_in_icon)

        # Set main sensor treeview's liststore
        treeview.set_model(model_filter)

        # Create a CellRendererText to render the data
        cell_label = gtk.CellRendererText()

        # Add treeview-column to treeview
        tvcolumn_label = gtk.TreeViewColumn(_("Sensors displayed in icon"),
                                          cell_label, text=self.__column_label)
        treeview.append_column(tvcolumn_label)

    def create_properties_dialog(self, sensor):
        """
        Create a dialog with individual sensors settings.
        
        Create a dialog with settings for high and low values and alarms for
        the given sensor
        @param prefs: opened glade XML file
        @param sensor: the sensor for which this settings dialog is for
        
        """
        prefs = gtk.Builder()
        prefs.add_from_file(ui_file)

        prop_dialog = prefs.get_object("sensor_properties_dialog")
        prop_dialog.set_title(sensor.label + " - " + _("properties"))
        prop_dialog.set_icon(self.__pref_dialog.get_icon())
        # Properties window should be on top of settings window
        prop_dialog.set_transient_for(self.__pref_dialog)

        # Get sensor value's type
        value_type = sensor.type

        # Adjustment for sensor high value spin button
        spin_button = prefs.get_object("spin_high_value")
        if value_type is TempValue:
            if sensor.unit == units.UNIT_CELSIUS:
                adj = gtk.Adjustment(sensor.high_value, -273, 200, 1, 10)
            elif sensor.unit == units.UNIT_FAHRENHEIT:
                adj = gtk.Adjustment(sensor.high_value, -460, 392, 1, 10)
            elif sensor.unit == units.UNIT_KELVIN:
                adj = gtk.Adjustment(sensor.high_value, 0, 473, 1, 10)
        elif value_type is RPMValue:
            adj = gtk.Adjustment(sensor.high_value, 0, 20000, 100, 1000)
        elif value_type is VoltValue:
            adj = gtk.Adjustment(sensor.high_value, -20, 20, 0.05, 1)
            spin_button.set_digits(2)

        # Set adjustment
        spin_button.set_adjustment(adj)
        spin_button.set_value(sensor.high_value)

        adj.connect('value-changed', lambda adjustment:
                        self.change_high_value(sensor, adjustment.get_value()))

        # Adjustment for sensor low value spin button
        spin_button = prefs.get_object("spin_low_value")
        if value_type is TempValue:
            if sensor.unit == units.UNIT_CELSIUS:
                adj = gtk.Adjustment(sensor.low_value, -273, 200, 1, 10)
            elif sensor.unit == units.UNIT_FAHRENHEIT:
                adj = gtk.Adjustment(sensor.low_value, -460, 392, 1, 10)
            elif sensor.unit == units.UNIT_KELVIN:
                adj = gtk.Adjustment(sensor.low_value, 0, 473, 1, 10)
        elif value_type is RPMValue:
            adj = gtk.Adjustment(sensor.low_value, 0, 20000, 100, 1000)
        elif value_type is VoltValue:
            adj = gtk.Adjustment(sensor.low_value, -20, 20, 0.05, 1)
            spin_button.set_digits(2)

        # Set adjustment
        spin_button.set_adjustment(adj)
        spin_button.set_value(sensor.low_value)

        adj.connect('value-changed', lambda adjustment:
                        self.change_low_value(sensor, adjustment.get_value()))

        # "Enable high alarm" CheckButton
        alarm_cbutton = prefs.get_object("check_high_alarm")
        alarm_cbutton.set_active(sensor.alarm_on_high)
        alarm_cbutton.connect('toggled', lambda w:
                                                self.toggle_high_alarm(sensor))

        # "Enable low alarm" CheckButton
        alarm_cbutton = prefs.get_object("check_low_alarm")
        alarm_cbutton.set_active(sensor.alarm_on_low)
        alarm_cbutton.connect('toggled', lambda w:
                                                 self.toggle_low_alarm(sensor))

        # Close button
        close_button = prefs.get_object("close_properties")
        close_button.connect('clicked', lambda w: prop_dialog.destroy())

        prop_dialog.show_all()


    # === Event handlers === #
    def alarm_cb(self, sensor, message):
        """Show alarm message with awn notify."""
        self.applet.notification.send(subject=None, body=message, icon=applet_logo)

    def height_changed_cb(self):
        """Update the applet's icon to reflect the new height."""
        self.__icon.set_height(self.applet.get_size())
        # Force icon update
        self.update_icon(True)

    # === Change setting methods === #
    def change_unit(self, unit):
        """Change unit for all sensors and update icon."""
        for sensor in self.sensors:
            sensor.unit = unit
        self.update_icon(True)

    def change_font_size(self, size_idx):
        """Change font size for overlay."""
        if len(self.main_sensors) is 1:
            self.__temp_overlay.props.font_sizing = single_font_sizes[size_idx]
            self.__temp_overlay.props.y_override = \
                                          30 + size_idx if size_idx < 2 else 28
        else:
            self.__temp_overlay.props.font_sizing = double_font_sizes[size_idx]
            self.__temp_overlay.props.y_override = \
                                          30 + size_idx if size_idx < 2 else 29

    def change_theme(self, theme):
        """Save theme setting and update icon."""
        if self.__icon is None:
            return
        self.__icon.theme(theme)
        # Force icon change
        self.update_icon(True)

    def change_show_value_overlay(self, show_value_overlay):
        """Change whether to show the valu in applet icon"""
        self.__temp_overlay.props.active = show_value_overlay

    def change_timeout(self, timeout):
        """Save timeout setting and change timer to new timeout."""
        self.update_icon(True)
        self.__icon_timer.change_interval(timeout)
        self.__main_dialog.change_interval(timeout)
        for sensor in self.sensors:
            # Set timeout for updaters
            if sensor.interface in [lmsensors.interface_name,
                                    nvidiasensors.interface_name]:
                sensor.updater.set_timeout(timeout)

    def change_high_value(self, sensor, value):
        """Save high value setting for a specific sensor and update icon."""
        # Apply high value change
        sensor.high_value = value
        # Save high values
        self.applet.settings["high_values"] = \
                                       [s.raw_high_value for s in self.sensors]
        if sensor in self.main_sensors:
            # Force icon update
            self.update_icon(True)

    def change_low_value(self, sensor, value):
        """Save low value setting for a specific sensor and update icon."""
        # Apply low value change
        sensor.low_value = value
        # Save low values
        self.applet.settings["low_values"] = \
                                        [s.raw_low_value for s in self.sensors]
        if sensor in self.main_sensors:
            # Force icon update
            self.update_icon(True)

    def change_in_icon(self, sensor, in_icon):
        """Save in_icon setting for a specific sensor and update icon."""
        # Apply in_icon value change
        sensor.in_icon = in_icon
        # Save in_icon values
        self.applet.settings["in_icon"] = \
                                   [sensor.in_icon for sensor in self.sensors]
        self.recreate_main_sensors()
        # Force icon update
        self.update_icon(True)

    def change_hand_color(self, color):
        """Apply hand color setting and update icon."""
        self.__icon.set_hand_color(color)
        # Force icon update
        self.update_icon(True)

    def update_dialog_rows(self):
        """
        Save dialog row setting and update main dialog.
        
        """
        # Save dialog_row values
        self.applet.settings["dialog_row"] = \
                                [sensor.dialog_row for sensor in self.sensors]
        # Recreate dialog
        self.__main_dialog.recreate()

    def toggle_high_alarm(self, sensor):
        """Toggle high alarm for a specific sensor and save it."""
        sensor.toggle_alarm_on_high()
        self.applet.settings["high_alarms"] = \
                             [sensor.alarm_on_high for sensor in self.sensors]

    def toggle_low_alarm(self, sensor):
        """Toggle low alarm for a specific sensor and save it."""
        sensor.toggle_alarm_on_low()
        self.applet.settings["low_alarms"] = \
                              [sensor.alarm_on_low for sensor in self.sensors]


    # === Treeview callbacks === #
    def label_edited_cb(self, cell_renderer, path, new_text):
        """
        Change specific sensor's label, save it and update dialog and icon.
        
        """
        # Change label value in liststore
        self.__liststore[path][self.__column_label] = new_text
        sensor = self.sensors[self.__liststore[path][self.__column_idx]]
        # Apply change to sensor
        sensor.label = new_text
        # Save labels
        self.applet.settings["labels"] = \
                                    [sensor.label for sensor in self.sensors]
        # Recreate dialog
        self.__main_dialog.recreate()

    def show_toggled_cb(self, cell_renderer, path):
        """
        Toggle specific sensor's "show in dialog" properity, save it and update
        dialog.
        
        """
        store = self.__liststore
        sensor = self.sensors[store[path][self.__column_idx]]

        # Toggle value in liststore
        store[path][self.__column_show] = not store[path][self.__column_show]
        # Apply change to sensor
        sensor.show = store[path][self.__column_show]
        # Save show property
        self.applet.settings["show"] = [sensor.show for sensor in self.sensors]
        # Recreate dialog
        self.__main_dialog.recreate()

    # === Button callbacks === #
    def properties_cb(self, widget, treeview):
        """Open properties dialog for selected sensor."""
        # Get selected sensor
        treeselection = treeview.get_selection()
        (model, iter) = treeselection.get_selected()
        # Something must be selected
        if iter is not None:
            # Sensor index
            idx = model[iter][self.__column_idx]
            self.create_properties_dialog(self.sensors[idx])

    def add_cb(self, widget, treeview_all):
        """
        Add selected sensors to main sensors (i.e. shown them in applet icon).
        
        """
        # Get selected sensor
        selection = treeview_all.get_selection()
        (model, iter) = selection.get_selected()
        # Something must be selected
        if iter is not None:
            # Sensor index
            idx = self.__liststore[iter][self.__column_idx]
            # Apply setting
            self.change_in_icon(self.sensors[idx], True)
            # Change value in model
            model[iter][self.__column_in_icon] = True
        self.update_icon_type()

    def remove_cb(self, widget, treeview_main):
        """
        Remove selected sensors from main sensors (i.e. do not shown them in
        applet icon).
        
        """
        # Get selected sensor
        selection = treeview_main.get_selection()
        (model_filter, iter) = selection.get_selected()
        # Something must be selected and at least one sensor must remain
        if iter is not None and len(treeview_main.get_model()) > 1:
            child_iter = model_filter.convert_iter_to_child_iter(iter)
            # Sensor index
            idx = self.__liststore[child_iter][self.__column_idx]
            # Apply setting
            self.change_in_icon(self.sensors[idx], False)
            # Change value in model
            self.__liststore[child_iter][self.__column_in_icon] = False
        self.update_icon_type()

    def up_cb(self, widget, treeview):
        """
        Move selected sensors up in main applet dialog.
        
        """
        # Get selected sensor
        selection = treeview.get_selection()
        (model, iter) = selection.get_selected()

        # Something must be selected
        if iter is not None:
            dialog_row = model[iter][self.__column_row]
            if dialog_row > 1:
                # Iterator pointing to predecessor (sensor/row that has
                # dialog_row one less than this one)
                row_pred = None
                for row in model:
                    if row[self.__column_row] == dialog_row - 1:
                        row_pred = row
                        break
                if row_pred is not None:
                    # Switch model_row-s
                    model[iter][self.__column_row] = dialog_row - 1
                    row_pred[self.__column_row] = dialog_row

                    # Apply setting
                    sensor = self.sensors[model[iter][self.__column_idx]]
                    sensor_pred = self.sensors[row_pred[self.__column_idx]]
                    sensor.dialog_row = dialog_row - 1
                    sensor_pred.dialog_row = dialog_row
                    self.update_dialog_rows()

    def down_cb(self, widget, treeview):
        """
        Move selected sensors down in the main applet dialog.
        
        """
        # Get selected sensor
        selection = treeview.get_selection()
        (model, iter) = selection.get_selected()
        # Something must be selected
        if iter is not None:
            dialog_row = model[iter][self.__column_row]
            if dialog_row < len(self.sensors):
                # Iter pointing to predecessor (sensor/row that has dialog_row
                # one less than this one)
                row_pred = None
                for row in model:
                    if row[self.__column_row] == dialog_row + 1:
                        row_pred = row
                        break
                if row_pred is not None:
                # Switch model_row-s
                    model[iter][self.__column_row] = dialog_row + 1
                    row_pred[self.__column_row] = dialog_row

                    # Apply setting
                    sensor = self.sensors[model[iter][self.__column_idx]]
                    sensor_pred = self.sensors[row_pred[self.__column_idx]]
                    sensor.dialog_row = dialog_row + 1
                    sensor_pred.dialog_row = dialog_row
                    self.update_dialog_rows()

    class MainDialog:

        def __init__(self, parent, main_dialog):

            self.__parent = parent
            self.__dialog = main_dialog

            self.create_content()

            # Create a timer, but do not start it
            self.__timer = parent.applet.timing.register(
                         self.update_values, parent.applet.settings["timeout"], False)

            self.__dialog.connect('show', self.dialog_shown_cb)
            self.__dialog.connect('hide', self.dialog_hidden_cb)

        def create_content(self):
            """
            (Re)creates sensor labels and values for main applet dialog
            
            """
            # List of sensors to be shown in dialog
            shown_sensors = dict()
            for sensor in self.__parent.sensors:
                if sensor.show:
                    shown_sensors[sensor.dialog_row] = sensor

            rows = shown_sensors.keys()
            rows.sort()

            # List of gtkLabel-s that contain the sensor value
            self.__value_labels = dict()

            new_vbox = gtk.VBox(False, 10)

            for row in rows:
                sensor = shown_sensors[row]
                hbox = gtk.HBox()
                label = gtk.Label(sensor.label + ':\t')
                self.__value_labels[sensor] = gtk.Label(
                                     str(sensor.value) + ' ' + sensor.unit_str)
                hbox.pack_start(label, False, False)
                hbox.pack_end(self.__value_labels[sensor], False, False)
                new_vbox.pack_start(hbox, False, False)

            new_vbox.show_all()
            self.__dialog.add(new_vbox)

        def recreate(self):
            # Destroy current dialog vbox
            self.__dialog.child.child.get_children()[-1].destroy()
            # Create new content
            self.create_content()

        def update_values(self):
            """Update main applet dialog with new values."""
            for sensor, label in self.__value_labels.iteritems():
                sensor.read_sensor()
                label.set_text(str(sensor.value) + ' ' + sensor.unit_str)

        def change_interval(self, timeout):
            self.__timer.change_interval(timeout)

        def dialog_shown_cb(self, dialog):
            """Start updating sensor values in main applet dialog."""
            self.__timer.start()
            # Force update
            self.update_values()

        def dialog_hidden_cb(self, dialog):
            """Stop update timer for main applet dialog."""
            self.__timer.stop()
Exemple #4
0
class SensorsApplet:
    """
    Applet to show the hardware sensors readouts.
    
    """
    def __init__(self, applet):
        """
        Initialize the entire applet.
        
        Create applet icon and main dialog, initialize sensors and load
        settings.
        
        """
        self.applet = applet

        # Init sensors
        no_sensors = not self.create_all_sensors()

        # If no sensors were found, display warning massage and icon, then exit
        if no_sensors:
            subject = _("Warning from Hardware Sensors: No sensors found")
            message = _("Install one or more of \
ACPI, HDDTemp, LM-Sensors, nvidia-settings or nvclock and restart the applet.")

            print subject + ".", message

            # Show massage with awn notify
            self.applet.notification.send(subject=subject,
                                          body=message,
                                          icon=applet_logo)
            # Show "no sensors found" icon
            self.applet.icon.file(no_sensors_icon, size=applet.get_size())
            self.applet.tooltip.set(message)
            return

        self.update_all_sensors()

        # Get a list of themes
        def is_dir(path):
            return os.path.isdir(os.path.join(theme_dir, path))

        self.__themes = filter(is_dir, os.listdir(theme_dir))
        self.__themes = [theme.replace("_", " ") for theme in self.__themes]
        self.__themes.sort()

        # == Settings == #
        # Load settings, setup rightclick menu and create settings dialog
        self.setup_preferences()

        # == Icon == #
        self.setup_icon()

        # Recreate upon awn height change
        self.applet.connect_size_changed(self.height_changed_cb)

        # == Dialog == #
        # Create main applet dialog showing selected sensors.
        main_dialog = self.applet.dialog.new("main", _("Sensors"))
        self.__main_dialog = self.MainDialog(self, main_dialog)

    # == Sensors == #
    def create_all_sensors(self):
        """
        Initialize sensors for all interfaces. Return False if no sensors are
        found.
        
        """
        self.sensors = []
        self.sensors += acpisensors.get_sensors()
        self.sensors += omnibooksensors.get_sensors()
        self.sensors += hddtempsensors.get_sensors()
        self.sensors += lmsensors.get_sensors()
        self.sensors += nvidiasensors.get_sensors()
        self.sensors += nvclocksensors.get_sensors()
        self.sensors += i8ksensors.get_sensors()

        # Check if any sensors were found
        if self.sensors == []:
            return False

        # Connect all the sensors to alarm callback function
        for sensor in self.sensors:
            sensor.connect_to_alarm(self.alarm_cb)

        return True

    def recreate_main_sensors(self):
        """
        Fill self.main_sensors with sensors that should be shown in the applet
        icon.
        
        """
        self.main_sensors = []
        for sensor in self.sensors:
            if sensor.in_icon:
                self.main_sensors.append(sensor)
        self.__icon.set_sensors(self.main_sensors)

    def update_all_sensors(self):
        """Update all of the sensor values."""
        for sensor in self.sensors:
            sensor.read_sensor()

    # == Applet icon == #
    def setup_icon(self):
        """
        Create icon, create overlay, run the first icon update and start icon
        timer.
        
        """
        self.__icon = SensorIcon(self.applet.settings["theme"],
                                 self.main_sensors, self.applet.get_size(),
                                 self.applet.settings["hand_color"])

        # Create text overlay
        self.__temp_overlay = OverlayText()
        self.change_font_size(self.applet.settings["font_size"])
        self.__temp_overlay.props.active = self.applet.settings[
            "show_value_overlay"]
        self.applet.add_overlay(self.__temp_overlay)

        self.__old_values = None
        # Call the first update and start the updater
        self.update_icon()
        self.__icon_timer = self.applet.timing.register(
            self.update_icon, self.applet.settings["timeout"])

    def update_icon_type(self):
        """Updates icon type"""
        if len(self.main_sensors) is 1:
            self.__icon.type("single")
        else:
            self.__icon.type("double")
        self.change_font_size(self.applet.settings["font_size"])
        self.update_icon(True)

    def update_icon(self, force=False):
        """
        Update applet icon to show the updated values.
        
        """
        for main_sensor in self.main_sensors:
            main_sensor.read_sensor()
        values = [sensor.value for sensor in self.main_sensors]
        # Check if values have changed
        if values != self.__old_values or force:
            self.__old_values = values

            # Get updated icon
            context = self.__icon.get_icon()
            # Set updated icon
            self.applet.icon.set(context)

            # Update overlay
            if len(values) is 1:
                overlay_text = str(values[0])
            else:
                overlay_text = str(values[0]) + " " + str(values[1])

            self.__temp_overlay.props.text = overlay_text

            # Update title
            title = "   ".join([
                "%s: %d %s" % (s.label, s.value, s.unit_str)
                for s in self.main_sensors
            ])
            self.applet.tooltip.set(title)

    # == Settings == #
    def setup_preferences(self):
        """
        Load global and sensor settings and replace them with defaults if
        not set.
        
        """
        # Setup the rightclick context menu.
        self.__pref_dialog = self.applet.dialog.new("preferences")
        self.__pref_dialog.set_resizable(True)

        prefs = gtk.Builder()
        prefs.add_from_file(ui_file)
        prefs.get_object("notebook").reparent(self.__pref_dialog.vbox)

        # = General settings = #

        self.setup_general_preferences(prefs)

        # Bind settings to the gtk+ widgets that control them and callbacks
        # that should be called on setting change.
        binder = self.applet.settings.get_binder(prefs)
        binder.bind("unit", "combobox_unit", key_callback=self.change_unit)
        binder.bind("timeout",
                    "spin_timeout",
                    key_callback=self.change_timeout)
        binder.bind("theme", "combobox_theme", key_callback=self.change_theme)
        binder.bind("show_value_overlay",
                    "checkbutton_show_value_overlay",
                    key_callback=self.change_show_value_overlay)
        binder.bind("font_size",
                    "combobox_font_size",
                    key_callback=self.change_font_size)
        binder.bind("hand_color",
                    "colorbutton_hand",
                    key_callback=self.change_hand_color)
        self.applet.settings.load_bindings(binder)

        # Set the color of the hand color colorbutton
        cb_hand = prefs.get_object("colorbutton_hand")
        cb_hand.set_da_color(self.applet.settings["hand_color"])

        # = Sensor settings = #

        # Copy to local variable for easy and fast access
        sensors = self.sensors

        sensor_default_settings = {
            # Sensor settings
            "ids": [str(sensor.id) for sensor in sensors],
            "labels": [sensor.label for sensor in sensors],
            "show": [sensor.show for sensor in sensors],
            "dialog_row": range(1,
                                len(sensors) + 1),
            "in_icon": [sensor.in_icon for sensor in sensors],
            "high_values": [sensor.high_value for sensor in sensors],
            "low_values": [sensor.low_value for sensor in sensors],
            "high_alarms": [sensor.alarm_on_high for sensor in sensors],
            "low_alarms": [sensor.alarm_on_low for sensor in sensors]
        }

        # Load settings and replace with defaults if not set.
        for key, value in sensor_default_settings.iteritems():
            if key not in self.applet.settings:
                self.applet.settings[key] = value

        # Copy to local variable for easy and fast access
        settings = self.applet.settings

        self.main_sensors = []
        new_sensors = False

        # Apply settings to sensors.
        for sensor in self.sensors:
            sensor.unit = settings["unit"]
            if str(sensor.id) in settings["ids"]:
                idx = settings["ids"].index(str(sensor.id))
                sensor.label = settings["labels"][idx]
                sensor.show = settings["show"][idx]
                sensor.dialog_row = settings["dialog_row"][idx]
                sensor.in_icon = settings["in_icon"][idx]
                sensor.raw_high_value = settings["high_values"][idx]
                sensor.raw_low_value = settings["low_values"][idx]
                sensor.alarm_on_high = settings["high_alarms"][idx]
                sensor.alarm_on_low = settings["low_alarms"][idx]
            else:
                new_sensors = True
            if sensor.in_icon:
                self.main_sensors.append(sensor)
            # Set timeout for updaters.
            if sensor.interface in [
                    lmsensors.interface_name, nvidiasensors.interface_name
            ]:
                sensor.updater.set_timeout(settings["timeout"])

        # If a sensor was lost, a new one found or if the order was changed
        if new_sensors or \
          len(sensors) != len(settings["ids"]) or \
          [str(sensor.id) for sensor in sensors] != settings["ids"]:

            # Sort sensors by dialog_row and renumber them in that order (to
            # eliminate any 'holes' in row order left by lost sensors and to
            # put the new sensors to the end).
            sorted_sensors = [sensor for sensor in sensors]
            sorted_sensors.sort(key=lambda s: s.dialog_row)
            # Renumber rows
            for row, sensor in enumerate(sorted_sensors):
                sensor.dialog_row = row + 1

            # Save all sensor settings.
            settings = self.applet.settings
            settings["ids"] = [str(sensor.id) for sensor in sensors]
            settings["labels"] = [sensor.label for sensor in sensors]
            settings["show"] = [sensor.show for sensor in sensors]
            settings["dialog_row"] = [sensor.dialog_row for sensor in sensors]
            settings["in_icon"] = [sensor.in_icon for sensor in sensors]
            settings["high_values"] = [s.high_value for s in sensors]
            settings["low_values"] = [s.low_value for s in sensors]
            settings["high_alarms"] = [s.alarm_on_high for s in sensors]
            settings["low_alarms"] = [s.alarm_on_low for s in sensors]

        # If none of the saved sensors has been selected as main, set default.
        if not self.main_sensors:
            # The default for the main sensor is the first sensor.
            sensors[0].in_icon = True
            self.main_sensors.append(sensors[0])
            self.applet.settings["in_icon"] = \
                                         [sensor.in_icon for sensor in sensors]

        if self.applet.settings["theme"] not in self.__themes:
            self.__icon = None
            self.applet.settings["theme"] = self.__themes[0]

        self.setup_sensor_preferences(prefs)

    def setup_general_preferences(self, prefs):
        """Setup the main settings window."""
        # Unit combobox
        unit_combobox = prefs.get_object("combobox_unit")
        awnlib.add_cell_renderer_text(unit_combobox)
        for i in units.UNIT_STR_LONG[:3]:
            unit_combobox.append_text(i)

        # Theme combobox
        theme_combobox = prefs.get_object("combobox_theme")
        awnlib.add_cell_renderer_text(theme_combobox)
        for theme in self.__themes:
            # Add filename with '_' replaced width space
            theme_combobox.append_text(theme)
        # If the set theme is not available, revert to default
        if self.applet.settings["theme"] not in self.__themes:
            self.__icon = None
            self.applet.settings["theme"] = self.__themes[0]

        # Font size combobox
        font_combobox = prefs.get_object("combobox_font_size")
        awnlib.add_cell_renderer_text(font_combobox)
        for font_size in font_size_names:
            font_combobox.append_text(font_size)

        # Font size combobox should be grayed out when value overlay is
        # disabled
        show_checkbutton = prefs.get_object("checkbutton_show_value_overlay")
        fontsize_hbox = prefs.get_object("hbox_font_size")
        fontsize_hbox.set_sensitive(show_checkbutton.get_active())
        show_checkbutton.connect(
            "toggled", lambda w: fontsize_hbox.set_sensitive(w.get_active()))

    def setup_sensor_preferences(self, prefs):
        """Setup the sensor settings tab part of window."""
        # All sensors treeview
        treeview_all = prefs.get_object("treeview_sensors")
        # Main sensors treeview
        treeview_main = prefs.get_object("treeview_main_sensors")

        self.setup_sensors_treeview(treeview_all)
        self.setup_main_sensors_treeview(treeview_main)

        # Properties button
        button_properties = prefs.get_object("button_properties")
        button_properties.connect('clicked', self.properties_cb, treeview_all)

        # Add button
        button_add = prefs.get_object("button_add")
        button_add.connect('clicked', self.add_cb, treeview_all)

        # Remove button
        button_remove = prefs.get_object("button_remove")
        button_remove.connect('clicked', self.remove_cb, treeview_main)

        # Up button
        button_up = prefs.get_object("button_up")
        button_up.connect('clicked', self.up_cb, treeview_all)

        # Down button
        button_down = prefs.get_object("button_down")
        button_down.connect('clicked', self.down_cb, treeview_all)

    def setup_sensors_treeview(self, treeview):
        """Create treeview with list of all sensors and their settings."""
        self.__liststore = gtk.ListStore(int, int, str, str, str, 'gboolean',
                                         'gboolean')
        self.__column_idx = 0
        self.__column_row = 1
        self.__column_interface = 2
        self.__column_name = 3
        self.__column_label = 4
        self.__column_show = 5
        self.__column_in_icon = 6

        # Fill liststore with data
        for idx, sensor in enumerate(self.sensors):
            # Add a row to liststore
            last = self.__liststore.append([
                idx, sensor.dialog_row, sensor.interface, sensor.name,
                sensor.label, sensor.show, sensor.in_icon
            ])

        # Set TreeView's liststore
        treeview.set_model(self.__liststore)

        # Create a CellRendererText to render the data
        cell_row = gtk.CellRendererText()
        cell_interface = gtk.CellRendererText()
        cell_name = gtk.CellRendererText()
        cell_label = gtk.CellRendererText()
        cell_show = gtk.CellRendererToggle()

        # Set labels to be editable
        cell_label.set_property('editable', True)
        # Make toggle buttons in the show column activatable
        cell_show.set_property('activatable', True)

        # Connect the edited event
        cell_label.connect('edited', self.label_edited_cb)
        # Connect the toggle event
        cell_show.connect('toggled', self.show_toggled_cb)

        # Create the TreeViewColumns to display the data, add the cell renderer
        # and set the cell "text" attribute to correct column (treeview
        # retrieves text from that column in liststore)
        tvcolumn_row = gtk.TreeViewColumn("#",
                                          cell_row,
                                          text=self.__column_row,
                                          visible=self.__column_show)
        tvcolumn_interface = gtk.TreeViewColumn(_("Interface"),
                                                cell_interface,
                                                text=self.__column_interface)
        tvcolumn_name = gtk.TreeViewColumn(_("Name"),
                                           cell_name,
                                           text=self.__column_name)
        tvcolumn_label = gtk.TreeViewColumn(_("Label"),
                                            cell_label,
                                            text=self.__column_label)
        # Set the cell "active" attribute to column_show - retrieve the state
        # of the toggle button from that column in liststore
        tvcolumn_show = gtk.TreeViewColumn(_("In dialog"),
                                           cell_show,
                                           active=self.__column_show)

        # Add treeview columns to treeview
        treeview.append_column(tvcolumn_row)
        treeview.append_column(tvcolumn_interface)
        treeview.append_column(tvcolumn_name)
        treeview.append_column(tvcolumn_label)
        treeview.append_column(tvcolumn_show)

        # Make name and label searchable
        treeview.set_search_column(self.__column_name)
        treeview.set_search_column(self.__column_label)

        # Allow sorting on the column
        tvcolumn_row.set_sort_column_id(self.__column_row)
        tvcolumn_interface.set_sort_column_id(self.__column_interface)
        tvcolumn_name.set_sort_column_id(self.__column_name)
        tvcolumn_label.set_sort_column_id(self.__column_label)
        tvcolumn_show.set_sort_column_id(self.__column_show)

        # Sort by dialog_row
        self.__liststore.set_sort_column_id(self.__column_row,
                                            gtk.SORT_ASCENDING)

    def setup_main_sensors_treeview(self, treeview):
        """Create treeview with list of sensors to be shown in applet icon."""
        # List store for main sensors
        model_filter = self.__liststore.filter_new()
        model_filter.set_visible_column(self.__column_in_icon)

        # Set main sensor treeview's liststore
        treeview.set_model(model_filter)

        # Create a CellRendererText to render the data
        cell_label = gtk.CellRendererText()

        # Add treeview-column to treeview
        tvcolumn_label = gtk.TreeViewColumn(_("Sensors displayed in icon"),
                                            cell_label,
                                            text=self.__column_label)
        treeview.append_column(tvcolumn_label)

    def create_properties_dialog(self, sensor):
        """
        Create a dialog with individual sensors settings.
        
        Create a dialog with settings for high and low values and alarms for
        the given sensor
        @param prefs: opened glade XML file
        @param sensor: the sensor for which this settings dialog is for
        
        """
        prefs = gtk.Builder()
        prefs.add_from_file(ui_file)

        prop_dialog = prefs.get_object("sensor_properties_dialog")
        prop_dialog.set_title(sensor.label + " - " + _("properties"))
        prop_dialog.set_icon(self.__pref_dialog.get_icon())
        # Properties window should be on top of settings window
        prop_dialog.set_transient_for(self.__pref_dialog)

        # Get sensor value's type
        value_type = sensor.type

        # Adjustment for sensor high value spin button
        spin_button = prefs.get_object("spin_high_value")
        if value_type is TempValue:
            if sensor.unit == units.UNIT_CELSIUS:
                adj = gtk.Adjustment(sensor.high_value, -273, 200, 1, 10)
            elif sensor.unit == units.UNIT_FAHRENHEIT:
                adj = gtk.Adjustment(sensor.high_value, -460, 392, 1, 10)
            elif sensor.unit == units.UNIT_KELVIN:
                adj = gtk.Adjustment(sensor.high_value, 0, 473, 1, 10)
        elif value_type is RPMValue:
            adj = gtk.Adjustment(sensor.high_value, 0, 20000, 100, 1000)
        elif value_type is VoltValue:
            adj = gtk.Adjustment(sensor.high_value, -20, 20, 0.05, 1)
            spin_button.set_digits(2)

        # Set adjustment
        spin_button.set_adjustment(adj)
        spin_button.set_value(sensor.high_value)

        adj.connect(
            'value-changed', lambda adjustment: self.change_high_value(
                sensor, adjustment.get_value()))

        # Adjustment for sensor low value spin button
        spin_button = prefs.get_object("spin_low_value")
        if value_type is TempValue:
            if sensor.unit == units.UNIT_CELSIUS:
                adj = gtk.Adjustment(sensor.low_value, -273, 200, 1, 10)
            elif sensor.unit == units.UNIT_FAHRENHEIT:
                adj = gtk.Adjustment(sensor.low_value, -460, 392, 1, 10)
            elif sensor.unit == units.UNIT_KELVIN:
                adj = gtk.Adjustment(sensor.low_value, 0, 473, 1, 10)
        elif value_type is RPMValue:
            adj = gtk.Adjustment(sensor.low_value, 0, 20000, 100, 1000)
        elif value_type is VoltValue:
            adj = gtk.Adjustment(sensor.low_value, -20, 20, 0.05, 1)
            spin_button.set_digits(2)

        # Set adjustment
        spin_button.set_adjustment(adj)
        spin_button.set_value(sensor.low_value)

        adj.connect(
            'value-changed', lambda adjustment: self.change_low_value(
                sensor, adjustment.get_value()))

        # "Enable high alarm" CheckButton
        alarm_cbutton = prefs.get_object("check_high_alarm")
        alarm_cbutton.set_active(sensor.alarm_on_high)
        alarm_cbutton.connect('toggled',
                              lambda w: self.toggle_high_alarm(sensor))

        # "Enable low alarm" CheckButton
        alarm_cbutton = prefs.get_object("check_low_alarm")
        alarm_cbutton.set_active(sensor.alarm_on_low)
        alarm_cbutton.connect('toggled',
                              lambda w: self.toggle_low_alarm(sensor))

        # Close button
        close_button = prefs.get_object("close_properties")
        close_button.connect('clicked', lambda w: prop_dialog.destroy())

        prop_dialog.show_all()

    # === Event handlers === #
    def alarm_cb(self, sensor, message):
        """Show alarm message with awn notify."""
        self.applet.notification.send(subject=None,
                                      body=message,
                                      icon=applet_logo)

    def height_changed_cb(self):
        """Update the applet's icon to reflect the new height."""
        self.__icon.set_height(self.applet.get_size())
        # Force icon update
        self.update_icon(True)

    # === Change setting methods === #
    def change_unit(self, unit):
        """Change unit for all sensors and update icon."""
        for sensor in self.sensors:
            sensor.unit = unit
        self.update_icon(True)

    def change_font_size(self, size_idx):
        """Change font size for overlay."""
        if len(self.main_sensors) is 1:
            self.__temp_overlay.props.font_sizing = single_font_sizes[size_idx]
            self.__temp_overlay.props.y_override = \
                                          30 + size_idx if size_idx < 2 else 28
        else:
            self.__temp_overlay.props.font_sizing = double_font_sizes[size_idx]
            self.__temp_overlay.props.y_override = \
                                          30 + size_idx if size_idx < 2 else 29

    def change_theme(self, theme):
        """Save theme setting and update icon."""
        if self.__icon is None:
            return
        self.__icon.theme(theme)
        # Force icon change
        self.update_icon(True)

    def change_show_value_overlay(self, show_value_overlay):
        """Change whether to show the valu in applet icon"""
        self.__temp_overlay.props.active = show_value_overlay

    def change_timeout(self, timeout):
        """Save timeout setting and change timer to new timeout."""
        self.update_icon(True)
        self.__icon_timer.change_interval(timeout)
        self.__main_dialog.change_interval(timeout)
        for sensor in self.sensors:
            # Set timeout for updaters
            if sensor.interface in [
                    lmsensors.interface_name, nvidiasensors.interface_name
            ]:
                sensor.updater.set_timeout(timeout)

    def change_high_value(self, sensor, value):
        """Save high value setting for a specific sensor and update icon."""
        # Apply high value change
        sensor.high_value = value
        # Save high values
        self.applet.settings["high_values"] = \
                                       [s.raw_high_value for s in self.sensors]
        if sensor in self.main_sensors:
            # Force icon update
            self.update_icon(True)

    def change_low_value(self, sensor, value):
        """Save low value setting for a specific sensor and update icon."""
        # Apply low value change
        sensor.low_value = value
        # Save low values
        self.applet.settings["low_values"] = \
                                        [s.raw_low_value for s in self.sensors]
        if sensor in self.main_sensors:
            # Force icon update
            self.update_icon(True)

    def change_in_icon(self, sensor, in_icon):
        """Save in_icon setting for a specific sensor and update icon."""
        # Apply in_icon value change
        sensor.in_icon = in_icon
        # Save in_icon values
        self.applet.settings["in_icon"] = \
                                   [sensor.in_icon for sensor in self.sensors]
        self.recreate_main_sensors()
        # Force icon update
        self.update_icon(True)

    def change_hand_color(self, color):
        """Apply hand color setting and update icon."""
        self.__icon.set_hand_color(color)
        # Force icon update
        self.update_icon(True)

    def update_dialog_rows(self):
        """
        Save dialog row setting and update main dialog.
        
        """
        # Save dialog_row values
        self.applet.settings["dialog_row"] = \
                                [sensor.dialog_row for sensor in self.sensors]
        # Recreate dialog
        self.__main_dialog.recreate()

    def toggle_high_alarm(self, sensor):
        """Toggle high alarm for a specific sensor and save it."""
        sensor.toggle_alarm_on_high()
        self.applet.settings["high_alarms"] = \
                             [sensor.alarm_on_high for sensor in self.sensors]

    def toggle_low_alarm(self, sensor):
        """Toggle low alarm for a specific sensor and save it."""
        sensor.toggle_alarm_on_low()
        self.applet.settings["low_alarms"] = \
                              [sensor.alarm_on_low for sensor in self.sensors]

    # === Treeview callbacks === #
    def label_edited_cb(self, cell_renderer, path, new_text):
        """
        Change specific sensor's label, save it and update dialog and icon.
        
        """
        # Change label value in liststore
        self.__liststore[path][self.__column_label] = new_text
        sensor = self.sensors[self.__liststore[path][self.__column_idx]]
        # Apply change to sensor
        sensor.label = new_text
        # Save labels
        self.applet.settings["labels"] = \
                                    [sensor.label for sensor in self.sensors]
        # Recreate dialog
        self.__main_dialog.recreate()

    def show_toggled_cb(self, cell_renderer, path):
        """
        Toggle specific sensor's "show in dialog" properity, save it and update
        dialog.
        
        """
        store = self.__liststore
        sensor = self.sensors[store[path][self.__column_idx]]

        # Toggle value in liststore
        store[path][self.__column_show] = not store[path][self.__column_show]
        # Apply change to sensor
        sensor.show = store[path][self.__column_show]
        # Save show property
        self.applet.settings["show"] = [sensor.show for sensor in self.sensors]
        # Recreate dialog
        self.__main_dialog.recreate()

    # === Button callbacks === #
    def properties_cb(self, widget, treeview):
        """Open properties dialog for selected sensor."""
        # Get selected sensor
        treeselection = treeview.get_selection()
        (model, iter) = treeselection.get_selected()
        # Something must be selected
        if iter is not None:
            # Sensor index
            idx = model[iter][self.__column_idx]
            self.create_properties_dialog(self.sensors[idx])

    def add_cb(self, widget, treeview_all):
        """
        Add selected sensors to main sensors (i.e. shown them in applet icon).
        
        """
        # Get selected sensor
        selection = treeview_all.get_selection()
        (model, iter) = selection.get_selected()
        # Something must be selected
        if iter is not None:
            # Sensor index
            idx = self.__liststore[iter][self.__column_idx]
            # Apply setting
            self.change_in_icon(self.sensors[idx], True)
            # Change value in model
            model[iter][self.__column_in_icon] = True
        self.update_icon_type()

    def remove_cb(self, widget, treeview_main):
        """
        Remove selected sensors from main sensors (i.e. do not shown them in
        applet icon).
        
        """
        # Get selected sensor
        selection = treeview_main.get_selection()
        (model_filter, iter) = selection.get_selected()
        # Something must be selected and at least one sensor must remain
        if iter is not None and len(treeview_main.get_model()) > 1:
            child_iter = model_filter.convert_iter_to_child_iter(iter)
            # Sensor index
            idx = self.__liststore[child_iter][self.__column_idx]
            # Apply setting
            self.change_in_icon(self.sensors[idx], False)
            # Change value in model
            self.__liststore[child_iter][self.__column_in_icon] = False
        self.update_icon_type()

    def up_cb(self, widget, treeview):
        """
        Move selected sensors up in main applet dialog.
        
        """
        # Get selected sensor
        selection = treeview.get_selection()
        (model, iter) = selection.get_selected()

        # Something must be selected
        if iter is not None:
            dialog_row = model[iter][self.__column_row]
            if dialog_row > 1:
                # Iterator pointing to predecessor (sensor/row that has
                # dialog_row one less than this one)
                row_pred = None
                for row in model:
                    if row[self.__column_row] == dialog_row - 1:
                        row_pred = row
                        break
                if row_pred is not None:
                    # Switch model_row-s
                    model[iter][self.__column_row] = dialog_row - 1
                    row_pred[self.__column_row] = dialog_row

                    # Apply setting
                    sensor = self.sensors[model[iter][self.__column_idx]]
                    sensor_pred = self.sensors[row_pred[self.__column_idx]]
                    sensor.dialog_row = dialog_row - 1
                    sensor_pred.dialog_row = dialog_row
                    self.update_dialog_rows()

    def down_cb(self, widget, treeview):
        """
        Move selected sensors down in the main applet dialog.
        
        """
        # Get selected sensor
        selection = treeview.get_selection()
        (model, iter) = selection.get_selected()
        # Something must be selected
        if iter is not None:
            dialog_row = model[iter][self.__column_row]
            if dialog_row < len(self.sensors):
                # Iter pointing to predecessor (sensor/row that has dialog_row
                # one less than this one)
                row_pred = None
                for row in model:
                    if row[self.__column_row] == dialog_row + 1:
                        row_pred = row
                        break
                if row_pred is not None:
                    # Switch model_row-s
                    model[iter][self.__column_row] = dialog_row + 1
                    row_pred[self.__column_row] = dialog_row

                    # Apply setting
                    sensor = self.sensors[model[iter][self.__column_idx]]
                    sensor_pred = self.sensors[row_pred[self.__column_idx]]
                    sensor.dialog_row = dialog_row + 1
                    sensor_pred.dialog_row = dialog_row
                    self.update_dialog_rows()

    class MainDialog:
        def __init__(self, parent, main_dialog):

            self.__parent = parent
            self.__dialog = main_dialog

            self.create_content()

            # Create a timer, but do not start it
            self.__timer = parent.applet.timing.register(
                self.update_values, parent.applet.settings["timeout"], False)

            self.__dialog.connect('show', self.dialog_shown_cb)
            self.__dialog.connect('hide', self.dialog_hidden_cb)

        def create_content(self):
            """
            (Re)creates sensor labels and values for main applet dialog
            
            """
            # List of sensors to be shown in dialog
            shown_sensors = dict()
            for sensor in self.__parent.sensors:
                if sensor.show:
                    shown_sensors[sensor.dialog_row] = sensor

            rows = shown_sensors.keys()
            rows.sort()

            # List of gtkLabel-s that contain the sensor value
            self.__value_labels = dict()

            new_vbox = gtk.VBox(False, 10)

            for row in rows:
                sensor = shown_sensors[row]
                hbox = gtk.HBox()
                label = gtk.Label(sensor.label + ':\t')
                self.__value_labels[sensor] = gtk.Label(
                    str(sensor.value) + ' ' + sensor.unit_str)
                hbox.pack_start(label, False, False)
                hbox.pack_end(self.__value_labels[sensor], False, False)
                new_vbox.pack_start(hbox, False, False)

            new_vbox.show_all()
            self.__dialog.add(new_vbox)

        def recreate(self):
            # Destroy current dialog vbox
            self.__dialog.child.child.get_children()[-1].destroy()
            # Create new content
            self.create_content()

        def update_values(self):
            """Update main applet dialog with new values."""
            for sensor, label in self.__value_labels.iteritems():
                sensor.read_sensor()
                label.set_text(str(sensor.value) + ' ' + sensor.unit_str)

        def change_interval(self, timeout):
            self.__timer.change_interval(timeout)

        def dialog_shown_cb(self, dialog):
            """Start updating sensor values in main applet dialog."""
            self.__timer.start()
            # Force update
            self.update_values()

        def dialog_hidden_cb(self, dialog):
            """Stop update timer for main applet dialog."""
            self.__timer.stop()