初衷
突发奇想写定时锁屏软件,是想限制连续专注时间,做到劳逸结合。最好是每45分钟休息一下,读篇文章/散个步/聊聊天。
操作系统Win10,
语言版本Python3.7.6
开发环境Pycharm
设计
1.实现锁屏
代码:
# -*- coding: UTF-8 -*-
from ctypes import *
user32 = windll.LoadLibrary('user32.dll')
user32.LockWorkStation()
测试效果:运行.py文件,系统会直接进入锁屏状态,需要输入锁屏密码来解锁。
参考:《用python写个一键锁屏的小脚本》,
地址https://editor.csdn.net/md/?articleId=112380986
2.实现计时秒表
要求:
计时可见。
通过GUI(图形用户界面)进行操作。
计算时间差:
time.time()返回当前时间的时间戳。
tkinter库:
tkinter是一个使用Python语言构建的GUI工具包。允许采用GUI的方式执行你的Python脚本。
英文教程地址:
https://python-textbok.readthedocs.io/en/1.0/Introduction_to_GUI_Programming.html介绍的部分原文:
事件的监听:Anything that happens in a user interface is an event.
窗口的组件:GUI elements, such as buttons, menus and various kinds of entry fields and display areas.
We call these elements widgets.
窗口的树型结构:We are going to construct a tree of widgets for our GUI – each widget will have a parent widget, all the way up to the root window of our application.
Python中文版官方文档:
tkinter教程地址,https://www.docs4dev.com/docs/zh/python/3.7.2rc1/all/library-tk.html。(感谢该项目的创建者和维护者)
(2)文档可以慢读,程序可以先写
测试程序:
import tkinter
import time
star = time.time()
def gettime():
elap = time.time() - star # 获取时间差
hours = int(elap / 3600)
minutes = int(elap / 60 - hours * 60.0)
seconds = int(elap - minutes * 60.0)
var.set('%02d:%02d:%02d' % (hours, minutes, seconds))
root.after(1, gettime) # 每隔1ms调用函数自身获取时间
root = tkinter.Tk()
root.title('电子时钟')
var = tkinter.StringVar()
lb = tkinter.Label(root, textvariable=var, fg='orange', font=("微软雅黑", 100)) # 设置字体大小颜色
lb.pack()
gettime()
root.mainloop()
测试效果:
使用到的tkinter内容:
tk = tkinter.TK() # 创建一个窗口的实例对象
tk.title() # 设置窗口标题
tkinter.Stringvar() # 窗口变量
lb = tkinter.Label(text="", fg="", bg="") # 窗口标签,包含窗口变量
lb.pack() # 一种理解是绑定事件
tk.mainloop() # 进入事件循环,保持监听事件
3.重新分析需求。
实现了计时器、锁屏的基本功能,接下来重新分析需求。
分析需求:
需要输入时间吗,或采用定长时间锁屏?定长。
突然想到输入时间,可以有包时的效果,更改需求为输入时间。
需要暂停或终止计时吗?不需要。
4.输入时间
测试代码:
def get_text():
global setime
setime = int(entry1.get())
root2.destroy()
def time_set():
global entry1, root2, setime
root2 = tkinter.Tk()
root2.title('设置时间')
root2.geometry('300x300')
lb = tkinter.Label(root2, text="输入锁屏Minutes:")
lb.pack()
entry1 = tkinter.Entry(root2)
entry1.pack()
anniu = tkinter.Button(root2, text='GO', command=get_text)
anniu.pack()
root2.mainloop()
参考:《python:从输入框中读取》,地址https://blog.csdn.net/hzliyaya/article/details/9321493?utm_source=blogxgwz0
5.集成测试
测试代码(较单元测试略有修改):
# -*- coding: UTF-8 -*-
import tkinter
import time
from ctypes import *
def gettime():
elap = time.time() - star # 获取时间差
hours = int(elap / 3600)
minutes = int(elap / 60 - hours * 60.0)
seconds = int(elap - minutes * 60.0)
var.set('%02d:%02d:%02d' % (hours, minutes, seconds))
root.after(1, gettime) # 每隔1ms调用函数自身获取时间
if minutes > setime-1:
user32 = windll.LoadLibrary('user32.dll')
user32.LockWorkStation()
exit(0)
def timing(): # 计时器
global root, var, star
star = time.time()
root = tkinter.Tk()
root.title('计时器')
var = tkinter.StringVar()
lb = tkinter.Label(root, textvariable=var, fg='orange', font=("微软雅黑", 100)) # 设置字体大小颜色
lb.pack()
gettime()
root.mainloop()
def get_text():
global setime
setime = int(entry1.get())
root2.destroy()
def time_set():
global entry1, root2, setime
root2 = tkinter.Tk()
root2.title('设置时间')
root2.geometry('300x300')
lb = tkinter.Label(root2, text="输入锁屏Minutes:")
lb.pack()
entry1 = tkinter.Entry(root2)
entry1.pack()
anniu = tkinter.Button(root2, text='GO', command=get_text)
anniu.pack()
root2.mainloop()
if __name__ == '__main__':
time_set()
timing()
测试图:
输入0会立即锁屏
输入2则关闭第一个窗口,打开第二个窗口开始计时:
6.打包成exe文件
命令行执行:pyinstaller -w lock.py
在打包生成的dist目录中找到lock.exe,即为最终exe程序。
运行lock.exe,如图:
debug和优化
exe循环锁屏
遇到一个奇怪的情况,打包脚本生成的exe文件会循环锁屏。做了一个对比测试,如下图。
运行方式 | 输入时间 | 是否循环锁屏 |
---|---|---|
直接运行py脚本 | 0 | × |
直接运行py脚本 | 1 | × |
直接运行py脚本 | 2 | × |
运行exe文件 | 0 | × |
运行exe文件 | 1 | √ |
蛮难受的问题,临时/最终解决方式:直接运行py脚本。
占用资源优化
发现问题,是因为开启脚本电脑风扇就会启动,说明CPU利用率提高。
对比CPU利用率如下,观察进程发现CPU占用率在20%初头。
-
开启py脚本前,CPU和磁盘文件利用率基本都在10%以内。
-
开启py脚本后,CPU利用率在20%-30%
原因分析:root.after(1, gettime)
,每隔1ms都会获取一次时间。
解决方式:每隔1秒获取一次时间,也就是1000毫秒。
(一分钟获取一次,计时器看上去就不是实时的,体验不好)
root.after(1000, gettime)
效果:进程的CPU占用率在1%以内。
最新代码
# -*- coding: UTF-8 -*-
import tkinter
import time
from ctypes import *
def gettime():
elap = time.time() - star # 获取时间差
hours = int(elap / 3600)
minutes = int(elap / 60 - hours * 60.0)
seconds = int(elap - minutes * 60.0)
var.set('%02d:%02d:%02d' % (hours, minutes, seconds))
root.after(1000, gettime) # 每隔1ms调用函数自身获取时间
if minutes > setime-1:
user32 = windll.LoadLibrary('user32.dll')
user32.LockWorkStation()
exit(0)
def timing(): # 计时器
global root, var, star
star = time.time()
root = tkinter.Tk()
root.title('计时器')
var = tkinter.StringVar()
lb = tkinter.Label(root, textvariable=var, fg='orange', font=("微软雅黑", 100)) # 设置字体大小颜色
lb.pack()
gettime()
root.mainloop()
def get_text():
global setime
setime = int(entry1.get())
root2.destroy()
def time_set():
global entry1, root2, setime
root2 = tkinter.Tk()
root2.title('设置时间')
root2.geometry('300x300')
lb = tkinter.Label(root2, text="输入锁屏Minutes:")
lb.pack()
entry1 = tkinter.Entry(root2)
entry1.pack()
anniu = tkinter.Button(root2, text='GO', command=get_text)
anniu.pack()
root2.mainloop()
if __name__ == '__main__':
time_set()
timing()
闲谈
锁屏木马
编写锁屏程序的过程中,由于没有做好循环控制,碰到了0秒循环锁屏的情况。幸好CSDN会自动保存草稿,心态被救。
循环锁屏的情况是通过重启电脑的手段解决的,如果把该锁屏程序设置为开机自启呢?联想冰河木马修改注册表、自启动的方式,以及最近的incaseformat蠕虫病毒,这个锁屏程序似乎也可以写成一个木马。
想象锁屏木马的效果,开机即无限锁屏。
如果像冰河木马一样修改注册表,单纯删除文件是无法清除的。
可以通过U盘、网页flash等方式传播这个恶作剧小程序。
最简单直接的解决方式,就是重装操作系统。