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

Ubuntu下Systemd服务运行Tkinter磁盘监控程序的GUI响应异常问题

磁盘使用率监控systemd服务GUI卡顿问题解决

问题背景

我编写了一段Python代码,在Ubuntu系统中作为systemd服务运行,实现磁盘使用率的监听功能:当磁盘使用率达到指定百分比时,弹出带“Ok”按钮的Tkinter警告窗口;点击“Ok”按钮会启动baobab(磁盘使用分析器),窗口随之关闭,程序循环运行直到使用率低于阈值。

核心问题

  • 直接在终端执行python3 /path/main.py,用fallocate -l 2000G Downloads/Test测试时,Tkinter弹窗正常,点击“Ok”可快速打开baobab,功能完全正常。
  • 将代码注册为systemd服务后,执行相同测试命令,Tkinter警告窗虽弹出,但点击“Ok”按钮后响应卡顿,窗口无法关闭,baobab启动缓慢;关闭baobab后,“Ok”按钮才恢复未点击状态。

已知systemd服务与Tkinter这类GUI程序存在兼容问题,附核心代码:

def monitor_storage(threshold_percentage=90, check_interval=5):
    window = tk.Tk()
    window.withdraw()

    while True:
        try:
            disk_usage = shutil.disk_usage("/")
            used_percentage = (disk_usage.total - disk_usage.free) / disk_usage.total * 100

            if used_percentage >= 100:
                response = messagebox.showwarning("Storage Alert",
                                                  f"Your storage is FULL \n({used_percentage:.2f}% used)!")
                if response == "ok":
                    open_disk_usage_analyzer()
            elif used_percentage > threshold_percentage:
                response = messagebox.showwarning("Storage Alert",
                                                  f"Your storage is almost full \n({used_percentage:.2f}% used)!")
                if response == "ok":
                    open_disk_usage_analyzer()

            # Check every N seconds
            time.sleep(check_interval)

        except Exception as e:
            # Log any exceptions
            logging.error(f"An error occurred: {e}")
            center_messagebox(tk.Tk(), "Error", f"An error occurred: {e}")

解决方案

1. 配置systemd服务的GUI环境变量

systemd服务默认运行在无GUI的会话环境,必须指定DISPLAY和XAUTHORITY变量,让程序连接到当前用户的X11会话。修改你的systemd服务文件(例如/etc/systemd/system/storage-monitor.service):

[Unit]
Description=Disk Storage Monitor
After=graphical.target

[Service]
Type=simple
User=<你的用户名>
Environment="DISPLAY=:0"
Environment="XAUTHORITY=/home/<你的用户名>/.Xauthority"
ExecStart=/usr/bin/python3 /path/main.py
Restart=always

[Install]
WantedBy=graphical.target
  • 替换<你的用户名>为实际系统用户名;
  • After=graphical.target确保服务在GUI会话启动后再运行;
  • WantedBy=graphical.target设置服务随桌面环境自动启动。

2. 修复Tkinter事件循环阻塞问题

原代码用time.sleep()阻塞了Tkinter的主事件循环,导致GUI响应卡顿。改用Tkinter的after()方法替代time.sleep(),同时异步启动baobab避免阻塞:
修改后的核心代码:

import tkinter as tk
from tkinter import messagebox
import shutil
import logging
import subprocess

def open_disk_usage_analyzer():
    # 异步启动baobab,不阻塞事件循环
    subprocess.Popen(["baobab"])

def check_storage(threshold_percentage=90):
    try:
        disk_usage = shutil.disk_usage("/")
        used_percentage = (disk_usage.total - disk_usage.free) / disk_usage.total * 100

        if used_percentage >= 100:
            response = messagebox.showwarning("存储警报",
                                              f"磁盘已完全占满\n({used_percentage:.2f}% 已使用)!")
            if response == "ok":
                open_disk_usage_analyzer()
        elif used_percentage > threshold_percentage:
            response = messagebox.showwarning("存储警报",
                                              f"磁盘即将占满\n({used_percentage:.2f}% 已使用)!")
            if response == "ok":
                open_disk_usage_analyzer()

        # 5秒后再次执行检查,替代time.sleep()
        root.after(5000, check_storage, threshold_percentage)

    except Exception as e:
        logging.error(f"发生错误: {e}")
        messagebox.showerror("错误", f"发生错误: {e}")
        root.after(5000, check_storage, threshold_percentage)

if __name__ == "__main__":
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    check_storage(90)
    root.mainloop()  # 启动Tkinter主事件循环
  • root.after()方法会在指定时间后调度函数执行,不会阻塞事件循环;
  • subprocess.Popen()异步启动baobab,避免等待程序退出才继续处理GUI事件,解决按钮卡顿问题。

3. 重新加载systemd服务配置

修改服务文件后,执行以下命令使配置生效:

sudo systemctl daemon-reload
sudo systemctl restart storage-monitor.service

内容的提问来源于stack exchange,提问作者Vlad Belo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 08:55:35