树莓派热循环仪GUI停止无效、界面冻结如何用线程解决
问题解决思路与修改方案
问题本质
首先明确:你遇到的问题必须通过多线程方案解决,你的代码存在两个核心错误:
- 热循环的while阻塞逻辑直接运行在UI主线程,占住了Qt的事件循环,导致所有UI交互(停止按钮点击、窗口重绘)都无法被处理,出现窗口冻结、停止按钮无效的问题
- 你用于控制循环启停的
switch是局部变量,不同函数内的switch作用域独立,修改后根本无法传递到正在运行的循环中,自然无法终止程序
具体修改方法
推荐使用Qt原生的QThread实现,兼容性更好,避免Python原生线程和Qt事件循环冲突的问题,修改步骤如下:
- 自定义工作线程类,将所有热循环控制、GPIO操作、温度采集逻辑移到工作线程中,用实例属性控制线程启停,需要回传温度等数据到UI的话可以自定义信号
- 主线程只负责UI交互,点击开始按钮时启动工作线程,点击停止按钮时修改工作线程的启停标记,等待线程安全退出
- 线程退出时做好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
相关产品推荐
相关产品推荐

