使用tkinter文本小部件

时间:2015-05-15 18:29:28

标签: python tkinter

如果用户被告知应用程序实际正在做什么,我想创建一个框。 我创建了一个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()。 也许我选择了错误的例子,但结果是一样的,所有的印刷品都在同一时刻出现。

2 个答案:

答案 0 :(得分:1)

当你打电话给sleep时,应用程序正是这样做的:它会睡觉。当它处于睡眠状态时,它无法更新显示屏。作为一般规则,您不应该在GUI中调用sleep

话虽如此,快速解决方法是确保在将内容打印到日志后调用update,以便Tkinter有机会更新屏幕。在self.widget.update_idletasks()的末尾添加write(重新绘制屏幕被视为"空闲任务")。

这不是一个正确的解决方案,但它足以说明为什么数据不会出现。正确的修复涉及调用睡眠。 stackoverflow上有很多与此相关的示例,几乎所有这些示例都涉及使用after方法。

答案 1 :(得分:0)

您还可以使用内置日志记录模块来实现

的目标
  

创建一个框,告知用户应用程序实际执行的操作...一个日志框。

我有同样的需求,并且集中在herehere提供的建议上。

我在下面创建了一个示例,用于说明使用Tkinter记录到GUI控件的概念。下面的示例根据您的要求记录到文本控件,但您可以通过将类MyHandlerText替换/复制到其他处理程序类(如MyHandlerLabelMyHandlerListbox),将日志消息发送到其他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()