|
| 1 | +#!/usr/bin/python3 |
| 2 | +# coding: utf-8 |
| 3 | +# |
| 4 | +# A simple indicator applet displaying cpu and memory information |
| 5 | +# for the budgie-desktop |
| 6 | +# |
| 7 | +# Author: fossfreedom <foss.freedom@gmail.com> |
| 8 | +# Original Homepage: http://launchpad.net/indicator-sysmonitor |
| 9 | +# Homepage: https://github.com/fossfreedom/indicator-sysmonitor |
| 10 | +# License: GPL v3 |
| 11 | +# |
| 12 | +from gettext import gettext as _ |
| 13 | +from gettext import textdomain, bindtextdomain |
| 14 | +import gi |
| 15 | +gi.require_version('Budgie', '1.0') |
| 16 | +from gi.repository import Budgie, GObject, GLib |
| 17 | +import sys |
| 18 | +import os |
| 19 | +import logging |
| 20 | +import json |
| 21 | +import tempfile |
| 22 | +from threading import Event |
| 23 | + |
| 24 | +gi.require_version('Gtk', '3.0') |
| 25 | +from gi.repository import Gtk |
| 26 | + |
| 27 | +from sysmonitor_common/preferences import Preferences |
| 28 | +from sysmonitor_common/preferences import __version__ |
| 29 | +from sysmonitor_common/sensors import SensorManager |
| 30 | + |
| 31 | +logging.basicConfig(level=logging.INFO) |
| 32 | + |
| 33 | +HELP_MSG = """<span underline="single" size="x-large">{title}</span> |
| 34 | +
|
| 35 | +{introduction} |
| 36 | +
|
| 37 | +{basic} |
| 38 | +• cpu: {cpu_desc} |
| 39 | +• mem: {mem_desc} |
| 40 | +• bat<i>%d</i>: {bat_desc} |
| 41 | +• net: {net_desc} |
| 42 | +• netcomp: {netcomp_desc} |
| 43 | +• totalnet: {totalnet_desc} |
| 44 | +• upordown: {upordown_desc} |
| 45 | +• publicip: {publicip_desc} |
| 46 | +
|
| 47 | +{compose} |
| 48 | +• fs//<i>mount-point</i> : {fs_desc} |
| 49 | +
|
| 50 | +<big>{example}</big> |
| 51 | +CPU {{cpu}} | MEM {{mem}} | root {{fs///}} |
| 52 | +""".format( |
| 53 | + title=_("Help Page"), |
| 54 | + introduction=_("The sensors are the names of the devices from which you want to retrive information. They must be placed between brackets."), |
| 55 | + basic=_("The basics are:"), |
| 56 | + cpu_desc=_("It shows the average of CPU usage."), |
| 57 | + mem_desc=_("It shows the physical memory in use."), |
| 58 | + bat_desc=_("It shows the available battery which id is %d."), |
| 59 | + net_desc=_("It shows the amount of data you are downloading and uploading through your network."), |
| 60 | + netcomp_desc=_("It shows the amount of data you are downloading and uploading through your network in a compact way."), |
| 61 | + totalnet_desc=("It shows the total amount of data you downloaded and uploaded through your network."), |
| 62 | + upordown_desc=_("It shows whether your internet connection is up or down (the sensor is refreshed every 10 seconds)."), |
| 63 | + publicip_desc=_("It shows your public IP address (the sensor is refreshed every 10 minutes)."), |
| 64 | + compose=_("Also there are the following sensors that are composed with two parts divided by two slashes."), |
| 65 | + fs_desc=_("Show available space in the file system."), |
| 66 | + example=_("Example:")) |
| 67 | + |
| 68 | + |
| 69 | +class IndicatorSysmonitor(object): |
| 70 | + SENSORS_DISABLED = False |
| 71 | + |
| 72 | + def __init__(self): |
| 73 | + self._preferences_dialog = None |
| 74 | + self._help_dialog = None |
| 75 | + |
| 76 | + self.ind = Gtk.Button.new() |
| 77 | + self.ind.set_label("Init...") |
| 78 | + |
| 79 | + self._create_menu() |
| 80 | + |
| 81 | + self.alive = Event() |
| 82 | + self.alive.set() |
| 83 | + |
| 84 | + self.sensor_mgr = SensorManager() |
| 85 | + self.load_settings() |
| 86 | + |
| 87 | + def _create_menu(self): |
| 88 | + """Creates the main menu and shows it.""" |
| 89 | + # create menu {{{ |
| 90 | + menu = Gtk.Menu() |
| 91 | + # add System Monitor menu item |
| 92 | + full_sysmon = Gtk.MenuItem(_('System Monitor')) |
| 93 | + full_sysmon.connect('activate', self.on_full_sysmon_activated) |
| 94 | + menu.add(full_sysmon) |
| 95 | + menu.add(Gtk.SeparatorMenuItem()) |
| 96 | + |
| 97 | + # add preferences menu item |
| 98 | + pref_menu = Gtk.MenuItem(_('Preferences')) |
| 99 | + pref_menu.connect('activate', self.on_preferences_activated) |
| 100 | + menu.add(pref_menu) |
| 101 | + |
| 102 | + # add help menu item |
| 103 | + help_menu = Gtk.MenuItem(_('Help')) |
| 104 | + help_menu.connect('activate', self._on_help) |
| 105 | + menu.add(help_menu) |
| 106 | + |
| 107 | + menu.show_all() |
| 108 | + |
| 109 | + self.popup = menu |
| 110 | + self.ind.connect('clicked', self.popup_menu) |
| 111 | + logging.info("Menu shown") |
| 112 | + # }}} menu done! |
| 113 | + |
| 114 | + def popup_menu(self, *args): |
| 115 | + self.popup.popup(None, None, None, None, 0, Gtk.get_current_event_time()) |
| 116 | + |
| 117 | + def update_indicator_guide(self): |
| 118 | + |
| 119 | + guide = self.sensor_mgr.get_guide() |
| 120 | + |
| 121 | + def update(self, data): |
| 122 | + # data is the dict of all sensors and their values |
| 123 | + # { name, label } |
| 124 | + |
| 125 | + # look through data and find out if there are any icons to be set |
| 126 | + for sensor in data: |
| 127 | + test_str = data[sensor].lower() |
| 128 | + if "use_icon" in test_str: |
| 129 | + path = data[sensor].split(":")[1] |
| 130 | + print(path) |
| 131 | + self.ind.set_icon_full(path, "") |
| 132 | + # now strip the icon output from data so that it is not displayed |
| 133 | + remaining = test_str.split("use_icon")[0].strip() |
| 134 | + if not remaining: |
| 135 | + remaining = " " |
| 136 | + |
| 137 | + data[sensor] = remaining |
| 138 | + |
| 139 | + if "clear_icon" in test_str: |
| 140 | + self.ind.set_icon_full(self.tindicator, "") |
| 141 | + |
| 142 | + remaining = test_str.split("clear_icon")[0].strip() |
| 143 | + if not remaining: |
| 144 | + remaining = " " |
| 145 | + |
| 146 | + data[sensor] = remaining |
| 147 | + |
| 148 | + label = self.sensor_mgr.get_label(data) |
| 149 | + |
| 150 | + |
| 151 | + def update_label(label): |
| 152 | + self.ind.set_label(label) |
| 153 | + return False |
| 154 | + if label and self.ind: |
| 155 | + GLib.idle_add(update_label, label.strip()) |
| 156 | + |
| 157 | + def load_settings(self): |
| 158 | + |
| 159 | + self.sensor_mgr.load_settings() |
| 160 | + self.sensor_mgr.initiate_fetcher(self) |
| 161 | + self.update_indicator_guide() |
| 162 | + |
| 163 | + # @staticmethod |
| 164 | + def save_settings(self): |
| 165 | + self.sensor_mgr.save_settings() |
| 166 | + |
| 167 | + def update_settings(self): |
| 168 | + self.sensor_mgr.initiate_fetcher(self) |
| 169 | + |
| 170 | + # actions raised from menu |
| 171 | + def on_preferences_activated(self, event=None): |
| 172 | + """Raises the preferences dialog. If it's already open, it's |
| 173 | + focused""" |
| 174 | + if self._preferences_dialog is not None: |
| 175 | + self._preferences_dialog.present() |
| 176 | + return |
| 177 | + |
| 178 | + self._preferences_dialog = Preferences(self) |
| 179 | + self._preferences_dialog.run() |
| 180 | + self._preferences_dialog = None |
| 181 | + |
| 182 | + def on_full_sysmon_activated(self, event=None): |
| 183 | + if GLib.find_program_in_path('mate-system-monitor') is not None: |
| 184 | + os.system('mate-system-monitor &') |
| 185 | + return |
| 186 | + |
| 187 | + if GLib.find_program_in_path('gnome-system-monitor') is not None: |
| 188 | + os.system('gnome-system-monitor &') |
| 189 | + |
| 190 | + def on_exit(self, event=None, data=None): |
| 191 | + """Action call when the main programs is closed.""" |
| 192 | + # cleanup temporary indicator icon |
| 193 | + os.remove(self.tindicator) |
| 194 | + # close the open dialogs |
| 195 | + if self._help_dialog is not None: |
| 196 | + self._help_dialog.destroy() |
| 197 | + |
| 198 | + if self._preferences_dialog is not None: |
| 199 | + self._preferences_dialog.destroy() |
| 200 | + |
| 201 | + logging.info("Terminated") |
| 202 | + self.alive.clear() # DM: why bother with Event() ??? |
| 203 | + |
| 204 | + try: |
| 205 | + Gtk.main_quit() |
| 206 | + except RuntimeError: |
| 207 | + pass |
| 208 | + |
| 209 | + def _on_help(self, event=None, data=None): |
| 210 | + """Raise a dialog with info about the app.""" |
| 211 | + if self._help_dialog is not None: |
| 212 | + self._help_dialog.present() |
| 213 | + return |
| 214 | + |
| 215 | + self._help_dialog = Gtk.MessageDialog( |
| 216 | + None, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO, |
| 217 | + Gtk.ButtonsType.OK, None) |
| 218 | + |
| 219 | + self._help_dialog.set_title(_("Help")) |
| 220 | + self._help_dialog.set_markup(HELP_MSG) |
| 221 | + self._help_dialog.run() |
| 222 | + self._help_dialog.destroy() |
| 223 | + self._help_dialog = None |
| 224 | + |
| 225 | +class BudgieSysMonitor(GObject.Object, Budgie.Plugin): |
| 226 | + """ This is simply an entry point into the SysMonitor applet |
| 227 | + Note you must always override Object, and implement Plugin. |
| 228 | + """ |
| 229 | + |
| 230 | + # Good manners, make sure we have unique name in GObject type system |
| 231 | + __gtype_name__ = "BudgieSysMonitor" |
| 232 | + |
| 233 | + def __init__(self): |
| 234 | + """ Initialisation is important. |
| 235 | + """ |
| 236 | + GObject.Object.__init__(self) |
| 237 | + |
| 238 | + def do_get_panel_widget(self, uuid): |
| 239 | + """ This is where the real fun happens. Return a new Budgie.Applet |
| 240 | + instance with the given UUID. The UUID is determined by the |
| 241 | + BudgiePanelManager, and is used for lifetime tracking. |
| 242 | + """ |
| 243 | + return BudgieSysMonitorApplet(uuid) |
| 244 | + |
| 245 | +class BudgieSysMonitorApplet(Budgie.Applet): |
| 246 | + """ Budgie.Applet is in fact a Gtk.Bin """ |
| 247 | + |
| 248 | + button = None |
| 249 | + |
| 250 | + def __init__(self, uuid): |
| 251 | + Budgie.Applet.__init__(self) |
| 252 | + |
| 253 | + # Add a button to our UI |
| 254 | + logging.info("start") |
| 255 | + |
| 256 | + if not os.path.exists(SensorManager.SETTINGS_FILE): |
| 257 | + sensor_mgr = SensorManager() |
| 258 | + sensor_mgr.save_settings() |
| 259 | + else: |
| 260 | + try: |
| 261 | + with open(SensorManager.SETTINGS_FILE,"r") as f: |
| 262 | + cfg = json.load(f) |
| 263 | + f.close() |
| 264 | + except: |
| 265 | + settings = { |
| 266 | + 'custom_text': 'cpu: {cpu} mem: {mem}', |
| 267 | + 'interval': 2, |
| 268 | + 'on_startup': False, |
| 269 | + 'sensors': { |
| 270 | + } |
| 271 | + } |
| 272 | + with open(SensorManager.SETTINGS_FILE,"w") as f: |
| 273 | + f.write(json.dumps(settings, indent=4, ensure_ascii=False)) |
| 274 | + f.close() |
| 275 | + |
| 276 | + self.app = IndicatorSysmonitor() |
| 277 | + self.button = self.app.ind |
| 278 | + self.button.set_relief(Gtk.ReliefStyle.NONE) |
| 279 | + self.add(self.button) |
| 280 | + self.show_all() |
0 commit comments