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

树莓派热循环仪GUI停止无效、界面冻结如何用线程解决

问题解决思路与修改方案

问题本质

首先明确:你遇到的问题必须通过多线程方案解决,你的代码存在两个核心错误:

  • 热循环的while阻塞逻辑直接运行在UI主线程,占住了Qt的事件循环,导致所有UI交互(停止按钮点击、窗口重绘)都无法被处理,出现窗口冻结、停止按钮无效的问题
  • 你用于控制循环启停的switch是局部变量,不同函数内的switch作用域独立,修改后根本无法传递到正在运行的循环中,自然无法终止程序

具体修改方法

推荐使用Qt原生的QThread实现,兼容性更好,避免Python原生线程和Qt事件循环冲突的问题,修改步骤如下:

  1. 自定义工作线程类,将所有热循环控制、GPIO操作、温度采集逻辑移到工作线程中,用实例属性控制线程启停,需要回传温度等数据到UI的话可以自定义信号
  2. 主线程只负责UI交互,点击开始按钮时启动工作线程,点击停止按钮时修改工作线程的启停标记,等待线程安全退出
  3. 线程退出时做好GPIO资源清理,避免引脚电平异常

核心修改代码示例

首先新增工作线程类

from PySide2.QtCore import QThread, Signal
import time, math
import RPi.GPIO as GPIO
import Adafruit_ADS1x15

# 全局参数保留
temp_denat = 95
heat_pin = 19
cool_pin = 18
pwm_fan = 12
low_volt = 21

# 自定义工作线程
class HeatCycleThread(QThread):
    # 可选:如果需要把温度数据传回UI更新,定义信号
    temp_updated = Signal(float, float) # 分别传递温度、电压

    def __init__(self):
        super().__init__()
        self.running = False # 线程启停控制标记,用实例属性不用局部变量

    def suhu_c(self, R, Ro=100000.0, To=25.0, beta=3950.0):
        suhu = math.log(R / Ro)
        suhu /= beta
        suhu += 1.0 /  (To + 273.15)
        suhu = (1.0 / suhu)
        suhu -=273.15
        return suhu

    def run(self):
        # 所有原来Program_Run里的逻辑都移到这里
        GPIO.setwarnings(False)
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(heat_pin,GPIO.OUT) 
        GPIO.setup(cool_pin,GPIO.OUT)
        GPIO.setup(pwm_fan,GPIO.OUT)
        GPIO.setup(low_volt,GPIO.OUT)
        GPIO.output(low_volt,GPIO.HIGH)
        adc = Adafruit_ADS1x15.ADS1115(address=0x49,busnum=1)
        
        GAIN = 2/3 
        t = 0
        rn = 100000
        told = time.time()

        cool = GPIO.PWM(heat_pin,490)
        heat = GPIO.PWM(cool_pin,490)
        fan = GPIO.PWM(pwm_fan,490)
        heat.start(0)
        cool.start(0)
        fan.start(0)

        self.running = True
        while self.running:
            range_t = time.time() - told
            bit = adc.read_adc_difference(0,gain=GAIN)
            voltage = (bit*6.144)/32768
            R = rn*((5*rn)-(voltage*2*rn))/((5*rn)+(voltage*2*rn))
            suhu = self.suhu_c(R)
        
            if suhu <= temp_denat:
                pwm = 96
                cool.ChangeDutyCycle(0)
                heat.ChangeDutyCycle(pwm)
                fan.ChangeDutyCycle(0)
            if suhu >= temp_denat:
                pwm = 80
                cool.ChangeDutyCycle(pwm)
                heat.ChangeDutyCycle(0)
                fan.ChangeDutyCycle(100)
              
            if range_t >= 1.00:
                print(t,' ADC bit:',round(bit),' bit', ',voltage: ',round(voltage,3),' volt','suhu', round(suhu,3), ' C','R:',round(R,3),'ohm')
                # 可选:发信号回UI更新
                self.temp_updated.emit(suhu, voltage)
                t += 1
                told = time.time()
        
        # 线程退出时清理GPIO
        heat.stop()
        cool.stop()
        fan.stop()
        GPIO.cleanup()

然后修改MainWindow中的逻辑

把原来的Program_Run、Start_Program、Stop_Program函数全部替换,在__init__里新增线程实例:

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        # --- 新增:初始化工作线程 ---
        self.heat_thread = HeatCycleThread()
        # 可选:绑定温度更新信号到UI更新函数,比如更新温度显示
        # self.heat_thread.temp_updated.connect(self.update_temp_display)

        # 原来的窗口初始化、阴影设置、其他按钮绑定逻辑保留,只修改启停按钮的绑定
        self.ui.START_btn.clicked.connect(self.start_program)
        self.ui.CANCEL_btn.clicked.connect(self.stop_program)
    
    def start_program(self):
        # 防止重复启动线程
        if not self.heat_thread.isRunning():
            self.heat_thread.start()
    
    def stop_program(self):
        if self.heat_thread.isRunning():
            # 设置标记让线程循环退出
            self.heat_thread.running = False
            # 等待线程安全退出
            self.heat_thread.wait()

    # 可选:重写关闭窗口事件,保证退出时线程安全停止
    def closeEvent(self, event):
        if self.heat_thread.isRunning():
            self.heat_thread.running = False
            self.heat_thread.wait()
        event.accept()

注意事项

  • 你原来代码里的from ui_interface4 import 4是明显错误,应该是from ui_interface4 import Ui_MainWindow,请自行修正
  • 不要在工作线程里直接操作UI控件,所有UI更新必须通过信号槽传递数据到主线程操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 03:15:04