我一直在慢慢学习Tkinter和面向对象的编程,但我已经将自己编程到这个角落。请原谅我对这个缺乏批判性思考,但我已经问过我认识的每个人谁比我更了解python,我们无法在这里找到有效的解决方案。
我有一个gui应用程序正在进行,旨在允许用户输入股票代码,为每个符号创建新标签,然后定期更新每个标签。 (有点像一个非常基本的etrade应用程序或其他东西)。我发现没有gui这样做很容易,因为我可以说:
while True:
sPrice = get_stock_price(s)
print sPrice
但是我将我的get_stock_price(s)函数绑定到一个按钮,该按钮产生一个子框架和一个包含在其中的标签。我遇到的问题是标签不会更新。朋友建议仅添加另一种方法来更新标签,但是我知道如何不断更新标签的唯一方法是做一个
while True:
# get new price
# update the label
# time.sleep(~1 minute?)
这导致我的gui窗口永远冻结和旋转。 我一直在阅读与这种特殊情况有关的所有其他线索,我看到了许多不同的建议;不要在主线程中调用sleep,不要使用root.update,使用事件,调用root.something.after(500,function),我试图实现。我留下的是一个代码的frankenstein仍将检索我的股票价值,但不会更新它们,以及一些我不知道如何更新或在我的代码中调用的方法。
我希望的是(可能很长,我知道。抱歉!)解释我做错了什么,以及如何解决它的建议。我真的希望自己理解并解决这个问题,但只要解释它们,代码解决方案就会很棒。
提前非常感谢!!!
PS:到目前为止,这是我的代码:
from Tkinter import *
import urllib
import re
import time
class MyApp(object):
def __init__(self, parent):
self.myParent = parent
self.myContainer1 = Frame(parent)
self.myContainer1.pack()
self.createWidgets()
button1 = Button(self.myContainer1, command = self.addStockToTrack)
self.myContainer1.bind("<Return>", self.addStockToTrack)
button1.configure(text = "Add Symbol")
button1.pack()
def createWidgets(self):
# title name
root.title("Stock App")
# creates a frame inside myContainer1
self.widgetFrame = Frame(self.myContainer1)
self.widgetFrame.pack()
# User enters stock symbol here:
self.symbol = Entry(self.widgetFrame)
self.symbol.pack()
self.symbol.focus_set()
def addStockToTrack(self):
s = self.symbol.get()
labelName = str(s) + "Label"
self.symbol.delete(0, END)
stockPrice = get_quote(s)
self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice))
self.labelName.pack()
self.myContainer1.after(500, self.get_quote)
def updateStock(self):
while True:
labelName = str(s) + "Label"
stockPrice = get_quote(s)
self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice))
self.labelName.pack()
time.sleep(10)
def get_quote(symbol):
base_url = 'http://finance.google.com/finance?q='
content = urllib.urlopen(base_url + symbol).read()
m = re.search('id="ref_\d*_l".*?>(.*?)<', content)
if m:
quote = m.group(1)
else:
quote = 'Not found: ' + symbol
return quote
root = Tk()
myapp = MyApp(root)
root.mainloop()
答案 0 :(得分:4)
您已经运行了无限循环,因此您不应该尝试添加另一个循环。相反,您可以使用after
方法使函数每隔一段时间重复调用一次。在您的情况下,您可以替换它:
def updateStock(self):
while True:
labelName = str(s) + "Label"
stockPrice = get_quote(s)
self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice))
self.labelName.pack()
time.sleep(10)
......用这个:
def updateStock(self):
labelName = str(s) + "Label"
stockPrice = get_quote()
self.labelName = Label(self.myContainer1, text = s.upper() + ": " + str(stockPrice))
self.labelName.pack()
self.after(10000, self.updateStock)
这将获得引用,添加标签,然后安排自己在10秒(10,000毫秒)内再次调用。
但是,我怀疑你想要每隔10秒创建一个新标签,对吗?最终窗口将填满标签。相反,您可以创建一次标签,然后在每次迭代中更新标签。例如,在init中创建一次self.label
,然后在循环中创建:
self.labelName.configure(text=s.upper() + ": " + str(stockPrice))
答案 1 :(得分:0)
您正在寻找线程。 将要运行的事件放在另一个线程中。见这个例子:
import thread, time
def myfunc(a1,a2):
while True:
print a1,a2
time.sleep(1)
thread.start_new_thread(myfunc,("test","arg2")
tkroot.mainloop()
现在你有一个与Tkinter窗口一起运行的函数,它每秒打印一次args。
编辑:我不知道为什么这么多的选票。 Tkinter可以很好地处理线程,我已经多次使用过这个技巧而没有问题。见这个例子:下载10 MB文件并将进度记录到Tkinter窗口。
没有线程:
import urllib2,thread
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.pack(fill=tk.BOTH, expand=1)
canvas = tk.Canvas(self)
self.text = canvas.create_text(18,18,anchor=tk.W,font="Purisa",text="Status: Press start to download...")
but=tk.Button(text="Start",command=self.start)
canvas.create_window((270,18),window=but)
canvas.pack(fill=tk.BOTH, expand=1)
self.canvas=canvas
def start(self):
#thread.start_new_thread(
self.download("http://ipv4.download.thinkbroadband.com/10MB.zip","10mb.zip")
#)
def onEnd(self):
self.canvas.itemconfig(self.text, text="Status: done!")
def download(self,url,file_name):
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)
file_size_dl = 0
block_sz = 1024*50 #50 kb
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = r"[%3.2f%%]" % (file_size_dl * 100. / file_size)
self.canvas.itemconfig(self.text,text="Status: downloading..."+status)
f.close()
self.onEnd()
def main():
root = tk.Tk()
root.resizable(0,0)
ex = Example(root)
root.geometry("300x70")
root.mainloop()
main()
窗口冻结,直到下载完成。
使用主题:
import urllib2,thread
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.pack(fill=tk.BOTH, expand=1)
canvas = tk.Canvas(self)
self.text = canvas.create_text(18,18,anchor=tk.W,font="Purisa",text="Status: Press start to download...")
but=tk.Button(text="Start",command=self.start)
canvas.create_window((270,18),window=but)
canvas.pack(fill=tk.BOTH, expand=1)
self.canvas=canvas
def start(self):
thread.start_new_thread(
self.download("http://ipv4.download.thinkbroadband.com/10MB.zip","10mb.zip")
)
def onEnd(self):
self.canvas.itemconfig(self.text, text="Status: done!")
def download(self,url,file_name):
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)
file_size_dl = 0
block_sz = 1024*50 #50 kb
while True:
buffer = u.read(block_sz)
if not buffer:
break
file_size_dl += len(buffer)
f.write(buffer)
status = r"[%3.2f%%]" % (file_size_dl * 100. / file_size)
self.canvas.itemconfig(self.text,text="Status: downloading..."+status)
f.close()
self.onEnd()
def main():
root = tk.Tk()
root.resizable(0,0)
ex = Example(root)
root.geometry("300x70")
root.mainloop()
main()
不冻结,文本正常更新。