[Sugar-devel] [PATCH sugar v2] Add cpu and memory resource indicator to frame

Sascha Silbe sascha-pgp at silbe.org
Sat Jan 22 07:38:08 EST 2011


From: anishmangal2002 <anishmangal2002 at gmail.com>

This patch adds an icon to the frame, whose palette
menu displays the memory and cpu resources. For computing
free memory, the code reads the /proc/meminfo file (thanks
quozl) and for computing cpu usage, the code reads the
/proc/stat file.

The frame icon is updated after every 5 seconds if required.
Similarly, the palette menu entries are updated after every
5 seconds as well.

Signed-off-by: anishmangal2002 <anishmangal2002 at gmail.com>
[style fixes, increase weight of memory utilisation for "system mood" so
bursts of CPU activity don't take us from "happy" directly to "sad";
pop up on left click since there is no primary action]
Signed-off-by: Sascha Silbe <sascha-pgp at silbe.org>
Reviewed-by: Sascha Silbe <sascha-pgp at silbe.org>
---
v1->v2: pop up palette on left click

 extensions/deviceicon/Makefile.am  |    1 +
 extensions/deviceicon/resources.py |  215 ++++++++++++++++++++++++++++++++++++
 po/POTFILES.in                     |    1 +
 3 files changed, 217 insertions(+), 0 deletions(-)

diff --git a/extensions/deviceicon/Makefile.am b/extensions/deviceicon/Makefile.am
index 118d866..0d15c38 100644
--- a/extensions/deviceicon/Makefile.am
+++ b/extensions/deviceicon/Makefile.am
@@ -4,6 +4,7 @@ sugar_PYTHON = 		\
 	__init__.py	\
 	battery.py	\
 	network.py	\
+	resources.py	\
 	speaker.py	\
 	touchpad.py	\
 	volume.py
diff --git a/extensions/deviceicon/resources.py b/extensions/deviceicon/resources.py
new file mode 100644
index 0000000..65e00dc
--- /dev/null
+++ b/extensions/deviceicon/resources.py
@@ -0,0 +1,215 @@
+# Copyright (C) Anish Mangal <anishmangal2002 at gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+from gettext import gettext as _
+import logging
+import os
+
+import gobject
+import gtk
+import gconf
+
+from sugar.graphics.tray import TrayIcon
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.palette import Palette
+from sugar.graphics import style
+
+
+_SYSTEM_MOODS = ['-sad', '-normal', '-happy']
+_ICON_NAME = 'computer'
+_UPDATE_INTERVAL = 5*1000
+
+
+class DeviceView(TrayIcon):
+
+    FRAME_POSITION_RELATIVE = 500
+
+    def __init__(self):
+        client = gconf.client_get_default()
+        self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
+        TrayIcon.__init__(self, icon_name=_ICON_NAME, xo_color=self._color)
+        self.create_palette()
+        self._icon_widget.connect('button-release-event', self._click_cb)
+
+    def create_palette(self):
+        self.palette = ResourcePalette(_('System resources'))
+        self.palette.set_group_id('frame')
+        self.palette.add_timer()
+        self.palette.connect('system-mood-changed',
+                self._system_mood_changed_cb)
+        return self.palette
+
+    def _system_mood_changed_cb(self, palette_, mood):
+        self.icon.props.icon_name = _ICON_NAME + mood
+
+    def _click_cb(self, widget, event):
+        self.palette_invoker.notify_right_click()
+
+
+class ResourcePalette(Palette):
+    __gsignals__ = {
+        'system-mood-changed': (gobject.SIGNAL_RUN_FIRST,
+                           gobject.TYPE_NONE,
+                           ([str])),
+    }
+
+    def __init__(self, primary_text):
+        Palette.__init__(self, label=primary_text)
+
+        self.vbox = gtk.VBox()
+        self.set_content(self.vbox)
+
+        self._cpu_text = gtk.Label()
+        self.vbox.pack_start(self._cpu_text, padding=style.DEFAULT_PADDING)
+        self._cpu_bar = gtk.ProgressBar()
+        self._cpu_bar.set_size_request(
+            style.zoom(style.GRID_CELL_SIZE * 4), -1)
+        self.vbox.pack_start(self._cpu_bar, padding=style.DEFAULT_PADDING)
+
+        self._memory_text = gtk.Label()
+        self.vbox.pack_start(self._memory_text, padding=style.DEFAULT_PADDING)
+        self._memory_bar = gtk.ProgressBar()
+        self._memory_bar.set_size_request(
+            style.zoom(style.GRID_CELL_SIZE * 4), -1)
+        self.vbox.pack_start(self._memory_bar, padding=style.DEFAULT_PADDING)
+
+        self._system_mood = None
+        try:
+            self._cpu_times = self._get_cpu_times_list()
+        except IOError:
+            logging.exception('An error ocurred while attempting to '
+                'read /proc/stat')
+            self._stop_computing_statistics()
+
+        self.vbox.show()
+        self._cpu_text.show()
+        self._cpu_bar.show()
+        self._memory_text.show()
+        self._memory_bar.show()
+
+    def add_timer(self):
+        gobject.timeout_add(_UPDATE_INTERVAL, self.__timer_cb)
+
+    def _get_cpu_times_list(self):
+        """Return various cpu times as read from /proc/stat
+
+        This method returns the following cpu times measured
+        in jiffies (1/100 of a second for x86 systems)
+        as an ordered list of numbers - [user, nice,
+        system, idle, iowait] where,
+
+        user: normal processes executing in user mode
+        nice: niced processes executing in user mode
+        system: processes executing in kernel mode
+        idle: twiddling thumbs
+        iowait: waiting for I/O to complete
+
+        Note: For systems having 2 or more CPU's, the above
+        numbers would be the cumulative sum of these times
+        for all CPU's present in the system.
+
+        """
+        return [int(count)
+           for count in file('/proc/stat').readline().split()[1:6]]
+
+    def _percentage_cpu_available(self):
+        """
+        Return free CPU resources as a percentage
+
+        """
+        _cpu_times_new = self._get_cpu_times_list()
+        _cpu_times_current = [(new - old)
+            for new, old in zip(_cpu_times_new, self._cpu_times)]
+        user_, nice_, system_, idle, iowait = _cpu_times_current
+        cpu_free = (idle + iowait) * 100.0 / sum(_cpu_times_current)
+        self._cpu_times = self._get_cpu_times_list()
+        return cpu_free
+
+    def _percentage_memory_available(self):
+        """
+        Return free memory as a percentage
+
+        """
+        for line in file('/proc/meminfo'):
+            name, value, unit_ = line.split()[:3]
+            if 'MemTotal:' == name:
+                total = int(value)
+            elif 'MemFree:' == name:
+                free = int(value)
+            elif 'Buffers:' == name:
+                buffers = int(value)
+            elif 'Cached:' == name:
+                cached = int(value)
+            elif 'Active:' == name:
+                break
+        return (free + buffers + cached) * 100.0 / total
+
+    def __timer_cb(self):
+        try:
+            cpu_in_use = 100 - self._percentage_cpu_available()
+            memory_in_use = 100 - self._percentage_memory_available()
+        except IOError:
+            logging.exception('An error ocurred while trying to '
+                'retrieve resource usage statistics')
+            self._stop_and_show_error()
+            return False
+        else:
+            self._cpu_text.set_label(_('CPU in use: %d%%' % cpu_in_use))
+            self._cpu_bar.set_fraction(float(cpu_in_use) / 100)
+            self._memory_text.set_label(_('Memory in use: %d%%' %
+                memory_in_use))
+            self._memory_bar.set_fraction(float(memory_in_use) / 100)
+
+            # both cpu_free and memory_free lie between 0-100
+            system_mood = _SYSTEM_MOODS[
+                    int(300 - (cpu_in_use + 2 * memory_in_use)) // 100]
+
+            # check if self._system_mood exists
+            try:
+                if self._system_mood != system_mood:
+                    self.emit('system-mood-changed', system_mood)
+                    self._system_mood = system_mood
+            except AttributeError:
+                self.emit('system-mood-changed', system_mood)
+                self._system_mood = system_mood
+
+            return True
+
+    def _stop_and_show_error(self):
+        """
+        Stop computing usage statistics and display an error message
+        since we've hit an exception.
+
+        """
+        # Use the existing _cpu_text label to display the error. Remove
+        # everything else.
+        self._cpu_text.set_size_request(
+            style.zoom(style.GRID_CELL_SIZE * 4), -1)
+        self._cpu_text.set_line_wrap(True)
+        self._cpu_text.set_text(_('Cannot compute CPU and memory usage '
+            'statistics!'))
+        self.vbox.remove(self._cpu_bar)
+        self.vbox.remove(self._memory_text)
+        self.vbox.remove(self._memory_bar)
+        self.emit('system-mood-changed', '-error')
+
+
+def setup(tray):
+    if not (os.path.exists('/proc/stat') and os.path.exists('/proc/meminfo')):
+        logging.warning('Either /proc/stat or /proc/meminfo not present. Not '
+            'adding the CPU and memory usage icon to the frame')
+        return
+    tray.add_device(DeviceView())
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9e46831..b799339 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -27,6 +27,7 @@ extensions/cpsection/updater/__init__.py
 extensions/cpsection/updater/view.py
 extensions/deviceicon/battery.py
 extensions/deviceicon/network.py
+extensions/deviceicon/resources.py
 extensions/deviceicon/speaker.py
 extensions/deviceicon/touchpad.py
 extensions/deviceicon/volume.py
-- 
1.7.2.3



More information about the Sugar-devel mailing list