Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Python (http://www.velocityreviews.com/forums/f43-python.html)
-   -   simple tkinter battery monitor (http://www.velocityreviews.com/forums/t956995-simple-tkinter-battery-monitor.html)

leonix.power@gmail.com 01-27-2013 10:59 PM

simple tkinter battery monitor
 
I tried to write a simple battery monitor for laptops which shows normally just the battery percentage, and when is clicked some more info.
If I click just one time it works, but if I click a second time, the additional info Label seems to be empty (but it holds the dimension of the StringVar content, even if it isn't displayed)
Where am I wrong?
---------------------------------------------------
#!/usr/bin/python3.2

from re import findall, search
from threading import Thread
from time import sleep
from subprocess import Popen, call, PIPE, STDOUT
from tkinter import *



class battery_monitor:

def __init__(self):
root=Tk()
# root.geometry("-0+0")
root.overrideredirect(True)
root.wm_attributes("-topmost", 1)
self.battery_string=StringVar()
self.battery_percent=StringVar()
self.battery_label=Label(root, extvariable=self.battery_string, font=("fixed", 9))
self.battery_icon=Label(root, textvariable=self.battery_percent, font=("fixed", 9), width=3)
self.battery_icon.grid()
t=Thread(target=self.update_battery_level_loop)
t.start()
root.bind("<Button-1>", self.display_details)
self.root=root
root.mainloop()

# displays a message about details of battery status
# i.e. "on-line" or "charging, 20 min left" and so on
def display_details(self, event):
self.battery_icon.grid_remove()
self.battery_label.grid()
self.root.update_idletasks()
sleep(1)
self.battery_label.grid_remove()
self.battery_icon.grid()
self.root.update_idletasks()

# dummy function used just to test the GUI
def read_battery_level(self):
self.level=100
return "battery is full"

# threaded function, should constantly update the battery level
def update_battery_level_loop(self):
self.read_battery_level()
while True:
self.battery_percent.set(self.level)
self.battery_string.set(self.read_battery_level())
sleep(5)





##############################################
#
# main

battery_monitor()

Dave Angel 01-27-2013 11:21 PM

Re: simple tkinter battery monitor
 
On 01/27/2013 05:59 PM, leonix.power@gmail.com wrote:
> I tried to write a simple battery monitor for laptops which shows normally just the battery percentage, and when is clicked some more info.
> If I click just one time it works, but if I click a second time, the additional info Label seems to be empty (but it holds the dimension of the StringVar content, even if it isn't displayed)
> Where am I wrong?


See inline comment.

> ---------------------------------------------------
> #!/usr/bin/python3.2
>
> from re import findall, search
> from threading import Thread
> from time import sleep
> from subprocess import Popen, call, PIPE, STDOUT
> from tkinter import *
>
>
>
> class battery_monitor:
>
> def __init__(self):
> root=Tk()
> # root.geometry("-0+0")
> root.overrideredirect(True)
> root.wm_attributes("-topmost", 1)
> self.battery_string=StringVar()
> self.battery_percent=StringVar()
> self.battery_label=Label(root, extvariable=self.battery_string, font=("fixed", 9))


I don't know tkinter very well, and I don't have 3.2, but the keyword
seems to be wrong. Don't you want textvariable= ??


> self.battery_icon=Label(root, textvariable=self.battery_percent, font=("fixed", 9), width=3)
> self.battery_icon.grid()
> t=Thread(target=self.update_battery_level_loop)
> t.start()
> root.bind("<Button-1>", self.display_details)
> self.root=root
> root.mainloop()
>
> # displays a message about details of battery status
> # i.e. "on-line" or "charging, 20 min left" and so on
> def display_details(self, event):
> self.battery_icon.grid_remove()
> self.battery_label.grid()
> self.root.update_idletasks()
> sleep(1)


Never use sleep() inside a gui's main thread. There are other ways to
delay the UI without locking it up.

> self.battery_label.grid_remove()
> self.battery_icon.grid()
> self.root.update_idletasks()
>
> # dummy function used just to test the GUI
> def read_battery_level(self):
> self.level=100
> return "battery is full"
>
> # threaded function, should constantly update the battery level
> def update_battery_level_loop(self):
> self.read_battery_level()
> while True:
> self.battery_percent.set(self.level)
> self.battery_string.set(self.read_battery_level())
> sleep(5)


This sleep() is okay.

>
>
>
>
>
> ##############################################
> #
> # main
>
> battery_monitor()
>



--
DaveA

Rick Johnson 01-28-2013 12:56 AM

Re: simple tkinter battery monitor
 
On Sunday, January 27, 2013 4:59:20 PM UTC-6, leonix...@gmail.com wrote:
> I tried to write a simple battery monitor for laptops
> which shows normally just the battery percentage, and when
> is clicked some more info.
>
> If I click just one time it works, but if I click a second
> time, the additional info Label seems to be empty (but it
> holds the dimension of the StringVar content, even if it
> isn't displayed)
>
> Where am I wrong?


Before i discover your code logic error, i want to expose style errors.

> ---------------------------------------------------
> #!/usr/bin/python3.2
>
> from re import findall, search
> from threading import Thread
> from time import sleep
> from subprocess import Popen, call, PIPE, STDOUT
> from tkinter import *


I am wondering why you would "selectively" import from the re module, which is a quite normal sized module, but then do the global import from tkinter, of which who's namespace is terribly polluted due to lack of packaging.

> class battery_monitor:


Bad naming convention here! ALL class identifiers must (at the very least) /start/ with a capital letter. My strong opinion is to cap EVERY word. So for example:

OptionMenu NOT Optionmenu
ComboBox NOT Combobox
etc...

Using "lowercase_with_underscores" should be reserved for interface methods and global functions.

> def __init__(self):
> root=Tk()
> # root.geometry("-0+0")
> root.overrideredirect(True)
> root.wm_attributes("-topmost", 1)


I just hate when people use 1 and 0 for True and False. I know Python allows such non-sense, but i just hate it because it can cause subtle bugs -- and most logical people agree with me.

> self.battery_string=StringVar()
> self.battery_percent=StringVar()


Two issues here. First you use a function/interface style to define a variable. Then you go and add insult to injury by using an ambiguous name. You should have used something like: "batteryStringVar" and "batteryPercentVar".

> self.battery_label=Label(root, extvariable=self.battery_string, font=("fixed", 9))
> self.battery_icon=Label(root, textvariable=self.battery_percent, font=("fixed", 9), width=3)
> self.battery_icon.grid()
> t=Thread(target=self.update_battery_level_loop)


Don't assign variables without leaving buffer spaces around the equals sign. Only omit the spaces when passing arguments to a class, method, or function.

> t.start()
> root.bind("<Button-1>", self.display_details)
> self.root=root
> root.mainloop()
>
> # displays a message about details of battery status
> # i.e. "on-line" or "charging, 20 min left" and so on


Oh gawd this is a major pet peeve of mine!!!!

Always place a comment at the same indention level of the function or class that it references. AND NEVER, EVER, place a comment OUTSIDE of a function, method, or class like you have done here.

> def display_details(self, event):
> self.battery_icon.grid_remove()
> self.battery_label.grid()
> self.root.update_idletasks()
> sleep(1)


You may want to look into the Universal Tkinter widget method: "w.after(ms, func)".

> self.battery_label.grid_remove()
> self.battery_icon.grid()
> self.root.update_idletasks()
>
> # dummy function used just to test the GUI
> def read_battery_level(self):
> self.level=100
> return "battery is full"
>
> # threaded function, should constantly update the battery level
> def update_battery_level_loop(self):
> self.read_battery_level()
> while True:
> self.battery_percent.set(self.level)
> self.battery_string.set(self.read_battery_level())
> sleep(5)



Finally, i don't understand why you are using such a deep indention level. Unknown Source said: "Four spaces thou shalt indent, and the number of thou indention shall be four."

Only after you clean up these style abominations by submitting a new example of your code will i offer any more help. Thanks.

leonix.power@gmail.com 01-30-2013 03:02 AM

Re: simple tkinter battery monitor
 
Thank you very much! fixed with w.after
Here is the code, works under Linux for those who have acpi.
My output of "acpi -V" is the following, the code is parsing the first line of the output. Any improvements are appreciated.

> $ acpi -V
> Battery 0: Discharging, 12%, 00:10:59 remaining
> Battery 0: design capacity 2200 mAh, last full capacity 1349 mAh = 61%
> Adapter 0: off-line
> Thermal 0: ok, 40.0 degrees C
> Thermal 0: trip point 0 switches to mode critical at temperature 98.0 degrees C
> Thermal 0: trip point 1 switches to mode passive at temperature 93.0 degrees C
> Cooling 0: Processor 0 of 10
> Cooling 1: Processor 0 of 10
> Cooling 2: Processor 0 of 10
> Cooling 3: Processor 0 of 10
> Cooling 4: LCD 0 of 9



----------------------------------------------------------
----------------------------------------------------------
----------------------------------------------------------


#!/usr/bin/python3.2

from re import findall, search
from threading import Thread
from time import sleep
from subprocess import Popen, call, PIPE, STDOUT
from tkinter import Tk, Label, StringVar



def runProcess(exe):
p=Popen(exe, stdout=PIPE, stderr=STDOUT)
while True:
retcode=p.poll()
line=p.stdout.readline()
yield line
if retcode is not None:
break


class BatteryMonitor:

def __init__(self):
root = Tk()
root.configure(padx=1, pady=1, bg="#555753")
root.geometry("-0+0")
root.overrideredirect(True)
root.wm_attributes("-topmost", True)
self.batteryExtendedStringVar = StringVar()
self.batteryPercentStringVar = StringVar()
self.batteryExtendedLabel = Label(root, textvariable=self.batteryExtendedStringVar, font=("fixed", 9), bg="#3e4446", fg="#d3d7cf", padx=10, pady=-1)
self.batteryPercentLabel = Label(root, textvariable=self.batteryPercentStringVar, font=("fixed", 9), width=4, bg="#3e4446", fg="#d3d7cf", padx=-1, pady=-1)
self.batteryPercentLabel.grid()
t = Thread(target=self.update_battery_level_loop)
t.start()
root.bind("<Button-1>", self.display_details)
self.root = root
root.mainloop()

def display_details(self, event):
# displays a message about details of battery status
# i.e. "on-line" or "charging, 20 min left" and so on
self.batteryPercentLabel.grid_remove()
self.batteryExtendedLabel.grid()
self.batteryExtendedLabel.after(1000, self.batteryExtendedLabel.grid_remove)
self.batteryPercentLabel.after(1000, self.batteryPercentLabel.grid)

def read_battery_level(self):
# dummy function used just to test the GUI
for line in runProcess(["acpi", "-V"]):
if line[11:-1]!=b"on-line":
self.level = findall(b"\d\d?", line[11:])[0]
else:
self.level = b"0"
return line[11:-1]

def update_battery_level_loop(self):
# threaded function, should constantly update the battery level
self.read_battery_level()
while True:
self.batteryPercentStringVar.set(str(self.level)[2:-1]+"%")
self.batteryExtendedStringVar.set(self.read_batter y_level())
if self.level == 2:
runProcess(["shutdown", "-h", "now"])
return
sleep(5)





##############################################
#
# main

BatteryMonitor()


All times are GMT. The time now is 05:10 PM.

Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57