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界面数字的动态跳动?

16 回复

不太会 md 格式 找了个网站贴了代码进去 然后所见所得修改了下贴进来 确实是好看了点
昨天试了大半天 看了多进程 多线程 锁 队列 但是还是写不出来


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()

核心要点:

  1. 线程分工:工作线程负责生成数字,主线程负责更新界面
  2. 线程安全:使用queue.Queue进行线程间通信,避免直接操作GUI组件
  3. 主线程控制:所有GUI操作都在主线程中执行
  4. 资源管理:通过running标志控制线程生命周期

一句话建议: 用队列做线程通信,主线程只负责更新界面。

印象里不能用 while true

会阻塞界面的绘制

哦 那是不是把 getprice 放进多线程里?通过队列取出这个返回值,但是昨天试了一天还是不成功;或者其他方法怎么实现呐

当然是多线程,在线程中完成对主界面的更新工作

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()

嗯请问下 t.setDaemon(True) 这个是不是更新线程的操作,查了下说这是设置守护线程,如果是更新线程的话,为什么 def threadGetPrice()函数下面还要加一句 while True 呐,

感觉到了进程 线程 这里 明显到了 python 的易学难精的地方了。。。
感觉大山来了 当初类 实例之类的马马虎虎地啃下来算是能看懂别人写的代码
但是进程 线程 锁之类的东西 完全看不懂 自猜: 好像搞懂 python 的运行机制才能玩转这些东西了 :)

有点理解那个 while true 了 交作业了 只能等到周一看行不行了 这样就能桌面显示了

这个 setDaemon 的作用并不是让代码循环执行。没有 while true,threadGetPrice()里的代码将只执行一次;
setDaemon(True)的作用:父线程启动了子线程,当父线程结束,子线程跟着同时被 kill;
setDaemon(False)的作用:父线程启动了子线程,当父线程结束,会等待子线程结束后,主线程才会结束;

如果把 setDaemon 改为 false,关闭窗体后 threadGetPrice ()继续执行,但此时主窗体已经不存在了,执行更新操作时会报错;

为啥会发生这个,具体原因我也没有确切答案。
猜测可能的原因是,tkinter 窗口的线程并不是手动创建的线程的父线程,关闭 tkinter 窗体的操作并不会等待手动创建的线程结束,所以就有问题了;
表现是:如果 setDaemon ( False ),关闭窗体后进程并不会立刻终止,而是等子线程完成后才会终止。

回到顶部