python-3.x Tkinter -当使用MVC模式中的上下文管理器以编程方式更改时,鼠标光标不会明显改变

siv3szwd  于 2023-06-07  发布在  Python
关注(0)|答案(1)|浏览(86)

Tkinter -在MVC模式下使用上下文管理器更改时,鼠标光标在屏幕上不会更改。

我试着用MVC模式和tkinter库做了一个应用程序。为了使控制器状态更易于管理,我尝试使用context manager和@property decorator来管理enum类型的控制器状态。此外,我想改变光标类型根窗口时,控制器的状态改变,但由于某种原因,光标没有改变预期的。下面我附上了代表这个问题的示例代码。我的PC上有Windows 10。我还尝试在Controller类的状态设置器方法中更改游标类型,但也不起作用。

from enum import Enum
import tkinter as tk

# enum with controller states
class APP_STATE(Enum):
    BUSY = 1
    IDLE = 0

# main window of the application    
class MainWnd(tk.Tk):
    def __init__(self, controller):
        self.controller = controller
        super(MainWnd, self).__init__()
        
        # initialize
        self.title('TEST')
        
        button = tk.Button(self, text="Start Long Running Event", command=self.controller.create_document)
        button.pack()

# context manager used to manage states of the controller
class ControllerManager:
    def __init__(self, parent):
        self.parent = parent

    def __enter__(self):
        self.parent.state = APP_STATE.BUSY
        print("cursor is {0}".format(self.parent.appInstance.cget("cursor")))
        self.parent.appInstance.config(cursor="wait")
        self.parent.appInstance.update()
        print("cursor should be wait and is {0}".format(self.parent.appInstance.cget("cursor")))

    def __exit__(self, exc_type, exc_value, traceback):
        self.parent.state = APP_STATE.IDLE
        print("cursor is {0}".format(self.parent.appInstance.cget("cursor")))
        self.parent.appInstance.config(cursor="")
        print("cursor should be default and is {0}".format(self.parent.appInstance.cget("cursor")))

        
class Controller(object):
    def __init__(self):
        self._state = APP_STATE.IDLE # state
        self.appInstance = MainWnd(self) # app window

        
    @property
    def state(self):
        return self._state
    
    @state.setter
    def state(self, new_value):
        if new_value == APP_STATE.BUSY:
            self._state = new_value
        elif new_value == APP_STATE.IDLE:
            self._state = new_value
    #######################################
    def run(self):
        self.appInstance.mainloop()
        
    #######################################      
    def create_document(self): # some long running task
        with ControllerManager(self):
            import time
            time.sleep(2)
            print("Task completed") 
     
cl = Controller()
cl.run()

上面提到的代码显示了光标的变化,但在执行代码时,我在屏幕上看不到它。我怀疑有什么东西阻塞了tkinter的config()和update()函数,但我不知道如何改变它使其工作。另外,我用类似的代码做了一些测试:

import tkinter as tk

class CursorManager:
    def __init__(self, widget, cursor_type):
        self.widget = widget
        self.cursor_type = cursor_type

    def __enter__(self):
        self.widget.config(cursor=self.cursor_type)

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.widget.config(cursor="")  # Reset cursor to default

# Create a Tkinter root window
root = tk.Tk()

def execute_task():
    # Use the CursorManager as a context manager to change the cursor
    with CursorManager(button, "watch"):
        # Simulate a long-running task
        import time
        time.sleep(2)
        print("Task completed")

# Create a button that triggers the task execution
button = tk.Button(root, text="Execute Task", command=execute_task)

# Display the button and start the Tkinter event loop
button.pack()
root.mainloop()

在这种情况下,它像预期的那样工作,但我不完全理解为什么它工作得很好,而前面的例子不工作。我错过了一些东西,但不知道它到底是什么。

jobtbby3

jobtbby31#

好吧,所以我尝试了线程,并提出了工作解决方案。不幸的是,它不是太优雅,但我没有更好的想法如何使它工作。我仍然不完全理解为什么我的问题中的示例1不起作用,而示例2起作用。所以,我的解决方案是在单独的线程中使用上下文管理器,那么tkinter mainloop显然不会被阻塞。下面是工作示例:

from enum import Enum
import tkinter as tk
import threading
from contextlib import contextmanager

class APP_STATE(Enum):
    BUSY = 1
    IDLE = 0
    
class MainWnd(tk.Tk):
    def __init__(self, controller):
        self.controller = controller
        super(MainWnd, self).__init__()
        
        # initialize
        self.title('TEST')
        
        button = tk.Button(self, text="Start Long Running Event", command=self.controller.create_document)
        button.pack()

class ControllerManager:
    def __init__(self, widget, cursor):
        self.cursor = cursor
        self.widget = widget
        
    def __enter__(self):
        print("ok")
        self.widget.config(cursor=self.cursor)
        
    def __exit__(self, exc_type, exc_value, traceback):
        self.widget.config(cursor="")

class Controller(object):
    def __init__(self):
        self._state = APP_STATE.IDLE
        self.appInstance = MainWnd(self)

    #######################################
    def run(self):
        self.appInstance.mainloop()
        
    #######################################      
    def create_document(self):
        def long_ops():
            with ControllerManager(self.appInstance, "wait"):
                import time
                time.sleep(4)
                print("Task completed")   
        work_thread = threading.Thread(target=long_ops)
        work_thread.start()

相关问题