Python中如何使用多线程实现tkinter界面数字的动态跳动?
import tkinter
import requests
import threading
from bs4 import BeautifulSoup
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36', 'Referer':'https://wallstreetcn.com/live/global' }
def getprice(url):
html=requests.get(url, headers=headers).text
soup=BeautifulSoup(html,'lxml')
price=soup.find('div',class_='price lt').get_text() #<div class="price lt" data-v-13b642d2="">12438.00</div>
return price
while True:
g=getprice('https://wallstreetcn.com/markets/indices/GER30INDEX')
top = tkinter.Tk()
label = tkinter.Label(top,text=g)
label.pack()
tkinter.mainloop()
top.wm_attributes('-topmost',1)
Python中如何使用多线程实现tkinter界面数字的动态跳动?
import tkinter as tk
import threading
import time
import queue
class DynamicNumberApp:
def __init__(self, root):
self.root = root
self.root.title("动态数字跳动")
# 创建队列用于线程间通信
self.queue = queue.Queue()
# 创建显示标签
self.number_label = tk.Label(root, text="0", font=("Arial", 48))
self.number_label.pack(pady=20)
# 创建控制按钮
self.start_button = tk.Button(root, text="开始跳动", command=self.start_jumping)
self.start_button.pack(side=tk.LEFT, padx=10)
self.stop_button = tk.Button(root, text="停止跳动", command=self.stop_jumping, state=tk.DISABLED)
self.stop_button.pack(side=tk.RIGHT, padx=10)
# 线程控制标志
self.running = False
self.worker_thread = None
# 定期检查队列
self.root.after(100, self.process_queue)
def start_jumping(self):
"""启动数字跳动线程"""
if not self.running:
self.running = True
self.start_button.config(state=tk.DISABLED)
self.stop_button.config(state=tk.NORMAL)
# 创建并启动工作线程
self.worker_thread = threading.Thread(target=self.number_jump_worker, daemon=True)
self.worker_thread.start()
def stop_jumping(self):
"""停止数字跳动"""
self.running = False
self.start_button.config(state=tk.NORMAL)
self.stop_button.config(state=tk.DISABLED)
def number_jump_worker(self):
"""工作线程函数:生成随机数字"""
import random
while self.running:
# 生成随机数字
new_number = random.randint(1, 100)
# 通过队列发送到主线程
self.queue.put(new_number)
# 控制跳动速度
time.sleep(0.1)
def process_queue(self):
"""处理队列中的消息(在主线程中执行)"""
try:
while True:
# 非阻塞获取队列中的数字
new_number = self.queue.get_nowait()
# 更新界面(在主线程中操作GUI)
self.number_label.config(text=str(new_number))
# 标记任务完成
self.queue.task_done()
except queue.Empty:
pass
# 继续定期检查队列
self.root.after(100, self.process_queue)
def on_closing(self):
"""窗口关闭时的清理"""
self.running = False
if self.worker_thread and self.worker_thread.is_alive():
self.worker_thread.join(timeout=1)
self.root.destroy()
# 创建主窗口
root = tk.Tk()
app = DynamicNumberApp(root)
# 设置关闭事件处理
root.protocol("WM_DELETE_WINDOW", app.on_closing)
# 启动主循环
root.mainloop()
核心要点:
- 线程分工:工作线程负责生成数字,主线程负责更新界面
- 线程安全:使用
queue.Queue进行线程间通信,避免直接操作GUI组件 - 主线程控制:所有GUI操作都在主线程中执行
- 资源管理:通过
running标志控制线程生命周期
一句话建议: 用队列做线程通信,主线程只负责更新界面。
印象里不能用 while true
会阻塞界面的绘制
对的
当然是多线程,在线程中完成对主界面的更新工作
getprice 操作放进新线程里,每次需要绘制的时候执行一次,修改对应的 label 的 text 属性即可
import tkinter
import requests
import threading
from bs4 import BeautifulSoup
import queue #import Queue
headers = {‘User-Agent’: ‘Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36’,
‘Referer’:‘https://wallstreetcn.com/live/global’
}
def getprice(url):
html=requests.get(url, headers=headers).text
soup=BeautifulSoup(html,‘lxml’)
price=soup.find(‘div’,class_=‘price lt’).get_text() #<div class=“price lt” data-v-13b642d2="">12438.00</div>
q.put(price)
return price
while True:
q = queue.Queue()
p = threading.Thread(target=getprice,args=(‘https://wallstreetcn.com/markets/indices/GER30INDEX’,))
p.start()
qprice=q.get()
top = tkinter.Tk()
label = tkinter.Label(top,text=qprice)
label.pack()
tkinter.mainloop()
top.wm_attributes(’-topmost’,1)
写了个 不知道这个爬虫网页价格放到 tk 里随价格变化而变化,实现了没。
您意思是绘制的 tk 也放进一个线程了吗? 6 楼那个怎么样?刚才试了可以显示价格,不知道堵塞没,但是功能太简单了。还想实现这些:价格有变化绘制 tk,没变化不用管。
from tkinter import *
import threading
import time
root = Tk()
label1 = Label(root,text=‘0’)
label1.pack()
def getPrice():
return time.time()
def threadGetPrice():
while(True):
label1[‘text’] = getPrice()
time.sleep(1)
t = threading.Thread(target=threadGetPrice,args=(),name=‘thread-refresh’)
t.setDaemon(True)
t.start()
root.mainloop()
大概是这么个意思
如果想实现价格变化时绘制(修改 text ),可以在 threadGetPrice()中进行判断
while 循环一次:构建队列,获取价格并放进队列,然后取出价格并绘制图形界面。
想实现价格有变化绘制图形,没变化不要管看来有点难度(针对我这种小白哈)
因为每次循环都是一个新的队列一个, 新的价格啊
如果只是为了修改界面上的价格,完全没必要使用队列。
简单的思路是,有一个线程,在 while true:中获取最新价格,然后看是否改变了决定要不要更新主界面对应的 label 标签的 text 值;循环最后加一个 time.sleep(1)每次获取操作间隔 1 秒
另外:新建线程的操作不要放在 while 中。。更新操作交给新创建的线程来做
import time
import tkinter
import requests
import threading
from bs4 import BeautifulSoup
headers = {‘User-Agent’: ‘Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.110 Safari/537.36’,
‘Referer’:‘https://wallstreetcn.com/live/global’
}
def getPrice(url):
(space)html=requests.get(url, headers=headers).text
(space)soup=BeautifulSoup(html,‘lxml’)
(space)price=soup.find(‘div’,class_=‘price lt’).get_text()
(space)return price
root = tkinter.Tk()
label1 = tkinter.Label(root,text=‘init…’)
label1.pack()
def threadGetPrice():
(space)while(True):
(space)(space)label1[‘text’] = getPrice(‘https://wallstreetcn.com/markets/indices/GER30INDEX’)
(space)(space)time.sleep(5)
t = threading.Thread(target=threadGetPrice,args=(),name=‘thread-refresh’)
t.setDaemon(True)
t.start()
root.mainloop()
感觉到了进程 线程 这里 明显到了 python 的易学难精的地方了。。。
感觉大山来了 当初类 实例之类的马马虎虎地啃下来算是能看懂别人写的代码
但是进程 线程 锁之类的东西 完全看不懂 自猜: 好像搞懂 python 的运行机制才能玩转这些东西了 :)
有点理解那个 while true 了 交作业了 只能等到周一看行不行了 这样就能桌面显示了 
这个 setDaemon 的作用并不是让代码循环执行。没有 while true,threadGetPrice()里的代码将只执行一次;
setDaemon(True)的作用:父线程启动了子线程,当父线程结束,子线程跟着同时被 kill;
setDaemon(False)的作用:父线程启动了子线程,当父线程结束,会等待子线程结束后,主线程才会结束;
如果把 setDaemon 改为 false,关闭窗体后 threadGetPrice ()继续执行,但此时主窗体已经不存在了,执行更新操作时会报错;
为啥会发生这个,具体原因我也没有确切答案。
猜测可能的原因是,tkinter 窗口的线程并不是手动创建的线程的父线程,关闭 tkinter 窗体的操作并不会等待手动创建的线程结束,所以就有问题了;
表现是:如果 setDaemon ( False ),关闭窗体后进程并不会立刻终止,而是等子线程完成后才会终止。


