极简开源Windows桌面定时提醒休息python程序

ops/2024/11/26 5:48:21/

        当我们长期在电脑面前坐太久后,会产生一系列健康风险,包括干眼症,颈椎,腰椎,肌肉僵硬等等。解决方案是在一定的时间间隔内我们需要have a break, 远眺可以缓解干眼症等眼部症状,站起来走动两步,或者做一些舒展动作,可以让我们身体肌肉放松。Microsoft Store的一些第三方免费定时提醒程序,如BreakTimer, 常常难以在约定的时间内唤起。其他一些有着类似功能且有更丰富特性的第三方程序需要注册缴费才能使用很多功能。这触发了我自己写一个程序来实现该功能。

        因为Python的功能强大,且开发程序的门槛低,所以我选择它,我电脑安装的版本是3.10. 第一步,开发一个可以显示在底部工具栏右边隐藏托盘的图标,当我们有鼠标放上去时,可以显示当前离下一次休息还剩下的时间,以分钟和秒计。点击鼠标右键时,可以弹出菜单显示当前剩余时间和退出按钮。

        要实现以上功能,需要安装第三方库pystray 和 pillow。代码如下:

python">import threading
from pystray import Icon, MenuItem, Menu
from PIL import Image, ImageDrawdef create_image():# Load an existing image for the tray iconicon_path = r"C:\technical learning\Python\take-break-icon.png"  # Path to your image file (e.g., .png or .ico)return Image.open(icon_path)def stop_program(icon, item):# Stop the video playback and exit the programglobal keep_runicon.stop()keep_run=Falsedef start_tray_icon(icon):# Create the system tray icon with a menuicon.run()myicon = Icon("VideoPlayer", create_image())
# Start the tray icon in a separate thread
tray_thread = threading.Thread(target=start_tray_icon, args=[myicon],daemon=True)
tray_thread.start()def update_tray_menu(icon):# Update the menu with the remaining timeglobal time_left#the tray menu has three items, time left, set timer, and stopmenu = Menu(MenuItem(f"Time left: {time_left[0]} minutes {time_left[1]} seconds", lambda icon,item:None),MenuItem('Set Timer', set_timer),  # Add the new menu option hereMenuItem('Stop', action=stop_program))icon.menu = menu#the title will show when you mouse over the iconicon.title = f"{time_left}"

        首先通过已有的图片创建一个Icon object,并创建一个线程来运行该object。因为要实时显示剩余时间,所以有一个update函数来对Menu内容进行更新。

        第二步,实现每隔预设的时间, 启动VLC播放器,播放一段指定的视频。同时计时器重新开始倒计时。

python">import subprocess
import time# Path to VLC and your video file
vlc_path = r"D:\Program Files (x86)\VideoLAN\VLC\vlc.exe"  # Update if needed
video_path = r"C:\technical learning\Python\Health_song_Fxx.mp4"#the minutes and seconds to pass to have a break periodically,default is 60 minutes  
time_set = (59,60)# Global variable to track time left
time_left = list(time_set)
keep_run = Truedef start_play_video():subprocess.run([vlc_path, video_path])while keep_run:update_tray_menu(myicon) #you can see the update of time_left instantlyif time_left[0]==-1: #it's the time for a breakvideo_play_thread = threading.Thread(target=start_play_video)video_play_thread.start()time_left = list(time_set)  # Reset time lefttime.sleep(1)if time_left[1]==0:time_left[1]=60time_left[0]-=1time_left[1] -= 1 

        主线程是一个while loop,每隔1s更新time_left,当time out,启动一个线程来通过subprocess来调用VLC播放器来播放视频,之所以用subprocess是这样一般可以带来前台窗体播放的效果,更好的提醒作用。当Icon点击了stop后,keep_run为False,循环退出,程序结束。

         最简单功能的桌面定时提醒程序这时候可以告一段落了,但是在你使用电脑的过程中,你可能本身会中途离开,比方说中午午餐,去开会,或者去做运动了。这时候电脑进入休眠状态。当你回来后,计时器还是会按照计算机休眠前剩余的时间继续计时,这个不太合理。因为这个时候你其实已经眼睛和身体已经得到了一些放松,起码没有一直盯着屏幕。所以应该重新开始计时。

        要实现Windows计算机从休眠中醒来重新计数,需要安装第三方库pywin32,别被名字糊弄,因为历史原因,它后续的版本也包括了64 bit windows

python">import win32api
import win32gui
import win32con#the class used to handle event of computer waking up from hibernation
class PowerEventHandler:def __init__(self):self.internal_variable = 0def handle_event(self, hwnd, msg, wparam, lparam):global time_leftif msg == win32con.WM_POWERBROADCAST and wparam == win32con.PBT_APMRESUMEAUTOMATIC:#print("Laptop woke up from hibernation!")time_left = [59,60]list(time_set)return True'''creates a custom Windows Message Handling Window'''
handler = PowerEventHandler()
wc = win32gui.WNDCLASS()
wc.lpszClassName = 'PowerHandler'
wc.hInstance = win32api.GetModuleHandle(None)
wc.lpfnWndProc = handler.handle_eventclass_atom = win32gui.RegisterClass(wc)
#create a window of the registered class with no size and invisible
hwnd = win32gui.CreateWindow(class_atom, 'PowerHandler', 0, 0, 0, 0, 0, 0, 0, wc.hInstance, None)

        创建一个不可见窗体来接受Windows系统的消息,当接收到从休眠中醒来的消息时,重置剩下的时间为预设值。同时你需要在while loop里不断地处理pending的Windows系统消息。

python">win32gui.PumpWaitingMessages()

        第四步,目前为止,隔多少时间休息的时间段是在程序里写死的,默认是60分钟。用户可能需要根据自己的偏好进行修改。那么需要在Icon的Menu里增加一个选项,点击后弹框进行时间段设置。 

         这里就要用到tkinter lib,这个默认在Pythond的安装包里,无需另外安装。

python">import tkinter as tk
from tkinter import simpledialog#a pop up dialog to set the time_set
def set_timer(icon, item):#after the user clicking the set buttondef validate_and_set_time():nonlocal root, error_labeltry:minutes = int(minute_input.get())seconds = int(second_input.get())#print(minutes,seconds)# Validate range [0, 60]if 0 <= minutes <= 60 and 0 <= seconds <= 60:with time_left_lock:global time_set,time_lefttime_set=(minutes,seconds)time_left=list(time_set)  #each time_set is set, time_let needs to update accordingly#print(time_left)root.destroy()  # Close dialog if input is validelse:error_label.config(text="Minutes and seconds must be between 0 and 60!")except ValueError:error_label.config(text="Please enter valid integers!")#create the dialogroot = tk.Tk()root.title("Set Timer")root.geometry("300x200")# Get screen width and heightscreen_width = root.winfo_screenwidth()screen_height = root.winfo_screenheight()# Calculate position x and y to center the windowwindow_width = 300window_height = 200position_x = (screen_width // 2) - (window_width // 2)position_y = (screen_height // 2) - (window_height // 2)# Set the geometry to center the windowroot.geometry(f"{window_width}x{window_height}+{position_x}+{position_y}")tk.Label(root, text="Set Timer").pack(pady=5)tk.Label(root, text="Minutes (0-60):").pack()minute_input = tk.Entry(root)minute_input.pack()tk.Label(root, text="Seconds (0-60):").pack()second_input = tk.Entry(root)second_input.pack()error_label = tk.Label(root, text="", fg="red")  # Label for error messageserror_label.pack(pady=5)#set the button call-back method to validate_and_set_timetk.Button(root, text="Set", command=validate_and_set_time).pack(pady=10)root.mainloop()

        上面的代码就包括了弹窗设计,用户输入数据校验,间隔时间段设置,以及剩余时间重置等。

        另外,在Icon的Menu里需增加一栏,用于设置间隔时间段。

python">MenuItem('Set Timer', set_timer),  # Add the new menu option here

        最后一步,为了让用户设置的时间段间隔永久生效,需要用一个文件来存储。 启动这个程序的时候,从这个文件读数据,退出程序的时候,把数据存入到该文件。

        这是鼠标移到Icon上,点击右键出现的Menu:

        下面是点击Set Timer后的弹框。

        我把这个项目放在Github上,供大家下载,并贡献自己的修改。 

https://github.com/Jessen-Li/have_a_break/


http://www.ppmy.cn/ops/136756.html

相关文章

一键部署zabbix-agent2的脚本

1、首先下载agent2的安装包 我是X86的centos 7系统&#xff0c;zabbix-agent2-5.0.42-1.el7.x86_64.rpm下载地址&#xff0c;另外很多国产系统统信、中科方德也适用这个版本。 这个网站里面有其他版本的&#xff0c;自行选择下载 https://repo.zabbix.com/zabbix/5.0/rhel/7/…

jmeter5.6.3安装教程

一、官网下载 需要提前配置好jdk的环境变量 jmeter官网&#xff1a;https://jmeter.apache.org/download_jmeter.cgi 选择点击二进制的zip文件 下载成功后&#xff0c;默认解压下一步&#xff0c;更改安装路径就行(我安装在D盘) 实用jmeter的bin目录作为系统变量 然后把这…

大数据新视界 -- Hive 数据分区:提升查询效率的关键步骤(下)(8/ 30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

线性代数空间理解

学习线性代数已经很久&#xff0c;但是在使用过程中仍然还是不明所以&#xff0c;比如不知道特征向量和特征值的含义、矩阵的相乘是什么意思、如何理解矩阵的秩……。随着遇到的次数越来越多&#xff0c;因此我决定需要对线性代数的本质做一次深刻的探讨了。 本次主要是参考了3…

10大排序总结

1. 冒泡排序 (Bubble Sort) def bubble_sort(arr):n len(arr)# 遍历数组中的每个元素for i in range(n):# 内层循环&#xff0c;从数组的第一个元素到倒数第 i 1 个元素for j in range(0, n - i - 1):# 如果当前元素比下一个元素大&#xff0c;则交换if arr[j] > arr[j …

241124学习日志——[CSDIY] [ByteDance] 后端训练营 [14]

CSDIY&#xff1a;这是一个非科班学生的努力之路&#xff0c;从今天开始这个系列会长期更新&#xff0c;&#xff08;最好做到日更&#xff09;&#xff0c;我会慢慢把自己目前对CS的努力逐一上传&#xff0c;帮助那些和我一样有着梦想的玩家取得胜利&#xff01;&#xff01;&…

C语言菜鸟入门·关键字·int的用法

目录 1. int关键字 1.1 取值范围 1.2 符号类型 1.3 运算 1.3.1 加法运算() 1.3.2 减法运算(-) 1.3.3 乘法运算(*) 1.3.4 除法运算(/) 1.3.5 取余运算(%) 1.3.6 自增()与自减(--) 1.3.7 位运算 2. 更多关键字 1. int关键字 int 是一个关键字&#xff0…

谈谈Mysql的常见基础问题

一.为什么Mysql选择B树作为索引&#xff1f; 索引(Index)是帮助MySQL高效获取数据的数据结构&#xff1b; 但是索引的数据结构有多种&#xff0c;如MySQL的InnoDB存储引擎支持B树索引还有哈希索引(可以理解为简单的HashMap)。 而针对关系型的数据&#xff0c;如果选用哈希索引…