· 背景和需求
因为疫情期间嘛,老爹也只能开网课,线上教棋了,但是网课又不开摄像头的那种,根本不知道学生在干嘛,这不就有了一个学生在那表面上着网课实际上是在那玩游戏,被他妈妈逮了个正着…因此我老爹就想看看能不能上网课的时候让他只能用上网课的那个程序,而正好我在看python的psutil的模块,就尝试着帮我老爹写一个看看喽
· 分析
1. 首先我需要检测正在运行进程,之后找到在封禁名单内的主进程,之后shutdown就可,这部分psutil模块就可以实现
2. 作为一个程序,我还是得写一个ui界面的,本来是想用tkinter的,不过Python这个标准GUI库功能确实不咋地,这里就现学了一下wxpython来作为程序的ui界面
3. ui界面这里主要是要提供一个最小化到托盘以及推出功能,不然的话现在的这些家长不太知道怎么退出就比较头疼了
· 代码实现
1. 主逻辑部分
· 这里我们把主逻辑部分写到一个线程里,为此我们需要线程模块,这里从threading模块
中导入Thread类
,之后定义我们自己的线程类,继承自Thread,主逻辑放到重写的run()方法
中
· 正式开始主逻辑部分,这里需要被限制程序的主进程名,为了以后好扩展,这里我们将这些主进程名放到一个列表里,之后通过转换为json格式存储到文件中,为此我写了一个config.py用来实现该功能
1 | import json |
· 运行config.py就会生成存放限制进程名的配置文件:learn_tool.conf,之后也可以直接去这个文件中增添其他要限制的进程(ps:本来是应该在ui界面里加入这个增添限制进程功能的,有点懒,先放着吧)
· 之后在类中写一个前面的run()方法中打开该文件,将其从json格式转为list即可;关键在之后:这里直接设置死循环,因为要不断的检测是否有被限制进程,使用psutil.pids()
获取所有当前正在运行的进程id的列表,之后for循环遍历,通过psutil.Process(pid).name()
来获取该pid对应的进程名,之后判断进程名是否在限制进程的list中即可:不在,则继续循环;在,则调用terminate()
方法来关闭进程
1 | # 自定义限制进程 |
· 上面代码中的log为我自定义的日志记录模块:log.py,用来将每次_关闭进程的信息及错误信息_存入到log.txt文件中
1 | import logging |
2· ui部分设计
· 这里选择了wxpython来进行ui设计,需要定义一个自己的Frame类,从wxpython继承
,在__init__()
中初始化窗口大小、窗口位置、ui背景颜色、托盘以及各种控件等,不过这里我们并不需要什么控件,只需要实现托盘和右击托盘退出功能
1 | import wx |
· 之后我们来实现ui界面的关键:托盘的实现,这里要用到wx.adv
,依旧定义我们自己的托盘类,继承自wx.adv.TaskBarIcon
;在__init__()
中初始化Frame、图标设置等,创建菜单需要重写CreatePopupMenu()
方法,在该方法中我们需要使用wx.Menu()
来创建菜单,之后通过wx.Menu.Append()
方法将菜单项添加到菜单中,这里每个菜单项需要用wx.MenuItem()
来创建,而每个菜单项的点击事件的绑定则使用Bind()
来完成,完成之后return menu
1 | # 托盘类 |
· 之后我们来定义上面的退出弹窗,依旧写一个自己的弹窗类,继承自wx.Dialog
,和wx.Frame类似,在__init__()
中进行初始化、拜访控件位置、设置弹窗大小位置等,这里主要是要实现给Button绑定事件和提取TextCtrl中的内容;绑定事件很简单,依旧是用Bind
;而提取TextCtrl中的输入内容也有现成的方法,调用TextCtrl的GetValue()方法
即可。对了,这里我们将退出密码依旧以文件形式保存,然后本来想做个加密算法的,不过,emmmm,懒嘛,就先明文保存到文件吧,哎嘿嘿
1 | # 定义退出时输入密码的Dialog |
· 至此基本功能、ui设计完毕,不过为了再玩一下wxpython,就又添加了一个启动界面,我们这里新建一个函数用来创建启动界面,两行代码就可实现,关键在于wx.adv.SplashScreen()
的使用
1 | def create_splash(): |
· 之后我们要新建自己的主程序类,继承自wx.App
,在这里重写OnInit()
方法(这是主程序入口类),将其Frame设置为我们的Frame实例即可
1 | class MainApp(wx.App): |
· 最后,创建我们主程序实例,启动我们的主逻辑进程并设置为守护进程(这样的话,当我们退出时就会关闭该进程),启动开始界面,mainloop我们的主程序
1 | if __name__ == "__main__": |
3. 代码打包问题
· 这里打包使用的是pyinstall,我的整个程序目录如下,打包则在main.py文件所在位置打开cmd进行打包,打包代码如下:
参数说明:-D:除了主程序外还会在dist文件夹下生成很多依赖文件
-i:选择.exe的图标文件
-noconsole:去除cmd黑框
程序目录:
打包命令: pyinstall -D main.py -i ./pic/app_icon.ico -noconsole
· 生成的程序在dist目录下;但是有一个大问题:程序所需的/data、/log、/pic等文件都没有打包进去,emmm,所以直接运行会报错,当你把main.exe依赖的文件复制进去之后就ok了