summaryrefslogtreecommitdiffstats
path: root/powermanager/guidance-power-manager.py
diff options
context:
space:
mode:
Diffstat (limited to 'powermanager/guidance-power-manager.py')
-rwxr-xr-xpowermanager/guidance-power-manager.py1134
1 files changed, 1134 insertions, 0 deletions
diff --git a/powermanager/guidance-power-manager.py b/powermanager/guidance-power-manager.py
new file mode 100755
index 0000000..41df5e8
--- /dev/null
+++ b/powermanager/guidance-power-manager.py
@@ -0,0 +1,1134 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+"""
+Copyright 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko
+
+Authors:
+ Sebastian Kügler <sebas@kde.org>
+ Jonathan Riddell <jriddell@ubuntu.com>
+ Luka Renko <lure@kubuntu.org>
+
+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.
+
+"""
+
+"""
+A frontend to HAL's power features for KDE.
+Supports screen brightness, battery level, plugged/unplugged notices, laptop lid closing actions
+Specification at https://wiki.kubuntu.org/KubuntuPowerManagement
+
+Issues:
+ - We have to keep polling HAL rather than listening for signals because the Python DBUS bindings
+ don't have Qt mainloop integration
+ - Written in Python so will be slow to load up, will probably port to C++ Qt 4.2 in future
+ - Should also handle UPS and bluetooth batteries
+ - systray applet should be hidden if no battery, but then how do you suspend if no battery?
+ (ksmserver integration please)
+ - Needs lots more testing
+ - Use KUniqueApplication again as soon as dcop problem is sorted outc
+ - dcop calls need patch to dcopexport.py, already submitted upstream
+"""
+
+import os
+import sys
+import subprocess
+import dbus
+
+from qt import *
+from kdecore import *
+from kdeui import *
+
+from dcopext import DCOPClient, DCOPApp # used to lock the screen
+from dcopexport import DCOPExObj
+
+from guidance_power_manager_ui import PowerManagerUI
+from notify import NotifyWidget
+from tooltip import ToolTip
+
+from powermanage import *
+
+POLL_INTERVAL = 5000 # in milliseconds
+
+class Notify(NotifyWidget):
+ """ Pop up a passive notication windows. """
+
+ def __init__(self,parent,msg,icon,caption=None):
+ NotifyWidget.__init__(self,parent,"notify")
+ self.setIcon(icon)
+ self.setText(msg)
+ if caption:
+ self.Caption(caption)
+
+ def setIcon(self,pixmap):
+ """ Set an icon to be displayed in the notification. """
+ if pixmap:
+ self.Icon.setPixmap(pixmap)
+
+ def setCaption(self,caption):
+ """ Text to show in bold letters. """
+ self.Caption.setText(QString("<b>")+caption+QString("</b>"))
+
+ def setText(self,msg):
+ """" Set actual notification message. """
+ self.Text.setText(msg)
+
+
+class PowerManager(PowerManagerUI):
+ """ Our configuration dialog. """
+
+ def __init__ (self, parent, name):
+ PowerManagerUI.__init__(self, parent, name)
+ KGlobal.iconLoader().addAppDir("guidance")
+
+ # The systray icon should show and hide the KDialogBase, not only this widget,
+ # therefore, it gets our parent as parent.
+ self.systray = KSystemTray(parent)
+ self.icon = "battery-charging-100"
+ self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
+ self.connect(self.systray, SIGNAL("quitSelected()"), self.quit)
+
+ # Configuration filename
+ self.config = KConfig("power-managerrc")
+
+ self.powermanager = PowerManage()
+
+ def prepare(self):
+ """ Prepare UI. """
+ self._initBrightness()
+ self._initLid()
+ self._initBattery()
+ self.lastidlesec = 0
+
+ self._initConfigKeywords()
+
+ self._initUI(self.parent())
+
+ self.configToUi()
+
+ # Polling: evil. can't receive signals in python-dbus unless we have a glib mainloop,
+ # so we need to poll
+ self.pollTimer = QTimer(self)
+ self.connect(self.pollTimer, SIGNAL("timeout()"), self.poll)
+ self.pollTimer.start(POLL_INTERVAL) # 5 second poll, maybe make this configurable
+ self.poll(False)
+
+ # check CPU freq policy and notify if it was changed
+ msg = self.checkCpuFreq()
+ if msg != "":
+ self.notify(msg)
+
+ self.systray.show()
+
+ def _initBrightness(self):
+ """ Check for brightness support and disable widgets if it's not there. """
+ if not self.powermanager.hasBrightness:
+ self.PoweredBrightnessLabel.hide()
+ self.PoweredBrightnessSlider.hide()
+ self.BatteryBrightnessLabel.hide()
+ self.BatteryBrightnessSlider.hide()
+
+ def _initLid(self):
+ """ Check for lid support and disable widgets if it's not there. """
+ if not self.powermanager.hasLid:
+ self.LaptopLidRadios.setEnabled(False)
+
+ def _initCB(self, combo, options, values):
+ """ Initialize QComboBox with proper values from provided options. """
+ combo.clear()
+ for option in options:
+ combo.insertItem(values[option])
+
+ def _getCB(self, combo, options):
+ """ Get current item from QComboBox from config file (string) value. """
+ try:
+ return options[combo.currentItem()]
+ except IndexError:
+ return ""
+
+ def _setCB(self, combo, options, default, value):
+ """ Set current item in QComboBox from string value. """
+ try:
+ num = options.index(value)
+ except ValueError:
+ num = default
+ pass
+ combo.setCurrentItem(num)
+
+ def _getRB(self, radios, options):
+ """ Get current item from QRadioButton from config file (string) value. """
+ try:
+ return options[radios.selectedId()]
+ except IndexError:
+ return ""
+
+ def _setRB(self, radios, options, default, value):
+ """ Set current item in QRadioButton from string value. """
+ try:
+ num = options.index(value)
+ except ValueError:
+ num = default
+ pass
+ radios.setButton(num)
+
+ def _checkOldConfig(self, value, blank):
+ """ Convert old numerical values to keywords. """
+ try:
+ num_val = int(value)
+ except ValueError:
+ return value
+ if blank:
+ if num_val == 0: return 'nothing'
+ if num_val == 1: return 'blank'
+ if num_val == 2: return 'suspend'
+ if num_val == 3: return 'hibernate'
+ if num_val == 4: return 'shutdown'
+ else:
+ if num_val == 0: return 'nothing'
+ if num_val == 1: return 'suspend'
+ if num_val == 2: return 'hibernate'
+ if num_val == 3: return 'shutdown'
+ return value
+
+
+ def _initConfigKeywords(self):
+ """ Define helper maps used with config file keywords. """
+ # map action keyword to displayed name (l10n)
+ self.act_name = {}
+ self.act_name['nothing'] = i18n("Do nothing")
+ self.act_name['blank'] = i18n("Blank screen")
+ self.act_name['suspend'] = i18n("Suspend")
+ self.act_name['hibernate'] = i18n("Hibernate")
+ self.act_name['shutdown'] = i18n("Shutdown")
+
+ # map action keyword to action methods
+ self.act_call = {}
+ self.act_call['nothing'] = None
+ self.act_call['blank'] = self.blankScreen
+ self.act_call['suspend'] = self.suspend
+ self.act_call['hibernate'] = self.hibernate
+ self.act_call['shutdown'] = self.shutdown
+
+ # map action keyword to notification description (l10n)
+ self.act_notify = {}
+ self.act_notify['nothing'] = i18n("doing nothing")
+ self.act_notify['blank'] = i18n("blanking screen")
+ self.act_notify['suspend'] = i18n("suspending")
+ self.act_notify['hibernate'] = i18n("hibernating")
+ self.act_notify['shutdown'] = i18n("shutting down")
+
+ # map action keyword to action icon used in notification window
+ self.act_icon = {}
+ self.act_icon['nothing'] = None
+ self.act_icon['blank'] = None
+ self.act_icon['suspend'] = SmallIcon("suspend")
+ self.act_icon['hibernate'] = SmallIcon("hibernate")
+ self.act_icon['shutdown'] = SmallIcon("exit")
+
+ # map policy keyword to displayed name (l10n)
+ self.freq_name = {}
+ self.freq_name['dynamic'] = i18n("Dynamic")
+ self.freq_name['powersave'] = i18n("Powersave")
+ self.freq_name['performance'] = i18n("Performance")
+
+ # map policy keyword to policy change methods
+ self.freq_call = {}
+ self.freq_call['dynamic'] = self.setCpuPolicyDynamic
+ self.freq_call['powersave'] = self.setCpuPolicyPowersave
+ self.freq_call['performance'] = self.setCpuPolicyPerformance
+
+
+ def _initUI(self, parent):
+ """ Build dynamic parts of the UI: context menu and tooltip. """
+ self.canSuspend = self.powermanager.canSuspend and not self.config.readBoolEntry("disableSuspend", False)
+ self.canHibernate = self.powermanager.canHibernate and not self.config.readBoolEntry("disableHibernate", False)
+
+ # Connect some signals. Updates in the dialogue apply instantly
+ self.connect(self.PoweredBrightnessSlider, SIGNAL("valueChanged(int)"), self.changePoweredBrightness)
+ self.connect(self.BatteryBrightnessSlider, SIGNAL("valueChanged(int)"), self.changeBatteryBrightness)
+
+ #Add a blank tooltip, the tooltipgroup signals are then used for our KPassivePopup
+ toolTipGroup = QToolTipGroup(self.systray)
+ QToolTip.add(self.systray, "", toolTipGroup, "blah")
+ self.connect(toolTipGroup, SIGNAL("showTip(const QString&)"), self.showTip)
+ self.connect(toolTipGroup, SIGNAL("removeTip()"), self.hideTip)
+
+ # Popup tooltip showing battery level
+ self.popup = KPassivePopup(self.systray)
+
+ self.tooltip = ToolTip(self.popup)
+
+ self._addBatteryWidgets()
+
+ self._addCpuWidgets()
+ self.popup.setView(self.tooltip)
+
+ # fill actions for LID
+ self.lid_act = ['nothing', 'blank', 'suspend', 'hibernate', 'shutdown']
+ self.lid_act_def = 0
+ # hide LID close actions that are not supported
+ if not self.canSuspend:
+ self.laptopClosedSuspend.hide()
+ if not self.canHibernate:
+ self.laptopClosedHibernate.hide()
+
+ # fill in only CPU policies that are supported by HW
+ self.cb_freq = [] # list of supported cpu freq policies
+ self.cb_freq_def = 0 # always use first policy as default
+ if self.powermanager.hasCpuFreqGovernors:
+ self.cb_freq = self.powermanager.getSupportedCpuPolicies()
+ if len(self.cb_freq) > 0:
+ self._initCB(self.PoweredFreqCombo, self.cb_freq, self.freq_name)
+ self._initCB(self.BatteryFreqCombo, self.cb_freq, self.freq_name)
+ else:
+ self.PoweredFreqLabel.hide()
+ self.PoweredFreqCombo.hide()
+ self.BatteryFreqLabel.hide()
+ self.BatteryFreqCombo.hide()
+
+ # fill actions in Idle/Critical battery combo boxes
+ self.cb_act = ['nothing'] # list of supported actions (keywords)
+ self.cb_act_def_critical = 0 # default action when critical battery
+ if self.canSuspend:
+ self.cb_act.append('suspend')
+ if self.canHibernate:
+ self.cb_act.append('hibernate')
+ self.cb_act_def_critical = len(self.cb_act) - 1 # hibernate
+ self.cb_act.append('shutdown')
+ if self.cb_act_def_critical == 0:
+ self.cb_act_def_critical = len(self.cb_act) - 1 # shutdown
+ self._initCB(self.PoweredIdleCombo, self.cb_act, self.act_name)
+ self._initCB(self.BatteryIdleCombo, self.cb_act, self.act_name)
+ self._initCB(self.BatteryCriticalCombo, self.cb_act, self.act_name)
+
+ self.connect(self.PoweredIdleCombo,SIGNAL("activated(int)"),self.slotPoweredIdleActivated)
+ self.connect(self.BatteryIdleCombo,SIGNAL("activated(int)"),self.slotBatteryIdleActivated)
+ self.connect(self.BatteryCriticalCombo,SIGNAL("activated(int)"),self.slotBatteryCriticalActivated)
+
+ # add suspend/hibernate to tray's context menu
+ menu = self.systray.contextMenu()
+ if self.canSuspend:
+ action = KAction( i18n("Suspend"), KShortcut(), self.suspend,
+ self.systray.actionCollection(), "suspend")
+ action.setIcon("suspend")
+ action.plug(menu)
+ if self.canHibernate:
+ action = KAction( i18n("Hibernate"), KShortcut(), self.hibernate,
+ self.systray.actionCollection(), "hibernate")
+ action.setIcon("hibernate")
+ action.plug(menu)
+
+ # add list of governators
+ if self.powermanager.hasCpuFreqGovernors and len(self.cb_freq) > 0:
+ submenu = KPopupMenu(menu)
+ for policy in self.cb_freq:
+ action = KRadioAction(self.freq_name[policy], KShortcut(),
+ self.freq_call[policy],
+ self.systray.actionCollection(), policy)
+ action.setExclusiveGroup("freqs")
+ action.plug(submenu)
+
+ policy = self.powermanager.getCpuPolicy()
+ if policy in self.cb_freq:
+ self.systray.actionCollection().action(policy).setChecked(True);
+ menu.insertItem(i18n("CPU policy"), submenu)
+
+
+ # KGlobalAccel crashes the application in pykde
+ # see http://mats.gmd.de/pipermail/pykde/2006-May/013224.html
+ #self.globalActions = KGlobalAccel(self)
+ #self.suspendShortcut = KShortcut("XF86Sleep")
+ #self.hibernateShortcut = KShortcut("XF86Standby")
+ #self.hshutdownShortcut = KShortcut("XF86PowerOff")
+ #self.globalActions.insert("suspend", i18n("Suspend"), i18n("what's this?"), self.suspendShortcut, #self.suspendShortcut, self.suspend)
+ #self.globalActions.updateConnections()
+
+ def _initBattery(self):
+ """ Remove non-battery-related widgets if there's no battery bay. """
+ if not self.powermanager.hasBattery:
+ # Disable the Batterybox in the config dialogue,
+ self.BatteryBox.setEnabled(False)
+ # And change the icon in the systray, remove the restore option
+ # This way, we're basically becoming a systray applet, you can
+ # hibernate and suspend from
+ self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
+ if self.powermanager.hasAC:
+ self.wasOnBattery = self.powermanager.onBattery()
+
+ def configToUi(self):
+ """ Setup the the values from the config file in the UI."""
+ # brightness.
+ if self.powermanager.hasBrightness:
+ brightness_high = self.powermanager.brightness_levels
+ self.BatteryBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1)
+ self.PoweredBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1)
+ self.BatteryBrightnessSlider.setValue(self.config.readNumEntry("batteryBrightness", int(brightness_high/2))) #default middle
+ self.PoweredBrightnessSlider.setValue(self.config.readNumEntry("poweredBrightness", brightness_high)) #default highest
+
+ tt_text = "Every step increases or decreases the brightness by %i%%" % int(100/brightness_high)
+ QToolTip.add(self.BatteryBrightnessSlider, tt_text)
+ QToolTip.add(self.PoweredBrightnessSlider, tt_text)
+
+ self.lockScreenOnResume.setChecked(self.config.readBoolEntry("lockOnResume", True))
+
+ # Idletime-related configuration
+ self._setCB(self.PoweredIdleCombo, self.cb_act, 0, str(self.config.readEntry("poweredIdleAction")))
+ self.PoweredIdleTime.setValue(self.config.readNumEntry("poweredIdleTime", 60))
+ self._setCB(self.BatteryIdleCombo, self.cb_act, 0, str(self.config.readEntry("batteryIdleAction")))
+ self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10))
+
+ self._setCB(self.PoweredFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("poweredFreqPolicy")))
+ self._setCB(self.BatteryFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("batteryFreqPolicy")))
+
+ self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10)) # default Do nothing
+ # battery critical and lid actions.
+ self._setCB(self.BatteryCriticalCombo, self.cb_act, self.cb_act_def_critical, self._checkOldConfig(self.config.readEntry("batteryCriticalAction", ""), False))
+ self._setRB(self.LaptopLidRadios, self.lid_act, self.lid_act_def, self._checkOldConfig(self.config.readEntry("laptopLidAction", ""), True))
+ self.CriticalRemainTime.setValue(self.config.readNumEntry("criticalRemainTime", BATTERY_CRITICAL_MINUTES))
+ self.criticalLevel = self.CriticalRemainTime.value()
+
+ # Call some slots to disable various spinboxes if necessary
+ self.slotBatteryCriticalActivated()
+ self.slotPoweredIdleActivated()
+ self.slotBatteryIdleActivated()
+
+
+ def uiToConfig(self):
+ """ Read all values from the UI and write them to the config file. """
+ self.config.writeEntry("poweredBrightness", self.PoweredBrightnessSlider.value())
+ self.config.writeEntry("batteryBrightness", self.BatteryBrightnessSlider.value())
+
+ self.config.writeEntry("poweredIdleTime", self.PoweredIdleTime.value())
+ self.config.writeEntry("poweredIdleAction", self._getCB(self.PoweredIdleCombo, self.cb_act))
+ self.config.writeEntry("batteryIdleTime", self.BatteryIdleTime.value())
+ self.config.writeEntry("batteryIdleAction", self._getCB(self.BatteryIdleCombo, self.cb_act))
+ self.config.writeEntry("poweredFreqPolicy", self._getCB(self.PoweredFreqCombo, self.cb_freq))
+ self.config.writeEntry("batteryFreqPolicy", self._getCB(self.BatteryFreqCombo, self.cb_freq))
+
+ self.config.writeEntry("batteryCriticalAction", self._getCB(self.BatteryCriticalCombo, self.cb_act))
+ self.config.writeEntry("criticalRemainTime", self.CriticalRemainTime.value())
+
+ self.config.writeEntry("laptopLidAction", self._getRB(self.LaptopLidRadios, self.lid_act))
+ self.config.writeEntry("lockOnResume", self.lockScreenOnResume.isChecked())
+
+ self.criticalLevel = self.CriticalRemainTime.value()
+
+ self.config.sync()
+
+ def quit(self):
+ """ Quit application. """
+ kapp.quit()
+
+ def showTip(self, text=""):
+ """ Pop up the tooltip showing battery data and CPU frequencies. """
+ self.popup.show()
+
+
+ def showBrightnessPopup(self):
+ if self.powermanager.onBattery():
+ value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue()
+ else:
+ value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue()
+ self.brightnessPopup = KPassivePopup.message('<b>Brightness:</b> '+str(value)+'%', self.systray)
+ """pop.setTimeout(3000)"""
+ self.brightnessPopup.show()
+
+ def setBrightnessUp(self):
+ """Increments slider value by 10%"""
+ if self.powermanager.onBattery():
+ self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())+max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1))
+ else:
+ self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())+max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1))
+ self.showBrightnessPopup()
+
+ def setBrightnessDown(self):
+ """Decrements slider value by 10%"""
+ if self.powermanager.onBattery():
+ self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())-max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1))
+ else:
+ self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())-max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1))
+ self.showBrightnessPopup()
+
+ def getBrightness(self):
+ """Work with percentages - it's a bit nicer"""
+ if self.powermanager.onBattery():
+ value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue()
+ else:
+ value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue()
+ return QString(str(value))
+
+ def hideTip(self):
+ """ Hide the tooltip."""
+ self.popup.hide()
+
+ def lockScreen(self):
+ """ locks the screen using kdesktop """
+ # create a new DCOP-Client:
+ client = DCOPClient()
+ # connect the client to the local DCOP-server:
+ client.attach()
+ # create a DCOP-Application-Object to talk to kdesktop:
+ kdesktop = DCOPApp('kdesktop', client)
+ # call a DCOP-function:
+ try:
+ ok, foo = kdesktop.KScreensaverIface.lock()
+ except:
+ print "Unable to lock the screen. The KDE Screensaver does not seem to be running."
+ def suspend(self):
+ """ Lock the screen and initiate a suspend to RAM (S3). """
+ if self.config.readBoolEntry("lockOnResume", True):
+ self.lockScreen()
+ try:
+ self.warningPopup.hide()
+ except AttributeError:
+ pass # No warningpopup, that's OK.
+ self.powermanager.suspend()
+ self.powermanager.resetIdleSeconds()
+
+ def hibernate(self):
+ """ Lock the screen and initiate a suspend to disk (S4). """
+ if self.config.readBoolEntry("lockOnResume", True):
+ self.lockScreen()
+ try:
+ self.warningPopup.hide()
+ except AttributeError:
+ pass # No warningpopup, that's OK.
+ self.powermanager.hibernate()
+ self.powermanager.resetIdleSeconds()
+
+ def shutdown(self):
+ """ Perform system shutdown. """
+ self.powermanager.shutdown()
+
+ def setCpuPolicyDynamic(self):
+ """Change frequ for all cpu"""
+ self.powermanager.setCpuPolicy('dynamic')
+ self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['dynamic']))
+
+ def setCpuPolicyPerformance(self):
+ """Change frequ for all cpu"""
+ self.powermanager.setCpuPolicy('performance')
+ self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['performance']))
+
+ def setCpuPolicyPowersave(self):
+ """Change frequ for all cpu"""
+ self.powermanager.setCpuPolicy('powersave')
+ self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['powersave']))
+
+ def trySuspend(self):
+ """ If supported, lock the screen and initiate a suspend to RAM (S3). """
+ if self.canSuspend:
+ self.suspend()
+ else:
+ print "Warning: DCOP suspend() called, but not supported."
+
+ def tryHibernate(self):
+ """ If supported, lock the screen and initiate a suspend to disk (S4). """
+ if self.canHibernate:
+ self.hibernate()
+ else:
+ print "Warning: DCOP hibernate() called, but not supported."
+
+ def blankScreen(self):
+ """ Lock and blank screen. """
+ if self.config.readBoolEntry("lockOnResume", True):
+ self.lockScreen()
+ self.powermanager.blankScreen()
+
+ def _getIcon(self):
+ """ Set systray icon depending on battery status/level. """
+ if self.powermanager.hasBattery:
+ if self.batt_state == "not present":
+ self.icon = "ac-adapter"
+ if self.batt_state == "charged":
+ self.icon = "battery-charging-100"
+ elif self.batt_state == "discharging":
+ if self.batt_level >= 95:
+ self.icon = "battery-discharging-100"
+ elif self.batt_level < 95 and self.batt_level >= 85:
+ self.icon = "battery-discharging-090"
+ elif self.batt_level < 85 and self.batt_level >= 75:
+ self.icon = "battery-discharging-070"
+ elif self.batt_level < 75 and self.batt_level >= 60:
+ self.icon = "battery-discharging-060"
+ elif self.batt_level < 65 and self.batt_level >= 45:
+ self.icon = "battery-discharging-050"
+ elif self.batt_level < 45 and self.batt_level >= 30:
+ self.icon = "battery-discharging-040"
+ elif self.batt_level < 30 and self.batt_level >= 20:
+ self.icon = "battery-discharging-030"
+ elif self.batt_level < 20 and self.batt_level >= 10:
+ self.icon = "battery-discharging-020"
+ elif self.batt_level < 10 and self.batt_level >= 5:
+ self.icon = "battery-discharging-010"
+ else:
+ self.icon = "battery-discharging-000"
+ elif self.batt_state == "charging":
+ if self.batt_level >= 95:
+ self.icon = "battery-charging-100"
+ elif self.batt_level < 95 and self.batt_level >= 85:
+ self.icon = "battery-charging-090"
+ elif self.batt_level < 85 and self.batt_level >= 75:
+ self.icon = "battery-charging-070"
+ elif self.batt_level < 75 and self.batt_level >= 60:
+ self.icon = "battery-charging-060"
+ elif self.batt_level < 65 and self.batt_level >= 45:
+ self.icon = "battery-charging-050"
+ elif self.batt_level < 45 and self.batt_level >= 30:
+ self.icon = "battery-charging-040"
+ elif self.batt_level < 30 and self.batt_level >= 20:
+ self.icon = "battery-charging-030"
+ elif self.batt_level < 20 and self.batt_level >= 10:
+ self.icon = "battery-charging-020"
+ elif self.batt_level < 10 and self.batt_level >= 5:
+ self.icon = "battery-charging-010"
+ else:
+ self.icon = "battery-charging-000"
+ else:
+ self.icon = "ac-adapter"
+ return self.icon
+
+ def getIcon(self):
+ """ Return current icon."""
+ return UserIcon(self.icon)
+
+ def setIcon(self):
+ """ Change the systray/tooltip icon."""
+ oldIcon = self.icon
+ self.icon = self._getIcon()
+ if self.icon != oldIcon:
+ self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
+ self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon)))
+
+ def notify(self, msg, icon=None):
+ """ Send a notification popup. """
+ if icon:
+ icon = QPixmap(icon)
+ else:
+ icon = QPixmap(SmallIcon("messagebox_info"))
+ try:
+ del self.warningPopup
+ except:
+ pass
+ self.warningPopup = KPassivePopup(self.systray)
+ label = Notify(self.warningPopup, msg, icon)
+ self.warningPopup.setView(label)
+ position = QPoint(5,5)
+ self.warningPopup.show(position)
+
+ def poll(self,notify=True):
+ """ Check for changes in plugged in status, battery status and laptop lid closed status. """
+ debug( "------------ POLL ---------------")
+
+ self.powermanager.checkHAL()
+ # Battery stuff:
+ # check for last state, and run plugged / unplugged message if the state changed.
+ if self.powermanager.hasBattery:
+ plugged_num = 0
+ self.batt_state = "not present" # unknown yet
+ self.batt_level = self.batt_remain = 0
+ self.batt_rate = self.batt_charge = self.batt_full = 0
+ for batt in self.powermanager.batteries:
+ state, level, remain, rate, current, full = self.powermanager.getBatteryState(batt)
+ self._updateBatteryWidget(batt, state, level, remain, rate)
+
+ ## notify plugged/unplugged batteries
+ if state == "not present":
+ if self.powermanager.batteryIsPresent[batt]:
+ self.notify(i18n("The battery has been removed."))
+ self.powermanager.batteryIsPresent[batt] = False
+ else: # battery present
+ if not self.powermanager.batteryIsPresent[batt]:
+ self.notify(i18n("The battery has been inserted."))
+ self.powermanager.batteryIsPresent[batt] = True
+
+ ## get cumulative charge levels/rate
+ self.batt_rate += rate
+ self.batt_charge += current
+ self.batt_full += full
+
+ ## calculate overall level (average of present batteries)
+ self.batt_remain += remain
+ self.batt_level += level
+ plugged_num += 1
+
+ ## calculate overall state (charging/discharging/charged)
+ if state in ("charging","discharging"):
+ self.batt_state = state
+ elif not self.batt_state in ("charging, discharging"):
+ self.batt_state = state
+
+ # if we know charge and full -> recalculate overall level
+ if self.batt_full > 0 and self.batt_charge > 0:
+ self.batt_level = 100 * self.batt_charge / self.batt_full
+ else:
+ # if more than one battery present, we need to calculate average level
+ if plugged_num > 1:
+ self.batt_level /= plugged_num
+
+ # if rate is reported, calculate remaining time on our own
+ if self.batt_rate > 0:
+ if self.batt_state == "charging":
+ self.batt_remain = 3600 * (float(self.batt_full - self.batt_charge) / self.batt_rate)
+ if self.batt_state == "discharging":
+ self.batt_remain = 3600 * (float(self.batt_charge) / self.batt_rate)
+
+ remain_h = self.batt_remain/3600
+ remain_m = (self.batt_remain/60)%60
+
+ blabel = i18n("<b>Battery:</b>")
+ if self.batt_state == "charged":
+ blabel += i18n(" fully charged")
+ elif self.batt_state == "charging":
+ blabel += i18n(" %i:%02ih to charge" % (remain_h,remain_m))
+ elif self.batt_state == "discharging":
+ blabel += i18n(" %i:%02ih remaining" % (remain_h,remain_m))
+ self.BattMainLabel.setText(blabel)
+
+ # update tray icon if needed
+ self.setIcon()
+
+ # check battery state
+ self.checkBatteryCritical()
+
+ # check Idletime
+ self.checkIdletime()
+
+ # CPU stuff
+ self._updateCpuWidgets()
+
+ if self.powermanager.hasBattery:
+ on_battery = self.powermanager.onBattery()
+ if self.powermanager.wasOnBattery != on_battery:
+ self.powermanager.wasOnBattery = on_battery
+ debug("poll: states differ")
+ if not on_battery:
+ debug("poll: Now on AC")
+ if notify:
+ self.powerHasBeenPlugged()
+ else:
+ debug("poll: Now on battery")
+ if notify:
+ self.powerHasBeenUnplugged()
+ else:
+ debug("poll: state is the same")
+
+ # Lid stuff
+ if self.powermanager.hasLid:
+ if self.powermanager.getLidClosedState():
+ if not self.powermanager.lidClosedState:
+ self.powermanager.lidClosedState = True
+
+ action = self._getRB(self.LaptopLidRadios, self.lid_act)
+ if not self.act_name.has_key(action):
+ action = self.act_name[self.lid_act_def]
+
+ if self.act_call[action] != None:
+ note = i18n("Laptop lid is closed, %1 now.").arg(self.act_notify[action])
+ self.notify(note, self.act_icon[action])
+ QTimer.singleShot(2000, self.act_call[action])
+ else:
+ self.powermanager.lidClosedState = False
+
+ def _addBatteryWidgets(self):
+ """ Adds progressbars to show battery status to the tooltip."""
+ BattLayout = QHBoxLayout(None,0,6,"BattLayout")
+
+ self.BattPixmap = QLabel(self.tooltip,"BattLabLayout")
+ self.BattPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.BattPixmap.sizePolicy().hasHeightForWidth()))
+ self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon)))
+ self.BattPixmap.setScaledContents(1)
+ BattLayout.addWidget(self.BattPixmap)
+ self.BattMainLabel = QLabel(self.tooltip,"BattMainLabel")
+ self.BattMainLabel.setText(i18n("<b>Battery:</b>"))
+ BattLayout.addWidget(self.BattMainLabel)
+
+ # Add to tooltip
+ self.tooltip.layout().addLayout(BattLayout)
+
+ # Create a progressbar and a label for every battery found, and add it to tooltip
+ self.BattLabel = {}
+ self.BattLayout = {}
+ self.BattProgress = {}
+ i = 1
+ for batt in self.powermanager.batteries:
+ self.BattLayout[batt] = QHBoxLayout(None,0,6,"BattBarLayout")
+ self.BattLabel[batt] = QLabel(self.tooltip,"BattLabel")
+ if len(self.powermanager.batteries) > 1:
+ self.BattLabel[batt].setText(i18n("Battery %i" % i))
+ self.BattLayout[batt].addWidget(self.BattLabel[batt])
+ self.BattProgress[batt] = KProgress(self.tooltip,"BattProgress")
+ self.BattProgress[batt].setMinimumSize(QSize(200,0))
+ self.BattLayout[batt].addWidget(self.BattProgress[batt])
+ self.tooltip.layout().addLayout(self.BattLayout[batt])
+ i += 1
+
+
+ def _updateBatteryWidget(self, batt, state, level, remain, rate):
+ """ Retrieve battery information and update the related widgets accordingly. """
+ self.BattProgress[batt].setEnabled(True)
+ self.BattProgress[batt].setTotalSteps(100)
+ self.BattProgress[batt].setProgress(level)
+ if state == "not present":
+ self.BattProgress[batt].setFormat(i18n("not present"))
+ elif state == "charging":
+ self.BattProgress[batt].setFormat(i18n("Charging (%p%)"))
+ elif state == "discharging":
+ if rate > 0:
+ showrate = rate/1000
+ self.BattProgress[batt].setFormat(i18n("Discharging (%p%)") + " - %.d W" % showrate)
+ else:
+ self.BattProgress[batt].setFormat(i18n("Discharging (%p%)"))
+ else:
+ self.BattProgress[batt].setFormat("%p%")
+
+ def _addCpuWidgets(self):
+ """ Adds progressbars to show CPU frequencies to the tooltip."""
+ if not SHOW_CPUFREQ:
+ return
+ if len(self.powermanager.cpus) == 0:
+ return
+
+ LabelLayout = QHBoxLayout(None,0,6,"layout5")
+
+ self.CpuPixmap = QLabel(self.tooltip,"CpuPixmap")
+ self.CpuPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.CpuPixmap.sizePolicy().hasHeightForWidth()))
+ self.CpuPixmap.setPixmap(QPixmap(UserIcon("processor")))
+ self.CpuPixmap.setScaledContents(1)
+ LabelLayout.addWidget(self.CpuPixmap)
+ self.CpuMainLabel = QLabel(self.tooltip,"CpuMainLabel")
+ self.CpuMainLabel.setText(i18n("<b>CPU Frequency:</b>"))
+ LabelLayout.addWidget(self.CpuMainLabel)
+
+ # Add to tooltip
+ self.tooltip.layout().addLayout(LabelLayout)
+
+ # Create a progressbar and a label for every CPU found, and add it to tooltip
+ self.CpuLabel = {}
+ self.CpuLayout = {}
+ self.CpuProgress = {}
+ i = 1
+ for cpu in self.powermanager.cpus:
+ self.CpuLayout[cpu] = QHBoxLayout(None,0,6,"layout2")
+ self.CpuLabel[cpu] = QLabel(self.tooltip,"CpuLabel")
+ if len(self.powermanager.cpus) > 1:
+ self.CpuLabel[cpu].setText(i18n("Processor %i" % i))
+ self.CpuLayout[cpu].addWidget(self.CpuLabel[cpu])
+ self.CpuProgress[cpu] = KProgress(self.tooltip,"CpuProgress")
+ self.CpuProgress[cpu].setFormat("%v MHz")
+ self.CpuLayout[cpu].addWidget(self.CpuProgress[cpu])
+ self.tooltip.layout().addLayout(self.CpuLayout[cpu])
+ i += 1
+
+ def slotPoweredIdleActivated(self, index=False):
+ """ Signal slot for activated powered idle action. """
+ if not index:
+ index = self.PoweredIdleCombo.currentItem()
+ self.PoweredIdleTime.setEnabled(index != 0)
+
+ def slotBatteryIdleActivated(self, index=False):
+ """ Signal slot for activated battery idle action. """
+ if not index:
+ index = self.BatteryIdleCombo.currentItem()
+ self.BatteryIdleTime.setEnabled(index != 0)
+
+ def slotBatteryCriticalActivated(self, index=False):
+ """ Signal slot for activated battery critical action. """
+ if not index:
+ index = self.BatteryCriticalCombo.currentItem()
+ self.CriticalRemainTime.setEnabled(index != 0)
+
+ def _updateCpuWidgets(self):
+ """ Retrieve CPU freq information and update the related widgets accordingly. """
+ if not SHOW_CPUFREQ:
+ return
+ if len(self.powermanager.cpus) == 0:
+ return
+
+ clabel = i18n("<b>CPU Frequency:</b>") + " "
+ policy = self.powermanager.getCpuPolicy()
+ if self.freq_name.has_key(policy):
+ clabel += self.freq_name[policy] # get l10n name
+ else:
+ clabel += policy
+ self.CpuMainLabel.setText(clabel)
+
+ for cpu in self.powermanager.cpus:
+ cpustate = self.powermanager.getCpuState(cpu)
+ if not cpustate['online']:
+ self.CpuProgress[cpu].setEnabled(False)
+ else:
+ self.CpuProgress[cpu].setEnabled(True)
+ self.CpuProgress[cpu].setTotalSteps(cpustate['max'])
+ self.CpuProgress[cpu].setProgress(cpustate['cur'])
+ if policy != "":
+ self.systray.actionCollection().action(policy).setChecked(True)
+ if policy in self.cb_freq:
+ self.systray.actionCollection().action(policy).setChecked(True)
+
+ def changePoweredBrightness(self, level=None):
+ """ Mains-powered brigthness slider has been moved. """
+ # Check if the state applies and adjust brightness immediately.
+ if not self.powermanager.onBattery() and self.powermanager.hasBrightness:
+ if not level:
+ level = self.PoweredBrightnessSlider.value()
+ self.powermanager.adjustBrightness(level)
+
+ def changeBatteryBrightness(self, level=None):
+ """ Battery-powered brigthness slider has been moved. """
+ # Check if the state applies and adjust brightness immediately.
+ if self.powermanager.onBattery() and self.powermanager.hasBrightness:
+ if not level:
+ level = self.BatteryBrightnessSlider.value()
+ self.powermanager.adjustBrightness(level)
+
+
+
+
+ def checkCpuFreq(self):
+ """ Adjust CPU frequency policy according to current state """
+ if not self.powermanager.hasCpuFreqGovernors:
+ return ""
+
+ if self.powermanager.onBattery():
+ policy = str(self.config.readEntry("batteryFreqPolicy"))
+ else:
+ policy = str(self.config.readEntry("poweredFreqPolicy"))
+ if policy == "":
+ policy = 'dynamic'
+
+ # check if specified policy is supported by HW
+ if not policy in self.cb_freq:
+ print "Warning: policy from config file not supported: ", policy
+ return ""
+
+ current_policy = self.powermanager.getCpuPolicy()
+ if current_policy != policy:
+ debug("Switching CPU policy from %s to %s." % (current_policy, policy))
+ self.powermanager.setCpuPolicy(policy)
+ return i18n("CPU frequency policy changed to %1.").arg(self.freq_name[policy])
+ elif current_policy == 'dynamic':
+ debug("Dynamic policy -> update policy (conservative/ondemand)")
+ self.powermanager.setCpuPolicy(policy)
+
+ debug("CPU policy will stay %s" % current_policy)
+ return ""
+
+ def powerHasBeenUnplugged(self):
+ """ Actions to perform when the plug has been pulled."""
+ if self.powermanager.hasBrightness:
+ self.powermanager.adjustBrightness(self.BatteryBrightnessSlider.value())
+ self.powermanager.setPowerSave(True)
+ self.checkBatteryCritical()
+ self.changeBatteryBrightness()
+ self.powermanager.setScreensaverBlankOnly(True)
+ self.powermanager.resetIdleSeconds()
+ msg = self.checkCpuFreq()
+ if self.powermanager.hasAC:
+ self.notify(i18n("The AC adapter has been unplugged, switching to battery mode.")+"\n"+msg, self.getIcon())
+
+ def powerHasBeenPlugged(self):
+ """ Actions to perform when AC adapter has been plugged in. """
+ if self.powermanager.hasBrightness:
+ self.powermanager.adjustBrightness(self.PoweredBrightnessSlider.value())
+ self.powermanager.setPowerSave(False)
+ self.changePoweredBrightness()
+ self.powermanager.setScreensaverBlankOnly(False)
+ msg = self.checkCpuFreq()
+ self.powermanager.resetIdleSeconds()
+ self.notify(i18n("The AC adapter has been plugged in, switching to AC mode.")+"\n"+msg, self.getIcon())
+
+ def checkBatteryCritical(self):
+ """ Check for warning and critical battery label and notify-warn or
+ initiate the configured action. """
+
+ if not self.powermanager.hasBattery:
+ return
+
+ if self.batt_state == "discharging":
+ currentLevel = int(self.batt_remain/60)
+
+ warningLevel = self.criticalLevel + 5 # warn five minutes before critical
+ criticalLevel = self.criticalLevel
+
+ debug("CurrentBat: %i, WarningBat: %i, CriticalBat: %i" % (currentLevel, warningLevel, criticalLevel))
+ # We only want to suspend if the chargelevel is above a certain threshold,
+ # it sometimes takes some time for HAL to report remaining time correctly
+ if currentLevel <= criticalLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD:
+ if not self.powermanager.criticalBatteryState and self.powermanager.onBattery():
+ self.powermanager.criticalBatteryState = True
+
+ action = str(self.config.readEntry("batteryCriticalAction"))
+ if not self.act_name.has_key(action):
+ action = self.act_name[self.cb_act_def_critical]
+
+ note = i18n("You are about to run out of battery power, %1 now.").arg(self.act_notify[action])
+ self.notify(note, self.act_icon[action])
+ if self.act_call[action] != None:
+ QTimer.singleShot(2000, self.act_call[action])
+ else:
+ self.powermanager.criticalBatteryState = False
+ if currentLevel <= warningLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD:
+ if not self.powermanager.warningBatteryState:
+ self.powermanager.warningBatteryState = True
+ self.notify(i18n("You are low on battery power."), self.getIcon())
+ else:
+ self.powermanager.warningBatteryState = False
+
+ def checkIdletime(self):
+ """ Reads the idle time and does some action. """
+ idlesec = round(self.powermanager.getIdleSeconds()/60, 2)
+ if self.powermanager.onBattery():
+ idleTime = self.config.readNumEntry("batteryIdleTime", 10)
+ action = str(self.config.readEntry("batteryIdleAction"))
+ else:
+ idleTime = self.config.readNumEntry("poweredIdleTime", 60)
+ action = str(self.config.readEntry("poweredIdleAction"))
+ if not self.act_name.has_key(action):
+ action = 'nothing'
+
+ if idlesec - self.lastidlesec > 100:
+ debug("last: %u" % (idlesec - self.lastidlesec))
+ return # probably bogus idleseconds right after suspend
+ self.lastidlesec = idlesec
+ if self.act_call[action] == None:
+ return # doing nothing anyway
+ if idlesec > idleTime:
+ note = i18n("System idle for at least %1 minutes, %2 now.").arg(idleTime).arg(self.act_notify[action])
+ self.notify(note, self.act_icon[action])
+ QTimer.singleShot(2000, self.act_call[action])
+
+
+
+def doDcop(kapp):
+ """ Register kvandale in dcop, so it can be controlled from outside. """
+ my_dcop = kapp.dcopClient()
+ #my_dcop.attach()
+ #my_dcop.registerAs("power-manager")
+
+
+class DcopIface (DCOPExObj):
+ """ Add some interface so we can use powermanager from the outside. """
+ def __init__ (self, app, id='power-manager'):
+ DCOPExObj.__init__ (self, id)
+ # addMethod (<signature>, <Python method>)
+ #self.addMethod ('QString getQuery()', gvd.getZoekbegrip)
+
+ # PM related.
+ self.addMethod ('void suspend ()', app.trySuspend)
+ self.addMethod ('void hibernate ()', app.tryHibernate)
+ self.addMethod ('void shutdown ()', app.shutdown)
+
+ # UI related.
+ self.addMethod ('void showTip ()', app.showTip)
+ #self.addMethod ('void show ()', app.parent().show)
+ #self.addMethod ('void hide ()', app.parent().hide)
+
+ #self.addMethod ('void plugged ()', app.powerHasBeenPlugged)
+ #self.addMethod ('void unplugged ()', app.powerHasBeenUnplugged)
+ self.addMethod ('bool onBattery ()', app.powermanager.onBattery)
+
+ self.addMethod('void brightnessUp ()', app.setBrightnessUp)
+ self.addMethod('void brightnessDown ()', app.setBrightnessDown)
+ self.addMethod('QString getBrightness ()', app.getBrightness)
+
+ #self.addMethod ('QString getCurrentResult()', gvd.getRawResult)
+
+
+class PowermanagerApp(KDialogBase):
+ """ The KDialog providing the OK, Apply and Cancel buttons."""
+
+ def __init__(self,parent=None,name=None):
+ """ Initialise dialog and set mainwidget. """
+ KGlobal.locale().insertCatalogue("guidance")
+ KGlobal.iconLoader().addAppDir("guidance")
+
+ # We would like to use a KUniqueApplication, but that breaks dcop due to some
+ # strange bug. The following line is the revenge code for this bug, it is
+ # intentionally ugly.
+ if len(os.popen("dcop |grep guidance-").readlines()) > 1:
+ print "There is already an instance of power manager running. Exiting."
+ sys.exit(0)
+
+ # Which buttons do we want?
+ KDialogBase.__init__(self,KJanusWidget.Swallow,i18n("Power Manager"),
+ KDialogBase.Ok|KDialogBase.Apply|KDialogBase.Cancel|KDialogBase.User1, KDialogBase.Close)
+ self.pmwidget = PowerManager(self,name)
+ self.setButtonText(KDialogBase.User1, i18n("About"))
+
+ if not self.pmwidget.powermanager.isLaptop():
+ print "This is not a laptop, quitting ... "
+ sys.exit(1)
+
+ self.pmwidget.prepare()
+
+ self.setMainWidget(self.pmwidget)
+ self.aboutus = KAboutApplication(self)
+
+ def slotOk(self):
+ """ The OK button has been pressed, save configuration and pass on do whatever
+ needs to be done by KDialog. """
+ self.pmwidget.uiToConfig()
+ self.pmwidget.checkCpuFreq()
+ KDialogBase.slotOk(self)
+
+ def slotApply(self):
+ """ The Apply button has been pressed, save configuration and pass on do whatever
+ needs to be done by KDialog. """
+ self.pmwidget.uiToConfig()
+ self.pmwidget.checkCpuFreq()
+ KDialogBase.slotApply(self)
+
+ def slotCancel(self):
+ """ The Cancel button has been pressed, reset some values and hide dialogue. """
+ # In case brightness has changed, we reset it to the configured value.
+ if self.pmwidget.powermanager.hasBrightness:
+ brightness_high = self.pmwidget.powermanager.brightness_levels
+ if not self.pmwidget.powermanager.onBattery():
+ level = self.pmwidget.config.readNumEntry("poweredBrightness", brightness_high)
+ else:
+ level = self.pmwidget.config.readNumEntry("batteryBrightness", int(brightness_high/2))
+ self.pmwidget.powermanager.adjustBrightness(level)
+ self.pmwidget.configToUi()
+ KDialogBase.slotCancel(self)
+
+ def slotUser1(self):
+ self.aboutus.show()
+
+# There's a bug in KUniqueApplication that shows the pid in the dcop name,
+# this fugly hack works around it.
+class PMApp(KApplication):
+
+ def name(self):
+ return "power-manager"
+
+
+if __name__ == "__main__":
+ aboutdata = KAboutData("guidance", "Power Manager", "0.8.0",
+ "Handles battery, display and suspend modes for your computer.", KAboutData.License_GPL,
+ "(C) 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko",
+ None, None, "jriddell@ubuntu.com")
+ aboutdata.addAuthor("Sebastian Kügler", "Developer", "sebas@kde.org","http://vizZzion.org")
+ aboutdata.addAuthor("Jonathan Riddell", "Developer", "jriddell@ubuntu.com")
+ aboutdata.addAuthor("Luka Renko", "Developer", "lure@kubuntu.org")
+ aboutdata.setProgramLogo(QImage("power-manager.png"))
+ KCmdLineArgs.init(sys.argv, aboutdata)
+ #kapp = KUniqueApplication(True, True, False)
+ #kapp = KApplication()
+ kapp = PMApp(True, True)
+ mainWindow = PowermanagerApp(None, "main window")
+ doDcop(kapp)
+ dcop_iface = DcopIface(mainWindow.pmwidget)
+ """Start helper module / button listener"""
+ try:
+ helperPid = os.spawnl(os.P_NOWAIT, os.path.dirname(__file__)+'/gpmhelper.py', 'gpmhelper.py')
+ except:
+ """Non-fatal if this fails"""
+ print "Unable to start button-listener"
+
+ kapp.exec_loop()
+
+ """Kill helper module / button listener"""
+ os.system('kill '+str(helperPid))