diff -Nru indicator-sysmonitor-0.4.1~quantal/build/indicator-sysmonitor indicator-sysmonitor-0.4.2/build/indicator-sysmonitor
--- indicator-sysmonitor-0.4.1~quantal/build/indicator-sysmonitor 2012-06-30 10:20:19.000000000 +0000
+++ indicator-sysmonitor-0.4.2/build/indicator-sysmonitor 2012-11-27 16:22:28.000000000 +0000
@@ -7,39 +7,211 @@
# Homepage: http://launchpad.net/indicator-sysmonitor
# License: GPL v3
#
+from gettext import gettext as _
+from gettext import textdomain, bindtextdomain
+textdomain("indicator-sysmonitor")
+bindtextdomain("indicator-sysmonitor", "./lang")
+
import sys
import os
import shutil
import json
-import string
import time
-from copy import deepcopy
from threading import Thread, Event
import subprocess
import psutil as ps
-
-import logging
-logging.basicConfig(file=sys.stderr,level=logging.INFO)
-
-import gobject
+import re
import gtk
+gtk.gdk.threads_init()
import appindicator
+import logging
+logging.basicConfig(file=sys.stderr, level=logging.INFO)
-VERSION='0.4.1~unreleased'
+B_UNITS = ['', 'KB', 'MB', 'GB', 'TB']
+VERSION = '0.5.1~unreleased'
+HELP_MSG = """{title}
+
+{introduction}
+
+{basic}
+• cpu: {cpu_desc}
+• mem: {mem_desc}
+• bat%d: {bat_desc}
+• net: {net_desc}
+
+{compose}
+• fs//mount-point : {fs_desc}
+
+{example}
+CPU {{cpu}} | MEM {{mem}} | root {{fs///}}
+""".format(
+ title=_("Help Page"),
+ introduction=_("The sensors are the names of the devices you want to \
+ retrieve information from. They must be placed between brackets."),
+ basic=_("The basics are:"),
+ cpu_desc=_("It shows the average of CPU usage."),
+ mem_desc=_("It shows the physical memory in use."),
+ bat_desc=_("It shows the available battery which id is %d."),
+ net_desc=_("It shows the amount of data you are downloading and uploading \
+ through your network."),
+ compose=_("Also there are the following sensors that are composed with \
+ two parts divided by two slashes."),
+ fs_desc=_("Show available space in the file system."),
+ example=_("Example:"))
+
+supported_sensors = re.compile("\A(mem|swap|cpu\d*|net|bat\d*|fs//.+)\Z")
+settings = {
+ 'custom_text': 'cpu: {cpu} mem: {mem}',
+ 'interval': 2,
+ 'on_startup': False,
+ 'sensors': {
+ # 'name' => (desc, cmd)
+ 'cpu\d*': (_('Average CPU usage'), True),
+ 'mem': (_('Physical memory in use.'), True),
+ 'net': (_('Network activity.'), True),
+ 'bat\d*': (_('Network activity.'), True),
+ 'fs//.+': (_('Available space in file system.'), True),
+ "swap": (_("Average swap usage"), True)
+
+ }
+ }
+
+
+class ISMError(Exception):
+ """General exception."""
+ def __init__(self, msg):
+ Exception.__init__(self, msg)
+
+
+def raise_dialog(parent, flags, type_, buttons, msg, title):
+ """It raise a dialog. It a blocking function."""
+ dialog = gtk.MessageDialog(
+ parent, flags, type_, buttons, msg)
+ dialog.set_title(title)
+ dialog.run()
+ dialog.destroy()
+
+
+class Sensor(object):
+ """Singleton"""
+ _instance = None
+ bat = re.compile("\Abat\d*\Z")
+ cpus = re.compile("\Acpu\d+\Z")
+
+ def __init__(self):
+ """It must not be called. Use Sensor.get_instace()
+ to retrieve an instance of this class."""
+ if Sensor._instance is not None:
+ raise Exception("Sensor class can not be instanceted twice.")
+ else:
+ Sensor._instance = self
+ self.update_regex()
+
+ @staticmethod
+ def update_regex(names=None):
+ if names is None:
+ names = settings["sensors"].keys()
+
+ reg = '|'.join(names)
+ reg = "\A({})\Z".format(reg)
+ global supported_sensors
+ supported_sensors = re.compile("{}".format(reg))
+
+ @classmethod
+ def get_instance(cls):
+ """Returns the unique instance of Sensor."""
+ if Sensor._instance is None:
+ Sensor._instance = Sensor()
+
+ return Sensor._instance
+
+ @staticmethod
+ def exists(name):
+ """Checks if the sensor name exists"""
+ return bool(supported_sensors.match(name))
+
+ @staticmethod
+ def check(sensor):
+ if sensor.startswith("fs//"):
+ path = sensor.split("//")[1]
+ if not os.path.exists(path):
+ raise ISMError(_("Path: {} doesn't exists.").format(path))
+
+ elif Sensor.cpus.match(sensor):
+ nber = int(sensor[3:])
+ if nber >= ps.NUM_CPUS:
+ raise ISMError(_("Invalid number of CPU."))
+
+ elif Sensor.bat.match(sensor):
+ bat_id = int(sensor[3:]) if len(sensor) > 3 else 0
+ if bat_id >= len(os.listdir("/proc/acpi/battery/")):
+ raise ISMError(_("Invalid number of Batterry."))
+
+ def add(self, name, desc, cmd):
+ """Adds a custom sensors."""
+ if Sensor.exists(name):
+ raise ISMError(_("Sensor name already in use."))
+
+ settings["sensors"][name] = (desc, cmd)
+ self.update_regex()
+
+ def delete(self, name):
+ """Deletes a custom sensors."""
+ sensors = settings['sensors']
+ names = sensors.keys()
+ if name not in names:
+ raise ISMError(_("Sensor is not defined."))
+
+ _desc, default = sensors[name]
+ if default is True:
+ raise ISMError(_("Can not delete default sensors."))
+
+ del sensors[name]
+ self.update_regex()
+
+ def edit(self, name, newname, desc, cmd):
+ """Edits a custom sensors."""
+ try:
+ sensors = settings['sensors']
+ _desc, default = sensors[name]
+
+ except KeyError:
+ raise ISMError(_("Sensor does not exists."))
+
+ if default is True:
+ raise ISMError(_("Can not edit default sensors."))
+ if newname != name:
+ if newname in sensors.keys():
+ raise ISMError(_("Sensor name already in use."))
+
+ sensors[newname] = (desc, cmd)
+ del sensors[name]
+ settings["custom_text"] = settings["custom_text"].replace(
+ name, newname)
+ self.update_regex()
-gtk.gdk.threads_init()
class StatusFetcher(Thread):
+ """It recollects the info about the sensors."""
+ digit_regex = re.compile(r'''\d+''')
+
def __init__(self, parent):
Thread.__init__(self)
- self.parent = parent
+ self._parent = parent
self.last = ps.cpu_times()
- def _fetch_cpu(self):
+ def _fetch_cpu(self, percpu=False):
+ if percpu:
+ return ps.cpu_percent(interval=0, percpu=True)
+
last = self.last
current = ps.cpu_times()
- total_time_passed = sum([v-last.__dict__[k] if not isinstance(v,list) else 0 for k,v in current.__dict__.iteritems()])
+ total_time_passed = sum(
+ [v - last.__dict__[k]
+ if not isinstance(v, list)
+ else 0
+ for k, v in current.__dict__.iteritems()])
sys_time = current.system - last.system
usr_time = current.user - last.user
@@ -53,546 +225,659 @@
else:
return 0
- def _fetch_mem(self):
- total_mem = subprocess.Popen("free -b | grep Mem | tr -s ' ' | cut -d\ -f 2",
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- free_mem = subprocess.Popen("free -b | grep Mem | tr -s ' ' | cut -d\ -f 4-",
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- free_mem = sum([int(i) for i in free_mem.split()])
- return 100 - 100 * float(free_mem) / float(total_mem)
+ def _fetch_swap(self):
+ """Return the swap usage in percent"""
+ usage = 0
+ total = 0
+ try:
+ with open("/proc/swaps") as swaps:
+ swaps.readline()
+ for line in swaps.readlines():
+ dummy, dummy, total_, usage_, dummy = line.split()
+ total += int(total_)
+ usage += int(usage_)
- def _fetch_bat(self, bat_id):
- current_bat = subprocess.Popen("grep 'remaining capacity:' /proc/acpi/battery/BAT%s/state |awk '{print $3}' |grep [0-9] || echo 10" % bat_id,
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- #total_bat = subprocess.Popen("cat /proc/acpi/battery/BAT0/info |grep -i 'design capacity:' |awk '{print $3}'",
- total_bat = subprocess.Popen("grep 'last full capacity:' /proc/acpi/battery/BAT%s/info |awk '{print $4}' |grep [0-9] || echo 65130" % bat_id,
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- return 100 * float(current_bat) / float(total_bat)
+ return usage * 100 / total
- def _fetch_net(self):
- total_net = subprocess.Popen("ifstat -a -n -q -S -T 0.5 1 | tail -1 | awk '{ printf(\"↓%dkB/s ↑%dkB/s\", $6, $7) }'", stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- return total_net
+ except IOError:
+ return "N/A"
- def _fetch_sensor(self, sensor_name):
- sensor_data = sensor_name.split('//')
+ def _fetch_mem(self):
+ """It gets the total memory info and return the used in percent."""
+ with open('/proc/meminfo') as meminfo:
+ total = StatusFetcher.digit_regex.findall(meminfo.readline()).pop()
+ free = StatusFetcher.digit_regex.findall(meminfo.readline()).pop()
+ meminfo.readline()
+ cached = StatusFetcher.digit_regex.findall(
+ meminfo.readline()).pop()
+ free = int(free) + int(cached)
+ return 100 - 100 * free / float(total)
- if (len(sensor_data) != 2):
- return 'N/A'
+ def _fetch_bat(self, batid):
+ """Fetch the the amount of remaining battery"""
+ try:
+ with open("/proc/acpi/battery/BAT{}/state".format(batid)) as state:
+ while True:
+ line = state.readline()
+ if "remaining capacity:" in line:
+ remaining = int(line.split()[2])
+ break
+
+ with open("/proc/acpi/battery/BAT{}/info".format(batid)) as info:
+ while True:
+ line = info.readline()
+ if "last full capacity" in line:
+ capacity = int(line.split()[3])
+ break
- sensor_item = sensor_data[1].replace('-', '.')
- postfix = ''
- if sensor_data[0] == 'hddtemp':
- sensor_cmd = 'netcat localhost 7634'
- netcat_value = subprocess.Popen(sensor_cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- sensor_value = ''
-
- for hd_data in self.parse_hddtemp_entries(netcat_value):
- if hd_data[0] == sensor_item:
- sensor_value = hd_data[2] + '°' + hd_data[3]
- else:
- sensor_cmd = 'sensors -A ' + sensor_data[0] + " | grep -i '" + sensor_item + "' | cut -f 2 -d ':' | awk '{print $1}'"
- if sensor_data[0] == 'nvidia':
- if sensor_item == 'gputemp':
- sensor_cmd = 'nvidia-settings -q [gpu:0]/GPUCoreTemp | grep "Attribute" | sed -e "s/.*: //g" -e "s/\.//g"'
- postfix = '°C'
- elif sensor_item == 'fanspeed':
- sensor_cmd = 'nvidia-settings -q [fan:0]/GPUCurrentFanSpeed | grep "Attribute" | sed -e "s/.*: //g" -e "s/\.//g"'
- postfix = ' RPM'
- else:
- sensor_cmd = None
- elif sensor_data[0] == 'ati':
- if sensor_item == 'gputemp':
- sensor_cmd = 'aticonfig --adapter=0 --od-gettemperature | grep "Sensor 0:" | awk ' + "'{ printf(\"%d\", $5) }'"
- postfix = '°C'
-
- sensor_value = subprocess.Popen(sensor_cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
-
- if len(sensor_value):
- sensor_value = self._clean_value(sensor_value) # Cleaning the + prefix
- return sensor_value + postfix
- else:
- if sensor_cmd == None:
- logging.info('Sensor not supported: ' + sensor_name)
- else:
- logging.info('Sensor command failed:\n' + sensor_cmd)
- return 'N/A'
-
- def _clean_value(self, value):
- if value.find('+') == 0:
- return value[1:]
- return value
-
- def _get_sensors_to_fetch(self):
- fmt = string.Formatter()
- tokens = []
- for token in fmt.parse(self.parent.custom_text):
- tokens.append(str(token[1]))
+ except IOError:
+ return "N/A"
+
+ return remaining * 100.0 / capacity
- return tokens
+ def _fetch_net(self):
+ """It use the command "ifstat" to know the total net usage"""
+ total_net = subprocess.Popen(
+ "ifstat -a -n -q -S -T 0.5 1 | tail -1 | awk \
+ '{ printf(\"↓%dkB/s ↑%dkB/s\", $6, $7) }'",
+ stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
+ return total_net
- def _fetch(self):
+ def fetch(self):
+ """Return a dict whose element are the sensors
+ and their values"""
res = {}
- for sensor in self._get_sensors_to_fetch():
+ cpus = None
+ for sensor in Preferences.sensors_regex.findall(
+ settings["custom_text"]):
+ sensor = sensor[1:-1]
if sensor == 'cpu':
- res['cpu'] = '%02.0f%%' % self._fetch_cpu()
+ res['cpu'] = "{:02.0f}%".format(self._fetch_cpu())
+ elif Sensor.cpus.match(sensor):
+ if cpus is None:
+ cpus = self._fetch_cpu(percpu=True)
+ res[sensor] = "{:02.0f}%".format(cpus[int(sensor[3:])])
+
elif sensor == 'mem':
- res['mem'] = '%02.0f%%' % self._fetch_mem()
- elif sensor.startswith('bat'):
- bat_id = sensor[3:]
- try:
- bat_id = int(bat_id)
- except:
- bat_id = 0
- res[sensor] = '%02.0f%%' % self._fetch_bat(bat_id)
+ res['mem'] = '{:02.0f}%'.format(self._fetch_mem())
elif sensor == 'net':
res['net'] = self._fetch_net()
- else:
- res[sensor] = '%s' % self._fetch_sensor(sensor)
+
+ elif Sensor.bat.match(sensor):
+ bat_id = int(sensor[3:]) if len(sensor) > 3 else 0
+ res[sensor] = '{:02.0f}%'.format(self._fetch_bat(bat_id))
+
+ elif sensor.startswith('fs//'):
+ parts = sensor.split('//')
+ res[sensor] = self._fetch_fs(parts[1])
+
+ elif sensor == "swap":
+ res[sensor] = '{:02.0f}%'.format(self._fetch_swap())
+
+ else: # custom sensor
+ res[sensor] = self._exec(settings["sensors"][sensor][1])
return res
- def _fetch_user(self, command):
+ def _exec(self, command):
+ """Execute a custom command."""
try:
- output = subprocess.Popen(command, stdout=subprocess.PIPE,
- shell=True).communicate()[0].strip()
- if output == '':
- output = '(no output)'
+ output = subprocess.Popen(command, stdout=subprocess.PIPE,
+ shell=True).communicate()[0].strip()
except:
- output = 'error'
- logging.error('Error running: '+command)
- return output
-
- def parse_hddtemp_entries(self, netcat_value):
- hddtemp_entries = []
-
- if len(netcat_value):
- for hd_row in netcat_value.strip('|').split('||'):
- hddtemp_entries.append(hd_row.split('|'))
+ output = _("Error")
+ logging.error(_("Error running: {}").format(command))
+
+ return output if output else _("(no output)")
- return hddtemp_entries
+ def _fetch_fs(self, mount_point):
+ """It returns the amount of bytes available in the fs in
+ a human-readble format."""
+ if not os.access(mount_point, os.F_OK):
+ return None
+
+ stat = os.statvfs(mount_point)
+ bytes_ = stat.f_bavail * stat.f_frsize
+
+ for unit in B_UNITS:
+ if bytes_ < 1024:
+ return "{} {}".format(bytes_, unit)
+ bytes_ /= 1024
def run(self):
- while(self.parent.alive.isSet()):
- if self.parent.mode_user:
- output = self._fetch_user(self.parent.user_command)
- self.parent.update_text(output, output)
- else:
- data = self._fetch()
- self.parent.update(data)
- time.sleep(self.parent.interval)
+ """It is the main loop."""
+ while self._parent.alive.isSet():
+ data = self.fetch()
+ self._parent.update(data)
+ time.sleep(settings["interval"])
+
+
+class SensorsListModel(object):
+ """A TreeView showing the available sensors. It allows to
+ add/edit/delete custom sensors."""
-class SensorsListModel:
def __init__(self, parent):
self.ind_parent = parent
- self.tree_store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
+ self._list_store = gtk.ListStore(str, str)
+ self._tree_view = gtk.TreeView(self._list_store)
- # lib sensors
- for sensor_data in subprocess.Popen('sensors', stdout=subprocess.PIPE, shell=True).communicate()[0].split('\n\n'):
- sensor_name = None
- skip_line = False
- for line in sensor_data.split('\n'):
- if len(line):
- if skip_line:
- skip_line = False
- else:
- if sensor_name == None:
- logging.info("New sensor found: " + line)
- sensor_name = line
- parent = self.tree_store.append(None, (sensor_name, None, False))
- skip_line = True
- else:
- logging.info("Sensor entry: " + line)
- self.tree_store.append(parent, (line, self.generate_sensor_item_name(sensor_name, line), False))
-
- # hddtemp
- hddtemp = subprocess.Popen('netcat localhost 7634', stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- if len(hddtemp):
- sensor_name = 'hddtemp'
- logging.info("New sensor found: " + sensor_name)
- parent = self.tree_store.append(None, (sensor_name, None, False))
- for hd_data in self.ind_parent.ind_parent.fetch.parse_hddtemp_entries(hddtemp):
- logging.info("Sensor entry: " + hd_data[0])
- self.tree_store.append(parent, (hd_data[0] + ' - ' + hd_data[1] + ' - ' + hd_data[2] + '°' + hd_data[3], self.generate_sensor_item_name(sensor_name, hd_data[0]), False))
-
- # nvidia GPU
- nvidia_model = subprocess.Popen("lspci | grep nVidia | sed -e 's/.*\[//g' -e 's/\].*//g'", stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- if len(nvidia_model):
- sensor_name = 'nvidia'
- logging.info("New sensor found: " + sensor_name)
- parent = self.tree_store.append(None, (sensor_name, None, False))
- self.tree_store.append(parent, (nvidia_model + ' - Temperature', self.generate_sensor_item_name(sensor_name, 'gputemp'), False))
- self.tree_store.append(parent, (nvidia_model + ' - Fan speed', self.generate_sensor_item_name(sensor_name, 'fanspeed'), False))
-
- # ati GPU
- ati_model = subprocess.Popen('lspci | grep \"ATI Radeon HD"' + " | sed -e 's/.*\[//g' -e 's/\].*//g'", stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- if len(ati_model):
- sensor_name = 'ati'
- logging.info("New sensor found: " + sensor_name)
- parent = self.tree_store.append(None, (sensor_name, None, False))
- self.tree_store.append(parent, (ati_model + ' - Temperature', self.generate_sensor_item_name(sensor_name, 'gputemp'), False))
+ sensors = settings['sensors']
+ for name in sensors.keys():
+ self._list_store.append([name, sensors[name][0]])
def get_view(self):
- self.view = gtk.HBox()
-
- self.vb = gtk.VBox(False, 3)
- l = gtk.Label('Sensors:')
- l.set_alignment(0, 0.5)
- self.vb.pack_start(l, expand=False, fill=False)
-
- self.tree_view = gtk.TreeView(self.tree_store)
-
- # setup the text cell renderer
- self.renderer = gtk.CellRendererText()
- self.renderer.set_property('editable', False)
-
- # sensor name render
- self.renderer1 = gtk.CellRendererText()
- self.renderer1.set_property('editable', False)
-
- # quick add checkbox render
- self.renderer2 = gtk.CellRendererToggle()
- self.renderer2.set_property('activatable', True)
- self.renderer2.connect('toggled', self.quick_add_cb_toggled, self.tree_store)
-
+ """It's call from Preference. It create the view and return it"""
+ vbox = gtk.VBox(False, 3)
# create columns
- self.column0 = gtk.TreeViewColumn('Sensor', self.renderer, text=0)
- self.column1 = gtk.TreeViewColumn('Identifier', self.renderer1, text=1)
- self.column2 = gtk.TreeViewColumn('', self.renderer2, active=2)
- self.tree_view.append_column(self.column0)
- self.tree_view.append_column(self.column1)
- self.tree_view.append_column(self.column2)
+ renderer = gtk.CellRendererText()
+ renderer.set_property('editable', False)
+ column = gtk.TreeViewColumn(_('Sensor'), renderer, text=0)
+ self._tree_view.append_column(column)
+
+ renderer = gtk.CellRendererText()
+ renderer.set_property('editable', False)
+ column = gtk.TreeViewColumn(_('Description'), renderer, text=1)
+ self._tree_view.append_column(column)
- self.tree_view.expand_all()
+ self._tree_view.expand_all()
sw = gtk.ScrolledWindow()
- sw.add_with_viewport(self.tree_view)
- self.vb.pack_start(sw, fill=True, expand=True)
+ sw.add_with_viewport(self._tree_view)
+ vbox.pack_start(sw, fill=True, expand=True)
- self.add_bt = gtk.Button('Add selected sensors')
- self.add_bt.connect('clicked', self.update_custom_text)
- self.vb.pack_end(self.add_bt, fill=False, expand=False)
-
- self.view.add(self.vb)
- self.view.show()
-
- return self.view
-
- def quick_add_cb_toggled(self, cell, path, tree_store):
- tree_store[path][2] = not tree_store[path][2]
- iter = tree_store.iter_children(tree_store.get_iter(path))
- while iter:
- tree_store.set_value(iter, 2, tree_store[path][2])
- iter = tree_store.iter_next(iter)
-
- def generate_sensor_item_name(self, sensor_name, sensor_item_label):
- return sensor_name + '//' + sensor_item_label.split(':')[0].lower().replace('.', '-')
-
- def update_custom_text(self, event=None):
- iter = self.tree_store.get_iter_root()
-
- while iter:
- iter_children = self.tree_store.iter_children(iter)
- while iter_children:
- if self.tree_store.get_value(iter_children, 2):
- current_text = self.ind_parent.custom_entry.get_text()
- to_add_value = '{' + self.tree_store.get_value(iter_children, 1) + '}'
- if string.find(current_text, to_add_value) == -1:
- self.ind_parent.custom_entry.set_text(current_text + ' ' + to_add_value)
- iter_children = self.tree_store.iter_next(iter_children)
+ # add buttons
+ hbox = gtk.HBox()
+ new_button = gtk.Button(stock=gtk.STOCK_NEW)
+ new_button.connect('clicked', self._on_edit_sensor)
+ # hbox.pack_start(new_button, fill=False, expand=False)
+ hbox.pack_start(new_button, fill=False, expand=False)
+
+ edit_button = gtk.Button(stock=gtk.STOCK_EDIT)
+ edit_button.connect('clicked', self._on_edit_sensor, False)
+ hbox.pack_start(edit_button, fill=False, expand=False)
+
+ del_button = gtk.Button(stock=gtk.STOCK_DELETE)
+ del_button.connect('clicked', self._on_del_sensor)
+ hbox.pack_start(del_button, fill=False, expand=False)
+
+ add_button = gtk.Button(stock=gtk.STOCK_ADD)
+ add_button.connect('clicked', self._on_add_sensor)
+ hbox.pack_end(add_button, fill=False, expand=False)
+ vbox.pack_end(hbox, fill=False, expand=False)
+
+ frame = gtk.Frame(_('Sensors'))
+ frame.add(vbox)
+ return frame
+
+ def _get_selected_row(self):
+ """Returns an iter for the selected rows in the view or None."""
+ model, pathlist = self._tree_view.get_selection().get_selected_rows()
+ if len(pathlist):
+ path = pathlist.pop()
+ return model.get_iter(path)
+ return None
+
+ def _on_add_sensor(self, evnt=None, data=None):
+ tree_iter = self._get_selected_row()
+ if tree_iter is None:
+ return
- iter = self.tree_store.iter_next(iter)
+ sensor = self._list_store.get_value(tree_iter, 0)
+ self.ind_parent.custom_entry.insert_text(
+ "{{{}}}".format(sensor), -1)
+
+ def _on_edit_sensor(self, evnt=None, blank=True):
+ """Raises a dialog with a form to add/edit a sensor"""
+ name = desc = cmd = ""
+ tree_iter = None
+ if not blank:
+ # edit, so get the info from the selected row
+ tree_iter = self._get_selected_row()
+ if tree_iter is None:
+ return
+
+ name = self._list_store.get_value(tree_iter, 0)
+ desc = self._list_store.get_value(tree_iter, 1)
+ cmd = settings["sensors"][name][1]
+
+ if cmd is True: # default sensor
+ raise_dialog(
+ self.ind_parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ _("Can not edit defualt sensors."), _("Error"))
+ return
+
+ dialog = gtk.Dialog(_("Edit Sensor"), self.ind_parent,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+ gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+ vbox = dialog.get_content_area()
+
+ hbox = gtk.HBox()
+ label = gtk.Label(_("Sensor"))
+ sensor_entry = gtk.Entry()
+ sensor_entry.set_text(name)
+ hbox.pack_start(label)
+ hbox.pack_end(sensor_entry)
+ vbox.pack_start(hbox)
+
+ hbox = gtk.HBox()
+ label = gtk.Label(_("Description"))
+ desc_entry = gtk.Entry()
+ desc_entry.set_text(desc)
+ hbox.pack_start(label)
+ hbox.pack_end(desc_entry)
+ vbox.pack_start(hbox)
+
+ hbox = gtk.HBox()
+ label = gtk.Label(_("Command"))
+ cmd_entry = gtk.Entry()
+
+ cmd_entry.set_text(cmd)
+ hbox.pack_start(label)
+ hbox.pack_end(cmd_entry)
+ vbox.pack_end(hbox)
-class Preferences(gtk.Window):
- AUTOSTART_PATH = os.getenv("HOME") + '/.config/autostart/indicator-sysmonitor.desktop'
+ dialog.show_all()
+ response = dialog.run()
+
+ if response == gtk.RESPONSE_ACCEPT:
+ try:
+ newname, desc, cmd = str(sensor_entry.get_text()), \
+ str(desc_entry.get_text()), str(cmd_entry.get_text())
+
+ if blank:
+ Sensor.get_instance().add(newname, desc, cmd)
+ else:
+ Sensor.get_instance().edit(name, newname, desc, cmd)
+ self._list_store.remove(tree_iter)
+
+ self._list_store.append([newname, desc])
+ ctext = self.ind_parent.custom_entry.get_text()
+ self.ind_parent.custom_entry.set_text(
+ ctext.replace(name, newname))
+
+ except ISMError as ex:
+ raise_dialog(
+ self.ind_parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ ex.message, _("Error"))
+
+ dialog.destroy()
+
+ def _on_del_sensor(self, evnt=None, data=None):
+ """Remove a custom sensor."""
+ tree_iter = self._get_selected_row()
+ if tree_iter is None:
+ return
+
+ name = self._list_store.get_value(tree_iter, 0)
+ try:
+ Sensor.get_instance().delete(name)
+ self._list_store.remove(tree_iter)
+ ctext = self.ind_parent.custom_entry.get_text()
+ self.ind_parent.custom_entry.set_text(
+ ctext.replace("{{{}}}".format(name), ""))
+
+ except ISMError as ex:
+ raise_dialog(
+ self.ind_parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ ex.message, _("Error"))
+
+
+class Preferences(gtk.Dialog):
+ """It define the the Preferences Dialog and its operations."""
+ AUTOSTART_PATH = '{}/.config/autostart/indicator-sysmonitor.desktop'\
+ .format(os.getenv("HOME"))
DESKTOP_PATH = '/usr/share/applications/indicator-sysmonitor.desktop'
+ sensors_regex = re.compile("{.+?}")
def __init__(self, parent):
- gtk.Window.__init__(self)
+ """It creates the widget of the dialogs"""
+ gtk.Dialog.__init__(self)
self.ind_parent = parent
- self.connect('delete-event', self.on_destroy)
+ self.custom_entry = None
+ self.interval_entry = None
+ self._create_content()
+ self.set_data()
+ self.show_all()
+ def _create_content(self):
+ """It creates the content for this dialog."""
+ self.connect('delete-event', self.on_cancel)
+ self.set_title(_('Preferences'))
+ self.resize(400, 350)
+ self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
notebook = gtk.Notebook()
notebook.set_border_width(4)
- vb = gtk.VBox(spacing=3)
- hb = gtk.HBox()
- hb.set_border_width(4)
- l = gtk.Label('Run on startup:')
- l.set_alignment(0, 0.5)
- hb.pack_start(l)
+ # General page of the notebook {{{
+ vbox = gtk.VBox(spacing=3)
+ hbox = gtk.HBox()
+
+ hbox.set_border_width(4)
+ label = gtk.Label(_('Run on startup:'))
+ label.set_alignment(0, 0.5)
+ hbox.pack_start(label)
self.autostart_check = gtk.CheckButton()
self.autostart_check.set_active(self.get_autostart())
- hb.pack_end(self.autostart_check, expand=False, fill=False)
- vb.pack_start(hb, expand=False, fill=False)
+ hbox.pack_end(self.autostart_check, expand=False, fill=False)
+ vbox.pack_start(hbox, expand=False, fill=False)
- hb = gtk.HBox()
- l = gtk.Label('This is indicator-sysmonitor version: %s' % VERSION)
- l.set_alignment(0.5, 0.5)
- hb.pack_start(l)
- vb.pack_end(hb)
-
- notebook.append_page(vb, gtk.Label('General'))
-
- vb = gtk.VBox(spacing=3)
- hb = gtk.VBox()
- self.custom_radio = gtk.RadioButton(label='Customize output:')
- hb.pack_start(self.custom_radio)
+ hbox = gtk.HBox()
+ label = gtk.Label(
+ _('This is indicator-sysmonitor version: {}').format(VERSION))
+ label.set_alignment(0.5, 0.5)
+ hbox.pack_start(label)
+ vbox.pack_end(hbox)
+ notebook.append_page(vbox, gtk.Label(_('General')))
+ # }}}
+
+ # Advanced page in notebook {{{
+ vbox = gtk.VBox() # main box
+ label = gtk.Label(_('Customize output:'))
+ label.set_alignment(0, 0)
+ vbox.pack_start(label, expand=False, fill=False)
self.custom_entry = gtk.Entry()
- hb.pack_end(self.custom_entry)
- vb.pack_start(hb, expand=False, fill=False)
+ vbox.pack_start(self.custom_entry, expand=False, fill=False)
- hb = gtk.VBox()
- self.user_radio = gtk.RadioButton(group=self.custom_radio, label='Use this command:')
- hb.pack_start(self.user_radio)
- self.user_entry = gtk.Entry(max=100)
- #info = gtk.Label('Use this option to specify a program to be run every time the indicator refreshes')
- #info.set_line_wrap(True)
- #hb.pack_end(info)
- hb.pack_end(self.user_entry)
- vb.pack_start(hb, expand=False, fill=False)
-
- hb = gtk.HBox()
- l = gtk.Label('Update interval:')
- l.set_alignment(0, 0.5)
- hb.pack_start(l)
+ hbox = gtk.HBox()
+ label = gtk.Label(_('Update interval:'))
+ label.set_alignment(0, 0)
+ hbox.pack_start(label)
self.interval_entry = gtk.Entry(max=4)
- self.interval_entry.set_width_chars(3)
- hb.pack_end(self.interval_entry, expand=False, fill=False)
- vb.pack_start(hb, expand=False, fill=False)
-
- notebook.append_page(vb, gtk.Label('Advanced'))
-
- if not parent.sensors_disabled:
- sensors_list = SensorsListModel(self)
- if sensors_list.tree_store.get_iter_root() != None:
- vb.pack_start(sensors_list.get_view())
-
- # footer
- vb = gtk.VBox()
- vb.pack_start(notebook)
+ self.interval_entry.set_width_chars(5)
+
+ hbox.pack_end(self.interval_entry, expand=False, fill=False)
+ vbox.pack_start(hbox, expand=False, fill=False)
+
+ sensors_list = SensorsListModel(self)
+ vbox.pack_start(sensors_list.get_view())
+ notebook.append_page(vbox, gtk.Label(_('Advanced')))
+ # }}}
+
+ # footer {{{
+ vbox = self.get_content_area()
+ vbox.pack_start(notebook)
buttons = gtk.HButtonBox()
buttons.set_layout(gtk.BUTTONBOX_EDGE)
- test = gtk.Button('Test')
+ test = gtk.Button(_('Test'))
test.connect('clicked', self.update_parent)
buttons.pack_start(test)
+ # TODO: add an info message on hover
+
cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
- cancel.connect('clicked', self.on_destroy)
+ cancel.connect('clicked', self.on_cancel)
buttons.pack_end(cancel)
+
close = gtk.Button(stock=gtk.STOCK_SAVE)
close.connect('clicked', self.on_save)
buttons.pack_end(close)
- vb.pack_end(buttons, expand=False)
-
- self.add(vb)
- self.set_title('Preferences')
- self.resize(400, 350)
+ vbox.pack_end(buttons, expand=False)
+ # }}}
- def run(self):
- self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
- self.show_all()
- self.set_data()
- gtk.main()
-
- def on_destroy(self, event=None, data=None):
- self.hide()
- gtk.main_quit()
- return False
+ def on_save(self, evnt=None, data=None):
+ """The action of the save button."""
+ try:
+ self.update_parent()
+ except Exception as ex:
+ error_dialog = gtk.MessageDialog(
+ None, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_CLOSE, ex.message)
+ error_dialog.set_title("Error")
+ error_dialog.run()
+ error_dialog.destroy()
+ return False
- def on_save(self, event=None, data=None):
- self.update_parent()
self.ind_parent.save_settings()
self.update_autostart()
- self.on_destroy()
+ self.destroy()
- def on_cancel(self, event=None, data=None):
+ def on_cancel(self, evnt=None, data=None):
+ """The action of the cancel button."""
self.ind_parent.load_settings()
- self.on_destroy()
+ self.destroy()
- def set_data(self):
- self.custom_entry.set_text(self.ind_parent.custom_text)
- self.user_entry.set_text(self.ind_parent.user_command)
- self.interval_entry.set_text(str(self.ind_parent.interval))
- if self.ind_parent.mode_user:
- self.user_radio.set_active(True)
- else:
- self.custom_radio.set_active(True)
-
- def update_parent(self, event=None):
+ def update_parent(self, evnt=None, data=None):
+ """It gets the config info from the widgets and sets them to the vars.
+ It does NOT update the config file."""
custom_text = self.custom_entry.get_text()
- user_text = self.user_entry.get_text()
- try: interval = int(self.interval_entry.get_text()); assert interval > 0
- except: interval = self.ind_parent.interval
- mode_user = [r for r in self.custom_radio.get_group() if r.get_active()][0]
- self.ind_parent.custom_text = custom_text
- self.ind_parent.user_command = user_text
- self.ind_parent.mode_user = (mode_user == self.user_radio)
- self.ind_parent.interval = interval
- self.ind_parent.force_update()
+
+ # check if the sensers are supported
+ sensors = Preferences.sensors_regex.findall(custom_text)
+ for sensor in sensors:
+ sensor = sensor[1:-1]
+ if not Sensor.exists(sensor):
+ raise ISMError(_("{{{}}} sensor not supported.").
+ format(sensor))
+ # Check if the sensor is well-formed
+ Sensor.check(sensor)
+
+ try:
+ interval = float(self.interval_entry.get_text())
+ if interval <= 0:
+ raise ISMError(_("Interval value is not valid."))
+
+ except ValueError:
+ raise ISMError(_("Interval value is not valid."))
+
+ settings["custom_text"] = custom_text
+ settings["interval"] = interval
+ # TODO: on_startup
+ self.ind_parent.update_indicator_guide()
+
+ def set_data(self):
+ """It sets the widgets with the config data."""
+ self.custom_entry.set_text(settings["custom_text"])
+ self.interval_entry.set_text(str(settings["interval"]))
def update_autostart(self):
autostart = self.autostart_check.get_active()
if not autostart:
try:
os.remove(Preferences.AUTOSTART_PATH)
- except: pass
+ except:
+ pass
else:
try:
- shutil.copy(Preferences.DESKTOP_PATH, Preferences.AUTOSTART_PATH)
- except Exception as e:
- logging.exception(e)
+ shutil.copy(Preferences.DESKTOP_PATH,
+ Preferences.AUTOSTART_PATH)
+ except Exception, ex:
+ logging.exception(ex)
def get_autostart(self):
return os.path.exists(Preferences.AUTOSTART_PATH)
-class IndicatorSysmonitor:
+
+class IndicatorSysmonitor(object):
SETTINGS_FILE = os.getenv("HOME") + '/.indicator-sysmonitor.json'
SENSORS_DISABLED = False
def __init__(self):
- self.preferences_dialog = None
- self.custom_text = 'cpu: {cpu} mem: {mem}'
- self.user_command = ''
- self.last_data, self.last_text, self.last_guide = {}, '', ''
- self.mode_user = False
- self.sensors_disabled = IndicatorSysmonitor.SENSORS_DISABLED
- self.interval = 2
+ self._preferences_dialog = None
+ self._help_dialog = None
+ self._fetcher = StatusFetcher(self)
+ self.alive = Event()
- self.ind = appindicator.Indicator ("indicator-sysmonitor",
+ self.ind = appindicator.Indicator("indicator-sysmonitor",
"sysmonitor",
appindicator.CATEGORY_SYSTEM_SERVICES)
- self.ind.set_status (appindicator.STATUS_ACTIVE)
+ self.ind.set_status(appindicator.STATUS_ACTIVE)
self.ind.set_label("Init...")
- self.menu = gtk.Menu()
-
- full_sysmon = gtk.MenuItem('System Monitor')
- full_sysmon.connect('activate', self.on_full_sysmon_activated)
- self.menu.add(full_sysmon)
- self.menu.add(gtk.SeparatorMenuItem())
+ self._create_menu()
+ self.load_settings()
+ self.alive.set()
+ self._fetcher.start()
+ logging.info("Fetcher started")
- pref_menu = gtk.MenuItem('Preferences')
+ def _create_menu(self):
+ """Creates the main menu and shows it."""
+ # create menu {{{
+ menu = gtk.Menu()
+ # add System Monitor menu item
+ full_sysmon = gtk.MenuItem(_('System Monitor'))
+ full_sysmon.connect('activate', self.on_full_sysmon_activated)
+ menu.add(full_sysmon)
+ menu.add(gtk.SeparatorMenuItem())
+
+ # add preferences menu item
+ pref_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
pref_menu.connect('activate', self.on_preferences_activated)
- self.menu.add(pref_menu)
+ menu.add(pref_menu)
+
+ # add help menu item
+ help_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_HELP)
+ help_menu.connect('activate', self._on_help)
+ menu.add(help_menu)
+ #add preference menu item
exit_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
exit_menu.connect('activate', self.on_exit)
- self.menu.add(exit_menu)
-
- self.menu.show_all()
-
- self.ind.set_menu(self.menu)
+ menu.add(exit_menu)
+ menu.show_all()
+ self.ind.set_menu(menu)
logging.info("Menu shown")
+ # }}} menu done!
- self.load_settings()
+ def update_indicator_guide(self):
+ """Updates the label guide from appindicator."""
+ data = self._fetcher.fetch()
+ for key in data:
+ if key.startswith('fs'):
+ data[key] = '000gB'
+ break
- self.alive = Event()
- self.alive.set()
- self.fetch = StatusFetcher(self)
- self.fetch.start()
- logging.info("Fetcher started")
+ data['mem'] = data['cpu'] = data['bat'] = '000%'
+ data['net'] = '↓666kB/s ↑666kB/s'
+
+ guide = settings['custom_text'].format(**data)
+ self.ind.set_property("label-guide", guide)
def update(self, data):
+ """It updates the appindicator text with the the values
+ from data"""
try:
- label = self.custom_text.format(**data)
- cdata = deepcopy(data)
- cdata['mem'] = cdata['cpu'] = cdata['bat'] = '000%'
- cdata['net'] = ''
- guide = self.custom_text.format(**cdata)
- except KeyError as e:
- logging.exception(e)
- logging.info('not found in dataset')
- return
- except:
- label = 'Unknown error'
- if not label:
- label = '(no output)'
- self.last_data = data
- self.last_guide = guide
- self.update_text(label, guide)
-
- def update_text(self, text, guide):
- self.last_text = text
- self.last_guide = guide
- self.ind.set_label(text, guide)
-
- def force_update(self):
- if self.mode_user:
- self.update_text(self.last_text, self.last_guide)
- else:
- self.update(self.last_data)
+ label = settings["custom_text"].format(**data) if len(data)\
+ else _("(no output)")
- def on_exit(self, event=None):
- logging.info("Terminated")
- self.alive.clear()
- try: gtk.main_quit()
- except RuntimeError: pass
+ except KeyError as ex:
+ label = _("Invalid Sensor: {}").format(ex.message)
+ except Exception as ex:
+ logging.exception(ex)
+ label = _("Unknown error: ").format(ex.message)
+
+ self.ind.set_label(label)
def load_settings(self):
+ """It gets the settings from the config file and
+ sets them to the correct vars"""
try:
with open(IndicatorSysmonitor.SETTINGS_FILE, 'r') as f:
- settings = json.load(f)
- self.mode_user = settings['mode_user']
- self.custom_text = settings['custom_text']
- self.user_command = settings['user_command']
- self.interval = settings['interval']
- self.sensors_disabled = settings.get('sensors_disabled', IndicatorSysmonitor.SENSORS_DISABLED)
- except Exception as e:
- logging.exception(e)
+ cfg = json.load(f)
+
+ if cfg['custom_text'] is not None:
+ settings['custom_text'] = cfg['custom_text']
+ if cfg['interval'] is not None:
+ settings['interval'] = cfg['interval']
+ if cfg['on_startup'] is not None:
+ settings['on_startup'] = cfg['on_startup']
+ if cfg['sensors'] is not None:
+ settings['sensors'] = cfg['sensors']
+
+ Sensor.update_regex()
+ self.update_indicator_guide()
+
+ except Exception as ex:
+ logging.exception(ex)
logging.error('Reading settings failed')
- def save_settings(self):
+ @staticmethod
+ def save_settings():
+ """It stores the current settings to the config file."""
# TODO: use gsettings
- settings = {'mode_user': self.mode_user,
- 'custom_text': self.custom_text,
- 'user_command': self.user_command,
- 'interval': self.interval,
- 'sensors_disabled': self.sensors_disabled}
- try:
- with open(IndicatorSysmonitor.SETTINGS_FILE, 'w') as f:
- f.write(json.dumps(settings))
- except Exception as e:
- logging.exception(e)
+ try:
+ with open(IndicatorSysmonitor.SETTINGS_FILE, 'w') as f:
+ f.write(json.dumps(settings))
+
+ except Exception as ex:
+ logging.exception(ex)
logging.error('Writting settings failed')
+ # actions raised from menu
def on_preferences_activated(self, event=None):
- self.preferences_dialog = Preferences(self)
- self.preferences_dialog.run()
+ """Raises the preferences dialog. If it's already open, it's
+ focused"""
+ if self._preferences_dialog is not None:
+ self._preferences_dialog.present()
+ return
+
+ self._preferences_dialog = Preferences(self)
+ self._preferences_dialog.run()
+ self._preferences_dialog = None
def on_full_sysmon_activated(self, event=None):
os.system('gnome-system-monitor &')
-from optparse import OptionParser
+ def on_exit(self, event=None, data=None):
+ """Action call when the main programs is closed."""
+ # close the open dialogs
+ if self._help_dialog is not None:
+ self._help_dialog.destroy()
+
+ if self._preferences_dialog is not None:
+ self._preferences_dialog.destroy()
+
+ logging.info("Terminated")
+ self.alive.clear()
+ try:
+ gtk.main_quit()
+ except RuntimeError:
+ pass
+
+ def _on_help(self, event=None, data=None):
+ """Raise a dialog with info about the app."""
+ if self._help_dialog is not None:
+ self._help_dialog.present()
+ return
+
+ self._help_dialog = gtk.MessageDialog(
+ None, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO,
+ gtk.BUTTONS_OK, None)
+
+ self._help_dialog.set_title(_("Help"))
+ self._help_dialog.set_markup(HELP_MSG)
+ self._help_dialog.run()
+ self._help_dialog.destroy()
+ self._help_dialog = None
+
+
+from optparse import OptionParser # TODO: optparse is deprecated
if __name__ == "__main__":
- parser = OptionParser("usage: %prog [options]", version="%prog "+VERSION)
+ parser = OptionParser("usage: %prog [options]", version="%prog " + VERSION)
parser.add_option("--config", "", default=None,
- help="use custom config file")
- parser.add_option("--disable-sensors", action="store_true",
- help="disable sensors", default=False)
- parser.add_option("--enable-sensors", action="store_true",
- help="re-enable sensors", default=False)
+ help=_("Use custom config file."))
(options, args) = parser.parse_args()
if options.config:
if not os.path.exists(options.config):
- print options.config, "does not exist!"
+ logging.error(_("{} does not exist!").format(options.config))
sys.exit(-1)
+ logging.info(_("Using config file: {}").format(options.config))
IndicatorSysmonitor.SETTINGS_FILE = options.config
- # setup an instance with config
- i = IndicatorSysmonitor()
- if options.disable_sensors:
- i.sensors_disabled = True
- i.save_settings()
- logging.info("Sensors disabled")
-
- if options.enable_sensors:
- i.sensors_disabled = False
- i.save_settings()
- logging.info("Sensors enabled")
+ if not os.path.exists(IndicatorSysmonitor.SETTINGS_FILE):
+ IndicatorSysmonitor.save_settings()
+ # setup an instance with config
+ app = IndicatorSysmonitor()
try:
gtk.main()
except KeyboardInterrupt:
- i.on_exit()
+ app.on_exit()
diff -Nru indicator-sysmonitor-0.4.1~quantal/debian/changelog indicator-sysmonitor-0.4.2/debian/changelog
--- indicator-sysmonitor-0.4.1~quantal/debian/changelog 2012-11-07 18:21:10.000000000 +0000
+++ indicator-sysmonitor-0.4.2/debian/changelog 2012-11-27 16:24:08.000000000 +0000
@@ -1,9 +1,16 @@
-indicator-sysmonitor (0.4.1~quantal) quantal; urgency=low
+indicator-sysmonitor (0.4.2) quantal; urgency=low
+
+ * bump version for quantal
+ * refactor from Gabriel
+
+ -- Alex Eftimie (alexef) Tue, 27 Nov 2012 16:23:57 +0300
+
+indicator-sysmonitor (0.4.1) precise; urgency=low
* bump version for precise
* minor changes
- -- Umair Riaz Wed, 07 Nov 2012 20:20:37 +0200
+ -- Alex Eftimie (alexef) Sat, 30 Jun 2012 10:20:57 +0300
indicator-sysmonitor (0.3.9~oneiric0) oneiric; urgency=low
diff -Nru indicator-sysmonitor-0.4.1~quantal/debian/control indicator-sysmonitor-0.4.2/debian/control
--- indicator-sysmonitor-0.4.1~quantal/debian/control 2012-11-07 18:21:20.000000000 +0000
+++ indicator-sysmonitor-0.4.2/debian/control 2012-11-27 16:24:08.000000000 +0000
@@ -1,11 +1,11 @@
Source: indicator-sysmonitor
Section: gnome
Priority: extra
-Maintainer: Umair Riaz , Ubuntu MOTU Team
+Maintainer: Ubuntu MOTU Team
XSBC-Original-Maintainer: Alex Eftimie
Build-Depends: debhelper (>= 7), cdbs
Build-Depends-Indep: python, python-central
-Standards-Version: 3.9.3
+Standards-Version: 3.9.1
XS-Python-Version: all
Package: indicator-sysmonitor
diff -Nru indicator-sysmonitor-0.4.1~quantal/debian/copyright indicator-sysmonitor-0.4.2/debian/copyright
--- indicator-sysmonitor-0.4.1~quantal/debian/copyright 2012-11-07 18:21:30.000000000 +0000
+++ indicator-sysmonitor-0.4.2/debian/copyright 2012-11-27 16:24:08.000000000 +0000
@@ -1,5 +1,5 @@
-This package was debianized by Umair Riaz - http://www.NoobsLab.com
-
+This package was debianized by Alex Eftimie on
+Fri Mar 25 14:39:23 EET 2011
Upstream Author(s):
diff -Nru indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor indicator-sysmonitor-0.4.2/indicator-sysmonitor
--- indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor 2012-06-30 10:25:50.000000000 +0000
+++ indicator-sysmonitor-0.4.2/indicator-sysmonitor 2012-11-27 16:24:08.000000000 +0000
@@ -7,39 +7,211 @@
# Homepage: http://launchpad.net/indicator-sysmonitor
# License: GPL v3
#
+from gettext import gettext as _
+from gettext import textdomain, bindtextdomain
+textdomain("indicator-sysmonitor")
+bindtextdomain("indicator-sysmonitor", "./lang")
+
import sys
import os
import shutil
import json
-import string
import time
-from copy import deepcopy
from threading import Thread, Event
import subprocess
import psutil as ps
-
-import logging
-logging.basicConfig(file=sys.stderr,level=logging.INFO)
-
-import gobject
+import re
import gtk
+gtk.gdk.threads_init()
import appindicator
+import logging
+logging.basicConfig(file=sys.stderr, level=logging.INFO)
-VERSION='0.4.1~unreleased'
+B_UNITS = ['', 'KB', 'MB', 'GB', 'TB']
+VERSION = '0.5.1~unreleased'
+HELP_MSG = """{title}
+
+{introduction}
+
+{basic}
+• cpu: {cpu_desc}
+• mem: {mem_desc}
+• bat%d: {bat_desc}
+• net: {net_desc}
+
+{compose}
+• fs//mount-point : {fs_desc}
+
+{example}
+CPU {{cpu}} | MEM {{mem}} | root {{fs///}}
+""".format(
+ title=_("Help Page"),
+ introduction=_("The sensors are the names of the devices you want to \
+ retrieve information from. They must be placed between brackets."),
+ basic=_("The basics are:"),
+ cpu_desc=_("It shows the average of CPU usage."),
+ mem_desc=_("It shows the physical memory in use."),
+ bat_desc=_("It shows the available battery which id is %d."),
+ net_desc=_("It shows the amount of data you are downloading and uploading \
+ through your network."),
+ compose=_("Also there are the following sensors that are composed with \
+ two parts divided by two slashes."),
+ fs_desc=_("Show available space in the file system."),
+ example=_("Example:"))
+
+supported_sensors = re.compile("\A(mem|swap|cpu\d*|net|bat\d*|fs//.+)\Z")
+settings = {
+ 'custom_text': 'cpu: {cpu} mem: {mem}',
+ 'interval': 2,
+ 'on_startup': False,
+ 'sensors': {
+ # 'name' => (desc, cmd)
+ 'cpu\d*': (_('Average CPU usage'), True),
+ 'mem': (_('Physical memory in use.'), True),
+ 'net': (_('Network activity.'), True),
+ 'bat\d*': (_('Network activity.'), True),
+ 'fs//.+': (_('Available space in file system.'), True),
+ "swap": (_("Average swap usage"), True)
+
+ }
+ }
+
+
+class ISMError(Exception):
+ """General exception."""
+ def __init__(self, msg):
+ Exception.__init__(self, msg)
+
+
+def raise_dialog(parent, flags, type_, buttons, msg, title):
+ """It raise a dialog. It a blocking function."""
+ dialog = gtk.MessageDialog(
+ parent, flags, type_, buttons, msg)
+ dialog.set_title(title)
+ dialog.run()
+ dialog.destroy()
+
+
+class Sensor(object):
+ """Singleton"""
+ _instance = None
+ bat = re.compile("\Abat\d*\Z")
+ cpus = re.compile("\Acpu\d+\Z")
+
+ def __init__(self):
+ """It must not be called. Use Sensor.get_instace()
+ to retrieve an instance of this class."""
+ if Sensor._instance is not None:
+ raise Exception("Sensor class can not be instanceted twice.")
+ else:
+ Sensor._instance = self
+ self.update_regex()
+
+ @staticmethod
+ def update_regex(names=None):
+ if names is None:
+ names = settings["sensors"].keys()
+
+ reg = '|'.join(names)
+ reg = "\A({})\Z".format(reg)
+ global supported_sensors
+ supported_sensors = re.compile("{}".format(reg))
+
+ @classmethod
+ def get_instance(cls):
+ """Returns the unique instance of Sensor."""
+ if Sensor._instance is None:
+ Sensor._instance = Sensor()
+
+ return Sensor._instance
+
+ @staticmethod
+ def exists(name):
+ """Checks if the sensor name exists"""
+ return bool(supported_sensors.match(name))
+
+ @staticmethod
+ def check(sensor):
+ if sensor.startswith("fs//"):
+ path = sensor.split("//")[1]
+ if not os.path.exists(path):
+ raise ISMError(_("Path: {} doesn't exists.").format(path))
+
+ elif Sensor.cpus.match(sensor):
+ nber = int(sensor[3:])
+ if nber >= ps.NUM_CPUS:
+ raise ISMError(_("Invalid number of CPU."))
+
+ elif Sensor.bat.match(sensor):
+ bat_id = int(sensor[3:]) if len(sensor) > 3 else 0
+ if bat_id >= len(os.listdir("/proc/acpi/battery/")):
+ raise ISMError(_("Invalid number of Batterry."))
+
+ def add(self, name, desc, cmd):
+ """Adds a custom sensors."""
+ if Sensor.exists(name):
+ raise ISMError(_("Sensor name already in use."))
+
+ settings["sensors"][name] = (desc, cmd)
+ self.update_regex()
+
+ def delete(self, name):
+ """Deletes a custom sensors."""
+ sensors = settings['sensors']
+ names = sensors.keys()
+ if name not in names:
+ raise ISMError(_("Sensor is not defined."))
+
+ _desc, default = sensors[name]
+ if default is True:
+ raise ISMError(_("Can not delete default sensors."))
+
+ del sensors[name]
+ self.update_regex()
+
+ def edit(self, name, newname, desc, cmd):
+ """Edits a custom sensors."""
+ try:
+ sensors = settings['sensors']
+ _desc, default = sensors[name]
+
+ except KeyError:
+ raise ISMError(_("Sensor does not exists."))
+
+ if default is True:
+ raise ISMError(_("Can not edit default sensors."))
+ if newname != name:
+ if newname in sensors.keys():
+ raise ISMError(_("Sensor name already in use."))
+
+ sensors[newname] = (desc, cmd)
+ del sensors[name]
+ settings["custom_text"] = settings["custom_text"].replace(
+ name, newname)
+ self.update_regex()
-gtk.gdk.threads_init()
class StatusFetcher(Thread):
+ """It recollects the info about the sensors."""
+ digit_regex = re.compile(r'''\d+''')
+
def __init__(self, parent):
Thread.__init__(self)
- self.parent = parent
+ self._parent = parent
self.last = ps.cpu_times()
- def _fetch_cpu(self):
+ def _fetch_cpu(self, percpu=False):
+ if percpu:
+ return ps.cpu_percent(interval=0, percpu=True)
+
last = self.last
current = ps.cpu_times()
- total_time_passed = sum([v-last.__dict__[k] if not isinstance(v,list) else 0 for k,v in current.__dict__.iteritems()])
+ total_time_passed = sum(
+ [v - last.__dict__[k]
+ if not isinstance(v, list)
+ else 0
+ for k, v in current.__dict__.iteritems()])
sys_time = current.system - last.system
usr_time = current.user - last.user
@@ -53,546 +225,659 @@
else:
return 0
- def _fetch_mem(self):
- total_mem = subprocess.Popen("free -b | grep Mem | tr -s ' ' | cut -d\ -f 2",
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- free_mem = subprocess.Popen("free -b | grep Mem | tr -s ' ' | cut -d\ -f 4-",
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- free_mem = sum([int(i) for i in free_mem.split()])
- return 100 - 100 * float(free_mem) / float(total_mem)
+ def _fetch_swap(self):
+ """Return the swap usage in percent"""
+ usage = 0
+ total = 0
+ try:
+ with open("/proc/swaps") as swaps:
+ swaps.readline()
+ for line in swaps.readlines():
+ dummy, dummy, total_, usage_, dummy = line.split()
+ total += int(total_)
+ usage += int(usage_)
- def _fetch_bat(self, bat_id):
- current_bat = subprocess.Popen("grep 'remaining capacity:' /proc/acpi/battery/BAT%s/state |awk '{print $3}' |grep [0-9] || echo 10" % bat_id,
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- #total_bat = subprocess.Popen("cat /proc/acpi/battery/BAT0/info |grep -i 'design capacity:' |awk '{print $3}'",
- total_bat = subprocess.Popen("grep 'last full capacity:' /proc/acpi/battery/BAT%s/info |awk '{print $4}' |grep [0-9] || echo 65130" % bat_id,
- stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- return 100 * float(current_bat) / float(total_bat)
+ return usage * 100 / total
- def _fetch_net(self):
- total_net = subprocess.Popen("ifstat -a -n -q -S -T 0.5 1 | tail -1 | awk '{ printf(\"↓%dkB/s ↑%dkB/s\", $6, $7) }'", stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- return total_net
+ except IOError:
+ return "N/A"
- def _fetch_sensor(self, sensor_name):
- sensor_data = sensor_name.split('//')
+ def _fetch_mem(self):
+ """It gets the total memory info and return the used in percent."""
+ with open('/proc/meminfo') as meminfo:
+ total = StatusFetcher.digit_regex.findall(meminfo.readline()).pop()
+ free = StatusFetcher.digit_regex.findall(meminfo.readline()).pop()
+ meminfo.readline()
+ cached = StatusFetcher.digit_regex.findall(
+ meminfo.readline()).pop()
+ free = int(free) + int(cached)
+ return 100 - 100 * free / float(total)
- if (len(sensor_data) != 2):
- return 'N/A'
+ def _fetch_bat(self, batid):
+ """Fetch the the amount of remaining battery"""
+ try:
+ with open("/proc/acpi/battery/BAT{}/state".format(batid)) as state:
+ while True:
+ line = state.readline()
+ if "remaining capacity:" in line:
+ remaining = int(line.split()[2])
+ break
+
+ with open("/proc/acpi/battery/BAT{}/info".format(batid)) as info:
+ while True:
+ line = info.readline()
+ if "last full capacity" in line:
+ capacity = int(line.split()[3])
+ break
- sensor_item = sensor_data[1].replace('-', '.')
- postfix = ''
- if sensor_data[0] == 'hddtemp':
- sensor_cmd = 'netcat localhost 7634'
- netcat_value = subprocess.Popen(sensor_cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- sensor_value = ''
-
- for hd_data in self.parse_hddtemp_entries(netcat_value):
- if hd_data[0] == sensor_item:
- sensor_value = hd_data[2] + '°' + hd_data[3]
- else:
- sensor_cmd = 'sensors -A ' + sensor_data[0] + " | grep -i '" + sensor_item + "' | cut -f 2 -d ':' | awk '{print $1}'"
- if sensor_data[0] == 'nvidia':
- if sensor_item == 'gputemp':
- sensor_cmd = 'nvidia-settings -q [gpu:0]/GPUCoreTemp | grep "Attribute" | sed -e "s/.*: //g" -e "s/\.//g"'
- postfix = '°C'
- elif sensor_item == 'fanspeed':
- sensor_cmd = 'nvidia-settings -q [fan:0]/GPUCurrentFanSpeed | grep "Attribute" | sed -e "s/.*: //g" -e "s/\.//g"'
- postfix = ' RPM'
- else:
- sensor_cmd = None
- elif sensor_data[0] == 'ati':
- if sensor_item == 'gputemp':
- sensor_cmd = 'aticonfig --adapter=0 --od-gettemperature | grep "Sensor 0:" | awk ' + "'{ printf(\"%d\", $5) }'"
- postfix = '°C'
-
- sensor_value = subprocess.Popen(sensor_cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
-
- if len(sensor_value):
- sensor_value = self._clean_value(sensor_value) # Cleaning the + prefix
- return sensor_value + postfix
- else:
- if sensor_cmd == None:
- logging.info('Sensor not supported: ' + sensor_name)
- else:
- logging.info('Sensor command failed:\n' + sensor_cmd)
- return 'N/A'
-
- def _clean_value(self, value):
- if value.find('+') == 0:
- return value[1:]
- return value
-
- def _get_sensors_to_fetch(self):
- fmt = string.Formatter()
- tokens = []
- for token in fmt.parse(self.parent.custom_text):
- tokens.append(str(token[1]))
+ except IOError:
+ return "N/A"
+
+ return remaining * 100.0 / capacity
- return tokens
+ def _fetch_net(self):
+ """It use the command "ifstat" to know the total net usage"""
+ total_net = subprocess.Popen(
+ "ifstat -a -n -q -S -T 0.5 1 | tail -1 | awk \
+ '{ printf(\"↓%dkB/s ↑%dkB/s\", $6, $7) }'",
+ stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
+ return total_net
- def _fetch(self):
+ def fetch(self):
+ """Return a dict whose element are the sensors
+ and their values"""
res = {}
- for sensor in self._get_sensors_to_fetch():
+ cpus = None
+ for sensor in Preferences.sensors_regex.findall(
+ settings["custom_text"]):
+ sensor = sensor[1:-1]
if sensor == 'cpu':
- res['cpu'] = '%02.0f%%' % self._fetch_cpu()
+ res['cpu'] = "{:02.0f}%".format(self._fetch_cpu())
+ elif Sensor.cpus.match(sensor):
+ if cpus is None:
+ cpus = self._fetch_cpu(percpu=True)
+ res[sensor] = "{:02.0f}%".format(cpus[int(sensor[3:])])
+
elif sensor == 'mem':
- res['mem'] = '%02.0f%%' % self._fetch_mem()
- elif sensor.startswith('bat'):
- bat_id = sensor[3:]
- try:
- bat_id = int(bat_id)
- except:
- bat_id = 0
- res[sensor] = '%02.0f%%' % self._fetch_bat(bat_id)
+ res['mem'] = '{:02.0f}%'.format(self._fetch_mem())
elif sensor == 'net':
res['net'] = self._fetch_net()
- else:
- res[sensor] = '%s' % self._fetch_sensor(sensor)
+
+ elif Sensor.bat.match(sensor):
+ bat_id = int(sensor[3:]) if len(sensor) > 3 else 0
+ res[sensor] = '{:02.0f}%'.format(self._fetch_bat(bat_id))
+
+ elif sensor.startswith('fs//'):
+ parts = sensor.split('//')
+ res[sensor] = self._fetch_fs(parts[1])
+
+ elif sensor == "swap":
+ res[sensor] = '{:02.0f}%'.format(self._fetch_swap())
+
+ else: # custom sensor
+ res[sensor] = self._exec(settings["sensors"][sensor][1])
return res
- def _fetch_user(self, command):
+ def _exec(self, command):
+ """Execute a custom command."""
try:
- output = subprocess.Popen(command, stdout=subprocess.PIPE,
- shell=True).communicate()[0].strip()
- if output == '':
- output = '(no output)'
+ output = subprocess.Popen(command, stdout=subprocess.PIPE,
+ shell=True).communicate()[0].strip()
except:
- output = 'error'
- logging.error('Error running: '+command)
- return output
-
- def parse_hddtemp_entries(self, netcat_value):
- hddtemp_entries = []
-
- if len(netcat_value):
- for hd_row in netcat_value.strip('|').split('||'):
- hddtemp_entries.append(hd_row.split('|'))
+ output = _("Error")
+ logging.error(_("Error running: {}").format(command))
+
+ return output if output else _("(no output)")
- return hddtemp_entries
+ def _fetch_fs(self, mount_point):
+ """It returns the amount of bytes available in the fs in
+ a human-readble format."""
+ if not os.access(mount_point, os.F_OK):
+ return None
+
+ stat = os.statvfs(mount_point)
+ bytes_ = stat.f_bavail * stat.f_frsize
+
+ for unit in B_UNITS:
+ if bytes_ < 1024:
+ return "{} {}".format(bytes_, unit)
+ bytes_ /= 1024
def run(self):
- while(self.parent.alive.isSet()):
- if self.parent.mode_user:
- output = self._fetch_user(self.parent.user_command)
- self.parent.update_text(output, output)
- else:
- data = self._fetch()
- self.parent.update(data)
- time.sleep(self.parent.interval)
+ """It is the main loop."""
+ while self._parent.alive.isSet():
+ data = self.fetch()
+ self._parent.update(data)
+ time.sleep(settings["interval"])
+
+
+class SensorsListModel(object):
+ """A TreeView showing the available sensors. It allows to
+ add/edit/delete custom sensors."""
-class SensorsListModel:
def __init__(self, parent):
self.ind_parent = parent
- self.tree_store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
+ self._list_store = gtk.ListStore(str, str)
+ self._tree_view = gtk.TreeView(self._list_store)
- # lib sensors
- for sensor_data in subprocess.Popen('sensors', stdout=subprocess.PIPE, shell=True).communicate()[0].split('\n\n'):
- sensor_name = None
- skip_line = False
- for line in sensor_data.split('\n'):
- if len(line):
- if skip_line:
- skip_line = False
- else:
- if sensor_name == None:
- logging.info("New sensor found: " + line)
- sensor_name = line
- parent = self.tree_store.append(None, (sensor_name, None, False))
- skip_line = True
- else:
- logging.info("Sensor entry: " + line)
- self.tree_store.append(parent, (line, self.generate_sensor_item_name(sensor_name, line), False))
-
- # hddtemp
- hddtemp = subprocess.Popen('netcat localhost 7634', stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- if len(hddtemp):
- sensor_name = 'hddtemp'
- logging.info("New sensor found: " + sensor_name)
- parent = self.tree_store.append(None, (sensor_name, None, False))
- for hd_data in self.ind_parent.ind_parent.fetch.parse_hddtemp_entries(hddtemp):
- logging.info("Sensor entry: " + hd_data[0])
- self.tree_store.append(parent, (hd_data[0] + ' - ' + hd_data[1] + ' - ' + hd_data[2] + '°' + hd_data[3], self.generate_sensor_item_name(sensor_name, hd_data[0]), False))
-
- # nvidia GPU
- nvidia_model = subprocess.Popen("lspci | grep nVidia | sed -e 's/.*\[//g' -e 's/\].*//g'", stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- if len(nvidia_model):
- sensor_name = 'nvidia'
- logging.info("New sensor found: " + sensor_name)
- parent = self.tree_store.append(None, (sensor_name, None, False))
- self.tree_store.append(parent, (nvidia_model + ' - Temperature', self.generate_sensor_item_name(sensor_name, 'gputemp'), False))
- self.tree_store.append(parent, (nvidia_model + ' - Fan speed', self.generate_sensor_item_name(sensor_name, 'fanspeed'), False))
-
- # ati GPU
- ati_model = subprocess.Popen('lspci | grep \"ATI Radeon HD"' + " | sed -e 's/.*\[//g' -e 's/\].*//g'", stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
- if len(ati_model):
- sensor_name = 'ati'
- logging.info("New sensor found: " + sensor_name)
- parent = self.tree_store.append(None, (sensor_name, None, False))
- self.tree_store.append(parent, (ati_model + ' - Temperature', self.generate_sensor_item_name(sensor_name, 'gputemp'), False))
+ sensors = settings['sensors']
+ for name in sensors.keys():
+ self._list_store.append([name, sensors[name][0]])
def get_view(self):
- self.view = gtk.HBox()
-
- self.vb = gtk.VBox(False, 3)
- l = gtk.Label('Sensors:')
- l.set_alignment(0, 0.5)
- self.vb.pack_start(l, expand=False, fill=False)
-
- self.tree_view = gtk.TreeView(self.tree_store)
-
- # setup the text cell renderer
- self.renderer = gtk.CellRendererText()
- self.renderer.set_property('editable', False)
-
- # sensor name render
- self.renderer1 = gtk.CellRendererText()
- self.renderer1.set_property('editable', False)
-
- # quick add checkbox render
- self.renderer2 = gtk.CellRendererToggle()
- self.renderer2.set_property('activatable', True)
- self.renderer2.connect('toggled', self.quick_add_cb_toggled, self.tree_store)
-
+ """It's call from Preference. It create the view and return it"""
+ vbox = gtk.VBox(False, 3)
# create columns
- self.column0 = gtk.TreeViewColumn('Sensor', self.renderer, text=0)
- self.column1 = gtk.TreeViewColumn('Identifier', self.renderer1, text=1)
- self.column2 = gtk.TreeViewColumn('', self.renderer2, active=2)
- self.tree_view.append_column(self.column0)
- self.tree_view.append_column(self.column1)
- self.tree_view.append_column(self.column2)
+ renderer = gtk.CellRendererText()
+ renderer.set_property('editable', False)
+ column = gtk.TreeViewColumn(_('Sensor'), renderer, text=0)
+ self._tree_view.append_column(column)
+
+ renderer = gtk.CellRendererText()
+ renderer.set_property('editable', False)
+ column = gtk.TreeViewColumn(_('Description'), renderer, text=1)
+ self._tree_view.append_column(column)
- self.tree_view.expand_all()
+ self._tree_view.expand_all()
sw = gtk.ScrolledWindow()
- sw.add_with_viewport(self.tree_view)
- self.vb.pack_start(sw, fill=True, expand=True)
+ sw.add_with_viewport(self._tree_view)
+ vbox.pack_start(sw, fill=True, expand=True)
- self.add_bt = gtk.Button('Add selected sensors')
- self.add_bt.connect('clicked', self.update_custom_text)
- self.vb.pack_end(self.add_bt, fill=False, expand=False)
-
- self.view.add(self.vb)
- self.view.show()
-
- return self.view
-
- def quick_add_cb_toggled(self, cell, path, tree_store):
- tree_store[path][2] = not tree_store[path][2]
- iter = tree_store.iter_children(tree_store.get_iter(path))
- while iter:
- tree_store.set_value(iter, 2, tree_store[path][2])
- iter = tree_store.iter_next(iter)
-
- def generate_sensor_item_name(self, sensor_name, sensor_item_label):
- return sensor_name + '//' + sensor_item_label.split(':')[0].lower().replace('.', '-')
-
- def update_custom_text(self, event=None):
- iter = self.tree_store.get_iter_root()
-
- while iter:
- iter_children = self.tree_store.iter_children(iter)
- while iter_children:
- if self.tree_store.get_value(iter_children, 2):
- current_text = self.ind_parent.custom_entry.get_text()
- to_add_value = '{' + self.tree_store.get_value(iter_children, 1) + '}'
- if string.find(current_text, to_add_value) == -1:
- self.ind_parent.custom_entry.set_text(current_text + ' ' + to_add_value)
- iter_children = self.tree_store.iter_next(iter_children)
+ # add buttons
+ hbox = gtk.HBox()
+ new_button = gtk.Button(stock=gtk.STOCK_NEW)
+ new_button.connect('clicked', self._on_edit_sensor)
+ # hbox.pack_start(new_button, fill=False, expand=False)
+ hbox.pack_start(new_button, fill=False, expand=False)
+
+ edit_button = gtk.Button(stock=gtk.STOCK_EDIT)
+ edit_button.connect('clicked', self._on_edit_sensor, False)
+ hbox.pack_start(edit_button, fill=False, expand=False)
+
+ del_button = gtk.Button(stock=gtk.STOCK_DELETE)
+ del_button.connect('clicked', self._on_del_sensor)
+ hbox.pack_start(del_button, fill=False, expand=False)
+
+ add_button = gtk.Button(stock=gtk.STOCK_ADD)
+ add_button.connect('clicked', self._on_add_sensor)
+ hbox.pack_end(add_button, fill=False, expand=False)
+ vbox.pack_end(hbox, fill=False, expand=False)
+
+ frame = gtk.Frame(_('Sensors'))
+ frame.add(vbox)
+ return frame
+
+ def _get_selected_row(self):
+ """Returns an iter for the selected rows in the view or None."""
+ model, pathlist = self._tree_view.get_selection().get_selected_rows()
+ if len(pathlist):
+ path = pathlist.pop()
+ return model.get_iter(path)
+ return None
+
+ def _on_add_sensor(self, evnt=None, data=None):
+ tree_iter = self._get_selected_row()
+ if tree_iter is None:
+ return
- iter = self.tree_store.iter_next(iter)
+ sensor = self._list_store.get_value(tree_iter, 0)
+ self.ind_parent.custom_entry.insert_text(
+ "{{{}}}".format(sensor), -1)
+
+ def _on_edit_sensor(self, evnt=None, blank=True):
+ """Raises a dialog with a form to add/edit a sensor"""
+ name = desc = cmd = ""
+ tree_iter = None
+ if not blank:
+ # edit, so get the info from the selected row
+ tree_iter = self._get_selected_row()
+ if tree_iter is None:
+ return
+
+ name = self._list_store.get_value(tree_iter, 0)
+ desc = self._list_store.get_value(tree_iter, 1)
+ cmd = settings["sensors"][name][1]
+
+ if cmd is True: # default sensor
+ raise_dialog(
+ self.ind_parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ _("Can not edit defualt sensors."), _("Error"))
+ return
+
+ dialog = gtk.Dialog(_("Edit Sensor"), self.ind_parent,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+ gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+ vbox = dialog.get_content_area()
+
+ hbox = gtk.HBox()
+ label = gtk.Label(_("Sensor"))
+ sensor_entry = gtk.Entry()
+ sensor_entry.set_text(name)
+ hbox.pack_start(label)
+ hbox.pack_end(sensor_entry)
+ vbox.pack_start(hbox)
+
+ hbox = gtk.HBox()
+ label = gtk.Label(_("Description"))
+ desc_entry = gtk.Entry()
+ desc_entry.set_text(desc)
+ hbox.pack_start(label)
+ hbox.pack_end(desc_entry)
+ vbox.pack_start(hbox)
+
+ hbox = gtk.HBox()
+ label = gtk.Label(_("Command"))
+ cmd_entry = gtk.Entry()
+
+ cmd_entry.set_text(cmd)
+ hbox.pack_start(label)
+ hbox.pack_end(cmd_entry)
+ vbox.pack_end(hbox)
-class Preferences(gtk.Window):
- AUTOSTART_PATH = os.getenv("HOME") + '/.config/autostart/indicator-sysmonitor.desktop'
+ dialog.show_all()
+ response = dialog.run()
+
+ if response == gtk.RESPONSE_ACCEPT:
+ try:
+ newname, desc, cmd = str(sensor_entry.get_text()), \
+ str(desc_entry.get_text()), str(cmd_entry.get_text())
+
+ if blank:
+ Sensor.get_instance().add(newname, desc, cmd)
+ else:
+ Sensor.get_instance().edit(name, newname, desc, cmd)
+ self._list_store.remove(tree_iter)
+
+ self._list_store.append([newname, desc])
+ ctext = self.ind_parent.custom_entry.get_text()
+ self.ind_parent.custom_entry.set_text(
+ ctext.replace(name, newname))
+
+ except ISMError as ex:
+ raise_dialog(
+ self.ind_parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ ex.message, _("Error"))
+
+ dialog.destroy()
+
+ def _on_del_sensor(self, evnt=None, data=None):
+ """Remove a custom sensor."""
+ tree_iter = self._get_selected_row()
+ if tree_iter is None:
+ return
+
+ name = self._list_store.get_value(tree_iter, 0)
+ try:
+ Sensor.get_instance().delete(name)
+ self._list_store.remove(tree_iter)
+ ctext = self.ind_parent.custom_entry.get_text()
+ self.ind_parent.custom_entry.set_text(
+ ctext.replace("{{{}}}".format(name), ""))
+
+ except ISMError as ex:
+ raise_dialog(
+ self.ind_parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
+ gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ ex.message, _("Error"))
+
+
+class Preferences(gtk.Dialog):
+ """It define the the Preferences Dialog and its operations."""
+ AUTOSTART_PATH = '{}/.config/autostart/indicator-sysmonitor.desktop'\
+ .format(os.getenv("HOME"))
DESKTOP_PATH = '/usr/share/applications/indicator-sysmonitor.desktop'
+ sensors_regex = re.compile("{.+?}")
def __init__(self, parent):
- gtk.Window.__init__(self)
+ """It creates the widget of the dialogs"""
+ gtk.Dialog.__init__(self)
self.ind_parent = parent
- self.connect('delete-event', self.on_destroy)
+ self.custom_entry = None
+ self.interval_entry = None
+ self._create_content()
+ self.set_data()
+ self.show_all()
+ def _create_content(self):
+ """It creates the content for this dialog."""
+ self.connect('delete-event', self.on_cancel)
+ self.set_title(_('Preferences'))
+ self.resize(400, 350)
+ self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
notebook = gtk.Notebook()
notebook.set_border_width(4)
- vb = gtk.VBox(spacing=3)
- hb = gtk.HBox()
- hb.set_border_width(4)
- l = gtk.Label('Run on startup:')
- l.set_alignment(0, 0.5)
- hb.pack_start(l)
+ # General page of the notebook {{{
+ vbox = gtk.VBox(spacing=3)
+ hbox = gtk.HBox()
+
+ hbox.set_border_width(4)
+ label = gtk.Label(_('Run on startup:'))
+ label.set_alignment(0, 0.5)
+ hbox.pack_start(label)
self.autostart_check = gtk.CheckButton()
self.autostart_check.set_active(self.get_autostart())
- hb.pack_end(self.autostart_check, expand=False, fill=False)
- vb.pack_start(hb, expand=False, fill=False)
+ hbox.pack_end(self.autostart_check, expand=False, fill=False)
+ vbox.pack_start(hbox, expand=False, fill=False)
- hb = gtk.HBox()
- l = gtk.Label('This is indicator-sysmonitor version: %s' % VERSION)
- l.set_alignment(0.5, 0.5)
- hb.pack_start(l)
- vb.pack_end(hb)
-
- notebook.append_page(vb, gtk.Label('General'))
-
- vb = gtk.VBox(spacing=3)
- hb = gtk.VBox()
- self.custom_radio = gtk.RadioButton(label='Customize output:')
- hb.pack_start(self.custom_radio)
+ hbox = gtk.HBox()
+ label = gtk.Label(
+ _('This is indicator-sysmonitor version: {}').format(VERSION))
+ label.set_alignment(0.5, 0.5)
+ hbox.pack_start(label)
+ vbox.pack_end(hbox)
+ notebook.append_page(vbox, gtk.Label(_('General')))
+ # }}}
+
+ # Advanced page in notebook {{{
+ vbox = gtk.VBox() # main box
+ label = gtk.Label(_('Customize output:'))
+ label.set_alignment(0, 0)
+ vbox.pack_start(label, expand=False, fill=False)
self.custom_entry = gtk.Entry()
- hb.pack_end(self.custom_entry)
- vb.pack_start(hb, expand=False, fill=False)
+ vbox.pack_start(self.custom_entry, expand=False, fill=False)
- hb = gtk.VBox()
- self.user_radio = gtk.RadioButton(group=self.custom_radio, label='Use this command:')
- hb.pack_start(self.user_radio)
- self.user_entry = gtk.Entry(max=100)
- #info = gtk.Label('Use this option to specify a program to be run every time the indicator refreshes')
- #info.set_line_wrap(True)
- #hb.pack_end(info)
- hb.pack_end(self.user_entry)
- vb.pack_start(hb, expand=False, fill=False)
-
- hb = gtk.HBox()
- l = gtk.Label('Update interval:')
- l.set_alignment(0, 0.5)
- hb.pack_start(l)
+ hbox = gtk.HBox()
+ label = gtk.Label(_('Update interval:'))
+ label.set_alignment(0, 0)
+ hbox.pack_start(label)
self.interval_entry = gtk.Entry(max=4)
- self.interval_entry.set_width_chars(3)
- hb.pack_end(self.interval_entry, expand=False, fill=False)
- vb.pack_start(hb, expand=False, fill=False)
-
- notebook.append_page(vb, gtk.Label('Advanced'))
-
- if not parent.sensors_disabled:
- sensors_list = SensorsListModel(self)
- if sensors_list.tree_store.get_iter_root() != None:
- vb.pack_start(sensors_list.get_view())
-
- # footer
- vb = gtk.VBox()
- vb.pack_start(notebook)
+ self.interval_entry.set_width_chars(5)
+
+ hbox.pack_end(self.interval_entry, expand=False, fill=False)
+ vbox.pack_start(hbox, expand=False, fill=False)
+
+ sensors_list = SensorsListModel(self)
+ vbox.pack_start(sensors_list.get_view())
+ notebook.append_page(vbox, gtk.Label(_('Advanced')))
+ # }}}
+
+ # footer {{{
+ vbox = self.get_content_area()
+ vbox.pack_start(notebook)
buttons = gtk.HButtonBox()
buttons.set_layout(gtk.BUTTONBOX_EDGE)
- test = gtk.Button('Test')
+ test = gtk.Button(_('Test'))
test.connect('clicked', self.update_parent)
buttons.pack_start(test)
+ # TODO: add an info message on hover
+
cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
- cancel.connect('clicked', self.on_destroy)
+ cancel.connect('clicked', self.on_cancel)
buttons.pack_end(cancel)
+
close = gtk.Button(stock=gtk.STOCK_SAVE)
close.connect('clicked', self.on_save)
buttons.pack_end(close)
- vb.pack_end(buttons, expand=False)
-
- self.add(vb)
- self.set_title('Preferences')
- self.resize(400, 350)
+ vbox.pack_end(buttons, expand=False)
+ # }}}
- def run(self):
- self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
- self.show_all()
- self.set_data()
- gtk.main()
-
- def on_destroy(self, event=None, data=None):
- self.hide()
- gtk.main_quit()
- return False
+ def on_save(self, evnt=None, data=None):
+ """The action of the save button."""
+ try:
+ self.update_parent()
+ except Exception as ex:
+ error_dialog = gtk.MessageDialog(
+ None, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_CLOSE, ex.message)
+ error_dialog.set_title("Error")
+ error_dialog.run()
+ error_dialog.destroy()
+ return False
- def on_save(self, event=None, data=None):
- self.update_parent()
self.ind_parent.save_settings()
self.update_autostart()
- self.on_destroy()
+ self.destroy()
- def on_cancel(self, event=None, data=None):
+ def on_cancel(self, evnt=None, data=None):
+ """The action of the cancel button."""
self.ind_parent.load_settings()
- self.on_destroy()
+ self.destroy()
- def set_data(self):
- self.custom_entry.set_text(self.ind_parent.custom_text)
- self.user_entry.set_text(self.ind_parent.user_command)
- self.interval_entry.set_text(str(self.ind_parent.interval))
- if self.ind_parent.mode_user:
- self.user_radio.set_active(True)
- else:
- self.custom_radio.set_active(True)
-
- def update_parent(self, event=None):
+ def update_parent(self, evnt=None, data=None):
+ """It gets the config info from the widgets and sets them to the vars.
+ It does NOT update the config file."""
custom_text = self.custom_entry.get_text()
- user_text = self.user_entry.get_text()
- try: interval = int(self.interval_entry.get_text()); assert interval > 0
- except: interval = self.ind_parent.interval
- mode_user = [r for r in self.custom_radio.get_group() if r.get_active()][0]
- self.ind_parent.custom_text = custom_text
- self.ind_parent.user_command = user_text
- self.ind_parent.mode_user = (mode_user == self.user_radio)
- self.ind_parent.interval = interval
- self.ind_parent.force_update()
+
+ # check if the sensers are supported
+ sensors = Preferences.sensors_regex.findall(custom_text)
+ for sensor in sensors:
+ sensor = sensor[1:-1]
+ if not Sensor.exists(sensor):
+ raise ISMError(_("{{{}}} sensor not supported.").
+ format(sensor))
+ # Check if the sensor is well-formed
+ Sensor.check(sensor)
+
+ try:
+ interval = float(self.interval_entry.get_text())
+ if interval <= 0:
+ raise ISMError(_("Interval value is not valid."))
+
+ except ValueError:
+ raise ISMError(_("Interval value is not valid."))
+
+ settings["custom_text"] = custom_text
+ settings["interval"] = interval
+ # TODO: on_startup
+ self.ind_parent.update_indicator_guide()
+
+ def set_data(self):
+ """It sets the widgets with the config data."""
+ self.custom_entry.set_text(settings["custom_text"])
+ self.interval_entry.set_text(str(settings["interval"]))
def update_autostart(self):
autostart = self.autostart_check.get_active()
if not autostart:
try:
os.remove(Preferences.AUTOSTART_PATH)
- except: pass
+ except:
+ pass
else:
try:
- shutil.copy(Preferences.DESKTOP_PATH, Preferences.AUTOSTART_PATH)
- except Exception as e:
- logging.exception(e)
+ shutil.copy(Preferences.DESKTOP_PATH,
+ Preferences.AUTOSTART_PATH)
+ except Exception, ex:
+ logging.exception(ex)
def get_autostart(self):
return os.path.exists(Preferences.AUTOSTART_PATH)
-class IndicatorSysmonitor:
+
+class IndicatorSysmonitor(object):
SETTINGS_FILE = os.getenv("HOME") + '/.indicator-sysmonitor.json'
SENSORS_DISABLED = False
def __init__(self):
- self.preferences_dialog = None
- self.custom_text = 'cpu: {cpu} mem: {mem}'
- self.user_command = ''
- self.last_data, self.last_text, self.last_guide = {}, '', ''
- self.mode_user = False
- self.sensors_disabled = IndicatorSysmonitor.SENSORS_DISABLED
- self.interval = 2
+ self._preferences_dialog = None
+ self._help_dialog = None
+ self._fetcher = StatusFetcher(self)
+ self.alive = Event()
- self.ind = appindicator.Indicator ("indicator-sysmonitor",
+ self.ind = appindicator.Indicator("indicator-sysmonitor",
"sysmonitor",
appindicator.CATEGORY_SYSTEM_SERVICES)
- self.ind.set_status (appindicator.STATUS_ACTIVE)
+ self.ind.set_status(appindicator.STATUS_ACTIVE)
self.ind.set_label("Init...")
- self.menu = gtk.Menu()
-
- full_sysmon = gtk.MenuItem('System Monitor')
- full_sysmon.connect('activate', self.on_full_sysmon_activated)
- self.menu.add(full_sysmon)
- self.menu.add(gtk.SeparatorMenuItem())
+ self._create_menu()
+ self.load_settings()
+ self.alive.set()
+ self._fetcher.start()
+ logging.info("Fetcher started")
- pref_menu = gtk.MenuItem('Preferences')
+ def _create_menu(self):
+ """Creates the main menu and shows it."""
+ # create menu {{{
+ menu = gtk.Menu()
+ # add System Monitor menu item
+ full_sysmon = gtk.MenuItem(_('System Monitor'))
+ full_sysmon.connect('activate', self.on_full_sysmon_activated)
+ menu.add(full_sysmon)
+ menu.add(gtk.SeparatorMenuItem())
+
+ # add preferences menu item
+ pref_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
pref_menu.connect('activate', self.on_preferences_activated)
- self.menu.add(pref_menu)
+ menu.add(pref_menu)
+
+ # add help menu item
+ help_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_HELP)
+ help_menu.connect('activate', self._on_help)
+ menu.add(help_menu)
+ #add preference menu item
exit_menu = gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
exit_menu.connect('activate', self.on_exit)
- self.menu.add(exit_menu)
-
- self.menu.show_all()
-
- self.ind.set_menu(self.menu)
+ menu.add(exit_menu)
+ menu.show_all()
+ self.ind.set_menu(menu)
logging.info("Menu shown")
+ # }}} menu done!
- self.load_settings()
+ def update_indicator_guide(self):
+ """Updates the label guide from appindicator."""
+ data = self._fetcher.fetch()
+ for key in data:
+ if key.startswith('fs'):
+ data[key] = '000gB'
+ break
- self.alive = Event()
- self.alive.set()
- self.fetch = StatusFetcher(self)
- self.fetch.start()
- logging.info("Fetcher started")
+ data['mem'] = data['cpu'] = data['bat'] = '000%'
+ data['net'] = '↓666kB/s ↑666kB/s'
+
+ guide = settings['custom_text'].format(**data)
+ self.ind.set_property("label-guide", guide)
def update(self, data):
+ """It updates the appindicator text with the the values
+ from data"""
try:
- label = self.custom_text.format(**data)
- cdata = deepcopy(data)
- cdata['mem'] = cdata['cpu'] = cdata['bat'] = '000%'
- cdata['net'] = ''
- guide = self.custom_text.format(**cdata)
- except KeyError as e:
- logging.exception(e)
- logging.info('not found in dataset')
- return
- except:
- label = 'Unknown error'
- if not label:
- label = '(no output)'
- self.last_data = data
- self.last_guide = guide
- self.update_text(label, guide)
-
- def update_text(self, text, guide):
- self.last_text = text
- self.last_guide = guide
- self.ind.set_label(text, guide)
-
- def force_update(self):
- if self.mode_user:
- self.update_text(self.last_text, self.last_guide)
- else:
- self.update(self.last_data)
+ label = settings["custom_text"].format(**data) if len(data)\
+ else _("(no output)")
- def on_exit(self, event=None):
- logging.info("Terminated")
- self.alive.clear()
- try: gtk.main_quit()
- except RuntimeError: pass
+ except KeyError as ex:
+ label = _("Invalid Sensor: {}").format(ex.message)
+ except Exception as ex:
+ logging.exception(ex)
+ label = _("Unknown error: ").format(ex.message)
+
+ self.ind.set_label(label)
def load_settings(self):
+ """It gets the settings from the config file and
+ sets them to the correct vars"""
try:
with open(IndicatorSysmonitor.SETTINGS_FILE, 'r') as f:
- settings = json.load(f)
- self.mode_user = settings['mode_user']
- self.custom_text = settings['custom_text']
- self.user_command = settings['user_command']
- self.interval = settings['interval']
- self.sensors_disabled = settings.get('sensors_disabled', IndicatorSysmonitor.SENSORS_DISABLED)
- except Exception as e:
- logging.exception(e)
+ cfg = json.load(f)
+
+ if cfg['custom_text'] is not None:
+ settings['custom_text'] = cfg['custom_text']
+ if cfg['interval'] is not None:
+ settings['interval'] = cfg['interval']
+ if cfg['on_startup'] is not None:
+ settings['on_startup'] = cfg['on_startup']
+ if cfg['sensors'] is not None:
+ settings['sensors'] = cfg['sensors']
+
+ Sensor.update_regex()
+ self.update_indicator_guide()
+
+ except Exception as ex:
+ logging.exception(ex)
logging.error('Reading settings failed')
- def save_settings(self):
+ @staticmethod
+ def save_settings():
+ """It stores the current settings to the config file."""
# TODO: use gsettings
- settings = {'mode_user': self.mode_user,
- 'custom_text': self.custom_text,
- 'user_command': self.user_command,
- 'interval': self.interval,
- 'sensors_disabled': self.sensors_disabled}
- try:
- with open(IndicatorSysmonitor.SETTINGS_FILE, 'w') as f:
- f.write(json.dumps(settings))
- except Exception as e:
- logging.exception(e)
+ try:
+ with open(IndicatorSysmonitor.SETTINGS_FILE, 'w') as f:
+ f.write(json.dumps(settings))
+
+ except Exception as ex:
+ logging.exception(ex)
logging.error('Writting settings failed')
+ # actions raised from menu
def on_preferences_activated(self, event=None):
- self.preferences_dialog = Preferences(self)
- self.preferences_dialog.run()
+ """Raises the preferences dialog. If it's already open, it's
+ focused"""
+ if self._preferences_dialog is not None:
+ self._preferences_dialog.present()
+ return
+
+ self._preferences_dialog = Preferences(self)
+ self._preferences_dialog.run()
+ self._preferences_dialog = None
def on_full_sysmon_activated(self, event=None):
os.system('gnome-system-monitor &')
-from optparse import OptionParser
+ def on_exit(self, event=None, data=None):
+ """Action call when the main programs is closed."""
+ # close the open dialogs
+ if self._help_dialog is not None:
+ self._help_dialog.destroy()
+
+ if self._preferences_dialog is not None:
+ self._preferences_dialog.destroy()
+
+ logging.info("Terminated")
+ self.alive.clear()
+ try:
+ gtk.main_quit()
+ except RuntimeError:
+ pass
+
+ def _on_help(self, event=None, data=None):
+ """Raise a dialog with info about the app."""
+ if self._help_dialog is not None:
+ self._help_dialog.present()
+ return
+
+ self._help_dialog = gtk.MessageDialog(
+ None, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_INFO,
+ gtk.BUTTONS_OK, None)
+
+ self._help_dialog.set_title(_("Help"))
+ self._help_dialog.set_markup(HELP_MSG)
+ self._help_dialog.run()
+ self._help_dialog.destroy()
+ self._help_dialog = None
+
+
+from optparse import OptionParser # TODO: optparse is deprecated
if __name__ == "__main__":
- parser = OptionParser("usage: %prog [options]", version="%prog "+VERSION)
+ parser = OptionParser("usage: %prog [options]", version="%prog " + VERSION)
parser.add_option("--config", "", default=None,
- help="use custom config file")
- parser.add_option("--disable-sensors", action="store_true",
- help="disable sensors", default=False)
- parser.add_option("--enable-sensors", action="store_true",
- help="re-enable sensors", default=False)
+ help=_("Use custom config file."))
(options, args) = parser.parse_args()
if options.config:
if not os.path.exists(options.config):
- print options.config, "does not exist!"
+ logging.error(_("{} does not exist!").format(options.config))
sys.exit(-1)
+ logging.info(_("Using config file: {}").format(options.config))
IndicatorSysmonitor.SETTINGS_FILE = options.config
- # setup an instance with config
- i = IndicatorSysmonitor()
- if options.disable_sensors:
- i.sensors_disabled = True
- i.save_settings()
- logging.info("Sensors disabled")
-
- if options.enable_sensors:
- i.sensors_disabled = False
- i.save_settings()
- logging.info("Sensors enabled")
+ if not os.path.exists(IndicatorSysmonitor.SETTINGS_FILE):
+ IndicatorSysmonitor.save_settings()
+ # setup an instance with config
+ app = IndicatorSysmonitor()
try:
gtk.main()
except KeyboardInterrupt:
- i.on_exit()
+ app.on_exit()
diff -Nru indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1.dsc indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1.dsc
--- indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1.dsc 2012-06-30 10:25:50.000000000 +0000
+++ indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1.dsc 2012-11-27 16:24:08.000000000 +0000
@@ -13,22 +13,22 @@
Package-List:
indicator-sysmonitor deb gnome extra
Checksums-Sha1:
- fd5c2dd6cb6e99c5903c8ead3826616a5beffdf0 7453 indicator-sysmonitor_0.4.1.tar.gz
+ 560bf0c320f72b8a4dbed1733594a68ab1ca4c32 9542 indicator-sysmonitor_0.4.1.tar.gz
Checksums-Sha256:
- 41e89910d793c63a7219007d053bca90cbb59ea63c62a9d837c0ee705be46d2f 7453 indicator-sysmonitor_0.4.1.tar.gz
+ a318e9c4fa8bd3bd35929b5f9649cb8692b5e4a6b0ca3133c32aceae5a8a024e 9542 indicator-sysmonitor_0.4.1.tar.gz
Files:
- 84c3e10a8aafd4abeb004c4c8ea91b27 7453 indicator-sysmonitor_0.4.1.tar.gz
+ 8776b09a50b753516e308e90de960831 9542 indicator-sysmonitor_0.4.1.tar.gz
Original-Maintainer: Alex Eftimie
Python-Version: all
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
-iQEcBAEBAgAGBQJP7tLoAAoJEH+e2WxogMg1FfsIAIBoT709BXmK8t3JR6qYBUPE
-OtthKrreGDBuJ5afZOST9HFvyUHJ2qO88mOvq0JNhQmN+Ki9SI36tj0t4Fj4BCow
-apnV1C9fqu9OQGmhK3fO6k1SPrEGHXkvZEVOUp+y526ky/ypQ+MGF7lyDG0RgO3/
-ZXWGd83qUrfRt2YIdnKNwRcoexfhoXj7EEPwm3BLKQpy3NSFE4WYOMw0abym8LeM
-07SdnRO4uwpzzzL9LG0Z2vME6RjskAfh5Kv9gVXJHzR5ZKA+l+4G/6AmzrGlUhgK
-rPet45PjyouHrTdFmwnAZGDPq4xIs7bwWubEo3vrBGAABBH+zqsCIIkt6SZjr6U=
-=gSwq
+iQEcBAEBAgAGBQJQtOjGAAoJEH+e2WxogMg1RHUIAKNIHzd1umj8qZTqVCi5x5/k
+q2ry/LxEeulzYH49QcZ0D3vDY6tqFH4F2tDpiXHWnknem1wVLDiUp2R7Ci8ZKKf1
+9aWABg0KKaUsEoUsbySBEanpWh1levZkGM9S4jXQwNWk4eZYsFL7do+2SAjVwtI0
+GsRlqvDJqX9okAT+L5SI2ok+vEEgr5xMLSAD+pTzw6urfzrEVMOPIXUB9qxuXMxE
+y6p3gUKK3oZ3YUGRn8b6dhKdN/VcYpoYlLfGmvBoysTGB3XpLvSOBbe1v+pEOo3R
+bonLMYKgY+eL7vMmp1ZrfEPlRDOkxOFKLBQ3mDvwpDdJ6IWSLAg8Il1JLPCDEWk=
+=bnJE
-----END PGP SIGNATURE-----
Binary files /tmp/SqGkQjEubw/indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1.tar.gz and /tmp/fg1sh5U9PW/indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1.tar.gz differ
Binary files /tmp/SqGkQjEubw/indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1_all.deb and /tmp/fg1sh5U9PW/indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1_all.deb differ
diff -Nru indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1_i386.changes indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1_i386.changes
--- indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1_i386.changes 2012-06-30 10:25:50.000000000 +0000
+++ indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1_i386.changes 1970-01-01 00:00:00.000000000 +0000
@@ -1,45 +0,0 @@
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-Format: 1.8
-Date: Sat, 30 Jun 2012 10:20:57 +0300
-Source: indicator-sysmonitor
-Binary: indicator-sysmonitor
-Architecture: source all
-Version: 0.4.1
-Distribution: precise
-Urgency: low
-Maintainer: Ubuntu MOTU Team
-Changed-By: Alex Eftimie (alexef)
-Description:
- indicator-sysmonitor - System Monitor Indicator
-Changes:
- indicator-sysmonitor (0.4.1) precise; urgency=low
- .
- * bump version for precise
- * minor changes
-Checksums-Sha1:
- bd7af4dd6abf1574bcd94188de1baf73208e3648 1236 indicator-sysmonitor_0.4.1.dsc
- fd5c2dd6cb6e99c5903c8ead3826616a5beffdf0 7453 indicator-sysmonitor_0.4.1.tar.gz
- b1905fccb3a836c50efc8f82d5b24ff99d21cfbf 8160 indicator-sysmonitor_0.4.1_all.deb
-Checksums-Sha256:
- 14f45c3d7cd0846ce82bf99b7b467172ee7f7fcd4867c5a557926de975762cad 1236 indicator-sysmonitor_0.4.1.dsc
- 41e89910d793c63a7219007d053bca90cbb59ea63c62a9d837c0ee705be46d2f 7453 indicator-sysmonitor_0.4.1.tar.gz
- df6200a4da28c5c64b9e607f90bc99cae2a7ffe9dc2009cd305bb8afda5a2fa0 8160 indicator-sysmonitor_0.4.1_all.deb
-Files:
- c766489e27010c2818b600a0dc1e112e 1236 gnome extra indicator-sysmonitor_0.4.1.dsc
- 84c3e10a8aafd4abeb004c4c8ea91b27 7453 gnome extra indicator-sysmonitor_0.4.1.tar.gz
- 510125f05d1998521acd41a3a4144f83 8160 gnome extra indicator-sysmonitor_0.4.1_all.deb
-Original-Maintainer: Alex Eftimie
-
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.11 (GNU/Linux)
-
-iQEcBAEBAgAGBQJP7tL6AAoJEH+e2WxogMg1CrMIAKLwmLy8neABAfHu+LZkKizx
-9UY8hjOQdx8USKIS7HmNr4dpt53clRC5n9uwgdnbtR9Zs89BTuHevCEDRJdr8neN
-pD+FkS68vXa5wKAvslnwQvn/HXlNjx0kA5LhXA6Ch3B9K/+SQ1/FSh16EC46tgHP
-ZtXLH3gMXH2vlyuKqwmUFctrb4Ub1W9z7ujuTRPKfuOqiSaaZu/mbsOKQuu1FJJK
-ihOMmeG9UKI5uGYQrwqc/FsczQJEebPr9VGM7zjIt9Wy4+xN76FYDceRHKHBztRR
-/jPU9hM7CRe7JTUiQd1TEzeLD/HMZ/JJPUjxqxUVK3J3RErSLXJiRDw/ZulhSBM=
-=Tx08
------END PGP SIGNATURE-----
diff -Nru indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1_i386.ppa.upload indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1_i386.ppa.upload
--- indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1_i386.ppa.upload 2012-06-30 10:25:50.000000000 +0000
+++ indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1_i386.ppa.upload 1970-01-01 00:00:00.000000000 +0000
@@ -1,4 +0,0 @@
-Successfully uploaded indicator-sysmonitor_0.4.1.dsc to ppa.launchpad.net for ppa.
-Successfully uploaded indicator-sysmonitor_0.4.1.tar.gz to ppa.launchpad.net for ppa.
-Successfully uploaded indicator-sysmonitor_0.4.1_all.deb to ppa.launchpad.net for ppa.
-Successfully uploaded indicator-sysmonitor_0.4.1_i386.changes to ppa.launchpad.net for ppa.
diff -Nru indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1_source.changes indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1_source.changes
--- indicator-sysmonitor-0.4.1~quantal/indicator-sysmonitor_0.4.1_source.changes 1970-01-01 00:00:00.000000000 +0000
+++ indicator-sysmonitor-0.4.2/indicator-sysmonitor_0.4.1_source.changes 2012-11-27 16:24:08.000000000 +0000
@@ -0,0 +1,42 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+Format: 1.8
+Date: Sat, 30 Jun 2012 10:20:57 +0300
+Source: indicator-sysmonitor
+Binary: indicator-sysmonitor
+Architecture: source
+Version: 0.4.1
+Distribution: precise
+Urgency: low
+Maintainer: Ubuntu MOTU Team
+Changed-By: Alex Eftimie (alexef)
+Description:
+ indicator-sysmonitor - System Monitor Indicator
+Changes:
+ indicator-sysmonitor (0.4.1) precise; urgency=low
+ .
+ * bump version for precise
+ * minor changes
+Checksums-Sha1:
+ 66cfb798443ec6ec07b491af4e9baf3c7db036f1 1236 indicator-sysmonitor_0.4.1.dsc
+ 560bf0c320f72b8a4dbed1733594a68ab1ca4c32 9542 indicator-sysmonitor_0.4.1.tar.gz
+Checksums-Sha256:
+ 57b37c098dc09d6003a656f8319b64576c1d7fef579c91a2643c7e75bcec6e48 1236 indicator-sysmonitor_0.4.1.dsc
+ a318e9c4fa8bd3bd35929b5f9649cb8692b5e4a6b0ca3133c32aceae5a8a024e 9542 indicator-sysmonitor_0.4.1.tar.gz
+Files:
+ 2cba0bdd4235f419a7a074007bf90c69 1236 gnome extra indicator-sysmonitor_0.4.1.dsc
+ 8776b09a50b753516e308e90de960831 9542 gnome extra indicator-sysmonitor_0.4.1.tar.gz
+Original-Maintainer: Alex Eftimie
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iQEcBAEBAgAGBQJQtOjiAAoJEH+e2WxogMg1vnkH/26ZTbXG1fszCYgxXpyNz//w
+I/SRZHGEZVQDeqgtp298ibBrHg0WI6keZd6II9AcaTP6Sx5eZMzgevrsiHkW47+D
+UFEH60gzlCo/BfENLIdlpMuOyxWPHl5R0UjljOxm8rCZK8ywaC7XLRzYOgD6+/ug
+lArae8A1sv+AgdEJxJG2l44H7fkv+FtkcDR2t9PlX24pb2jI7F4ikSlTu3/+22K4
+NV7G3uWYtTx2Ci1QoO4sLQIs2lnjFexbox8Q/Pu+FfYgYnhVwJDywsOtPPJm/ruv
+A8cjFQz4/prOkAV9yepCfyccnCwxXswpnZ3pMUcFEQ/Q0x2QXy2YN2EjjNuRu+0=
+=8M1o
+-----END PGP SIGNATURE-----