2 battery.py - A Battery Status Monitor for ROX
3 (based largely on Baroque by Tilo Riemer)
6 ############################################################################
8 ## $Id: baroque.py,v 1.13 2004/01/08 04:26:11 rds Exp $
10 ## Copyright (C) 2002-2003 Rds <rds@rdsarts.com> and
11 ## Tilo Riemer <riemer@lincvs.org>
12 ## All rights reserved.
14 ## Baroque is a merge of BatMonitor and the old Baroque
16 ## Redistribution and use in source and binary forms, with or without
17 ## modification, are permitted provided that the following conditions
20 ## 1. Redistributions of source code must retain the above copyright
21 ## notice, this list of conditions and the following disclaimer.
22 ## 2. Redistributions in binary form must reproduce the above copyright
23 ## notice, this list of conditions and the following disclaimer in the
24 ## documentation and/or other materials provided with the distribution.
25 ## 3. The name of the author may not be used to endorse or promote products
26 ## derived from this software without specific prior written permission.
28 ## THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
29 ## IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
30 ## OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
31 ## IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
32 ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
33 ## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 ## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 ## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 ## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
37 ## THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ###############################################################################
41 # standard library modules
42 import sys
, os
, time
, gtk
, gobject
, rox
43 from rox
import applet
, filer
, tasks
44 from rox
.options
import Option
51 # Options.xml processing
53 rox
.setup_app_options(APP_NAME
, site
='hayber.us')
54 Menu
.set_save_name(APP_NAME
, site
='hayber.us')
57 WARN
= Option('warn', True)
58 WARN_LEVEL
= Option('warn_level', 10)
59 TIMER
= Option('timeout', "1000")
60 THEME
= Option('theme', 'Color')
62 #Enable notification of options changes
63 rox
.app_options
.notify()
65 BATT_TYPE
= -1 # 0 = ACPI, 1 = APM
71 if pynotify
.init(APP_NAME
):
78 # Initalize the battery object
83 OFFLINE
= acpi
.OFFLINE
84 except (ImportError, NotImplementedError, EnvironmentError):
86 import pmu
#for PowerMac support
90 except (ImportError, NotImplementedError, EnvironmentError):
96 except (ImportError, NotImplementedError, EnvironmentError):
97 rox
.croak(_("Sorry, but we could not load a Power Management module. Your system is not configured with power management support."))
100 class Battery(applet
.Applet
):
101 """A Battery Status Monitor Applet"""
107 def __init__(self
, id):
108 """Initialize applet."""
109 applet
.Applet
.__init
__(self
, id)
111 # load the applet icon
112 self
.image
= gtk
.Image()
114 self
.pixbuf
= self
.images
[0]
115 self
.image
.set_from_pixbuf(self
.pixbuf
)
119 self
.vertical
= self
.get_panel_orientation() in ('Right', 'Left')
121 self
.set_size_request(8, -1)
123 self
.set_size_request(-1, 8)
126 self
.tooltips
= gtk
.Tooltips()
132 self
.add_events(gtk
.gdk
.BUTTON_PRESS_MASK
)
133 self
.connect('button-press-event', self
.button_press
)
134 self
.connect('size-allocate', self
.resize
)
135 self
.connect('delete_event', self
.quit
)
136 rox
.app_options
.add_notify(self
.get_options
)
138 # user adjustable timer to control how often hardware is polled
139 self
.timer
= gobject
.timeout_add(int(TIMER
.int_value
) * 100, self
.update_display
)
141 self
.update_display()
142 self
.update_tooltip()
145 def load_icons(self
):
146 """load the icons for the selected theme"""
152 'battery10', 'battery20', 'battery40', 'battery60',
153 'battery80', 'battery100',
154 'charging10', 'charging20', 'charging40', 'charging60',
155 'charging80', 'charging100',
157 self
.images
.append(gtk
.gdk
.pixbuf_new_from_file(os
.path
.join(rox
.app_dir
, 'themes', theme
, name
+'.svg')))
159 def notify(self
, string
):
161 n
= pynotify
.Notification(APP_NAME
, string
, 'battery-caution')
162 n
.set_urgency(pynotify
.URGENCY_CRITICAL
)
163 n
.attach_to_widget(self
)
168 def update_display(self
):
169 """Updates all the parts of our applet, and cleans it up if needed."""
171 percent
= BATTERY
.percent()
173 if WARN
.value
== 'True':
174 if (BATTERY
.charging_state() == OFFLINE
and percent
<= WARN_LEVEL
.int_value
):
175 if self
.warned
== False:
176 self
.notify(_("Warning. Battery is currently at %d%%") % (BATTERY
.percent(),))
196 if BATTERY
.charging_state():
199 pb
= self
.images
[index
]
201 self
.resize_image(self
.size
)
203 self
.update_tooltip()
205 return 1 # to keep the timer going!
208 def update_tooltip(self
):
209 self
.tooltips
.set_tip(self
, self
.status() + self
.percent() + '%')
214 if BATTERY
.charging_state() == 1:
215 txt
= _("AC Online: ")
216 elif BATTERY
.charging_state() == 2:
217 txt
= _("Charging: ")
219 # Discharing from the battery
221 temp2
= BATTERY
.time()
222 temp
= int(temp2
/ 60)
225 txt
= _("Calculating... ")
227 txt
= "%s (%d:%02d) " % (_("Battery"), temp
, temp2
)
230 temp
= BATTERY
.estimated_lifetime()
231 temp2
= int(60 * (temp
- int(temp
)))
232 txt
= "%s (%d:%02d) " % (_("Battery"), temp
, temp2
)
238 return str(BATTERY
.percent())
240 def resize(self
, widget
, rectangle
):
241 """Called when the panel sends a size."""
243 size
= rectangle
[2] -2
245 size
= rectangle
[3] -2
246 if size
!= self
.size
:
247 self
.resize_image(size
)
249 def resize_image(self
, size
):
250 """Resize the application image."""
251 scaled_pixbuf
= self
.pixbuf
.scale_simple(size
, size
, gtk
.gdk
.INTERP_BILINEAR
)
252 self
.image
.set_from_pixbuf(scaled_pixbuf
)
255 def button_press(self
, window
, event
):
256 """Handle mouse clicks by popping up the matching menu."""
257 # if event.button == 1:
259 # if event.button == 2:
261 if event
.button
== 3:
262 self
.appmenu
.popup(self
, event
, self
.position_menu
)
264 def get_panel_orientation(self
):
265 """ Return panel orientation and margin for displaying a popup menu.
266 Position in ('Top', 'Bottom', 'Left', 'Right').
268 pos
= self
.socket
.property_get('_ROX_PANEL_MENU_POS', 'STRING', False)
271 side
, margin
= pos
.split(',')
274 side
, margin
= None, 2
277 def get_options(self
, widget
=None, rebuild
=False, response
=False):
278 """Used as the notify callback when options change."""
279 if THEME
.has_changed
:
281 self
.update_display()
283 if TIMER
.has_changed
:
284 if self
.timer
: gobject
.source_remove(self
.timer
)
285 self
.timer
= gobject
.timeout_add(int(TIMER
.int_value
) * 100, self
.update_display
)
287 def show_options(self
, button
=None):
288 """Open the options edit dialog."""
292 """Display an InfoWin self."""
293 from rox
import InfoWin
294 InfoWin
.infowin(APP_NAME
)
296 def build_appmenu(self
):
297 """Build the right-click app menu."""
299 items
.append(Menu
.Action(_('Info...'), 'get_info', '', gtk
.STOCK_DIALOG_INFO
))
300 items
.append(Menu
.Action(_('Options...'), 'show_options', '', gtk
.STOCK_PREFERENCES
))
301 items
.append(Menu
.Separator())
302 items
.append(Menu
.Action(_('Close'), 'quit', '', gtk
.STOCK_CLOSE
))
303 self
.appmenu
= Menu
.Menu('other', items
)
304 self
.appmenu
.attach(self
, self
)
306 def quit(self
, *args
):
307 """Quit applet and close everything."""
308 if self
.timer
: gobject
.source_remove(self
.timer
)