【问题标题】:Is it possible to pass a function to a class and have it be stored so that it can be called in response to an event?是否可以将函数传递给类并存储它以便可以调用它以响应事件?
【发布时间】:2020-08-31 04:13:22
【问题描述】:

我正在尝试将键盘控制添加到我用来控制机器人的 Tkinter 程序中。我的目标是让某些键在按下时运行运动命令,在松开时运行停止命令。

this Stack Overflow post 的帮助下,我已经成功地完成了这项工作,但是这种方法需要我将函数调用写入自定义 KeyTracker 类中,用于对键进行去抖动,这很不方便。当我创建 KeyTracker 类的新实例时,是否有某种方法可以将我希望密钥调用的函数作为参数传递给它?例如,对于 W 键,这将类似于以下任一行:

# To bind events to the press and release of the W key:
self.key_tracker_W = KeyTracker('W', 'w', self.key_pressed_W, self.key_released_W)
top.bind( "<KeyPress-w>",   self.key_tracker_W.report_key_press   )
top.bind( "<KeyRelease-w>", self.key_tracker_W.report_key_release )

# To bind events to the press and release of the w key:
self.key_tracker_W = KeyTracker('W', 'w')
top.bind( "<KeyPress-w>",   self.key_tracker_W.report_key_press(self.key_pressed_W) )
top.bind( "<KeyRelease-w>", self.key_tracker_W.report_key_release(self.key_released_W) )

这样,我可以根据需要多次使用该类,而不必为每次新的使用添加代码。

我做了一个按比例缩小的示例程序。窗口打开后,如果按 W、A、S 或 D 键,则会将相应的文本打印到 shell。

##############################################################################################
## IMPORT LIBRARIES:

import tkinter as tk
import time
import threading

##############################################################################################
## Debouncer For Keyboard Events:
class KeyTracker():
    #=====================================================================================
    """
    The code for this class was modified from an example found at:
    https://stackoverflow.com/questions/27215326/tkinter-keypress-keyrelease-events
    
    The KeyPress event gets called twice, but that isn't a problem for my needs.
    """
    #=====================================================================================
    def __init__(self, KEY_U, KEY_L):
        self.key_U             = KEY_U
        self.key_L             = KEY_L
        self.last_press_time   = 0
        self.last_release_time = 0
    #=====================================================================================
    def is_pressed(self):
        return time.time() - self.last_press_time < 0.1
    #=====================================================================================
    def report_key_press(self, event):
        if (event.keysym == self.key_U or event.keysym == self.key_L):
            if not( self.is_pressed() ):
                print("Key Pressed")
                
                # I don't like having to have this if statement here:
                if   (self.key_U == 'W'):  app.key_pressed_W()
                elif (self.key_U == 'A'):  app.key_pressed_A()
                elif (self.key_U == 'S'):  app.key_pressed_S()
                elif (self.key_U == 'D'):  app.key_pressed_D()
                # I would rather be calling a function that was passed to the class as an argument.
            
            self.last_press_time = time.time()
    #=====================================================================================
    def report_key_release(self, event):
        if (event.keysym == self.key_U or event.keysym == self.key_L):
            self.timer = threading.Timer(0.1, self.report_key_release_callback, args = [event])
            self.timer.start()
    #=====================================================================================
    def report_key_release_callback(self, event):
        if not(self.is_pressed()):
            print("Key Released")
            
            # I don't like having to have this if statement here:
            if   (self.key_U == 'W'):  app.key_released_W()
            elif (self.key_U == 'A'):  app.key_released_A()
            elif (self.key_U == 'S'):  app.key_released_S()
            elif (self.key_U == 'D'):  app.key_released_D()
            # I would rather be calling a function that was passed to the class as an argument.
        
        self.last_release_time = time.time()
    #=====================================================================================
##############################################################################################
## Class For Creating The Main Window:
class CLASS_Window_Main(tk.Frame):
    #=====================================================================================
    def __init__(self, master = None):
        #-------------------------------------------------------------------------
        tk.Frame.__init__(self, master)  # super().__init__(master)
        #-------------------------------------------------------------------------
        self.grid( sticky = tk.N + tk.S + tk.E + tk.W )   # Resize window contents when window is resized.
        self.GUI_Create_Widgets()
        #-------------------------------------------------------------------------
    #=====================================================================================
    def GUI_Create_Widgets(self):
        #-------------------------------------------------------------------------
        ## Create Window:
        top = self.winfo_toplevel()
        top.rowconfigure(    0, weight = 1 )
        top.columnconfigure( 0, weight = 1 )
        #-------------------------------------------------------------------------
        ## Bind Window Resized Event:
        top.bind( "<Configure>", self.GUI_Window_Resized )
        #-------------------------------------------------------------------------
        ## Bind Keyboard Events:
        
        # Bind events to the W key:
        self.key_tracker_W = KeyTracker('W','w')
        top.bind( "<KeyPress-w>",   self.key_tracker_W.report_key_press   )
        top.bind( "<KeyPress-W>",   self.key_tracker_W.report_key_press   )
        top.bind( "<KeyRelease-w>", self.key_tracker_W.report_key_release )
        top.bind( "<KeyRelease-W>", self.key_tracker_W.report_key_release )
        
        
        # Bind events to the A key:
        self.key_tracker_A = KeyTracker('A','a')
        top.bind( "<KeyPress-a>",   self.key_tracker_A.report_key_press   )
        top.bind( "<KeyPress-A>",   self.key_tracker_A.report_key_press   )
        top.bind( "<KeyRelease-a>", self.key_tracker_A.report_key_release )
        top.bind( "<KeyRelease-A>", self.key_tracker_A.report_key_release )
        
        
        # Bind events to the S key:
        self.key_tracker_S = KeyTracker('S','s')
        top.bind( "<KeyPress-s>",   self.key_tracker_S.report_key_press   )
        top.bind( "<KeyPress-S>",   self.key_tracker_S.report_key_press   )
        top.bind( "<KeyRelease-s>", self.key_tracker_S.report_key_release )
        top.bind( "<KeyRelease-S>", self.key_tracker_S.report_key_release )
        
        
        # Bind events to the D key:
        self.key_tracker_D = KeyTracker('D','d')
        top.bind( "<KeyPress-d>",   self.key_tracker_D.report_key_press   )
        top.bind( "<KeyPress-D>",   self.key_tracker_D.report_key_press   )
        top.bind( "<KeyRelease-d>", self.key_tracker_D.report_key_release )
        top.bind( "<KeyRelease-D>", self.key_tracker_D.report_key_release )
        
        #-------------------------------------------------------------------------
    #=====================================================================================
    ## Run This Function When The Window Is Resized:
    def GUI_Window_Resized(self, event):
        print("The window was resized")
    #=====================================================================================
    ## Functions To Be Run In Response To Keys:
    
    def key_pressed_W(self):
        print("KEYPRESS = W")
    
    def key_released_W(self):
        print("KEYRELEASE = W")
    
    
    
    def key_pressed_A(self):
        print("KEYPRESS = A")
    
    def key_released_A(self):
        print("KEYRELEASE = A")
    
    
    
    def key_pressed_S(self):
        print("KEYPRESS = S")
    
    def key_released_S(self):
        print("KEYRELEASE = S")
    
    
    
    def key_pressed_D(self):
        print("KEYPRESS = D")
    
    def key_released_D(self):
        print("KEYRELEASE = D")
    
    #=====================================================================================
##############################################################################################
## RUN PROGRAMME:

print("Programme Start")

# Run the Tkinter programme:
root = tk.Tk()
root.geometry("200x200")          # Set the default size of the window.
app = CLASS_Window_Main()         # Create CLASS_Window_Main instance.
app.master.title("Window Title")  # Set the title of the window.
app.mainloop()                    # Start CLASS_Window_Main.
##############################################################################################

我知道我可以按原样使用我现在拥有的东西,但我内心的完美主义者一直在唠叨我,说他希望这门课“多才多艺”。您能提供的任何帮助将不胜感激。

【问题讨论】:

  • 我不知道我是否理解问题,但您可以将函数作为任何其他值传递给其他函数或类。您已经将函数传递给 bind() 。在 Tkinter 中,您还可以在 tkButton(... command=function_name)root.after(time, function_name) 中传递函数,通常它被称为 callback
  • @furas 好的,但是如何运行已作为参数传递的函数?假设我们采用函数def report_key_press(self, event, callback):。如何让程序运行在 report_key_press 中的特定点传递给 callback 的函数?
  • 您使用 () 运行它 - callback() - 顺便说一句。简单示例:display = print 及以后的display("Hello World")
  • @furas 谢谢!我内心的完美主义者现在很幸福。

标签: python tkinter


【解决方案1】:

是否可以将函数传递给类并将其存储起来,以便在响应事件时调用它?

是的,你可以像这样传递函数。

在您的示例中,您只需将回调保存在 __init__ 方法中,然后再使用它。

class KeyTracker():
    def __init__(self, KEY_U, KEY_L, press_callback, release_callback):
        self.press_callback = press_callback
        self.release_callback = release_callback
        ...

您以后可以像使用任何函数一样使用它:

class KeyTracker():
    def report_key_release_callback(self, event):
        # call the stored callback
        self.release_callback(event)

【讨论】:

    猜你喜欢
    • 2017-05-20
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    • 2015-06-21
    相关资源
    最近更新 更多