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

Anish Mangal anishmangal2002 at gmail.com
Sat Jul 10 03:43:49 EDT 2010


Thanks for reviewing this patch.

I think I have addressed all issues raised by tomeu, silbe and quozl.

Additionally, I've put the portion of code that accesses the
/proc/stat and /proc/meminfo files in a try...except block. When an
exception occurs, all the widgets barring _cpu_text are removed and
_cpu_text displays an error message "Cannot compute CPU and memory
usage statistics!" in the palette.

--
Anish

On Sat, Jul 10, 2010 at 1:04 PM, anishmangal2002
<anishmangal2002 at gmail.com> wrote:
> 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 palette menu entries are only updated (in one second
> intervals) when the palette menu is visible thus
> possibly saving cpu cycles.
>
> Signed-off-by: anishmangal2002 <anishmangal2002 at gmail.com>
> ---
>  extensions/deviceicon/Makefile.am  |    3 +-
>  extensions/deviceicon/resources.py |  188 ++++++++++++++++++++++++++++++++++++
>  2 files changed, 190 insertions(+), 1 deletions(-)
>  create mode 100644 extensions/deviceicon/resources.py
>
> diff --git a/extensions/deviceicon/Makefile.am b/extensions/deviceicon/Makefile.am
> index 8a2e765..038c059 100644
> --- a/extensions/deviceicon/Makefile.am
> +++ b/extensions/deviceicon/Makefile.am
> @@ -5,4 +5,5 @@ sugar_PYTHON =          \
>        battery.py      \
>        network.py      \
>        speaker.py      \
> -       volume.py
> +       volume.py       \
> +       resources.py
> diff --git a/extensions/deviceicon/resources.py b/extensions/deviceicon/resources.py
> new file mode 100644
> index 0000000..7503bef
> --- /dev/null
> +++ b/extensions/deviceicon/resources.py
> @@ -0,0 +1,188 @@
> +# 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
> +
> +from jarabe.frame.frameinvoker import FrameWidgetInvoker
> +
> +class DeviceView(TrayIcon):
> +
> +    FRAME_POSITION_RELATIVE = 500
> +
> +    def __init__(self):
> +        client = gconf.client_get_default()
> +        color = XoColor(client.get_string('/desktop/sugar/user/color'))
> +        TrayIcon.__init__(self, icon_name='computer', xo_color=color)
> +        self.set_palette_invoker(FrameWidgetInvoker(self))
> +
> +    def create_palette(self):
> +        palette = ResourcePalette(_('System resources'))
> +        palette.set_group_id('frame')
> +        return palette
> +
> +class ResourcePalette(Palette):
> +
> +    def __init__(self, primary_text):
> +        Palette.__init__(self, label=primary_text)
> +
> +        self._popup_cb_id = self.connect('popup', self._popup_cb)
> +        self._popdown_cb_id = self.connect('popdown', self._popdown_cb)
> +
> +        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_text.show()
> +
> +        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._cpu_bar.show()
> +
> +        self._memory_text = gtk.Label()
> +        self.vbox.pack_start(self._memory_text, padding=style.DEFAULT_PADDING)
> +        self._memory_text.show()
> +
> +        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._memory_bar.show()
> +
> +        self.vbox.show()
> +
> +        try:
> +            self._cpu_times = self._get_cpu_times_list()
> +        except Exception:
> +            logging.exception('An error ocurred while attempting to '
> +                'read /proc/stat')
> +            self._remove_callbacks()
> +
> +    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_free = self._percentage_cpu_available()
> +            memory_free = self._percentage_memory_available()
> +            self._cpu_text.set_label(_('CPU free: %d%%' % cpu_free))
> +            self._cpu_bar.set_fraction(cpu_free/100.0)
> +            self._memory_text.set_label(_('Memory free: %d%%' % memory_free))
> +            self._memory_bar.set_fraction(memory_free/100.0)
> +            return True
> +        except Exception:
> +            logging.exception('An error ocurred while trying to '
> +                'retrieve resource usage statistics')
> +            self._remove_callbacks()
> +            return False
> +
> +    def _remove_callbacks(self):
> +        """
> +        Stop computing usage statistics and display and error message
> +        since we've hit an exception.
> +
> +        """
> +        self.disconnect(self._popup_cb_id)
> +        self.disconnect(self._popdown_cb_id)
> +
> +        # 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)
> +
> +    def _popup_cb(self, gobject_ref):
> +        self.__timer_cb()
> +        self._timer = gobject.timeout_add(2000, self.__timer_cb)
> +
> +    def _popdown_cb(self, gobject_ref):
> +        gobject.source_remove(self._timer)
> +
> +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())
> --
> 1.7.1
>
>


More information about the Sugar-devel mailing list