Python 如何从不同的类访问GUI类属性?
我想访问MyApp类中定义的文本字段,以便从MyThread类def run(self)中写入“步骤2”: 诸如此类: self.text.insert(1.0,“第二步”) 代码: 我对穿线不太熟悉,因此非常感谢您的帮助 @abarnet使用您的答案,我这样做了,但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):
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()