gpt4 book ai didi

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

转载 作者:太空宇宙 更新时间:2023-11-03 18:55:28 27 4
gpt4 key购买 nike

我想访问 MyApp 类中定义的文本字段,以在 MyThread 类 def run(self) 中写入“步骤 2”:像这样的东西:

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

我对线程不太熟悉,因此我们将不胜感激

@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 对象。

解决此问题的方法有很多种,但我认为最简单的方法是创建一个普通的旧字符串,受锁保护,并在任何时候将 Text 的内容复制到该变量中变化。

在您的代码中,您刚刚获得了一个静态 Text 对象,您可以将其写入明确定义的位置,这使这变得很容易。如果它是动态变化的,您要么想要绑定(bind)一个事件,要么附加一个 StringVar跟踪它,但让我们在这里保持简单。

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说:

No other methods (except for the constructor) should be overridden in a subclass. In other words, only override the __init__() and run() methods of this class.

默认的 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()

关于python - 如何从不同的类访问GUI类属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17355231/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com