Kimi对话记录
豆包您好,我想要在这一周内完成这份代码的细致学习,请帮我列一个计划表帮助我明晰每天需要学什么知识到什么程度,用什么方法。才能真正学会并且能够运用这份代码。我现在是一个零基础的小白,python的基础在基础逻辑运算,还不会面向对象编程,我想要一个星期内完全理解这份代码有可能吗
你好!我是Kimi,不是豆包哦 😊
直接回答你的问题:一周内从零基础(不会面向对象)到完全理解这份代码,难度很高,但并非不可能。这份代码涉及4个技术栈的交叉,你需要高强度投入(每天4-6小时)。
我为你制定了一个**“先跑通,再深入”**的冲刺计划,分为两个阶段:第1-3天补基础,第4-7天攻代码。
📅 七天学习计划表
Day 1:Python面向对象速成
目标:理解类、对象、继承、方法
| 时间 | 内容 | 具体任务 |
|---|---|---|
| 上午 | 类与对象 | 写一个Person类,有name属性和say()方法 |
| 下午 | __init__和self |
理解为什么方法第一个参数是self,做练习:写Car类,初始化color和speed |
| 晚上 | 继承 | 写ElectricCar(Car),添加battery属性 |
检验标准:能独立写出三层继承结构,不报错
推荐资源:
- 廖雪峰Python教程(面向对象章节)→ 2小时
- 或B站"Python面向对象"任意高播放量视频
Day 2:NumPy数组操作
目标:理解ndarray、切片、广播、mask
| 时间 | 内容 | 具体任务 |
|---|---|---|
| 上午 | 创建数组 | np.zeros, np.ones, np.arange,理解dtype |
| 下午 | 索引切片 | 重点练arr[-1000:]取最后1000个,np.concatenate拼接 |
| 晚上 | 布尔mask | arr[arr > 0]筛选,~np.isnan(arr)取非空值 |
检验标准:能不用for循环,纯用numpy实现:创建8×20000的零数组,把每行最后100个点提取出来
关键代码对照(这份代码里的用法):
# 代码中的实际用法
self.data = [np.zeros(capacity, dtype=np.float32) for _ in range(8)] # 列表推导式
data = np.concatenate((self.data[ch][idx:], self.data[ch][:idx])) # 环形缓冲拼接
valid_data = data[~np.isnan(data)] # 去除NaN
Day 3:PyQt5基础 + 信号槽机制
目标:能写出有按钮、有定时刷新的窗口
| 时间 | 内容 | 具体任务 |
|---|---|---|
| 上午 | 基础窗口 | 抄一个教程,显示空白窗口,设置标题和大小 |
| 下午 | 控件布局 | 添加QPushButton、QCheckBox,用QHBoxLayout排列 |
| 晚上 | 信号与槽 | 重点:点击按钮改变标签文字;用QTimer每秒更新数字 |
检验标准:写出一个窗口,有开始/暂停按钮,点击后标签在"运行中"和"已暂停"切换,同时一个数字Label每秒+1
关键概念(这份代码的核心):
# 信号槽 = 观察者模式
self.plot_timer = QtCore.QTimer(self)
self.plot_timer.timeout.connect(self._refresh_plots) # 定时触发
self.plot_timer.start(UPDATE_INTERVAL) # 每60ms刷新
Day 4:串口通信 + 生产者-消费者模型
目标:理解SerialThread和LogWriterThread的协作
| 时间 | 内容 | 具体任务 |
|---|---|---|
| 上午 | 串口基础 | 安装pyserial,用serial.tools.list_ports列出可用串口 |
| 下午 | 线程基础 | 理解QThread vs 普通Python线程,为什么GUI用QThread |
| 晚上 | 队列通信 | 核心:写两个线程,一个每秒生产数字,一个消费并打印,用queue.Queue传递 |
检验标准:画出这份代码的线程交互图:
- 主线程(UI)←——数据——
SerialThread SerialThread——日志队列→LogWriterThread
必须搞懂的代码段:
# SerialThread(生产者)
self.log_queue.put_nowait(log_line) # 非阻塞放入
# LogWriterThread(消费者)
item = self.log_queue.get(timeout=0.2) # 阻塞等待
Day 5:数字信号处理入门
目标:理解滤波器的作用,不用推导公式
| 时间 | 内容 | 具体任务 |
|---|---|---|
| 上午 | 采样定理 | 理解500Hz采样率意味着能测最高250Hz信号 |
| 下午 | 滤波器类型 | 知道低通、高通、带通、陷波的区别,看频响曲线图 |
| 晚上 | scipy.signal | 跑通iirnotch和firwin的示例代码,观察输入输出 |
检验标准:能解释这份代码中三个滤波步骤的作用:
- 中值滤波 → 去除脉冲噪声(保护方波边沿)
- 50Hz陷波 → 去除市电干扰
- FIR带通 → 保留0.5-80Hz有效信号
直观理解(不用数学):
# 想象成一个筛子
zi = 上次筛完剩下的渣子(状态)
output, new_zi = lfilter(b, a, input, zi=old_zi) # 继续筛,保持连续性
Day 6:代码精读 + 修改实验
目标:逐行注释,做小改动看效果
| 时间 | 任务 | 方法 |
|---|---|---|
| 上午 | 注释ChannelBuffer | 给每行加中文注释,画环形缓冲示意图 |
| 下午 | 修改显示参数 | 改PLOT_LENGTH=500看效果,改UPDATE_INTERVAL看卡顿 |
| 晚上 | 添加打印调试 | 在get_recent()里打印idx和is_full状态 |
关键实验:
- 验证环形缓冲:在
append()里加print(f"ch{ch}写入位置{idx}"),观察满20000后是否回绕到0 - 观察滤波效果:在
_process_serial_data里对比val滤波前后的值
Day 7:综合实战 + 小功能开发
目标:独立添加一个功能,证明你学会了
任务(三选一):
| 难度 | 任务 | 考察点 |
|---|---|---|
| ⭐⭐ | 添加"保存截图"按钮 | 文件对话框、图像保存 |
| ⭐⭐⭐ | 添加第9个"参考通道"显示 | 数组扩展、UI布局 |
| ⭐⭐⭐⭐ | 实现简单的数据回放功能 | 文件读取、模拟实时播放 |
检验标准:功能可运行,能向他人解释实现原理
⚠️ 关键风险提示
1. 第3天最容易放弃
PyQt5的信号槽机制很反直觉,如果卡住了:
- 不要深究
pyqtSignal的底层实现 - 先死记硬背:“连接(
connect)一个函数,信号发时函数被执行”
2. 没有硬件怎么办?
这份代码依赖串口硬件,第4天后你需要:
# 临时修改:模拟数据模式
def run(self):
import random
while self.running:
# 模拟8通道正弦波
t = time.time()
values = [np.sin(2*np.pi*10*t + i) * 100 for i in range(8)]
raw = ",".join(map(str, values))
# ... 后续处理不变
3. 滤波器数学跳过
如果Day5看到公式头疼,直接跳过推导,记住:
iirnotch(50/250, 30)= 在50Hz处挖个坑firwin(61, [0.5/250, 80/250])= 只让0.5-80Hz通过
📊 每日时间分配建议
理论学习 : 代码实践 : 调试排错 = 4 : 4 : 2
不要做的事:
- ❌ 第一天就试图运行这份完整代码(会报各种依赖错误,打击信心)
- ❌ 死磕PyQt5的C++底层实现
- ❌ 试图推导FFT公式
要做的事:
- ✅ 每天结束前,把当天代码 push 到 GitHub
- ✅ 遇到报错先复制到Google/StackOverflow,30分钟解决不了就问
- ✅ 用Jupyter Notebook做NumPy实验(即时反馈)
🎯 最终检验清单
一周后,你应该能:
- 不看代码,画出程序架构图(三个线程的关系)
- 解释
ChannelBuffer如何解决"满缓冲后波形消失" - 修改采样率到1000Hz,并知道需要改哪几个参数
- 添加一个新按钮,控制某个通道的显示/隐藏
如果完成了以上四项,恭喜你,这份代码你已经**掌握70%**了!剩下的30%是信号处理工程和PyQt5高级技巧,需要项目实战积累。
需要我针对某一天的具体内容展开更详细的教程吗?比如Day3的PyQt5信号槽,我可以给你写3个渐进式示例代码。
Q2
你好,我现在已经完成了Day1的学习,学习好了面向对象编程中的类,对象,继承,多态的基本概念。但是看到相应教程后还有关于文件和异常的教程,这部分重要吗,对于理解这份代码有必要继续学习后面的教程吗,还是应该做一些习题巩固一下面向对象编程
你好!恭喜完成Day1!🎉
直接回答:文件和异常在这份代码中都有用到,但不需要深入,建议先巩固面向对象,Day2-3遇到时再查文档。
为什么这样建议?
这份代码中的文件/异常用法很简单
| 知识点 | 代码中的实际用法 | 复杂度 |
|---|---|---|
| 文件写入 | open(path, "w").write(data) |
⭐ 一行搞定 |
| 异常处理 | try...except Exception: pass |
⭐ 捕获忽略 |
| 队列 | queue.Queue |
⭐⭐ 线程安全容器 |
对比面向对象的重要性:
- 文件/异常:知道
open()和try语法就能用 - 面向对象:不懂
self、继承、方法重写,整份代码完全看不懂
你的下一步(推荐路线)
现在 ──→ 做3道OOP练习题巩固 ──→ 开始Day2 NumPy
↓ 跳过文件/异常专项学习
遇到时再查(见下方速查表)
📝 面向对象巩固练习(3道题)
题目1:环形缓冲类(贴近实战)
# 要求:实现一个简易版 ChannelBuffer
class SimpleBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.data = [0.0] * capacity # 用列表模拟
self.index = 0
self.is_full = False
def append(self, value):
"""写入一个值,满了就覆盖最旧的"""
pass # 你的代码
def get_all(self):
"""按时间顺序返回所有数据(从旧到新)"""
pass # 你的代码
# 测试
buf = SimpleBuffer(5)
for i in [1,2,3,4,5,6,7]: # 写入7个数,但容量只有5
buf.append(i)
print(buf.get_all()) # 期望输出 [3,4,5,6,7]
题目2:继承与重写
class BaseThread:
def __init__(self, name):
self.name = name
self.running = False
def start(self):
self.running = True
print(f"{self.name} 启动")
def run(self):
raise NotImplementedError("子类必须实现")
# 要求:继承BaseThread,实现一个打印数字的线程
class PrintThread(BaseThread):
pass # 你的代码
t = PrintThread("打印线程")
t.start()
t.run() # 期望:每秒钟打印一个递增数字
题目3:组合关系(这份代码的核心设计模式)
# 这份代码中:ADCPlotter "包含" SerialThread 和 LogWriterThread
# 这叫"组合"(Composition),比继承更常用
class Engine:
def start(self):
print("引擎启动")
class Car:
def __init__(self):
self.engine = Engine() # 组合:Car包含一个Engine
def start(self):
self.engine.start() # 委托调用
print("汽车启动")
# 要求:模仿上面的代码,实现 Window类包含 PlotWidget 和 ControlPanel
完成标准:能独立写出题目3,理解"组合优于继承"
📋 文件/异常速查表(遇到时看)
不需要现在学,收藏起来,写代码时复制粘贴:
# ========== 文件操作(这份代码里只有这一处)==========
with open("file.csv", "w", buffering=1024*1024) as f:
f.write("header\n")
f.write("data\n")
# with语句自动关闭文件,buffering设置1MB缓冲区减少磁盘写入
# ========== 异常处理(这份代码的套路)==========
try:
risky_operation() # 可能出错的代码
except SpecificError: # 捕获特定异常(推荐)
handle_it()
except Exception: # 捕获所有异常(这份代码常用,但生产环境慎用)
pass # 出错就忽略,保证程序不崩溃
# ========== 队列(线程间通信)==========
import queue
q = queue.Queue(maxsize=1000) # 设上限防内存溢出
try:
q.put_nowait(item) # 非阻塞放入,满了抛异常
except queue.Full:
pass # 满了就丢弃
try:
item = q.get(timeout=0.1) # 阻塞等待0.1秒
except queue.Empty:
pass # 超时继续循环
⚠️ 特别提醒:这份代码的"坑"
明天开始Day2前,先看一下这份代码的特殊语法,别被吓到:
# 1. 列表推导式(创建8个数组)
self.data = [np.zeros(capacity) for _ in range(8)]
# 等价于:
self.data = []
for _ in range(8):
self.data.append(np.zeros(capacity))
# 2. 解包赋值(同时给多个变量赋值)
self.b_notch, self.a_notch = iirnotch(w0, Q)
# 函数返回两个值,分别赋给左边两个变量
# 3. 星号解包(函数调用时展开列表)
values = [1.0, 2.0, 3.0]
func(*values) # 等价于 func(1.0, 2.0, 3.0)
如果上面三种语法不熟悉,花30分钟单独搜索练习,比看完整个教程效率高。
你的具体行动
今天剩余时间(假设2小时):
| 时间 | 行动 |
|---|---|
| 40min | 做上面3道OOP练习题 |
| 20min | 检查是否理解"组合"关系 |
| 60min | 预习Day2:安装numpy,试一下np.zeros和arr[-10:] |
明天开始Day2前,确认你能回答:
SimpleBuffer的get_all()为什么要处理is_full两种情况?Car和Engine是继承还是组合关系?- 这份代码中
ADCPlotter和SerialThread是什么关系?
如果都能答对,面向对象基础就扎实了,可以自信进入NumPy!
需要我提供3道题的参考答案,或者详细解释"组合"在这份代码中的具体应用吗?
Q3
class BaseThread:
def init(self, name):
self.name = name
self.running = False
def start(self):
self.running = True
print(f"{self.name} 启动")
def run(self):
raise NotImplementedError("子类必须实现")
要求:继承BaseThread,实现一个打印数字的线程
class PrintThread(BaseThread):
pass # 你的代码
t = PrintThread(“打印线程”)
t.start()
t.run() # 期望:每秒钟打印一个递增数字。这个题目我看不懂,可以帮我解释一下吗
好的!我来一步步拆解这道题,它考察的是继承 + 方法重写。
题目要求拆解
| 要求 | 含义 |
|---|---|
继承BaseThread |
PrintThread 是 BaseThread 的子类 |
实现一个打印数字的线程 |
重写 run() 方法,让它有实际功能 |
每秒钟打印一个递增数字 |
用 time.sleep(1) 实现延迟,用变量记录数字 |
先看父类 BaseThread 提供了什么
class BaseThread:
def __init__(self, name):
self.name = name # 保存名字
self.running = False # 运行标志
def start(self):
self.running = True # 标记为运行中
print(f"{self.name} 启动") # 打印启动信息
def run(self):
# 父类故意不实现,逼子类必须重写
raise NotImplementedError("子类必须实现")
关键设计:run() 用 raise 强制子类重写,这是模板方法模式。
你需要做什么?
class PrintThread(BaseThread):
# 1. 重写 __init__:添加自己的属性(计数器)
def __init__(self, name):
super().__init__(name) # 先调用父类的初始化
self.count = 0 # 新增:记录打印的数字
# 2. 重写 run():实现具体功能
def run(self):
while self.running: # 只要running为True就一直跑
print(self.count) # 打印当前数字
self.count += 1 # 数字加1
time.sleep(1) # 等1秒
完整代码 + 测试
import time
class BaseThread:
def __init__(self, name):
self.name = name
self.running = False
def start(self):
self.running = True
print(f"{self.name} 启动")
def run(self):
raise NotImplementedError("子类必须实现")
class PrintThread(BaseThread):
def __init__(self, name):
super().__init__(name) # 必须!否则name和running不存在
self.count = 0
def run(self):
while self.running:
print(self.count)
self.count += 1
time.sleep(1)
# 测试
t = PrintThread("打印线程")
t.start() # 输出: 打印线程 启动
t.run() # 开始打印 0, 1, 2, 3...(每秒一个)
关键概念图解
继承关系:
BaseThread (父类)
├── name, running (属性)
├── start() (已实现)
└── run() (强制重写) ← 抽象方法
PrintThread (子类)
├── 继承 name, running, start()
├── 新增 count (自己的属性)
└── 重写 run() (自己的实现)
常见错误
# ❌ 错误1:忘记 super().__init__()
class PrintThread(BaseThread):
def __init__(self, name):
self.count = 0
# 没调用super()!name和running不存在
def run(self):
print(self.name) # 报错!AttributeError: 'PrintThread' object has no attribute 'name'
# ❌ 错误2:没理解 while self.running
def run(self):
print(self.count) # 只打印一次就结束
self.count += 1
time.sleep(1)
# ✅ 正确:用循环持续运行
def run(self):
while self.running: # 检查标志位,为False时停止
...
对比这份EEG代码
这份代码里也是同样的模式:
class LogWriterThread(QtCore.QThread): # 继承QThread
def run(self): # ← 重写run(),线程启动后执行这里
while self.running:
# 具体逻辑...
模板:
class 子类(父类):
def __init__(self, 参数):
super().__init__(参数) # 继承父类的"基础设施"
self.自己的属性 = 值 # 添加自己的"特色"
def run(self):
# 实现自己的"业务逻辑"
pass
明白了吗?核心是:继承拿基础设施,重写实现个性化 🎯
Q4
我们的对话会留着吗,如果关机的话
方法3:开启新对话时带上下文
下次找我时,先发这段:
“我正在学习一份PyQt5+NumPy的EEG数据采集代码,已完成Day1面向对象学习,准备开始Day2 NumPy。之前的学习记录丢失了,请给我一份简化的7天计划回顾。”
我会快速恢复上下文。
DAY2
Q1
线程是什么意思,BaseThread,PrintThread是什么
线程 = 一个独立干活的人
BaseThread = 老板定的规矩(所有员工必须遵守)
PrintThread = 具体干活的员工(按规矩做事)
DAY3
Q1
我现在学到这一步了
import os
import sys
# 修复 Qt 插件路径
qt_path = os.path.join(os.path.dirname(sys.executable), 'Lib', 'site-packages', 'PyQt5', 'Qt5', 'plugins')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.join(qt_path, 'platforms')
from PyQt5 import QtWidgets, QtCore
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("按钮测试哈哈哈")
self.resize(800, 600)
# 显示数字的标签
self.label = QtWidgets.QLabel("0", self)
self.label.setGeometry(350, 250, 100, 60)
font = self.label.font()
font.setPointSize(30)
self.label.setFont(font)
self.count = 0
# ===== 关键:创建定时器 =====
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.update_count)
self.timer.start(1000) # 每秒更新
# 原按钮
self.button = QtWidgets.QPushButton("点我", self)
self.button.setGeometry(150, 100, 100, 40)
self.button.clicked.connect(self.on_click)
# 暂停按钮
self.pause_btn = QtWidgets.QPushButton("暂停", self)
self.pause_btn.setGeometry(150, 150, 100, 40)
self.pause_btn.clicked.connect(self.toggle_pause)
self.is_running = True
def update_count(self):
"""每秒执行:数字+1"""
self.count += 1
self.label.setText(str(self.count))
def on_click(self):
"""原按钮点击"""
self.button.setText("已点击")
print("按钮被点了!")
def toggle_pause(self):
"""暂停/继续切换"""
if self.is_running:
self.timer.stop()
self.pause_btn.setText("继续")
self.is_running = False
else:
self.timer.start(1000)
self.pause_btn.setText("暂停")
self.is_running = True
# 运行
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
可以运行成功但是代码看不太懂,我应该怎么做
KIMI
代码能运行但看不懂,这是正常的学习阶段!我来帮你分层理解。
三步读懂法(从外到内)
第一步:看骨架(30秒理解整体)
# 任何PyQt5程序都是这个结构
导入模块 → 修复路径 → 定义窗口类 → 创建应用 → 显示窗口 → 运行
你现在只需要记住:代码分三大块
| 块 | 作用 |
|---|---|
| 头部(1-9行) | 导入+修复环境 |
| 类(11-52行) | 定义窗口长什么样、有什么功能 |
| 尾部(54-58行) | 启动程序 |
第二步:看类的结构(2分钟)
class MyWindow(QtWidgets.QMainWindow): # 继承:我是一种窗口
def __init__(self): # 初始化:创建时自动执行
super().__init__() # 先调用父类的初始化
# ===== 1. 设置窗口本身 =====
self.setWindowTitle("...") # 标题
self.resize(800, 600) # 大小
# ===== 2. 创建控件 =====
self.label = QtWidgets.QLabel(...) # 数字显示
self.button = QtWidgets.QPushButton(...) # 点我按钮
self.pause_btn = QtWidgets.QPushButton(...) # 暂停按钮
# ===== 3. 连接信号槽 =====
self.timer.timeout.connect(self.update_count) # 定时→更新数字
self.button.clicked.connect(self.on_click) # 点击→改文字
self.pause_btn.clicked.connect(self.toggle_pause) # 点击→暂停/继续
# ===== 4. 启动定时器 =====
self.timer.start(1000)
# ===== 5. 定义回调函数 =====
def update_count(self): ... # 定时器到时执行
def on_click(self): ... # 按钮点击执行
def toggle_pause(self): ... # 暂停按钮执行
记忆口诀:设窗口 → 创控件 → 连信号 → 启动 → 写回调
第三步:挑一行最不懂的,问我
你现在告诉我:哪一行最看不懂?
比如:
super().__init__()是啥?self.timer.timeout.connect(...)什么意思?self.is_running为什么要记录状态?
选一行,我专门讲透它!
快速检验
不看代码,能说出这些吗?
| 问题 | 答案 |
|---|---|
| 怎么改窗口标题? | self.setWindowTitle("新标题") |
| 怎么让定时器每2秒触发? | self.timer.start(2000) |
| 怎么让按钮文字变红色? | (还没学,查文档或问我) |
能答出2个,说明你已经理解60%了!
下一步建议
| 你的选择 | 行动 |
|---|---|
| 想彻底搞懂这100行 | 挑一行问我,讲透为止 |
| 想继续学新东西 | 进入EEG代码,对比结构 |
| 今天累了 | 保存代码,明天继续 |
选哪个? 💪
Q2
self.timer.timeout.connect(self.update_count) # 定时→更新数字。这里是怎么链接的,这个语法我好像没学过
KIMI
这是 PyQt5 的信号槽机制,也是这份 EEG 代码的核心!我来彻底讲透它。
一句话解释
信号槽= “当A发生时,自动执行B”
| 角色 | 作用 | 例子 |
|---|---|---|
| 信号 (Signal) | 某个事件发生了 | 定时器时间到了、按钮被点击了 |
| 槽 (Slot) | 要执行的函数 | 更新数字、改变文字、暂停定时器 |
| connect() | 把信号和槽连起来 | 信号.connect(槽) |
拆解你的代码
self.timer.timeout.connect(self.update_count)
# │ │ │ │
# │ │ │ └─ 槽:要执行的函数(更新数字)
# │ │ └─ 连接方法
# │ └─ 信号:定时器超时(时间到了)
# └─ 定时器对象
执行流程:
定时器启动 → 等待1000ms → 发出timeout信号 → 自动调用update_count()
↑ ↓
└──────────── 循环重复 ←────────────────────────┘
对比:不用信号槽 vs 用信号槽
❌ 不用信号槽(传统写法,很难写)
while True:
time.sleep(1) # 卡住等待
update_count() # 手动调用
# 问题:卡在这里,窗口无法响应点击!
✅ 用信号槽(PyQt5写法,非阻塞)
self.timer.start(1000) # 不阻塞,后台运行
# 时间到了自动调用,窗口还能响应其他操作
关键优势:不卡住主程序,多个信号可以同时监听。
更多例子(这份 EEG 代码里的)
# 1. 按钮点击
self.button.clicked.connect(self.on_click)
# 点击按钮 → 执行 on_click()
# 2. 串口收到数据(EEG代码)
self.serial_thread.data_received.connect(self._on_serial_line)
# 串口收到一行 → 更新状态栏
# 3. 日志文件切换(EEG代码)
self.log_thread.log_rotated.connect(self._on_log_rotated)
# 生成新日志文件 → 显示通知
记忆模板
# 对象.信号.connect(函数)
self.谁.什么事件.connect(做什么)
# 实例
self.timer.timeout.connect(self.update) # 时间到→更新
self.button.clicked.connect(self.click) # 点击→执行
self.serial.data_received.connect(self.show) # 收到数据→显示
你现在能回答吗?
| 问题 | 答案 |
|---|---|
timeout 是什么? |
定时器的信号,时间到了就发 |
connect() 是什么? |
连接信号和槽的方法 |
为什么写 self.update_count 不加括号? |
传的是函数本身,不是执行结果 |
下一步
选择:
- ⭐ 巩固:改代码,让
update_count每2秒执行一次 - ⭐⭐ 挑战:添加新按钮,点击后
print("hello") - ⭐⭐⭐ 进阶:看 EEG 代码里的
data_received.connect(...)
选哪个? 💪
Q3
可以解释一下这一段吗 # 显示数字的标签
self.label = QtWidgets.QLabel("0", self)
self.label.setGeometry(350, 250, 100, 60)
font = self.label.font()
font.setPointSize(30)
self.label.setFont(font)
KIMI
这是 创建并设置标签控件,用来显示数字。逐行拆解:
逐行解释
self.label = QtWidgets.QLabel("0", self)
# │ │ │ │
# │ │ │ └─ 父窗口(放在这个窗口里)
# │ │ └─ 初始显示的文字:"0"
# │ └─ 标签控件类
# └─ 实例变量(后面要用它改文字)
self.label.setGeometry(350, 250, 100, 60)
# │ x y 宽 高
# └─ 设置位置和大小(像素)
font = self.label.font() # 获取当前字体
font.setPointSize(30) # 设置字号为30磅
self.label.setFont(font) # 应用新字体
可视化
窗口 (800×600)
│
└── 标签 (350,250) 位置
┌──────────┐
│ 0 │ ← 100宽 × 60高,30号字
└──────────┘
为什么分开写?
| 写法 | 效果 |
|---|---|
QLabel("0") |
默认小字,位置(0,0) |
+ setGeometry() |
移到中间,变大 |
+ setFont() |
字变大看得清 |
一句话总结
创建标签 → 放中间 → 字变大,让数字清晰可见。
快速修改
# 改初始文字
self.label = QtWidgets.QLabel("开始", self) # "0" → "开始"
# 改位置(移到左上角)
self.label.setGeometry(50, 50, 100, 60) # (350,250) → (50,50)
# 改字号
font.setPointSize(50) # 30 → 50,超大字
DAY4
Q1:为什么这段暂停代码是,当self.thread.paused为真,即暂停按钮被按下的时候执行self.thread.resume()继续的逻辑。而self.thread.paused为假时,执行self.thread.pause()暂行的逻辑呢?
def toggle_thread(self):
"""暂停/继续"""
if self.thread.paused:
self.thread.resume()
self.toggle_btn.setText("暂停")
else:
self.thread.pause()
self.toggle_btn.setText("继续")
KIMI
┌─────────────┐
│ 运行中 │ ← paused=False, 按钮显示"暂停"
│ (正在计数) │
└──────┬──────┘
│ 点击
▼
┌─────────────┐
│ 暂停 │ ← paused=True, 按钮显示"继续"
│ (冻结画面) │
└──────┬──────┘
│ 点击
└────────→ 回到"运行中"
场景:正在运行,用户点击"暂停"按钮
self.thread.paused = False # 当前状态
if self.thread.paused: # False,不执行
resume()
else: # 执行这里!
self.thread.pause() # 暂停线程
self.toggle_btn.setText("继续") # 按钮变"继续"
场景:已暂停,用户点击"继续"按钮
self.thread.paused = True # 当前状态
if self.thread.paused: # True,执行这里!
self.thread.resume() # 恢复线程
self.toggle_btn.setText("暂停") # 按钮变"暂停"
else:
pause()
更多推荐


所有评论(0)