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

如何解决秒表与手部检测联动时摄像头画面静止问题

问题分析

你的合并代码存在几个致命问题:

  1. Tkinter主循环阻塞:obj.mainloop()是阻塞函数,一旦执行就会停在这里,后面的OpenCV帧更新代码再也不会运行,所以摄像头只显示第一帧就静止了。
  2. 错误的代码结构:把stopwatch类定义放在了OpenCV的while True循环内部,每次循环都会重新定义这个类,完全不符合编程规范,也会导致逻辑混乱。
  3. 秒表代码的小bug:pause_time方法里的self.running == False是比较运算符,不是赋值,应该改为self.running = False,否则暂停后无法重新启动秒表。
修复后的代码

我们用多线程分离Tkinter界面和OpenCV摄像头处理,避免主循环阻塞:

import tkinter as tk
import cv2
from cvzone.HandTrackingModule import HandDetector
import threading

class Stopwatch(tk.Frame):
    def __init__(self, window=None):
        super().__init__(window)
        self.window = window
        self.new_time = ''
        self.running = False
        self.total_hours = 0
        self.total_minutes = 0
        self.total_seconds = 0
        self.pack()
        self.features()

    def features(self):
        self.stopwatch_label = tk.Label(self, text='00:00:00', background="black", foreground="white", font=('arial', 85, "bold"))
        self.stopwatch_label.pack()

        self.start_time_button = tk.Button(self, text="START", height=5, width=7, font=('arial', 19, "bold"), background="green", command=self.start_time)
        self.start_time_button.pack(side=tk.LEFT)

        self.stop_time_button = tk.Button(self, text="STOP", height=5, width=7, font=('arial', 19, "bold"), background="red", command=self.pause_time)
        self.stop_time_button.pack(side=tk.LEFT)

        self.reset_time_button = tk.Button(self, text="RESET", height=5, width=7, font=('arial', 19, "bold"), background="yellow", command=self.reset_time)
        self.reset_time_button.pack(side=tk.LEFT)

        self.quit_button = tk.Button(self, text="QUIT", height=5, width=7, font=('arial', 19, "bold"), background="blue", command=self.quit_app)
        self.quit_button.pack(side=tk.LEFT)

        self.window.title('STOPWATCH')

    def start_time(self):
        if not self.running:
            self.change()
            self.running = True

    def pause_time(self):
        if self.running:
            self.stopwatch_label.after_cancel(self.new_time)
            self.running = False  # 修复赋值错误

    def reset_time(self):
        if self.running:
            self.stopwatch_label.after_cancel(self.new_time)
            self.running = False
        self.total_hours, self.total_minutes, self.total_seconds = 0, 0, 0
        self.stopwatch_label.config(text="00:00:00")

    def change(self):
        self.total_seconds += 1
        if self.total_seconds == 60:
            self.total_minutes += 1
            self.total_seconds = 0
        if self.total_minutes == 60:
            self.total_hours += 1
            self.total_minutes = 0

        total_hours_string = f"{self.total_hours}" if self.total_hours > 9 else f'0{self.total_hours}'
        total_minutes_string = f"{self.total_minutes}" if self.total_minutes > 9 else f'0{self.total_minutes}'
        total_seconds_string = f"{self.total_seconds}" if self.total_seconds > 9 else f'0{self.total_seconds}'
        
        self.stopwatch_label.config(text=f"{total_hours_string}:{total_minutes_string}:{total_seconds_string}")
  
        self.new_time = self.stopwatch_label.after(1000, self.change)  # 修正为每秒更新一次,符合秒表逻辑

    def quit_app(self):
        self.window.quit()
        global camera_running
        camera_running = False

def camera_thread():
    global camera_running
    camera_running = True
    frame = cv2.VideoCapture(0)
    handDetector = HandDetector(detectionCon=0.8)
    
    while camera_running:
        res, img = frame.read()
        if not res:
            break
        hands = handDetector.findHands(img)
        cv2.imshow("Hands", img)
        # 处理摄像头窗口的退出逻辑
        if cv2.waitKey(10) & 0xFF == ord('q'):
            camera_running = False
            break
    frame.release()
    cv2.destroyAllWindows()

if __name__ == "__main__":
    # 启动摄像头线程
    threading.Thread(target=camera_thread, daemon=True).start()
    # 启动Tkinter主循环
    root = tk.Tk()
    obj = Stopwatch(window=root)
    obj.mainloop()
关键修复点
  • 多线程分离:把摄像头的帧读取和显示放在单独线程,避免与Tkinter主循环互相阻塞。
  • 修正代码结构:将Stopwatch类定义放在全局作用域,不再嵌套在循环内。
  • 修复秒表bug:修正pause_time方法的赋值错误,调整秒表更新间隔为1秒。
  • 统一退出逻辑:通过全局变量camera_running,确保点击QUIT按钮时能同时终止摄像头线程并释放资源。

内容的提问来源于stack exchange,提问作者Andrea Gutiérrez Ceballos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 00:52:40