python - 是否可以将函数传递给类并存储它以便可以调用它以响应事件?
问题描述
我正在尝试将键盘控制添加到我用来控制机器人的 Tkinter 程序中。我的目标是让某些键在按下时运行运动命令,并在释放时运行停止命令。
在这篇 Stack Overflow 帖子的帮助下,我设法让它工作了,但是这种方法需要我将函数调用写入用于对键进行去抖动的自定义 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.
##############################################################################################
我知道我可以按原样使用我现在拥有的东西,但我内心的完美主义者一直在唠叨我,说他希望这门课“更通用”。您能提供的任何帮助将不胜感激。
解决方案
是否可以将函数传递给类并存储它以便可以调用它以响应事件?
是的,你可以像这样传递函数。
在您的示例中,您只需将回调保存在__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)
推荐阅读
- rust - 为什么可以在 String 上调用 Colorize trait 方法,而 String 不实现 Colorize
- javascript - FetchError:对 http://localhost:4006/graphql 的请求失败,原因:套接字挂起
- android - Flutter AppBar 上方有多余空间
- pyspark - 将字符串转换为日期时间时的 Pyspark 问题
- python - Python Selenium - 如何点击动态生成的href页面链接
- node.js - 如何为我的 PRN 项目使用 Traefik 制作 Https,现在它返回 Bad Gateway 502
- sql - uuid_generate_v4 扩展在命令行中不可用
- c# - 将页面路由添加到旧版 C#/ASPX Web 应用程序
- ruby-on-rails - 使用 devise-jwt 进行测试时添加 Bearer 令牌的更简单方法?
- python - 条件为真时开始动画