simple tkinter battery monitor

Discussion in 'Python' started by leonix.power@gmail.com, Jan 27, 2013.

  1. Guest

    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()
     
    , Jan 27, 2013
    #1
    1. Advertising

  2. Dave Angel Guest

    On 01/27/2013 05:59 PM, 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
     
    Dave Angel, Jan 27, 2013
    #2
    1. Advertising

  3. Rick Johnson Guest

    On Sunday, January 27, 2013 4:59:20 PM UTC-6, 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.
     
    Rick Johnson, Jan 28, 2013
    #3
  4. Guest

    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_battery_level())
    if self.level == 2:
    runProcess(["shutdown", "-h", "now"])
    return
    sleep(5)





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

    BatteryMonitor()
     
    , Jan 30, 2013
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Maria
    Replies:
    10
    Views:
    617
    Mark McIntyre
    Feb 4, 2004
  2. Maria
    Replies:
    8
    Views:
    454
    Maria Gaitani
    Feb 4, 2004
  3. kid joe

    (OT) Battery question

    kid joe, Jun 28, 2008, in forum: C Programming
    Replies:
    18
    Views:
    497
    M.I.5¾
    Jul 7, 2008
  4. ScottZ
    Replies:
    2
    Views:
    391
    Jorgen Grahn
    Nov 2, 2008
  5. Replies:
    4
    Views:
    354
Loading...

Share This Page