Sedentary reminder GUI using Python

This is a Python code sample to implement a sedentary reminder GUI on your Windows based personal computers. This tool uses Tkinter for GUI and win32 library to calculate total session times based on windows events like login, logoff, lock, unlock, etc.

This tool has mainly 2 modules, named as scheduler.py and reminder.py. scheduler.py is built using pywin32 library and helps in tracking session lock and unlock time. Using this, we can calculate total login time for the day, and also current session active time. reminder.py is a TKinter GUI which displays messages.

Both these modules are scheduled to run in background in our windows machines.

In this example, we are going to configure following reminders.

Hourly reminder to drink water.

If the session continuously active for more than an hour, a reminder popup to have a walk and rest your eyes.

Also, a message within the popup, that mentions total login time for the day.

You can build on this to apply additional logic as per your need. Note that this tool is made for the Windows OS using pywin32 library. We have similar libraries for other operating systems which can be used to develop same tool for other OS as well.

  1. Install Python, Tkinter and pywin32

    Download and install the latest version of python from Python Website.

    Keep your favorite Python editor ready. This can be a basic notepad application available by default for your OS or you can download and install any free editor like VS Code.

    Set up virtual environment also to avoid any unexpected errors.

    Open command prompt and navigate to the folder where you are going to place the code and activate virtual environment.

    Install TKinter using command pip install tk

    Please refer Tkinter Quick Reference if needed.

    Install pywin32 using command pip install pywin32

    To check if libraries installed successfully, type command pip freeze
    Should give an output like:

    pywin32==302
    tk==0.1.0
    
  2. Scheduler

    scheduler.py creates a hidden window which runs in background and calls function trackSessionChanges for each event. In this function, we are only tracking session change event group with id 689 and events lock and unlock with ids 7 and 8.

    Above events calls a function writeTimeToJson, which records the session start time and total active seconds for the day in a json file. On session lock, total time gets updated. On unlock, session start time is recorded.

    scheduler.py and reminder.py uses data stored in data.json file. Make sure to add this file in the same folder and place a square bracket [] inside before running for the first time.

    Copied
    import win32api as api
    import win32con as con
    import win32gui as gui
    import win32ts as ts
    import datetime
    import time
    import json
    import os
    
    windowid = 0
    totSecs = 0
    jsnfile = os.path.dirname(__file__) + '\data.json'
    sessStartTime = datetime.datetime.now()
    itm = []
    
    def trackSessionChanges(wid, evgrp, evid, sessid):
        global sessStartTime, totSecs
        if evgrp == 689:#Id for session change group
            if(evid == 8):#Session unlock
                writeTimeToJson(datetime.datetime.now(), 'unlock')
            elif(evid == 7):#Session lock
                writeTimeToJson(datetime.datetime.now(), 'lock')
    
    def writeTimeToJson(tm, type):
        itm = []
        tmStr = str(tm.year) + '-' + str(tm.month) + '-' + str(tm.day) + ' ' + str(tm.hour) + ':' + str(tm.minute) + ':' + str(tm.second)
        with open(jsnfile) as i:
            itm = json.load(i)
        if(type == 'unlock'):
            if(len(itm) == 0):
                itm.append({'sessionStartTime': tmStr, 'totSecs': 0, 'sessActive': 'Y'})
            else:
                jsonTme = datetime.datetime.strptime(itm[0]['sessionStartTime'], '%Y-%m-%d %H:%M:%S')
                if(tm.day > jsonTme.day): #Old session started yesterday. So reset seconds
                    itm = []
                    itm.append({'sessionStartTime': tmStr, 'totSecs': 0, 'sessActive': 'Y'})
                else:
                    secs = itm[0]['totSecs']
                    itm = []
                    itm.append({'sessionStartTime': tmStr, 'totSecs': secs, 'sessActive': 'Y'})
        elif(type == 'lock'):
            if(len(itm) == 0):
                itm.append({'sessionStartTime': tmStr, 'totSecs': 0, 'sessActive': 'N'})
            else:
                jsonTme = datetime.datetime.strptime(itm[0]['sessionStartTime'], '%Y-%m-%d %H:%M:%S')
                secs = int(itm[0]['totSecs']) + (tm - jsonTme).seconds
                tm = itm[0]['sessionStartTime']
                itm = []
                itm.append({'sessionStartTime': tm, 'totSecs': secs, 'sessActive': 'N'})
    
        with open(jsnfile, 'w') as cw:
            json.dump(itm, cw)
    
    cname = "HiddenWindow"
    hndle = api.GetModuleHandle(None)
    wc = gui.WNDCLASS()
    wc.hInstance = hndle
    wc.lpszClassName = cname
    wc.lpfnWndProc = trackSessionChanges
    cl = gui.RegisterClass(wc)
    w = gui.CreateWindow(cl, cname, 0, 0, 0, con.CW_USEDEFAULT, con.CW_USEDEFAULT, 0, 0, hndle, None)
    gui.UpdateWindow(w)
    ts.WTSRegisterSessionNotification(w, ts.NOTIFY_FOR_ALL_SESSIONS)
    
    if __name__ == '__main__':
        gui.PumpMessages()
    
  3. Reminder

    Create reminder.py in the same folder and add below code. This creates a Tkinter window to display messages. getMessage() function in this module has the logic to check for the time to display the popup message. If session is active and checks satisfies, a message as in below screenshot is displayed. You can modify this logic as per your need. Update sessMessageInterval value to change the message frequency. Here it is set to 3600 seconds (1 Hour).

    Sedentary reminder python
    Copied
    from tkinter import *
    import tkinter as tk
    import datetime
    import time
    import json
    import os
    
    jsnfile = os.path.dirname(__file__) + '\data.json'
    
    def getMessage():
        msg = ''
        sessMessageInterval = 3600 #1 hour
        now = datetime.datetime.now()
        with open(jsnfile) as i:
            itm = json.load(i)
        sstTm = datetime.datetime.strptime(itm[0]['sessionStartTime'], '%Y-%m-%d %H:%M:%S')
        secs = int(itm[0]['totSecs']) + (now - sstTm).seconds
        if(itm[0]['sessActive'] == 'Y'):
            if ((now - sstTm).seconds > sessMessageInterval) and (((now - sstTm).seconds % sessMessageInterval) <= 600): #10 min
                msg = ('Current session has been active\nfor more than an hour.\n' +
                        'Please have a walk.\nRelax your eyes and body.')
                sessLastMsgTime = now
                openWindow(msg, secs)
            elif(time.localtime().tm_min < 10): #This will run only once an hour
                msg = ('Take a break for drinks')
                openWindow(msg, secs)
    
    def openWindow(msg, secs):
        root = tk.Tk()
        root.title("Reminder")
        root.geometry("350x300")
        txtS = tk.StringVar()
        txtM = tk.StringVar()
        #print(secs)
        hr = str(int(secs // 3600))
        min = str(int((secs % 3600) // 60))
    
        def close():
            root.destroy()
            return  
    
        lblS= Label(root, textvariable=txtS)
        lblS.configure(font=('Calibri Bold', 15), fg='Black')
        txtS.set("Todays' Total Login Time: " + hr + " hr: " + min + " min.")
        lblS.pack(fill='both', expand=True)
        lblM= Label(root, textvariable=txtM)
        txtM.set(msg)
        lblM.configure(font=('Calibri Bold', 15), fg='Blue')
        lblM.pack(fill='both', expand=True)
    
        btnClose = tk.Button(root, text ="Close", command = close)
        btnClose.configure(font=('Calibri Bold', 15), fg='Red')
        btnClose.pack(side=BOTTOM)
    
        root.mainloop()
    
    if __name__ == '__main__':
        getMessage()
    
  4. Batch Files

    When we test our modules, make sure both these modules are running at the same time. So open 2 cmd windows and run both modules using command, python reminder.py or py reminder.py

    After testing we have to schedule these 2 modules using windows scheduler to run in background. For this, create 2 batch files, scheduler.bat and reminder.bat and add the below code in these batch files. Make sure to provide the right path of the python and code here. C:\Users\UserName\AppData\Local\Programs\Python\Python310\pythonw.exe "CodePath\scheduler.py"; exit 0 C:\Users\UserName\AppData\Local\Programs\Python\Python310\pythonw.exe "CodePath\reminder.py"

    Use command where.exe python to get the python path to use in batch file.
    We are using pythonw.exe to make sure that we are not getting the command window when the modules runs in background.

  5. Using Windows Scheduler to schedule your modules

    Last step is to schedule these 2 batch files in Windows scheduler.
    scheduler module tracks the session locks and unlocks. So this should be running in the background always. So schedule this to start at the user logon
    We will schedule the reminder module to run every 10 minutes. getMessage() function is called each time to check for the time to display the message. If we are changing the frequency of reminder module call, make sure to update minutes in code as well in line numbers 20 and 25.

    Sedentary reminder scheduler
Absolute Code Works - Python Topics