Python 计时器不适用于每n秒递归调用自身的方法

Python 计时器不适用于每n秒递归调用自身的方法,python,timer,tkinter,web-scraping,Python,Timer,Tkinter,Web Scraping,我制作了一个程序(使用beautifulsoup)计算比特币的价格,并将其显示给用户。但是,我希望价格每30秒左右更新一次,所以我使用了“线程”模块并使用了它的计时器。无论我在timer参数中输入多少秒,程序都会每秒调用自己5次,无论seconds参数是什么。代码如下: from bs4 import BeautifulSoup from tkinter import * import requests import threading root = Tk() def bitcoinPric

我制作了一个程序(使用beautifulsoup)计算比特币的价格,并将其显示给用户。但是,我希望价格每30秒左右更新一次,所以我使用了“线程”模块并使用了它的计时器。无论我在timer参数中输入多少秒,程序都会每秒调用自己5次,无论seconds参数是什么。代码如下:

from bs4 import BeautifulSoup
from tkinter import *
import requests
import threading

root = Tk()

def bitcoinPrice():
    url = 'http://www.coindesk.com/price/'
    r = requests.get(url)
    soup = BeautifulSoup(r.text, 'html.parser')
    btcPrice = soup.find('div', attrs=
    {'class' : 'bpi-value bpiUSD'}
                 )
    btcIndexDown = soup.find('span', attrs=
    {'class' : 'bpi-change changeUSD data-down'}
                     )
    btcIndexUp = soup.find('span', attrs=
    {'class' : 'bpi-change changeUSD data-up'}
                     )
    if(btcIndexDown is None):

        return btcPrice.text + "(" + btcIndexUp.text + ")"

    else:

        return btcPrice.text + "(" + btcIndexDown.text + ")"




def bitcoinLabel():

    theLabel = Label(root, text = "-")
    theLabel.config(font = 'bold')
    theLabel.pack()
    updateBtcPrice(theLabel)




def updateBtcPrice(theLabel):
    if '-' in theLabel.cget("text"):
        theLabel.config(fg = 'red')
    else:
        theLabel.config(fg = 'green')

    theLabel.configure(text = bitcoinPrice())
    root.update()
    print("Hello")
    threading.Timer(5.0, updateBtcPrice(theLabel)).start()

try:
    bitcoinLabel()
except:
    pass

我认为问题在于你没有正确使用定时器接口。请尝试:

threading.Timer(5.0, updateBtcPrice, theLabel).start()

不同之处在于,与您的代码相反,在安排事件时,此版本实际上并不调用updateBtcPrice。

我认为问题在于您错误地使用了计时器接口。请尝试:

threading.Timer(5.0, updateBtcPrice, theLabel).start()

不同之处在于,与您的代码相反,此版本在计划事件时并不实际调用updateBtcPrice。

我最初遇到的问题是threading.Timer方法无法正常工作。这是因为threading.Timer()方法使用不当。而不是
threading.Timer(5.0,updatebcprice(theLabel).start()
我必须像
threading.Timer(5.0,updatebcprice,theLabel).start()那样编写它(由mfred提到)。这解决了一个问题,但也解决了很多其他问题。我现在遇到了一个运行时错误
主线程不在主循环中
,因为我的线程在第一次调用该方法后没有使用主循环。J.F Sebastian指出了这一点,所以我通过全局声明和初始化一些变量并将我的主循环放入全局处理 以下是迄今为止的计划:

from bs4 import BeautifulSoup
from tkinter import *
import requests
import threading
from threading import Event, Thread
from timeit import default_timer
from tkinter import messagebox

def bitcoinPrice():
    url = 'http://www.coindesk.com/price/'
    r = requests.get(url)
    soup = BeautifulSoup(r.text, 'html.parser')
    btcPrice = soup.find('div', attrs=
    {'class' : 'bpi-value bpiUSD'}
                 )
    btcIndexDown = soup.find('span', attrs=
    {'class' : 'bpi-change changeUSD data-down'}
                     )
    btcIndexUp = soup.find('span', attrs=
    {'class' : 'bpi-change changeUSD data-up'}
                     )

    if(btcIndexDown is None):
        return btcPrice.text + "(" + btcIndexUp.text + ")"

    else:
        return btcPrice.text + "(" + btcIndexDown.text + ")"

def bitcoinLabel():
    try:
        theLabel.configure(text = bitcoinPrice())
        if '-' in theLabel.cget("text"):
            theLabel.config(fg = 'red')
        else:
            theLabel.config(fg = 'green')

        tm = threading.Timer(30, bitcoinLabel).start()

        if root is None:
            tm.stop()
    except RuntimeError:
    sys.exit(0)

def on_closing():
    root.destroy()
    sys.exit(0)

root = Tk()
theLabel = Label(root, text = "")
theLabel.config(font = 'bold')
theLabel.pack()

bitcoinLabel()
root.wm_protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()

虽然代码的功能正在发挥作用(我最初遇到的问题已经解决),但仍有改进的余地。可以做的一个改进是在退出tkinter gui时立即终止线程。相反,现在需要30秒(线程每次调用该方法之间的间隔)在它意识到gui消失和程序退出之前。除此之外,它现在可以工作了。

我最初遇到的问题是threading.Timer方法不能正常工作。这是因为threading.Timer()方法使用不当。而不是
threading.Timer(5.0,updateBtcPrice(theLabel).start()
我必须像
threading.Timer(5.0,updateBtcPrice,theLabel.start())那样编写它。
(由mfred提到)。这解决了一个问题,但也解决了很多其他问题。我现在遇到了一个运行时错误
主线程不在主循环中
,因为我的线程在第一次调用该方法后没有使用主循环。J.F Sebastian指出了这一点,所以我通过全局声明和初始化一些变量并将我的主循环放入全局处理 以下是迄今为止的计划:

from bs4 import BeautifulSoup
from tkinter import *
import requests
import threading
from threading import Event, Thread
from timeit import default_timer
from tkinter import messagebox

def bitcoinPrice():
    url = 'http://www.coindesk.com/price/'
    r = requests.get(url)
    soup = BeautifulSoup(r.text, 'html.parser')
    btcPrice = soup.find('div', attrs=
    {'class' : 'bpi-value bpiUSD'}
                 )
    btcIndexDown = soup.find('span', attrs=
    {'class' : 'bpi-change changeUSD data-down'}
                     )
    btcIndexUp = soup.find('span', attrs=
    {'class' : 'bpi-change changeUSD data-up'}
                     )

    if(btcIndexDown is None):
        return btcPrice.text + "(" + btcIndexUp.text + ")"

    else:
        return btcPrice.text + "(" + btcIndexDown.text + ")"

def bitcoinLabel():
    try:
        theLabel.configure(text = bitcoinPrice())
        if '-' in theLabel.cget("text"):
            theLabel.config(fg = 'red')
        else:
            theLabel.config(fg = 'green')

        tm = threading.Timer(30, bitcoinLabel).start()

        if root is None:
            tm.stop()
    except RuntimeError:
    sys.exit(0)

def on_closing():
    root.destroy()
    sys.exit(0)

root = Tk()
theLabel = Label(root, text = "")
theLabel.config(font = 'bold')
theLabel.pack()

bitcoinLabel()
root.wm_protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()

虽然代码的功能正在发挥作用(我最初遇到的问题已经解决),但仍有改进的余地。可以做的一个改进是在退出tkinter gui时立即终止线程。相反,现在需要30秒(线程每次调用该方法之间的间隔)在它意识到gui消失和程序退出之前。除此之外,它现在可以工作了。

Hi@mfred。它运行一次之后,我得到以下错误:
self.function(*self.args,**self.kwargs)TypeError:updatebcprice()参数*之后必须是序列,而不是标签
@DanZoe:您可能需要传递
[theLabel]
而不是这里的标签。您好,@J.F.Sebastian it no说以下
运行时错误:主线程不在主循环中
@DanZoe:这是一个单独的问题。您的代码中有多个问题。@mfred的回答修复了其中一个问题,但不是所有问题。@J.F.Sebastian您好,我完全修复了这个问题。我放置了主循环在全局范围内创建标签,然后将threading.timer方法放入bitcoinLabel中。它现在每30秒更新一次。我只是有一个问题。因为线程每30秒运行一次,所以在我单击tkinter窗口上的X按钮后,线程需要30秒才能退出。有没有办法杀死所有线程最近当退出tkinter窗口时?Hi@mfred。它运行一次,然后我得到以下错误:
self.function(*self.args,**self.kwargs)TypeError:updateBtcPrice()参数在*之后必须是一个序列,而不是Label
@DanZoe:您可能需要传递
[theLabel]
而不是这里的标签。您好,@J.F.Sebastian it no说以下
运行时错误:主线程不在主循环中
@DanZoe:这是一个单独的问题。您的代码中有多个问题。@mfred的回答修复了其中一个问题,但不是所有问题。@J.F.Sebastian您好,我完全修复了这个问题。我放置了主循环在全局范围内创建标签,然后将threading.timer方法放入bitcoinLabel中。它现在每30秒更新一次。我只是有一个问题。因为线程每30秒运行一次,所以在我单击tkinter窗口上的X按钮后,线程需要30秒才能退出。有没有办法杀死所有线程最近退出tkinter窗口时?要运行不在tkinter中阻塞的函数,可以。要运行阻塞函数,如
bitcointPrice()
,您可以。避免从其他线程调用tkinter的方法,将您的代码安排为仅在主线程中调用GUI代码。一种安全的方法是使用队列在线程之间通信,以便只有GUI线程运行GUI代码。要运行不在tkinter中阻塞的函数,您可以。要运行阻塞函数,如
bitcointPrice()
,您可以。避免从其他线程调用tkinter的方法,将您的代码安排为仅在主线程中调用GUI代码。一种安全的方法是使用队列在线程之间进行通信,以便只有GUI线程运行GUI代码,您的方法是错误的。如果调用
tm.cancel(),您可以解决直接出现的问题。
(在W上使用
tm=Timer(…);tm.start()