class She(base._TextBox): ''' Widget to display the Super Hybrid Engine status. can display either the mode or CPU speed on eeepc computers.''' defaults = manager.Defaults( ('font', 'Arial', 'Text Font'), ('fontsize', None, 'Calculated if None.'), ('padding', None, 'Padding Left and Right. Calculated in None.'), ('background', '000000', 'Background Colour'), ('foreground', 'ffffff', 'Foreground Colour'), ('device', '/sys/devices/platform/eeepc/cpufv', 'sys path to cpufv'), ('format', 'speed', 'Type of info to display "speed" or "name"'), ('update_delay', 0.5, 'Update Time in seconds.'), ) def __init__(self, width=bar.CALCULATED, **config): base._TextBox.__init__(self, 'CPU', **config) self.modes = { '0x300': {'name': 'Performance', 'speed': '1.6GHz'}, '0x301': {'name': 'Normal', 'speed': '1.2GHz'}, '0x302': {'name': 'PoswerSave', 'speed': '800MHz'} } self.modes_index = self.modes.keys().sort() self.mode = None def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) self.timeout_add(self.update_delay, self.update) def _get_mode(self): with open(self.device) as f: mode = f.read().strip() return mode def update(self): mode = self._get_mode() if mode != self.mode: self.mode = mode self.draw() return True def draw(self): if self.mode in self.modes.keys(): self.text = self.modes[self.mode][self.format] else: self.text = self.mode base._TextBox.draw(self)
class NetworkStatus(base._TextBox): '''Widget that displays network status if theme_path is set it draws the widget as an icon''' defaults = manager.Defaults( ("font", "Arial", "Text font"), ("fontsize", None, "Font pixel size. Calculated if None."), ("padding", 3, "Padding left and right. Calculated if None."), ("background", None, "Background colour."), ("foreground", "#ffffff", "Foreground colour."), ("theme_path", None, "Path of the icons"), ("update_interval", 0.2, "Update time in seconds."), ) def __init__(self, **config): base._TextBox.__init__(self, 'NET', width=bar.CALCULATED, **config) if self.theme_path: self.width_type = bar.STATIC self.width = 0 self.surfaces = {} self.netstate = "disconnected" def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) if self.theme_path: self.setup_images() self.timeout_add(self.update_interval, self.update) def update(self): networkprocess = subprocess.Popen(['nmcli', '-f', 'state', '-t', 'nm'], stdout=subprocess.PIPE) state = networkprocess.communicate() if state[0] != self.netstate: self.netstate = state[0] self.draw() return True def setup_images(self): for img_name in ('network-online', 'network-acquiring', 'network-offline'): try: img = cairo.ImageSurface.create_from_png( os.path.join(self.theme_path, '%s.png' % img_name)) except cairo.Error, error: self.theme_path = None self.width_type = bar.CALCULATED self.qtile.log.add(error) self.qtile.log.add('Switching to text mode') return input_width = img.get_width() input_height = img.get_height() sp = input_height / float(self.bar.height - 1) width = input_width / sp if width > self.width: self.width = int(width) + self.actual_padding * 2 imgpat = cairo.SurfacePattern(img) scaler = cairo.Matrix() scaler.scale(sp, sp) scaler.translate(self.actual_padding * -1, 0) imgpat.set_matrix(scaler) imgpat.set_filter(cairo.FILTER_BEST) self.surfaces[img_name] = imgpat
class Backlight(base._TextBox): """ A simple widget to show the current brightness of a monitor. """ filenames = {} defaults = manager.Defaults( ('font', 'Arial', 'Text font'), ('fontsize', None, 'Font pixel size. Calculated if None.'), ('padding', 3, 'Padding left and right. Calculated if None.'), ('background', None, 'Background colour.'), ('foreground', '#ffffff', 'Foreground colour.'), ('backlight_name', 'acpi_video0', 'ACPI name of a backlight device'), ('brightness_file', 'brightness', 'Name of file with the ' 'current brightness in /sys/class/backlight/backlight_name'), ('max_brightness_file', 'max_brightness', 'Name of file with the ' 'maximum brightness in /sys/class/backlight/backlight_name'), ('update_delay', .2, 'The delay in seconds between updates'), ) def __init__(self, **config): base._TextBox.__init__(self, "LIGHT", bar.CALCULATED, **config) def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) self.timeout_add(self.update_delay, self.update) def _load_file(self, name): try: path = os.path.join(BACKLIGHT_DIR, self.backlight_name, name) with open(path, 'r') as f: return f.read().strip() except IOError: return False except Exception: self.log.exception("Failed to get %s" % name) def _get_info(self): try: info = { 'brightness': float(self._load_file(self.brightness_file)), 'max': float(self._load_file(self.max_brightness_file)), } except TypeError: return False return info def _get_text(self): info = self._get_info() if info == False: return 'Error' percent = info['brightness'] / info['max'] return FORMAT.format(percent=percent) def update(self): ntext = self._get_text() if ntext != self.text: self.text = ntext self.bar.draw() return True
class Battery(_Battery): """ A simple but flexible text-based battery widget. """ defaults = manager.Defaults( ('low_foreground', 'FF0000', 'font color when battery is low'), ('format', '{char} {percent:2.0%} {hour:d}:{min:02d}', 'Display format'), ('charge_char', '^', 'Character to indicate the battery is charging'), ('discharge_char', 'V', 'Character to indicate the battery' ' is discharging'), *_Battery.defaults.defaults) def __init__(self, low_percentage=0.10, **config): base._TextBox.__init__(self, "BAT", bar.CALCULATED, **config) self.low_percentage = low_percentage def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) self.timeout_add(self.update_delay, self.update) def _get_text(self): info = self._get_info() if info == False: return 'Error' ## Set the charging character try: if info['stat'] == DISCHARGING: char = self.discharge_char time = info['now'] / info['power'] elif info['stat'] == CHARGING: char = self.charge_char time = (info['full'] - info['now']) / info['power'] else: return 'Full' except ZeroDivisionError: time = -1 ## Calculate the battery percentage and time left if time >= 0: hour = int(time) min = int(time * 60) % 60 else: hour = -1 min = -1 percent = info['now'] / info['full'] if info['stat'] == DISCHARGING and percent < self.low_percentage: self.layout.colour = self.low_foreground else: self.layout.colour = self.foreground return self.format.format(char=char, percent=percent, hour=hour, min=min) def update(self): ntext = self._get_text() if ntext != self.text: self.text = ntext self.bar.draw() return True
class _Battery(base._TextBox): ''' Base battery class ''' filenames = {} defaults = manager.Defaults( ('font', 'Arial', 'Text font'), ('fontsize', None, 'Font pixel size. Calculated if None.'), ('padding', 3, 'Padding left and right. Calculated if None.'), ('background', None, 'Background colour.'), ('foreground', '#ffffff', 'Foreground colour.'), ('battery_name', 'BAT0', 'ACPI name of a battery, usually BAT0'), ('status_file', 'status', 'Name of status file in' ' /sys/class/power_supply/battery_name'), ('energy_now_file', None, 'Name of file with the ' 'current energy in /sys/class/power_supply/battery_name'), ('energy_full_file', None, 'Name of file with the maximum' ' energy in /sys/class/power_supply/battery_name'), ('power_now_file', None, 'Name of file with the current' ' power draw in /sys/class/power_supply/battery_name'), ('update_delay', 1, 'The delay in seconds between updates'), ) def _load_file(self, name): try: path = os.path.join(BAT_DIR, self.battery_name, name) with open(path, 'r') as f: return f.read().strip() except IOError: if name == 'current_now': return 0 return False except Exception: self.log.exception("Failed to get %s" % name) def _get_param(self, name): if name in self.filenames: return self._load_file(self.filenames[name]) else: ## Don't have the file name cached, figure it out file_list = BATTERY_INFO_FILES.get(name, []) if getattr(self, name, None): ## If a file is manually specified, check it first file_list.insert(0, getattr(self, name)) ## Iterate over the possibilities, and return the first valid value for file in file_list: value = self._load_file(file) if not (value in (False, None)): self.filenames[name] = file return value ## If we made it this far, we don't have a valid file. Just return 0. return 0 def _get_info(self): try: info = { 'stat': self._get_param('status_file'), 'now': float(self._get_param('energy_now_file')), 'full': float(self._get_param('energy_full_file')), 'power': float(self._get_param('power_now_file')), } except TypeError: return False return info
class BatteryIcon(_Battery): ''' Battery life indicator widget ''' defaults = manager.Defaults( ('theme_path', default_icon_path(), 'Path of the icons'), ('custom_icons', {}, 'dict containing key->filename icon map'), *_Battery.defaults.defaults) def __init__(self, **config): base._TextBox.__init__(self, '0', width=bar.CALCULATED, **config) if self.theme_path: self.width_type = bar.STATIC self.width = 0 self.surfaces = {} self.current_icon = 'battery-missing' self.icons = dict([(x, '{0}.png'.format(x)) for x in ( 'battery-missing', 'battery-caution', 'battery-low', 'battery-good', 'battery-full', 'battery-caution-charging', 'battery-low-charging', 'battery-good-charging', 'battery-full-charging', 'battery-full-charged', )]) self.icons.update(self.custom_icons) def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) self.setup_images() self.timeout_add(self.update_delay, self.update) def _get_icon_key(self): key = 'battery' info = self._get_info() if info == False or not info.get('full'): key += '-missing' else: percent = info['now'] / info['full'] if percent < .2: key += '-caution' elif percent < .4: key += '-low' elif percent < .8: key += '-good' else: key += '-full' if info['stat'] == CHARGING: key += '-charging' elif info['stat'] == CHARGED: key += '-charged' return key def update(self): icon = self._get_icon_key() if icon != self.current_icon: self.current_icon = icon self.draw() return True def draw(self): if self.theme_path: self.drawer.clear(self.bar.background) self.drawer.ctx.set_source(self.surfaces[self.current_icon]) self.drawer.ctx.paint() self.drawer.draw(self.offset, self.width) else: self.text = self.current_icon[8:] base._TextBox.draw(self) def setup_images(self): for key, name in self.icons.iteritems(): try: path = os.path.join(self.theme_path, name) img = cairo.ImageSurface.create_from_png(path) except cairo.Error: self.theme_path = None self.qtile.log.add('Battery Icon switching to text mode') return input_width = img.get_width() input_height = img.get_height() sp = input_height / float(self.bar.height - 1) width = input_width / sp if width > self.width: self.width = int(width) + self.actual_padding * 2 imgpat = cairo.SurfacePattern(img) scaler = cairo.Matrix() scaler.scale(sp, sp) scaler.translate(self.actual_padding * -1, 0) imgpat.set_matrix(scaler) imgpat.set_filter(cairo.FILTER_BEST) self.surfaces[key] = imgpat
class Metrics(base._TextBox): defaults = manager.Defaults( ("font", "Arial", "Metrics font"), ("fontsize", None, "Metrics pixel size. Calculated if None."), ("padding", None, "Metrics padding. Calculated if None."), ("background", "000000", "Background colour"), ("foreground", "ffffff", "Foreground colour") ) def __init__(self, **kwargs): base._TextBox.__init__(self, **kwargs) self.cpu_usage, self.cpu_total = self.get_cpu_stat() self.interfaces = {} self.idle_ifaces = {} def _configure(self, qtile, bar): base._TextBox._configure(self, qtile, bar) self.timeout_add(0, self._update) def get_cpu_stat(self): stat = [int(i) for i in open('/proc/stat').readline().split()[1:]] return sum(stat[:3]), sum(stat) def get_cpu_usage(self): new_cpu_usage, new_cpu_total = self.get_cpu_stat() cpu_usage = new_cpu_usage - self.cpu_usage cpu_total = new_cpu_total - self.cpu_total self.cpu_usage = new_cpu_usage self.cpu_total = new_cpu_total return 'Cpu: %d%%' % (float(cpu_usage) / float(cpu_total) * 100.) def get_mem_usage(self): info = {} for line in open('/proc/meminfo'): key, val = line.split(':') info[key] = int(val.split()[0]) mem = info['MemTotal'] mem -= info['MemFree'] mem -= info['Buffers'] mem -= info['Cached'] return 'Mem: %d%%' % (float(mem) / float(info['MemTotal']) * 100) def get_net_usage(self): interfaces = [] basedir = '/sys/class/net' for iface in os.listdir(basedir): j = os.path.join ifacedir = j(basedir, iface) statdir = j(ifacedir, 'statistics') idle = iface in self.idle_ifaces try: if int(open(j(ifacedir, 'carrier')).read()): rx = int(open(j(statdir, 'rx_bytes')).read()) tx = int(open(j(statdir, 'tx_bytes')).read()) if iface not in self.interfaces: self.interfaces[iface] = (rx, tx) old_rx, old_tx = self.interfaces[iface] self.interfaces[iface] = (rx, tx) rx = rx - old_rx tx = tx - old_tx if rx or tx: idle = False self.idle_ifaces[iface] = 0 rx = humanize_bytes(rx) tx = humanize_bytes(tx) interfaces.append('%s: %s / %s' % (iface, rx, tx)) except: pass if idle: interfaces.append( '%s: %-11s' % (iface, ("%ds idle" % self.idle_ifaces[iface])) ) self.idle_ifaces[iface] += 1 if self.idle_ifaces[iface] > 30: del self.idle_ifaces[iface] return " | ".join(interfaces) def _update(self): self.update() self.timeout_add(1, self.update) return False def update(self): stat = [self.get_cpu_usage(), self.get_mem_usage()] net = self.get_net_usage() if net: stat.append(net) self.text = " | ".join(stat) self.bar.draw() return True