【问题标题】:GUI Button hold down - tkinterGUI按钮按住 - tkinter
【发布时间】:2018-01-08 07:05:05
【问题描述】:

我正在尝试在 python 中做一个 GUI 来控制我的机器人汽车。我的问题是如何执行确定按住按钮的功能。我想在按住按钮时移动汽车,并在松开按钮时停止汽车。

from Tkinter import * 

hold_down = False 
root = Tk()

def button_hold(event):
      hold_down=true
      while hold_down== True: 
               print('test statement')
               hold_down = root.bind('<ButtonRelease-1>',stop_motor)

def stop_motor(event):
       hold_down= False
       print('button released')

button = Button(root, text ="forward")
button.pack(side=LEFT)
root.bind('<Button-1>',button_forward)
root.mainloop()

我正在尝试模拟我在 answer 中发现的内容

我尝试在带有布尔值的while 循环中执行此操作。当用户按下按钮时,布尔值变为True,代码进入while循环。当用户释放按钮时,布尔值更改为 False 并且代码从循环中退出,但在此代码中,无论我是否释放按钮,布尔值始终保持为真。

编辑:我希望调用一个函数,直到出现条件。要调用的函数是hold_down(),要检查的条件是释放按钮。

更新:我找到了让它工作的方法。

【问题讨论】:

  • 你能在这里分享你的解决方案吗?我也有同样的问题。

标签: python tkinter robotics


【解决方案1】:

按下按钮时设置标志,释放按钮时取消设置标志。无需循环,因为您已经在运行循环 (mainloop)

from Tkinter import * 
running = False
root = Tk()
def start_motor(event):
    global running
    running = True
    print("starting motor...")

def stop_motor(event):
    global running
    print("stopping motor...")
    running = False

button = Button(root, text ="forward")
button.pack(side=LEFT)
button.bind('<ButtonPress-1>',start_motor)
button.bind('<ButtonRelease-1>',stop_motor)
root.mainloop()

假设您确实想在按键被按下时做某事,您可以使用after 设置动画循环。例如,要在按下按钮时每秒调用一次打印语句,您可以添加一个执行打印语句的函数,然后安排自己在一秒钟后调用。停止按钮只需要取消任何挂起的作业。

这是一个例子。与原始代码的主要区别在于添加了move 函数。我还添加了第二个按钮来显示如何使用相同的功能前进或后退。

from Tkinter import * 
running = False
root = Tk()
jobid = None

def start_motor(direction):
    print("starting motor...(%s)" % direction)
    move(direction)

def stop_motor():
    global jobid
    root.after_cancel(jobid)
    print("stopping motor...")

def move(direction):
    global jobid
    print("Moving (%s)" % direction)
    jobid = root.after(1000, move, direction)

for direction in ("forward", "backward"):
    button = Button(root, text=direction)
    button.pack(side=LEFT)
    button.bind('<ButtonPress-1>', lambda event, direction=direction: start_motor(direction))
    button.bind('<ButtonRelease-1>', lambda event: stop_motor())

root.mainloop()

【讨论】:

  • 不应该绑定按钮而不是主窗口吗?
  • @StevenSummers:也许吧。目前尚不清楚 OP 想要什么。原始代码绑定到主窗口。这取决于他们是否希望鼠标按钮单击 GUI 中的任何位置,或者仅在按钮小部件上单击。现在我重新阅读了这个问题,也许你是对的。我会改变我的答案。
  • 我希望用户按下按钮(不是 GUI 中的任何位置)并在用户释放按钮的情况下重复该功能。
【解决方案2】:

您可能想尝试repeatinterval 选项。它的工作方式是,只要用户按住按钮,按钮就会不断触发。 repeatinterval 参数本质上是让程序知道它应该多久触发一次按钮。这是解释的链接:

http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/button.html

在页面内搜索“repeatinterval”。

此参数的另一个名称是repeatdelay

【讨论】:

    【解决方案3】:

    基于 Bryan Oakley 的使用标志来模拟按住按钮的答案。问题是您的 tkinter 应用程序中不能有任何 while 循环来表示 在运行时向前移动汽车

    这就是我建议使用线程的原因。这样你就可以在后台运行一个while循环来检查汽车是否应该向前移动。

    from threading import Thread
    from Tkinter import *    
    
    running = False
    root = Tk()
    
    def start_motor(event):
        global running
        print("starting motor...")
        running = True
    
    def stop_motor(event):
        global running
        running = False
        print("stopping motor...")
    
    def move_forward():
        while True: # Thread will run infinitely in the background
            if running:
                print("Car is moving forward...\n")
    
    button = Button(root, text ="forward")
    button.pack(side=LEFT)
    button.bind('<ButtonPress-1>',start_motor)
    button.bind('<ButtonRelease-1>',stop_motor)
    
    # Create and start the new thread
    t = Thread(target = move_forward, args = ())
    t.start()
    
    root.mainloop()
    

    【讨论】:

    • 后台线程总是占用 100% 的 CPU 时间——不好。
    • 为避免不必要的时候后台线程消耗CPU时间,在start_motor中创建并启动线程在running = True之后,而不是在启动时创建并运行它,并将move_forward更改为运行while running 而不是 while True
    【解决方案4】:

    试试这个...

    from Tkinter import *  
    root = Tk()
    global hold_down
    
    def button_hold(event):
        hold_down = True
        while hold_down: 
            print('test statement')
    
    def stop_motor(event):
        hold_down = False
        print('button released')
    
    button = Button(root, text ="forward")
    button.pack(side=LEFT)
    root.bind('<Button-1>',button_hold)
    root.bind('<ButtonRelease-1>',stop_motor)
    root.mainloop()
    

    【讨论】:

    • 它进入了一个无限循环。它就像它总是保持真实(留在循环中),无论如果释放按钮,程序不会跳转到另一条指令,总是保持在循环中。我尝试在 while 循环中使用 ButtonReleased 的 root.bind,但也没有用。
    • 这行不通。您可以防止事件循环能够处理按钮释放。
    【解决方案5】:

    @Danny 试试下面的代码:

    def stop_motor(事件): print('按钮释放') 返回错误

    此答案运行一次 print 'test statement'。当按钮被按下时,while 循环运行一次。

    @Bryan Oakley 按下按钮时设置标志,释放按钮时取消设置标志。不需要循环,因为您已经在运行循环(主循环)

    from Tkinter import * 
    running = False
    
    root = Tk()
    def start_motor(event):
        global running
        running = True
        print("starting motor...")
    
    def stop_motor(event):
        global running
        print("stopping motor...")
        running = False
    
    button = Button(root, text ="forward")
    button.pack(side=LEFT)
    root.bind('<ButtonPress-1>',start_motor)
    root.bind('<ButtonRelease-1>',stop_motor)
    root.mainloop()
    

    当按下按钮时,上面的答案会处于无限循环中。

    @ Joseph Farah 你可能想试试 repeatinterval 选项。它的工作方式是,只要用户按住按钮,按钮就会不断触发。 repeatinterval 参数本质上让程序知道它应该多久触发一次按钮。这是解释的链接:

    http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/button.html

    在页面内搜索“repeatinterval”。

    这个参数的另一个名字是repeatdelay。

    我在按钮小部件的参数选项中设置了重复间隔,但它不重复命令。

    感谢大家的回答。仍在寻求解决这个问题。

    【讨论】:

    • 我可以看看你的 button_forward 函数的代码吗?
    • button_forward函数的代码与button_hold的代码相同。我改变了他们的名字,但完全一样。
    • 我想做的是这个。我想做到这一点,当用户(我)按住“前进”按钮时,程序继续执行使汽车前进的代码,当我释放按钮时停止汽车 - 停止运行代码。
    • @user38029:这是什么意思:_“当按下按钮时,上面的答案会处于无限循环中。”+?我的解决方案没有进入循环。另外,它完全按照您的要求执行 - 当您按下按钮时它会打印一条消息,并在您释放时打印另一条消息。如果您需要更多内容,请澄清您的问题。您是否希望在按键时重复调用某个函数?
    • @BryanOakley:是的,我希望在按下键时重复调用一个函数。因为我想模拟按下键时继续运行汽车,释放键时停止汽车。很抱歉造成误会,现在清楚了吗?我想要一个函数被调用,直到条件发生。要调用的函数是 run_motor() 和检查按钮是否被释放的条件。有什么不懂的告诉我。
    猜你喜欢
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多