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

使用pythonw运行Arduino监控脚本时出现窗口闪烁问题

问题:pythonw运行监控脚本时反复弹出窗口,无报错日志

用Arduino+Python做了简易电脑监控屏,正常运行没问题,但想实现开机无窗口启动,改用pythonw.exe或.pyw后缀运行时,会有窗口弹出,还每隔2-3秒反复开闭。用普通python.exe运行则完全正常,日志文件始终为空。

原代码

import serial
import psutil
import GPUtil
import time
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from PIL import Image
import pystray
import threading
import logging

logging.basicConfig(filename='C:\\Users\\natha\\Documents\\GitHub\\arduino-serial-monitor\\log.txt', level=logging.ERROR)

try:
    run = True
    display = True


    devices = AudioUtilities.GetSpeakers()
    interface = devices.Activate(
        IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
    volume = cast(interface, POINTER(IAudioEndpointVolume))


    ser = serial.Serial('COM4', 9600, timeout=1)

    def create_menu():
        return pystray.Menu(
            pystray.MenuItem('Quit', on_quit_callback),
            pystray.MenuItem('Reboot', on_reboot_callback),
            pystray.MenuItem(lambda text: 'Display Off' if display else 'Display On', on_display_off_callback)
        )

    def on_quit_callback(icon, item):
        global run
        icon.stop()
        ser.write('SHUTDOWN\n'.encode())
        run = False

    def on_display_off_callback(icon, item):
        global display
        display = False if display else True

    def on_reboot_callback(icon, item):
        ser.write('REBOOT\n'.encode())


    image = Image.open('C:\\Users\\natha\\Documents\\GitHub\\arduino-serial-monitor\\icon.png')
    icon = pystray.Icon('System Monitor', image, 'System Monitor', menu=create_menu())
    icon_thread = threading.Thread(target=icon.run)
    icon_thread.start()

    time.sleep(2)  # Wait for 2 seconds to give the Arduino time to get ready

    while run:
        gpus = GPUtil.getGPUs()
        gpu = gpus[0]

        cpu_usage = max(psutil.cpu_percent(interval=1, percpu=True))
        memory = psutil.virtual_memory()
        memory_usage = memory.percent
        memory_usage_raw = round(memory.used / (1024 ** 3), 2)
        memory_total = round(memory.total / (1024 ** 3))
        gpu_usage = round(gpu.load * 100)
        gpu_temp = gpu.temperature

        timePrint = time.strftime('%H:%M', time.localtime())

        volumePercent = round(volume.GetMasterVolumeLevelScalar() * 100)

        if ser.is_open:
            if display:
                ser.write(f'INFO:CPU:{cpu_usage},MEM_RAW:{memory_usage_raw},MEM_TOTAL:{memory_total},GPU:{gpu_usage},TEMP:{gpu_temp},TIME:{timePrint},VOL:{volumePercent}\n'.encode())
            else:
                ser.write('DISPLAY_OFF\n'.encode())
        else:
            print('Serial port is not open')
        
        #if ser.in_waiting > 0:
        #    try:
        #        
        #    except UnicodeDecodeError:
        #        pass  # Ignore the error

        time.sleep(0.5)  # Wait for 0.5 second before sending the next set of data
except Exception as e:
    logging.exception(e)

解决办法

1. 替换GPUtil模块,避免控制台窗口泄露

GPUtil在pythonw环境下会调用控制台窗口,导致反复弹出。改用nvidia-smi命令直接获取GPU信息:

# 替换原GPUtil相关代码
import subprocess

def get_gpu_data():
    try:
        # 调用nvidia-smi获取GPU使用率和温度,强制不创建窗口
        output = subprocess.check_output(
            ["nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu", "--format=csv,noheader,nounits"],
            creationflags=subprocess.CREATE_NO_WINDOW
        )
        gpu_util, gpu_temp = output.decode().strip().split(', ')
        return int(gpu_util), int(gpu_temp)
    except Exception as e:
        logging.exception("获取GPU信息失败")
        return 0, 0

然后在循环中替换原GPU相关代码:

# 替换原gpus = GPUtil.getGPUs()及以下行
gpu_usage, gpu_temp = get_gpu_data()

2. 强制隐藏控制台窗口

在代码最开头添加以下代码,确保所有可能的控制台窗口被隐藏:

import ctypes
# 隐藏当前控制台窗口
ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)

3. 修复线程异常捕获

原代码无法捕获托盘图标线程中的异常,修改线程启动逻辑:

def run_tray_icon():
    try:
        icon.run()
    except Exception as e:
        logging.exception("托盘图标线程出错")

icon_thread = threading.Thread(target=run_tray_icon)
icon_thread.start()

同时把logging.basicConfig移到代码最开头,确保日志初始化优先:

import logging
logging.basicConfig(filename='C:\\Users\\natha\\Documents\\GitHub\\arduino-serial-monitor\\log.txt', level=logging.ERROR)

4. 优化串口操作

在退出回调中添加串口关闭逻辑,避免资源泄漏:

def on_quit_callback(icon, item):
    global run
    icon.stop()
    ser.write('SHUTDOWN\n'.encode())
    ser.close()  # 关闭串口
    run = False

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:53:10