You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优雅停止由VBS启动、使用Timeloop模块的Pythonw后台运行脚本

优雅终止你的桌面清理脚本解决方案

咱先从你的最低期望开始实现,再一步步升级到系统托盘的更高期望,彻底解决强制杀进程的麻烦。


一、让脚本在任务管理器中可识别并单独终止

现在用pythonw.exe运行的话,所有Python后台进程都叫这个名字,根本分不清哪个是你的清理脚本。咱给进程改个独特的名字,这样任务管理器里一眼就能找到它:

步骤1:安装进程标题库

打开命令行,安装setproctitle(跨平台,Windows上完美兼容):

pip install setproctitle

步骤2:修改main.py设置进程名

在main.py最开头加上这两行:

import setproctitle
setproctitle.setproctitle("DesktopCleaner")

现在运行脚本后,打开任务管理器的「详细信息」标签,就能找到名为DesktopCleaner的进程,直接右键「结束任务」就可以单独终止它,不用再杀所有pythonw.exe了!


二、创建CLI工具一键终止脚本

如果不想每次都开任务管理器,咱写个小脚本,双击就能终止清理程序:

编写stop_cleaner.py

在同一个目录下新建stop_cleaner.py,内容如下:

import psutil

def stop_desktop_cleaner():
    # 遍历所有进程,找到我们的清理脚本
    for proc in psutil.process_iter(['name', 'cmdline']):
        try:
            # 匹配进程名或者命令行里的脚本路径,双重保险
            if proc.info['name'] == 'pythonw.exe' and 'desktop_cleaner\\main.py' in ' '.join(proc.info['cmdline']):
                proc.terminate()
                print("✅ 桌面清理脚本已成功终止")
                return
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            pass
    print("❌ 未找到运行中的桌面清理脚本")

if __name__ == "__main__":
    stop_desktop_cleaner()

做成一键运行的bat脚本

再新建一个stop_cleaner.bat,内容如下:

@echo off
python D:\PythonDevelopment\desktop_cleaner\stop_cleaner.py
pause

以后双击这个bat文件,就能直接终止脚本了,非常方便!


三、实现系统托盘常驻(更高期望)

之前托盘图标闪来闪去,是因为你用的win10toast每次弹出通知都会临时创建托盘图标,通知消失后图标就销毁了,所以每隔40秒闪一次。咱换用pystray库做常驻托盘,还能右键退出:

步骤1:依赖安装

pip install pystray pillow

步骤2:修改main.py实现常驻托盘

替换main.py的最后部分,整合Timeloop和托盘功能(完整修改后的代码如下,关键部分我标了注释):

import time, os, os.path as path, win32api, logging, shutil
from win10toast import ToastNotifier
from timeloop import Timeloop
from datetime import timedelta
import setproctitle
import pystray
from PIL import Image
import threading

# 设置独特进程名,方便任务管理器识别
setproctitle.setproctitle("DesktopCleaner")

tl = Timeloop()
toast = ToastNotifier()

logging.basicConfig(
    filename="D:\\PythonDevelopment\\desktop_cleaner\\app.log",
    level=logging.DEBUG,
    format="[%(asctime)s - %(levelname)s]: %(message)s",
    datefmt="%d-%b-%y %H:%M:%S",
)

def initial_check():
    """Checks if all conditions are satisfied to start cleaning process. If condtions are not met, it tries to setup all the required conditions."""
    report = {}
    desktop = path.expanduser("~\Desktop")
    desktop_store = path.expanduser("~\desktop_store")
    if path.exists(desktop) and path.isdir(desktop):
        logging.info("Located desktop directory at {}".format(desktop))
        report["desktop"] = desktop
        report["proceed"] = True
    else:
        logging.error("Cannot found desktop directory at {}".format(desktop))
        report["desktop"] = None
        report["proceed"] = False
    if path.exists(desktop_store) and path.isdir(desktop_store):
        logging.info("Located `desktop_store` directory at {}".format(desktop_store))
        report["desktop_store"] = desktop_store
    else:
        logging.error("`desktop_store` does not exist at {}".format(desktop))
        os.makedirs(desktop_store)
        report["desktop_store"] = desktop_store
        logging.info("`desktop_store` directory created")
    return report

def content_mover(src: str, dest: str):
    """moves all content from one directory to another"""
    src_content = os.listdir(src)
    rejected = []
    cannotmove = []
    logging.info(f"Content in the directory to be cleaned:- {src_content}")
    for item in src_content:
        if (
            os.path.splitext(item)[1] == ".lnk"
            or os.path.splitext(item)[1] == ".ini"
            or "Zoom background" in item
            or "Recycle Bin" in item
        ):
            rejected.append(item)
        else:
            try:
                shutil.move(os.path.join(src, item), dest)
            except shutil.Error as err:
                print(err)
                logging.error(str(err))
                if "already exists" in str(err):
                    cannotmove.append(item)
    if len(rejected) != 0:
        logging.warning(f"Items rejected before cleaning started:- {rejected}")
    if len(cannotmove) != 0:
        logging.warning(f"Items that already exists in desktop_store:- {cannotmove}")

@tl.job(interval=timedelta(seconds=40))
def main_desktop_cleaner():
    """This function gets called at an interval of 3 hours. Other required functions gets called inside this"""
    logging.info(
        f"======================NEW SESSION<{time.ctime()}>======================"
    )
    toast.show_toast(
        "Desktop Cleaner",
        "Desktop Cleaner started at {}".format(time.ctime())
    )
    initial_report = initial_check()
    if initial_report["proceed"]:
        logging.info("Ready to begin cleaning process")
        initial_report[
            "desktop"
        ] = "D:\\PythonDevelopment\\cleaner"  # temporary desktop alternative
        content_mover(initial_report["desktop"], initial_report["desktop_store"])
    else:
        logging.error("Proceed clearance failed")
        toast.show_toast(
            "Desktop Cleaner",
            "Required conditions were not met. Cleaning stopped."
        )

# 托盘退出函数:停止Timeloop并退出程序
def on_exit(icon, item):
    tl.stop()
    icon.stop()
    os._exit(0)

# 创建常驻托盘图标
def create_tray_icon():
    # 这里可以替换成你自己的ico图标路径,比如:Image.open("cleaner_icon.ico")
    # 下面是生成一个简单的蓝色图标作为示例
    image = Image.new('RGB', (64, 64), color=(77, 148, 255))
    # 托盘菜单:只有一个退出选项
    menu = pystray.Menu(
        pystray.MenuItem('退出桌面清理', on_exit)
    )
    # 创建托盘实例
    icon = pystray.Icon("Desktop Cleaner", image, "桌面清理工具", menu)
    # 启动托盘(这个方法会阻塞主线程,所以要放到后面)
    icon.run()

if __name__ == "__main__":
    # 先运行一次清理任务
    main_desktop_cleaner()
    # 把Timeloop放到后台线程运行,避免阻塞托盘
    tl_thread = threading.Thread(target=tl.start, args=(True,), daemon=True)
    tl_thread.start()
    # 启动常驻托盘(主线程在这里阻塞,直到点击退出)
    create_tray_icon()

效果说明

修改后运行脚本,系统托盘会常驻一个图标,右键点击就能看到「退出桌面清理」选项,点击后脚本会优雅停止Timeloop并退出,再也不用强制杀进程了!而且之前的图标闪烁问题也彻底解决了。


内容的提问来源于stack exchange,提问作者Curious Learner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 21:52:32