定时器不适用于每n秒递归调用一次的方法

时间:2015-12-12 01:24:21

标签: python timer tkinter web-scraping

我制作了一个以比特币为代价的程序(通过使用beautifulsoup)并将其显示给用户。但是,我希望价格每30秒左右更新一次,所以我使用了"线程"模块并使用它的Timer。无论我输入timer参数多少秒,无论秒参数是多少,程序每秒调用自己5次。这是代码:

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

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:0)

我最初遇到的问题是threading.Timer方法无法正常工作。这是因为不正确使用threading.Timer()方法。而不是threading.Timer(5.0, updateBtcPrice(theLabel).start()我必须像threading.Timer(5.0, updateBtcPrice, theLabel).start()那样写它。 (由mfred提及)。这解决了一个问题,但揭示了很多其他问题。我现在得到一个RuntimeError main thread is not in main loop因为我的线程在第一次调用该方法后没有使用主循环。 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消失并且程序退出。除此之外,它现在有效。