我在使用带有Tkinter的Python检测Raspberry Pi上的GPIO事件时遇到了一个奇怪的问题。
单击startGameButton
(调用start_game
函数)后,try
块中会添加GPIO事件,并且while循环运行30秒。在此期间,我期待引脚23上的GPIO事件GPIO.Falling
发生,每次事件发生时,p2ScoreEvent
函数都应该执行。实际发生的事情是事件似乎只在第一次发生时触发,如果我继续导致GPIO.Falling
事件发生,则在while循环完成之前不会发生任何事情。一旦循环完成,如果事件多次发生,它会再次调用p2ScoreEvent
,但就是这样。
一旦我在start_game
的循环中,事件检测就完美无缺。我也验证了这一部分:
try:
GPIO.add_event_detect(P1PIN, GPIO.FALLING, callback=self.p2ScoreEvent)
while (time.time() - start_time) < game_time
print "listening"
time.sleep(5)
except:
print "Something went wrong..."
GPIO.cleanup()
当不是函数的一部分时,
在命令行中正确运行。
以下是给我提出问题的完整代码段:
from Tkinter import *
import time
import RPi.GPIO as GPIO
class App:
def p2ScoreEvent(self, p1pin):
print "ScoreEvent"
global p2score
p2score = p2score + 1
p2diag.set(p2diagString + repr(p2score))
def start_game(self):
global p2score
start_time = time.time()
game_time = 30 #length of game
P1PIN = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(P1PIN, GPIO.IN)
GPIO.add_event_detect(P1PIN, GPIO.FALLING, callback=self.p2ScoreEvent)
try:
GPIO.add_event_detect(P1PIN, GPIO.FALLING, callback=self.p2ScoreEvent)
while (time.time() - start_time) < game_time
print "listening"
time.sleep(5)
except:
print "Something went wrong..."
GPIO.cleanup()
def __init__(self, master):
frame = Frame(master)
global p2diagString
p2diagString = "Player 2 Score: "
global p2score
p2score = 0
global p2diag
p2diag = StringVar()
p2diag.set(p2diagString + repr(p2score))
p2Label = Label(root, fg="white", bg="blue", textvariable=p2diag).grid(row=1, column=1)
self.startGameButton = Button(
root, text="Start Game!", command=self.start_game
)
self.startGameButton.grid(row=3, columnspan=2)
root = Tk()
app = App(root)
root.mainloop()
我认为这与调用start_game
的函数有关,但我不确定。我没有太多的python经验,所以我在理解究竟发生了什么方面有点麻烦。
为什么GPIO事件仅在第一次发生时发生,为什么它会在while循环结束时触发一次且仅发生一次,如果实际发生的次数超过2次?
答案 0 :(得分:1)
mainloop()
完成程序中的所有工作 - 它运行事件功能(和其他功能) - 一个接一个 - 它看起来像多任务处理。但是,如果任何函数工作太长时间(例如,它使用while True
或time.sleep()
,则mainloop
可以执行其他函数。
所以不要使用time sleep()
和长时间循环,而是使用root.after(time, function)
重复运行某个函数。
我无法测试它,但它看起来像这样:
def my_loop(self):
if (time.time() - self.start_time) < self.game_time:
print "listening"
root.after(5000, self.my_loop) # run my_loop again after 5000 milliseconds
def start_game(self):
global p2score
# use self. to get access in other function
self.start_time = time.time()
self.game_time = 30 #length of game
P1PIN = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(P1PIN, GPIO.IN)
GPIO.add_event_detect(P1PIN, GPIO.FALLING, callback=self.p2ScoreEvent)
try:
GPIO.add_event_detect(P1PIN, GPIO.FALLING, callback=self.p2ScoreEvent)
self.my_loop() # run my_loop first time
except:
print "Something went wrong..."
GPIO.cleanup()
BTW:
您可以使用self.end_time
进行更少的计算
def my_loop(self):
if time.time() < self.end_time:
print "listening"
root.after(5000, self.my_loop) # run my_loop again after 5000 milliseconds
def start_game(self):
global p2score
# use self. to get access in other function
# self.game_time = 30
self.end_time = time.time() + 30
<强>顺便说一句:强>
我们使用classes
和self.
来不使用global
所有代码都可能如下所示:
from Tkinter import *
import time
import RPi.GPIO as GPIO
class App():
def __init__(self, master):
self.master = master
self.frame = Frame(master)
self.p2diagString = "Player 2 Score: "
self.p2score = 0
self.p2diag = StringVar()
self.p2diag.set(self.p2diagString + str(self.p2score))
p2Label = Label(self.frame, fg="white", bg="blue", textvariable=self.p2diag)
p2Label.grid(row=1, column=1)
self.startGameButton = Button(
self.frame, text="Start Game!", command=self.start_game
)
self.startGameButton.grid(row=3, columnspan=2)
def p2ScoreEvent(self, p1pin):
print "ScoreEvent"
self.p2score += 1
self.p2diag.set(self.p2diagString + str(self.p2score))
def start_game(self):
self.game_time = 30
self.end_time = time.time() + self.game_time
P1PIN = 23
GPIO.setmode(GPIO.BCM)
GPIO.setup(P1PIN, GPIO.IN)
GPIO.add_event_detect(P1PIN, GPIO.FALLING, callback=self.p2ScoreEvent)
try:
GPIO.add_event_detect(P1PIN, GPIO.FALLING, callback=self.p2ScoreEvent)
self.my_loop()
except:
print "Something went wrong..."
GPIO.cleanup()
def my_loop(self):
if time.time() < self.end_time:
print "listening"
root.after(5000, self.my_loop) # run my_loop again after 5000 milliseconds
def run(self):
self.master.mainloop()
#----------------------------------------------------------------------
App(Tk()).run()
我把__init__
作为第一个函数放在了类中 - 它更容易阅读 - 每个人都希望在课程开始时__init__
。
我使用str()
代替repl()
在课堂上我不使用外部变量。我内部都有变量。