我用Tkinter写了一个小程序,用不同的交换API打印出比特币的价格。我希望能够每x秒更新一次数据,但我无法弄清楚如何。我的猜测是我需要使用 .after( delay_ms, callback, args )
方法。
我是对的吗?
# Python 2.7.6. Calling exchange APIs.
import time, json, requests
from Tkinter import *
root = Tk()
def bitstampUSD():
bitstampUSDTick = requests.get( 'https://www.bitstamp.net/api/ticker/' )
return bitstampUSDTick.json()['last']
def btceUSD():
btceBtcTick = requests.get( 'https://btc-e.com/api/2/btc_usd/ticker' )
return btceBtcTick.json()['ticker']['last']
bitstampUSDLive = float( bitstampUSD() )
btceUSDLive = float( btceUSD() )
photo = PhotoImage( file = './images/blackcoin_500_small.gif' )
text1 = Text( root, height = 30, width = 31 )
text1.insert( END,'\n' )
text1.image_create( END, image = photo )
text1.pack( side = LEFT )
text2 = Text( root, height = 30, width = 60 )
scroll = Scrollbar( root, command = text2.yview )
text2.configure( yscrollcommand = scroll.set )
text2.tag_configure( 'bold_italics', font = ( 'Arial', 12, 'bold', 'italic' ) )
text2.tag_configure( 'bold', font = ( 'Arial', 12, 'bold' ) )
text2.tag_configure( 'big', font = ( 'Verdana', 20, 'bold' ) )
text2.tag_configure( 'medium', font = ( 'Verdana', 14, 'bold' ) )
text2.tag_configure( 'color', font = ( 'Tempus Sans ITC', 12, 'bold' ), foreground = '#476042' )
text2.tag_bind( 'follow', '<1>', lambda e, t = text2: t.insert( END, "Not now, maybe later!" ) )
text2.insert( END, '\nCrypto Price Ticker\n', 'big' )
text2.insert( END, "\nBitcoin Exchange Rates\n", "medium" )
text2.insert( END, "%.2f" % bitstampUSDLive )
text2.insert( END, " USD - Bitstamp\n" )
text2.insert( END, "%.2f" % btceUSDLive )
text2.insert( END, " USD - BTC-e\n" )
text2.pack( side = LEFT )
scroll.pack( side = RIGHT, fill = Y )
root.mainloop()
答案 0 :(得分:0)
.after()
方法会(可能)有所帮助,但要小心。Tkinter有一个非常强大的基于事件的内部和基于时间的调度服务。
在为混合GUI +非GUI活动设计基于时间和链接的调度方案时,应该小心谨慎。通常情况下,错链请求和/或不良调度逻辑可能会破坏/挂起Tkinter .mainloop()
调度程序。
近乎实时的系统设计的一些先前经验将有助于实现响应式GUI部分和#34;良好涂油&#34; /错误处理的非GUI部分,不会使Tkinter .mainloop()
和远程数据API提供程序失去稳定性。接口
.after( wms, TASK, *args ) # non-GUI, pure MVC-Controller time-based TASK scheduling
.after_idle( TASK, *args ) # IMPORTANT method to defer a TASK into MVC-idle-<STATE>
.after_cancel( aTaskID ) # IMPORTANT method to release a scheduled TASK
.update_idletasks() # IMPORTANT method to enforce to process TASK(s) ( if any ) deferred into an idle-tasks-queue before next MVC-idle-<STATE>
将Tkinter用于GUI层服务及其定时(调度)服务是一种常见的做法。
您的主要设计应将子任务分为事件驱动的控制模式(因为一旦您输入.mainloop()
,这是您仍然可以控制非GUI的唯一手段。 背景&#34;活动)。
当您将流程重新设计为分离&#34;层中的处理控制时。 (其中只有一个是已经def
- 背景的比特币交换API对话)你还需要提供手段&amp;控制更新到纯GUI - 代码的一部分(最有可能通过.StringVar()
,IntVar()
,.BooleanVar()
,.DoubleVar()
及其相关的触发.trace_variable()
工具
为了将非GUI异步事件作为代码驱动的Tkinter激励发送,还有一种强大的.event_generate( <eventNameId>, **args )
方法。
良好的实时设计的另一个标志是它将减少流量。作为一个简单的例子,状态完整的设计将避免在所有情况下重新绘制GUI,其中没有从外部数据源检索或从内部处理中生成相同的值。
总是有机会安排调用比特币交换API提供的值的同步刷新。在松散时序设计的情况下,这不会是一个麻烦。然而,走得更快将开始产生新的问题。一些技术(公平队列调度,传输延迟,响应端到端延迟)和一些非技术性。
对于远程报价流或报价发布API的处理,还可以记住官方条款和条款。适用于数据访问的条件。有些API有服务器端速率限制控制,有些没有,有些会默默地丢弃会话,有些会因为违反这些条款而将你列入黑名单。条件。
使用来自Exchange提供的报价流处理可以避免与条款和条款发生冲突。条件,在刷新率增加和/或时间紧张的情况下,如果在早期设计阶段没有得到应有的谨慎处理,每个条件都是常见的陷阱。
答案 1 :(得分:0)
是的,你是对的。如果要定期调用某个函数,请使用after
,然后让函数使用after
重新安排自己。
例如,如果您有一个名为getRates
的函数,并且您想要大约每15秒调用一次,那么您可以像这样编写它:
def getRates():
# put the code here to get the rates
...
# call this function again as soon as possible after 15 seconds has elapsed
root.after(15000, getRates)
第一次调用时,它将大约每15秒运行一次,直到您退出程序。
请记住,如果getRates
速度很慢(并且可能是从Web服务获取数据),那么您的GUI可能会有无响应的时刻。如果是这种情况,您可以在线程中运行getRates
函数,并将其写入线程安全队列。然后,您可以使用相同的技巧来从队列中提取最新数据并将其放入UI中。