【问题标题】:Using Thread in TKinter GUI在 TKinter GUI 中使用线程
【发布时间】:2021-12-11 17:23:25
【问题描述】:

我正在开发一个 Tkinter 桌面应用程序项目。当我需要使用运行后端代码的线程时,我开始跌跌撞撞。 我知道为了跨线程共享变量,我们应该使用全局变量。下面是最小的代码。

obj = None

class Frame:
    def __init__(self, frame):
        self.middle_frame = frame
        self.start_button = ttk.Button(self.middle_frame, text='Start', command=self.start)
        self.start_button.grid(row=0, column=0)
        self.stop_button = ttk.Button(self.middle_frame, text='Stop', command=self.stop)
        self.stop_button.grid(row=0, column=1)
        self.stop_button.config(state='disabled')

    def start(self):
        self.thread = threading.Thread(target=self.start_connection)
        self.thread.start()
        self.start_button.config(state='disabled')
        self.stop_button.config(state='normal')
    
    def start_connection(self):
        global obj
        obj = MainManager() # Starts the Backend Loop


    def stop(self):
        global obj
        obj.close_connection() # Want to break the loop here
        self.thread.join()


        self.stop_button.config(state='disabled')
        self.start_button.config(state='normal')
        

运行此代码时,我得到 obj.close_connection() AttributeError:'NoneType' object has no attribute 'close_connection'。但我期待 obj 成为 MainManager() 的对象。 我哪里错了?帮我解决这个问题。

【问题讨论】:

  • 您检查过obj 究竟是什么吗?显然它不是你想的那样。
  • 我解决了。 @Oli 的答案正是我出错的地方

标签: python multithreading tkinter


【解决方案1】:

您的代码问题与tkinter 或实际上与多线程无关。问题是MainManager__init__ 方法内开始循环,如下所示:

class MainManager:
    def __init__(self):
        self.alive = True

        # this part runs until stopped
        self.start_doing_stuff()

    def start_doing_stuff(self):
        while self.alive:
            sleep(1)
            print('doing stuff')

    def stop(self):
        self.alive = False

这是一个代码 sn-p 与您的错误类似:

from threading import Thread
from time import sleep

obj = None


class MainManager:
    def __init__(self):
        self.alive = True

        self.start_doing_stuff()

    def start_doing_stuff(self):
        while self.alive:
            sleep(1)
            print('doing stuff')

    def stop(self):
        self.alive = False


def test():
    global obj
    print('about to assign to my_global')
    obj = MainManager()
    print('assigned to my_global')


print(f'{obj=}')
t = Thread(target=test)
t.start()
sleep(3)
print(f'{obj=}')
obj.stop()
t.join()

因为在调用stop 方法之前__init__ 方法不会终止,所以obj = MainManager() 语句中对MainManager() 的调用永远不会终止,因此obj 不会被分配给。这意味着当obj.stop()被调用时,obj仍然是None

这可以解决我让__init__ 方法终止,并将长时间运行的代码放入稍后调用的单独方法中:

from threading import Thread
from time import sleep

obj = None


class MainManager:
    def __init__(self):
        self.alive = True

    def start_doing_stuff(self):
        while self.alive:
            sleep(1)
            print('doing stuff')

    def stop(self):
        self.alive = False


def test():
    global obj
    print('about to assign to my_global')
    obj = MainManager()
    print('assigned to my_global')
    obj.start_doing_stuff()


print(f'{obj=}')
t = Thread(target=test)
t.start()
sleep(3)
print(f'{obj=}')
obj.stop()
t.join()

【讨论】:

  • 非常感谢!!!这是我的错误。现在它工作正常!
【解决方案2】:

你导入MainManager了吗,你运行start_connection()了吗?

此外,您将分配委托给线程。您确定这会影响与主线程相同的obj 吗?也许主线程保持“无”并且委托更改副本中的值。添加一个日志或打印语句并确保为obj 分配了MainManager() 对象并打印该对象的内存位置。在主类中也打印obj 变量的内存位置。检查是否相同。

这是打印变量的内存地址的方式:

print(hex(id(your_variable)))

【讨论】:

  • 为python 3添加括号,否则与python2相同
  • 是的,我启动了连接,但我是从 __init__() 启动后端循环(无限)。因此,变量 obj 没有分配给类 (MainManager())。现在,我更改了代码以启动连接并在另一个函数中循环,现在它可以工作了。我已经通过打印变量的 id 弄清楚了。谢谢大佬!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-27
  • 2021-12-16
  • 2019-06-12
  • 1970-01-01
  • 2014-02-23
相关资源
最近更新 更多