Need critics and recomendations about my brightness program

Hi! Need feedback
How would you rate my brightness program made on python, using ttkbootstrap? I implemented day / night time that you write in hours, brightness value in percents(i called it force in the program), their sensitivity(how quick brightness changes), two app themes, speaker(text above all and below the app name, that shows a little bit information) and theme changer for windows only, if it’s night time. It also able to hide in the tray if you try to close it(to close or open from tray just right click):apple:
Here all code! :

from concurrent.futures import thread
from ensurepip import bootstrap
import glob
from struct import pack
import threading
from turtle import left
from unittest import skip
from screen_brightness_control import get_brightness, set_brightness, fade_brightness
from time import sleep, strftime
from tkinter import *
from ttkbootstrap.constants import *
import ttkbootstrap as tb
import pystray
import PIL.Image
import threading
import ttkbootstrap.style
import winreg

firstSecond = int()
firstChange = int()
secondSecond = int()
secondChange = int()
daySmoothness = int()
eveningSmoothness = int()

brightnessUp = None
dayTime = None
x = get_brightness()
currentBrightness = int(x[0])
speakerText = ""
theme = 0
anim = 0
check = 0

root = tb.Window(themename="darkly")
root.title("BRIGHTNESS PROGRAM")
root.geometry("700x650")
root.iconbitmap("lightIcon.ico")

try:
    savedData = open("savedData.txt", "r")
    content = savedData.read()
    savedData.close()
    content = content.split()
    speakerText = "Savedata file found"
except:
    savedData = open("savedData.txt", "w")
    content = savedData.write("")
    savedData.close()
    speakerText = "Created saveddata file"
    
    savedData = open("savedData.txt", "r")
    content = savedData.read()
    savedData.close()
    content = content.split()

try:
    firstSecond = int(content[0])
    firstChange = int(content[1])
    secondSecond = int(content[2])
    secondChange = int(content[3])
    daySmoothness = int(content[4])
    eveningSmoothness = int(content[5])
    theme = int(content[6])
    check = int(content[7])
    
    if theme == 1:
        ttkbootstrap.Style("morph")
    else:
        ttkbootstrap.Style("darkly")
    sleep(1)
except:
    speakerText = "There are no prepared variables"
    sleep(1)
    
def ChangeBrightness():
        global currentBrightness
        global dayTime
        global anim
        global check
        x = get_brightness()
        currentBrightness = int(x[0])
            
        if dayTime == True:
            if currentBrightness > firstChange:
                currentBrightness -= 1
                if anim == 0:
                    anim = 1
                else:
                    anim = 0
                global speakerText
                if anim == 0:
                    speaker.config(text="Brightness-")
                else:
                    speaker.config(text="Brightness -")
                set_brightness(currentBrightness)
            elif currentBrightness < firstChange:
                currentBrightness += 1
                if anim == 0:
                    anim = 1
                else:
                    anim = 0
                if anim == 0:
                    speaker.config(text="Brightness+")
                else:
                    speaker.config(text="Brightness +")
                set_brightness(currentBrightness)
            elif currentBrightness == firstChange:
                sleep(1)
                speaker.config(text="Brightness == First Change")
        else:
            if currentBrightness > secondChange:
                currentBrightness -= 1
                if anim == 0:
                    anim = 1
                else:
                    anim = 0
                if anim == 0:
                    speaker.config(text="Brightness-")
                else:
                    speaker.config(text="Brightness -")
                set_brightness(currentBrightness)
            elif currentBrightness < secondChange:
                currentBrightness += 1
                if anim == 0:
                    anim = 1
                else:
                    anim = 0
                if anim == 0:
                    speaker.config(text="Brightness+")
                else:
                    speaker.config(text="Brightness +")
                set_brightness(currentBrightness)
            elif currentBrightness == secondChange:
                sleep(1)
                speaker.config(text="Brightness == Second Change")
                
        if dayTime == True:
            threading.Timer(daySmoothness, ChangeBrightness).start()
        elif dayTime == False:
            threading.Timer(eveningSmoothness, ChangeBrightness).start()
        else:
            threading.Timer(10, ChangeBrightness).start()
            
def changeTheme():
    global theme
    if theme == 1:
        ttkbootstrap.Style("darkly")
        theme = 0
    elif theme == 0:
        ttkbootstrap.Style("morph")
        theme = 1
        

def program():
    currentSecond = int(strftime("%H"))
    global dayTime
    global check
    if currentSecond >= firstSecond and currentSecond < secondSecond:
        dayTime = True
        if check == 1:
            keypath = r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
            valuename = "AppsUseLightTheme"
            darkmodevalue = 1
        
            key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, keypath, 0, winreg.KEY_WRITE)
            winreg.SetValueEx(key, valuename, 0, winreg.REG_DWORD, darkmodevalue)
            winreg.CloseKey(key)        
    elif currentSecond >= secondSecond or currentSecond < firstSecond:
        dayTime = False
        if check == 1:
            keypath = r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
            valuename = "AppsUseLightTheme"
            darkmodevalue = 0
        
            key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, keypath, 0, winreg.KEY_WRITE)
            winreg.SetValueEx(key, valuename, 0, winreg.REG_DWORD, darkmodevalue)
            winreg.CloseKey(key)  
                
    threading.Timer(60, program).start()

programT = threading.Thread(target=program)
programT.daemon = True
programT.start()

ChangeBrightnessT = threading.Thread(target=ChangeBrightness)
ChangeBrightnessT.daemon = True
ChangeBrightnessT.start()

def save():
    try:
        global firstSecond
        firstSecond = int(fsEntry.get())
        global secondSecond
        secondSecond = int(ssEntry.get())
        global firstChange
        firstChange = int(ffEntry.get())
        global secondChange
        secondChange = int(sfEntry.get())
        global daySmoothness
        daySmoothness = int(smEntry.get())
        global eveningSmoothness
        eveningSmoothness = int(smEntry2.get())
        global theme
        global check
        savedData = open("savedData.txt", "w")
        savedData.write(f"{firstSecond}\n{firstChange}\n{secondSecond}\n{secondChange}\n{daySmoothness}\n{eveningSmoothness}\n{theme}\n{check}")
        savedData.close()
        global speakerText
        speaker.config(text="Saved!")
    except:
        speaker.config(text="Wrong text")
        
def checker():
    global check
    if check == 0:
        check = 1
    else:
        check = 0
        
def restoreFromTray(icon, item):
    global trayed
    trayed = False
    root.deiconify()
    icon.stop()
    
def close(icon, item):
    icon.stop()
    root.quit()
    
def minimizeToTray():
    root.withdraw()
    image = PIL.Image.open("lightIcon.ico")
    menu = (pystray.MenuItem("Show", restoreFromTray), pystray.MenuItem("Quit", close))
    icon = pystray.Icon("BRIGHTNESS PROGRAM", image, "BP", menu)
    icon.run_detached()
    
root.protocol("WM_DELETE_WINDOW", minimizeToTray)
        
myLabel = tb.Label(text="Brightness Program", font=("Berlin Sans FB", 35), bootstyle="default")
myLabel.pack(pady=0)

speaker = tb.Label(text=f"{speakerText}", font=("Berlin Sans FB", 15), bootstyle="default")
speaker.pack(pady=0)

fsLabel = tb.Label(text="First time", font=("Berlin Sans FB", 10), bootstyle="default")
fsLabel.pack()

fsEntry = tb.Entry(root, bootstyle="info", font=("Berlin Sans FB", 10), foreground="light blue", width=4)
fsEntry.pack(pady=5)

fsEntry.insert(0, firstSecond)

ffLabel = tb.Label(text="First force", font=("Berlin Sans FB", 10), bootstyle="default")
ffLabel.pack()

ffEntry = tb.Entry(root, bootstyle="info", font=("Berlin Sans FB", 10), foreground="light blue", width=3)
ffEntry.pack(pady=5)

ffEntry.insert(0, firstChange)

smLabel = tb.Label(text="Day Smoothness", font=("Berlin Sans FB", 10), bootstyle="default")
smLabel.pack()

smEntry = tb.Entry(root, bootstyle="info", font=("Berlin Sans FB", 12), foreground="light blue", width=3)
smEntry.pack(pady=5)

smEntry.insert(0, daySmoothness)

myLabel2 = tb.Label(text="|", font=("Berlin Sans FB", 10), bootstyle="default")
myLabel2.pack(padx=5)

ssLabel = tb.Label(text="Second time", font=("Berlin Sans FB", 10), bootstyle="default")
ssLabel.pack()

ssEntry = tb.Entry(root, bootstyle="info", font=("Berlin Sans FB", 10), foreground="light blue", width=4)
ssEntry.pack(pady=5)

ssEntry.insert(0, secondSecond)

sfLabel = tb.Label(text="Second force", font=("Berlin Sans FB", 10), bootstyle="default")
sfLabel.pack()

sfEntry = tb.Entry(root, bootstyle="info", font=("Berlin Sans FB", 10), foreground="light blue", width=3)
sfEntry.pack(pady=5)

sfEntry.insert(0, secondChange)

smLabel2 = tb.Label(text="Evening Smoothness", font=("Berlin Sans FB", 10), bootstyle="default")
smLabel2.pack()

smEntry2 = tb.Entry(root, bootstyle="info", font=("Berlin Sans FB", 12), foreground="light blue", width=3)
smEntry2.pack(pady=5)

smEntry2.insert(0, eveningSmoothness)

myButton = tb.Button(text="SAVE", bootstyle="primary, outline", command=save)
myButton.pack(pady=20)

themeButton = tb.Button(text="☀", bootstyle="primary, outline", command=changeTheme)
themeButton.pack(side=LEFT, padx=10)

if check == 0:
    var1 = IntVar(value=0)
else:
    var1 = IntVar(value=1)
myCheck = tb.Checkbutton(bootstyle="primary", text="Change Windows Theme in the Evening", variable=var1, onvalue=1, offvalue=0, command=checker)
myCheck.pack(pady=0, side=LEFT)

root.mainloop()

I’m relatively a beginner, so the whole program was made thankfully by using many guides, and of couse i see that you have to write down exact hours(without minutes) and i used a lot of lines(easily could make it slimmer) but it was planned for my inner use every of my day, and because i’m a beginner, i decided not to create too complex things, just make enough. What do you think? Maybe you have better ideas, similar experience or even better code suggest, i’ll be glad to read❗

Well, if this program will be used only by You and it works as expected then you don’t need any critique from us :stuck_out_tongue:

But jokes aside: do you want to expand your program further? Is there a possibility you may want to change it in the future?

If so, then you have to ask yourself if there are parts of your program that:

  • are hard to read (by your future self)
  • feel “not right” / “like an ugly hack”

this will point you to what may need to be rewritten.

In programming world making a code that works is only the first part. Unless this is an “one shot program”, you will want from your program to be easy to modify in the future.

2 Likes

The following two imports:

from tkinter import *
from ttkbootstrap.constants import *

should be imported as (or apply your own alias):

import tkinter as tk
importttkbootstrap.constants as ttk_const

This recommendation is to avoid name collision. If for example the tkinter module has a function with the same name as in your script, which function will your program use? This dilemma can be avoided by prefixing the one from the tkinter module with its name (or alias). For example:

tk.calc()  # The one from tkinter - if it is not used, it still avoids name collision
calc()     # Your own defined function

Imports can be imported with aliases so that the names are not so verbose. For example, if you imported a module named rumplestiltsken and you wanted to use a function named calc, instead of calling it as:

rumplestiltsken.calc()

you can instead call it as:

import rumplestiltsken as rump
rump.calc()

Just a few small suggestions.

2 Likes

I would simply like to see how do i manage to make an easy app from other’s view and good ideas!

To make programs easier to manage, it would be a good idea to start compartmentalizing your scripts into separate modules. Each module having a distinct flavor. For example, if there are classes, functions or variables that perform similar or have related functionality, you can separate them into their own modules. You may add verification test code in each module to verify their functionality. You can do this by adding the following line of code:

if name == '__main__':  # All code defined below, will be executed ONLY if the file is tested as the main module or if it is the main module in the application.
    # Either application code or module test code goes here

comparmentalizing_scripts

This prevents your code from appearing like a long scroll and enhancing its manageability. In the illustration above, it is assumed that file_a is the main script module and file_b is the imported module.
One additional recommendation is to add comments to your code, including docstrings.

2 Likes

Figured I’d just make an assortment of remarks, inline with the code
below.

 [...]
 from time import sleep, strftime
 from tkinter import *
 from ttkbootstrap.constants import *
 [...]

Others have mentioned import *. We usually don’t do it because you
don’t have much idea about what names will come in. When you’re doing a
programme like this which is heavily based on tk then it isn’t
unreasonable. So here you’ve imported * from just 2 tk-related
modules, and with lucky they will probably not conflict because they
expect to work together. That’s fine, as far as it goes.

However… My recommendation is that if you’re using import * then
that should be your first import. This is because otherwise the import
might overwrite a name from a previous import. I left the from time import sleep, strftime import there as an example. Supposing one of the
tk modules contained a sleep name? Then from this point on calling
sleep(...) would call the one from the tk module.

This is because imports are essentially assignment statements, not very
different from:

 x = 1   # x is now 1
 x += 3  # x is now 4

The import loads a module (if not already loaded elsewhere) and then
binds names from that mdule to names in your local namespace. Just like
any other variable assignment.

So do the * imports first, then all the explicit imports afterwards.
That way you can be sure that a name means what you expected.

Alternatively, just import the names you need, though that can get
tedious. But you’re already doing it for the other modules :slight_smile:

Onward:

 firstSecond = int()
 firstChange = int()

Just say firstSecond = 0. That’s what int() will be returning
anyway. Python variables do not have types. Python values have
types. So you’re not declaring that firstSecond is an int here.
You’re just calling the int class’ constructor and you get back the
default, which is 0.

 try:
     savedData = open("savedData.txt", "r")
     content = savedData.read()
     savedData.close()

We normally open/close files like this:

 with open("savedData.txt", "r") as savedData:
     content = savedData.read()

The with statement operates a “context manager”, obtained from
open() and arranges to close() when you leave the with statement
automatically. This avoids ever forgetting to close, is succinct, and
will still close even if some exception is raised inside the with.

     content = content.split()
     speakerText = "Savedata file found"
 except:

Try to never do a “bare except”. This except will catch any
exception. Suppose you misspelled savedData in the code above. In
Python, that raises a NameError exception. With a bare except you’ll
catch that and treat a programming error like a file-not-found error.
And hide it!

The rule of thumb with exceptions is: only catch what you expect to
handle. In your case you’re making a default file if it is missing. (I
wouldn’t bother, myself, but it is a reasonable thing to do.) So do
this:

 except FileNotFoundError:

instead. That way other error will escape so that you can inspect
them. And either decide to handle them or often: decide not to handle
them. let the user do that - they have the wider context.

     savedData = open("savedData.txt", "w")
     content = savedData.write("")
     savedData.close()
     speakerText = "Created saveddata file"

You don’t need to write(""). The open() will create that file.
Writing nothing to it will change nothing, and in fact at the OS layer
probably do nothing, also. Like I said, personally I wouldn’t do this.
I’d just provide the default data:

 content = []

and fall through.

     savedData = open("savedData.txt", "r")
     content = savedData.read()
     savedData.close()
     content = content.split()

Here you’re repeating the file load. Supposing you changes how the file
was parsed eg made it some format like JSON or CSV or INI? You’de need
to repeat that effort both here and in the earlier clause where you read
the file. Move this out to a function which reads the file and returns
the content. That way there would be just one piece of code to keep
correct.

 try:
     firstSecond = int(content[0])
     firstChange = int(content[1])
     secondSecond = int(content[2])
     secondChange = int(content[3])
     daySmoothness = int(content[4])
     eveningSmoothness = int(content[5])
     theme = int(content[6])
     check = int(content[7])

You can write this as:

     firstSecond, firstChange, secondSecond, \
         secondChange, daySmoothness, eveningSmoothness, \
         theme, check = content

This is Python’s “unpacking assignment”, quite handy.

     if theme == 1:
         ttkbootstrap.Style("morph")
     else:
         ttkbootstrap.Style("darkly")
     sleep(1)
 except:
     speakerText = "There are no prepared variables"
     sleep(1)

The other rule of thumb for try/except is: enclose only the code
whose exceptions you will handle. You I’d write this as:

 try:
     firstSecond, firstChange, secondSecond, \
         secondChange, daySmoothness, eveningSmoothness, \
         theme, check = content
 except:
     speakerText = "There are no prepared variables"

and do the theme stuff afterwards.

Also, this is another bare except. Don’t do that. Let’s see what
Python does:

 >>> a, b, c = []
 Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
 ValueError: not enough values to unpack (expected 3, got 0)

so just catch ValueError:

 except ValueError:

 def ChangeBrightness():

Conventionally we define functions up the top, ahead of the main code.

         global currentBrightness
         global dayTime
         global anim
         global check

We also, as a rule, try to avoid global variables. For this programme,
which is small and single purpose, this isn’t such a large deal. But
later you might want to put all this state (currentBrightness and so
forth) in a small class and pass an instance of the class around as a
parameter.

         if dayTime == True:

Just write:

 if dayTime:

If you need to compare with False, we would write:

 if not dayTime:

Oh, lokk, here’s one of each :slight_smile:

         if dayTime == True:
             threading.Timer(daySmoothness, ChangeBrightness).start()
         elif dayTime == False:
             threading.Timer(eveningSmoothness, ChangeBrightness).start()
         else:
             threading.Timer(10, ChangeBrightness).start()

Otherwise, this is a pretty good.

2 Likes

Seeing the actual exception message can be very useful in some circumstances. Try using this:

try:
    # Do stuff
except Exception as e:
    # e contains the error message.
    speakerText = "There are no prepared variables. " + e
    sleep(1)
1 Like