【问题标题】:How can I bind the spacebar to 2 functions on Tkinter?如何将空格键绑定到 Tkinter 上的 2 个函数?
【发布时间】:2021-05-13 00:18:49
【问题描述】:

所以我正在尝试制作一个计时器(如 cstimer.net),但我遇到了这个问题

我已经绑定了空格键来启动计时器,然后,一旦你启动了计时器,再次点击它就会停止它。问题是当我按下空格键时,它会启动并完成计时器。而时间,不会重置,只是将经过的时间添加到最后一次。

这是我所做的:

import pyglet
import os
import time
from tkinter import *

# Import scripts
import sys
sys.path.insert(1, './scripts')

import scrambler
import timer
import saver

class Application():
    def __init__(self, app):
        self.app = app

        pyglet.font.add_file('./data/fonts/DS-DIGIB.TTF')  # Add the time font
        # Add the body (used for scramble and more elements) font
        pyglet.font.add_file('./data/fonts/OverPassMono-Light.ttf')

        # Create side panel
        self.sidepanel = Frame(self.app, bg='red', width=200, height=800)

        # Generate scramble
        self.scramble_output = scrambler.gen_333scramble()

        #Bind spacebar to start timer
        self.app.bind('<Key>', self.updater_start)

        # Define button, start time and scramble Widget and show them
        self.display = Label(self.app, text='0.00', bg='#232931', fg='white', anchor=CENTER, font=('DS-DIGITAL', 64), pady=100)
        self.scramble = Label(self.app, text=self.scramble_output, bg='#232931', fg='white', font=('Overpass Mono Light', 12), anchor=NW)
        self.start_button = Button(self.app, text='Start', fg='white', bg='#4ecca3', command=self.start, anchor=CENTER)
            
        #Show in grid
        self.display.grid(row=1, column=1, sticky=EW, padx=275, pady=150)
        self.start_button.grid(row=2, column=1, columnspan=2, padx=275, sticky=N, ipadx=20)
        self.scramble.grid(row=0, column=1, sticky=EW, padx=275)


    def start(self):
        # Unshow last time or starting time
        self.display.grid_remove()

        # Start counting time
        self.start_time = timer.start()

        #Bind spacebar to stop timer

        self.app.bind('<Key>', self.updater_stop)

         # Create widgets to show the timer is running and finish button
        self.state = Label(self.app, text='.', bg='#232931', fg='white', font=('DS-DIGITAL', 32))
        self.state.grid(row=1, column=1, sticky=EW, padx=560, pady=300)
        self.finish_button = Button(self.app, text='FINISH', fg='white', bg='#4ecca3', command=self.finish)
        self.finish_button.grid(row=2, column=1, columnspan=2, padx=275, sticky=N, ipadx=20)

        # Unshow start button and scramble widgets

        self.start_button.grid_remove()
        self.scramble.grid_remove()

    def updater_start(self, e):  
        if e.keysym=='space':
            print('Hitted spacebar')
            self.start()

    def updater_stop(self, e):
        if e.keysym=='space':
            print('Hitted spacebar')
            self.finish()

    def finish(self):
        self.message = timer.finish(self.start_time)
        # Display formatted time
        self.display = Label(self.app, text=self.message, bg='#232931', fg='white', font=('DS-DIGITAL', 64), pady=100)


        #Save time on times.json
        saver.save_time(self.message, self.scramble_output)

        # Unshow state and finish button widgets
        self.finish_button.grid_remove()
        self.state.grid_remove()

        # Redefine widgets
        self.start_button = Button(self.app, text='Start', fg='white', bg='#4ecca3', command=self.start)
        self.scramble_output = scrambler.gen_333scramble()
        self.scramble = Label(self.app, text=self.scramble_output, bg='#232931', fg='white', font=('Overpass Mono Light', 12), anchor=NW)

        # Show widgets on screen and formatted time
        self.display.grid(row=1, column=1, sticky=EW, padx=275, pady=150)
        self.start_button.grid(row=2, column=1, columnspan=2, padx=275, sticky=N, ipadx=20)
        self.scramble.grid(row=0, column=1, sticky=EW, padx=275)

您可以知道,savertimerscrambler 模块是我制作的脚本,因此我可以对代码进行切片。计时器脚本只是在开始时获取 time.time() 变量并用当前时间减去它,然后将其格式化为小时:秒:分钟。

【问题讨论】:

  • 您忘记在 finish() 函数中重置绑定。
  • 你应该只绑定一个函数并检查这个函数内部是否定时器正在运行 - 然后启动或停止它。

标签: python tkinter


【解决方案1】:

与其创建新的小部件,不如更新现有小部件的值。 我使用 tk.after 来更新计时器,使用 True/False 来启动/停止计时器。 请注意,某些导入已被注释掉以供我运行。

# import pyglet
# import os
import time
from datetime import timedelta
from tkinter import *


# Import scripts
# import sys
# sys.path.insert(1, './scripts')

# import scrambler
# import timer
# import saver

class Application():
    def __init__(self, app):
        self.app = app

        # pyglet.font.add_file('./data/fonts/DS-DIGIB.TTF')  # Add the time font
        # Add the body (used for scramble and more elements) font
        # pyglet.font.add_file('./data/fonts/OverPassMono-Light.ttf')

        # Create side panel
        self.sidepanel = Frame(self.app, bg='red', width=200, height=800)

        # Generate scramble
        self.scramble_output = 'scramble_output goes here' # scrambler.gen_333scramble()

        #Bind spacebar to start timer
        self.app.bind('<space>', self.toggle_timer_running)

        # Define button, start time and scramble Widget and show them
        self.display = Label(self.app, text='0.00', bg='#232931', fg='white', anchor=CENTER, font=('DS-DIGITAL', 64), pady=100)
        self.scramble = Label(self.app, text=self.scramble_output, bg='#232931', fg='white', font=('Overpass Mono Light', 12), anchor=NW)
        self.start_button = Button(self.app, text='Start', fg='white', bg='#4ecca3', command=self.toggle_timer_running, anchor=CENTER)
            
        #Show in grid
        self.display.grid(row=1, column=1, sticky=EW, padx=275, pady=150)
        self.start_button.grid(row=2, column=1, columnspan=2, padx=275, sticky=N, ipadx=20)
        self.scramble.grid(row=0, column=1, sticky=EW, padx=275)

        self._timer_running = False
        self.start_time = self.end_time = time.time()
        self.update()

    def toggle_timer_running(self, event=None):
        self._timer_running = not self._timer_running
        
        if self._timer_running:
            self.start_time = time.time()
            self.start_button.config(text='Finish')
        else:
            self.start_button.config(text='Start')

    def update(self):
        if self._timer_running:
            self.end_time = time.time()
        duration = self.end_time - self.start_time
        self.display.configure(text=timedelta(seconds= duration))
        self.app.after(100, self.update)


if __name__ == '__main__':
    app = Tk()
    Application(app)
    app.mainloop()

【讨论】:

    【解决方案2】:

    我建议为按下空格键的次数设置一个变量。绑定空格键时,检查是否为偶数(0、2、4等)。这意味着它应该开始。否则,它应该结束

    from tkinter import *
    
    root = Tk()
    NUM_SPACE_PRESS = 0
    
    
    def space_press(e):
        global NUM_SPACE_PRESS
        if NUM_SPACE_PRESS % 2 == 0:
            Label(text='<start here>').pack()
            NUM_SPACE_PRESS += 1
        else:
            Label(text='<end here>').pack()
            NUM_SPACE_PRESS += 1
    
    
    root.bind("<space>", space_press)
    
    root.mainloop()
    

    【讨论】:

    • 使用布尔标志然后使用flag = not flag 切换它的阶段更简单。
    【解决方案3】:

    您应该使用一个带有变量的函数,即。 runningTrue/False 告知要做什么 - 停止或启动计时器。

    import tkinter as tk
    
    def space_press(event):
        global running
            
        if running:
            l['text'] = 'timer stoped'
        else:
            l['text'] = 'timer started'
    
        # switch state
        running = not running
    
    # --- main ---
    
    running = False
    
    root = tk.Tk()
    
    l = tk.Label(root, text='press space')
    l.pack()
    
    root.bind("<space>", space_press)
    
    root.mainloop()
    

    【讨论】:

      【解决方案4】:

      您需要在finish() 函数中重置"&lt;space&gt;" 绑定。

      以下是根据您的修改的示例:

      class Application():
          def __init__(self, app):
              ...
              self.bind(self.start) # replace self.app.bind("<Key>", self.updater_start)
              ...
      
          # added function
          def bind(self, callback=None):
              self.app.bind("<space>", callback)
      
          def start(self, event=None):
              self.bind()  # disable space binding when busy
              ...
              # removed self.app.bind("<Key>", self.updater_stop)
              ...
              self.bind(self.finish)
      
          def finish(self, event=None):
              self.bind()  # disable space binding when busy
              ...
              self.bind(self.start)
      

      请注意,我已删除 updater_start()updater_stop() 函数。

      【讨论】:

        猜你喜欢
        • 2013-03-23
        • 1970-01-01
        • 2013-06-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-30
        • 1970-01-01
        • 2018-05-08
        相关资源
        最近更新 更多