如果用户被告知应用程序实际正在做什么,我想创建一个框。 我创建了一个Text Widget,用于显示我在应用程序关键点编写的print语句,以便它可以作为一个日志框。
为此,我将stdout重定向到小部件本身的子类"已升级"用我在另一篇文章中看到的写方法。 这确实有效,但我注意到一个问题,使得这个盒子几乎没用。
如果您运行代码,您可以看到句子一次出现。对我来说更令人费解的是,不仅仅是" wait2"功能 看起来很复杂,但即使是调用函数的打印语句,#34; wait1"也显示在过程结束时。
为什么会这样?如果执行时框中显示的语句怎么办?
from Tkinter import *
import sys
import time
root = Tk()
class addwritemethod(object):
def __init__(self, widget):
self.widget = widget
def write(self, string):
self.widget.configure(state="normal")
self.widget.insert("end", string)
self.widget.see("end")
self.widget.configure(state="disabled")
def wait1():
print "Can you see me?"
wait2()
def wait2():
for i in range(10):
time.sleep(5)
print "Long time no see!"
t_go = Button(root, text= "Start!", width =12, command = wait1)
t_go.pack(side = LEFT, padx = 20, pady = 20)
tlog = Text(root,wrap = "word")
tlog.pack(side="top", fill="both", expand=True)
tlog.configure(state="disabled")
sys.stdout = addwritemethod(tlog)
mainloop()
编辑:我要感谢回答我的人并道歉:我没有提供所有必需的信息。
我在测试代码中加上time.sleep()只是为了向你展示行为。在实际应用程序中,我通过ssh与Paramiko传输文件,我不使用sleep()。
也许我选择了错误的例子,但结果是一样的,所有的印刷品都在同一时刻出现。
答案 0 :(得分:1)
当你打电话给sleep
时,应用程序正是这样做的:它会睡觉。当它处于睡眠状态时,它无法更新显示屏。作为一般规则,您不应该在GUI中调用sleep
。
话虽如此,快速解决方法是确保在将内容打印到日志后调用update
,以便Tkinter有机会更新屏幕。在self.widget.update_idletasks()
的末尾添加write
(重新绘制屏幕被视为"空闲任务")。
这不是一个正确的解决方案,但它足以说明为什么数据不会出现。正确的修复涉及不调用睡眠。 stackoverflow上有很多与此相关的示例,几乎所有这些示例都涉及使用after方法。
答案 1 :(得分:0)
您还可以使用内置日志记录模块来实现
的目标创建一个框,告知用户应用程序实际执行的操作...一个日志框。
我在下面创建了一个示例,用于说明使用Tkinter记录到GUI控件的概念。下面的示例根据您的要求记录到文本控件,但您可以通过将类MyHandlerText
替换/复制到其他处理程序类(如MyHandlerLabel
,MyHandlerListbox
),将日志消息发送到其他GUI组件,等(为处理程序类选择自己的名称)。然后,您将拥有一个感兴趣的各种GUI控件的处理程序。大" a-ha"对我来说,时刻是python.org鼓励的模块级getLogger概念。
import Tkinter
import logging
import datetime
# this item "module_logger" is visible only in this module,
# (but you can create references to the same logger object from other modules
# by calling getLogger with an argument equal to the name of this module)
# this way, you can share or isolate loggers as desired across modules and across threads
# ...so it is module-level logging and it takes the name of this module (by using __name__)
# recommended per https://docs.python.org/2/library/logging.html
module_logger = logging.getLogger(__name__)
class simpleapp_tk(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.grid()
self.mybutton = Tkinter.Button(self, text="ClickMe")
self.mybutton.grid(column=0,row=0,sticky='EW')
self.mybutton.bind("<ButtonRelease-1>", self.button_callback)
self.mytext = Tkinter.Text(self, state="disabled")
self.mytext.grid(column=0, row=1)
def button_callback(self, event):
now = datetime.datetime.now()
module_logger.info(now)
class MyHandlerText(logging.StreamHandler):
def __init__(self, textctrl):
logging.StreamHandler.__init__(self) # initialize parent
self.textctrl = textctrl
def emit(self, record):
msg = self.format(record)
self.textctrl.config(state="normal")
self.textctrl.insert("end", msg + "\n")
self.flush()
self.textctrl.config(state="disabled")
if __name__ == "__main__":
# create Tk object instance
app = simpleapp_tk(None)
app.title('my application')
# setup logging handlers using the Tk instance created above
# the pattern below can be used in other threads...
# ...to allow other thread to send msgs to the gui
# in this example, we set up two handlers just for demonstration (you could add a fileHandler, etc)
stderrHandler = logging.StreamHandler() # no arguments => stderr
module_logger.addHandler(stderrHandler)
guiHandler = MyHandlerText(app.mytext)
module_logger.addHandler(guiHandler)
module_logger.setLevel(logging.INFO)
module_logger.info("from main")
# start Tk
app.mainloop()