2013-06-28 61 views
1

我想要访问在MyApp的类中定义的文本字段从MyThread的类DEF运行(个体)在它的步骤2'写: 事端那样:如何从不同的类访问GUI类属性?

self.text.insert(1.0,“步骤二“)

代码:

import threading 
import time 
from Tkinter import * 


class MyThread(threading.Thread): 

    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     time.sleep(5) 
     #i want to access the text here 

class MyApp(Frame): 


    def __init__(self, master): 
     Frame.__init__(self, master) 

     self.my_widgets() 

    def my_widgets(self): 
     self.grid() 

     self.my_button = Button(self, text="Start my function", 
              command=self.my_function) 
     self.my_button.grid(row=0, column=0) 

     self.text = Text(self, width = 60, height = 5, wrap = WORD) 
     self.text.grid(row = 10, column = 0, columnspan = 2, sticky = W) 

    def my_function(self): 
     self.text.insert(1.0,"Step one") 

     mt = MyThread() 
     mt.start() 


root = Tk() 
root.title("Client") 
root.geometry("500x500") 
app = MyApp(root) 

root.mainloop() 

我不是很熟悉线程,因此任何帮助将apreciated

@abarnet使用你的答案下来我这样做,但GUI没有响应WHI它正在等待连接

from Tkinter import * 
from mtTkinter import * 
import socket 
import sys 


class Application(Frame): 

    def __init__(self, master): 

     Frame.__init__(self, master) 
     self.grid() 
     self.create_widgets() 

    def create_widgets(self): 

     self.submit_button = Button(self, text='start', command = self.my_function) 
     self.submit_button.grid(row = 2, column = 0, sticky = W) 

     self.text = Text(self, width = 60, height = 5, wrap = WORD) 
     self.text.grid(row = 10, column = 0, columnspan = 2, sticky = W) 

    def my_function(self): 

     mt = MyThread() 
     mt.start() 


class MyThread(threading.Thread): 

    def __init__(self): 
     threading.Thread.__init__(self) 


    def start(self): 
     app.text.insert(6.0,'server started') 
     app.text.update_idletasks() 

     app.text.insert(6.0,'\n'+'waiting for client') 

     self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.s.bind(('',1090)) 
     self.s.listen(1) 



     self.sc, address = self.s.accept() 
     address = str(address) 

     self.instruction = Label(self, text = 'got connection from '+address) 
     self.instruction.grid(row=7, column = 0, columnspan = 2, sticky = W) 
     self.instruction.update_idletasks() 

     msg = self.sc.recv(1024) 
     s=msg.find('.') 
     g=msg[s:] 
     i=1 
     f = open('file_'+ str(i)+g,'wb') #open in binary 
     i=i+1 
     while (True):  
      l = self.sc.recv(1024) 

      while (l): 

       f.write(l) 
       f.flush() 
       l = self.sc.recv(1024) 
     f.close()  

     self.sc.close() 

     self.s.close() 


root = Tk() 
root.title("Server") 
root.geometry("500x250") 
app = Application(root) 

root.mainloop() 
+0

你的新的问题是简单的;你只是混淆了'Thread.start'和'Thread.run'。查看我编辑的答案以获取完整的详细信息 – abarnert

+0

它工作得很好,谢谢。 如果我想在MyThread类中执行命令的GUI类中添加另一个按钮我应该怎么做 我想按下一个按钮关闭套接字即执行命令self.sc.close –

+0

如果要控制工作线程以任何方式来自主线程,您需要某种同步或消息传递。但这确实是一个新问题,在评论领域中解释太困难了,所以请开一个关于它的新问题。 – abarnert

回答

1

Tkinter不是线程安全的。这意味着你不能从另一个线程访问Tkinter对象。

有很多方法可以解决这个问题,但我认为最简单的方法是创建一个普通的旧字符串,并用锁保护,并在其变化时将该内容复制到该变量中。

在您的代码中,您刚刚获得了一个静态的Text对象,您可以在明确定义的位置写入该对象,这使得这很容易。如果它动态变化,你可能想绑定一个事件,或者附加一个StringVartrace它,但让我们在这里保持简单。

class MyThread(threading.Thread): 

    def __init__(self): 
     threading.Thread.__init__(self) 

    def run(self): 
     time.sleep(5) 
     with app.text_lock: 
      text_value = app.text_value 

class MyApp(Frame): 
    def __init__(self, master): 
     Frame.__init__(self, master) 
     self.text_value = '' 
     self.text_lock = threading.Lock() 
     self.my_widgets() 

    def my_widgets(self): 
     # ... 

    def my_function(self): 
     self.text.insert(1.0,"Step one") 

     with self.text_lock: 
      self.text_value = "Step one" + self.text_value 
     # ... 

# ... 

不同的选项是使用mtTkinter,它通过拦截所有GUI方法并使它们通过一个队列给你一个线程安全的Tkinter。如果你不明白这意味着什么,这是神奇的。您的MyThread.run方法只能以主线程中的代码相同的方式访问app.text


此外,值得注意的是,您的代码可能没有任何好的理由使用线程。

如果你想一些代码,在5秒左右运行,而不是创建一个线程5秒入睡,只是问的Tkinter至约5秒钟运行:

class MyApp(Frame): 
    # ... 

    def my_function(self): 
     self.text.insert(1.0,"Step one") 

     self.after(5000, self.my_thing_to_do_later) 

    def my_thing_to_do_later(self): 
     # same code you would put in MyThread.run, but now 
     # you're on the main thread, so you can just access 
     # self.text directly 

当然,像任何其他事件处理程序,如果你想在5秒后做的事情需要很长时间或需要阻止或不管,这是行不通的。但我怀疑你的代码是这种情况。


在你的代码的新版本时,几乎得到了一切权利,除了一两件事:

你把你的后台线程功能MyThread.start而不是MyThread.run

由于the documentation说:

没有其他的方法(除了构造函数)应该在子类中重写。换句话说,只能覆盖此类的__init__()run()方法。

默认的start方法是一个函数,它启动一个新线程,然后新线程执行self.run()。所以,如果你覆盖self.run,那么你放在那里的任何东西都会在后台线程中运行。但是如果你重载start,而不是创建一个新的线程,它会执行你在那里实现的任何东西 - 在你的情况下,它是一个阻塞函数。

所以,只需将start重命名为run,并且一切正常。


作为一个便笺,有一件事帮助我看看我是否意外地阻止了主线程,是在角落里加了一个小时钟。例如,在App,添加此方法:

def update_clock(self): 
    self.clock.configure(text=time.strftime('%H:%M:%S')) 
    self.after(1000, self.update_clock) 

然后,在create_widgets结束时,添加这些行:

self.clock = Label(self) 
self.clock.grid(row=2, column=1) 
self.update_clock() 
+0

非常感谢,但它mtTkinter可用已经或应该我安装它第一 –

+0

如果我想从贵组的条目中的值到其他类我应该怎么做 –

+0

您必须安装mtTkinter;它不是内置在Python中的。 – abarnert