Python 如何从不同的类访问GUI类属性?

Python 如何从不同的类访问GUI类属性?,python,multithreading,user-interface,Python,Multithreading,User Interface,我想访问MyApp类中定义的文本字段,以便从MyThread类def run(self)中写入“步骤2”: 诸如此类: self.text.insert(1.0,“第二步”) 代码: 我对穿线不太熟悉,因此非常感谢您的帮助 @abarnet使用您的答案,我这样做了,但GUI在等待连接时没有响应 from Tkinter import * from mtTkinter import * import socket import sys class Application(Frame):

我想访问MyApp类中定义的文本字段,以便从MyThread类def run(self)中写入“步骤2”: 诸如此类:

self.text.insert(1.0,“第二步”)

代码:

我对穿线不太熟悉,因此非常感谢您的帮助

@abarnet使用您的答案,我这样做了,但GUI在等待连接时没有响应

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()

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

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

在您的代码中,您只需要在定义良好的位置写入一个静态的
文本
对象,这使得这很容易。如果它是动态变化的,您可能希望绑定一个事件,或者附加一个
StringVar
trace
来跟踪它,但是让我们在这里保持简单

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
        # ...

# ...

另一个选项是使用,它通过截取所有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

正如上面所说:

子类中不应重写任何其他方法(构造函数除外)。换句话说,只重写此类的
\uuu init\uu()
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()

非常感谢,但是它已经可以使用了,或者我应该先安装它吗?如果我想从gui类中的条目中获取值到另一个类,我应该做什么?您必须安装mtTkinter;它不是Python内置的。我不理解你的第二个评论。但是…除了GUI对象之外,您可以在线程之间共享任何内容,就像我共享
文本\u值一样。你只需要一个
Lock()
,每个人在修改或访问变量时都会抓住它,除此之外,它与在单个线程中使用变量是一样的;您刚刚混淆了
线程。启动
线程。运行
。有关详细信息,请参阅我编辑的答案。效果很好,谢谢。如果我想在GUI类中添加另一个按钮来执行MyThread类中的命令,我应该做什么我想按一个按钮来关闭套接字,即执行命令self.sc.close如果你想从主线程以任何方式控制工作线程,你需要某种同步或消息传递。但这确实是一个新问题,而且在评论栏中很难解释,所以请打开一个新问题。
self.clock = Label(self)
self.clock.grid(row=2, column=1)
self.update_clock()