Tkinter中的Python RaspberryPi GPIO事件检测失败

时间:2014-07-26 00:33:50

标签: python events tkinter raspberry-pi gpio

我在使用带有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次?

1 个答案:

答案 0 :(得分:1)

mainloop()完成程序中的所有工作 - 它运行事件功能(和其他功能) - 一个接一个 - 它看起来像多任务处理。但是,如果任何函数工作太长时间(例如,它使用while Truetime.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

<强>顺便说一句:

我们使用classesself.来不使用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()

在课堂上我不使用外部变量。我内部都有变量。