2017-01-15 5 views
0

Это вторая версия программы, с которой я борюсь. Я отправил более ранний вопрос с первой версией программы, но мне не повезло.Как прерывать цикл GPIO RPi в любое время с помощью кнопки TKinter

Вот моя «Большая картинная идея» на случай, если она поможет вам найти место, где я ухожу. У меня много растений для воды, и я хочу создать графический интерфейс с TKinter, который появится на сенсорном экране на малиновой пи. Pi управляет реле 110 В, которое подключается к водяному насосу. Его просто базовая петля «вкл/выкл» для z # растений.

Пользователь имеет три ползунка для регулировки, X, Y и Z. X - как долго должен быть включен водяной насос, Y - как долго должен быть выключен водяной насос (так что у вас есть время, чтобы физически переместить шланг на следующую установку), а Z - общее количество заводов, которые вы должны воды. Раньше я просто позволял циклу работать навсегда, потому что всегда мог CTRL-C завершить программу, но теперь я пытаюсь сделать все это на сенсорном экране без клавиатуры - более удобный, чем преподавание того, кто не знать, что означает Linux, как использовать терминал. Он также должен запускаться как программа, к которой люди привыкли видеть, поэтому кнопки сенсорного экрана GUI (например, смартфоны и планшеты).

Код работает хорошо по большей части, он останавливается и запускается без сбоев. Я понял, кнопка запуска, и есть кнопка сброса, чтобы очистить значения ползунка, если вы хотите начать значения над нулями. Но проблема BIG, которая, кажется, затрагивает многих людей здесь, - это кнопка «ВЫХОД».

Мне нужно, чтобы пользователь мог нажать кнопку TKinter EXIT в любое время во время цикла и знать программу, когда кнопка нажата, и выполнять ее немедленно, в основном прерывая цикл. Я много пробовал, ничего не работает. Checkbuttons будет ждать только завершения цикла, а затем обновить значение переключения. В этот момент уже слишком поздно; Мне нужен пользовательский контроль во время всего цикла полива.

Я также не хочу, чтобы программа выходила, когда нажата кнопка EXIT, а я хочу, чтобы цикл GPIO остановился на значении «ВЫКЛ» и в основном сбросил программу в начальное состояние.

Я знаю, что time.sleep() очень грубо, но я не нашел другого способа сделать это. Я видел, как люди говорили о «потоке», я понятия не имею, что это значит, и похоже, что я никогда не буду. Я также видел, как люди говорят о функции «после()» TKinters, опять же, мне нужны очень четкие примеры ответов. Просто говоря «Использовать потоки» не дает никакой помощи - я ноб!

from tkinter import * 
    import RPi.GPIO as GPIO 
    import time 


    master = Tk() 


    def onoffcycle(): 
     GPIO.setwarnings(False) 
     GPIO.setmode(GPIO.BCM) 
     GPIO.setup(14, GPIO.OUT) 
     GPIO.setup(14, GPIO.LOW) 
     y=off.get() 
     y=float(y) 
     x=on.get() 
     x=float(x) 
     GPIO.output(14, True) 
     print("On")+str(x) 
     time.sleep(x) 
     GPIO.output(14, False) 
     print("Off")+str(y) 
     time.sleep(y) 


    def start(): 
     print("Prepare to water in 10 seconds...") 
     time.sleep(10) 
     z=cycle.get() 
     z=float(z) 
     while z > 0: 
      onoffcycle() 
      z=z-1 
      print("Cycles remaining:")+str(z) 
      ###tog() 
      ###if t==1: 
       ###reset() 
     else: 
      reset() 
     ### this program runs onoffcycle() for z number of cycles as set by the slider. 
     ### It should check for an exit toggle, then run the program for one cycle, 
     ### then check for exit, then another round. countdown from cycle.get variable 


    def reset(): 
     GPIO.cleanup() 
     on.set(0) 
     off.set(0) 
     cycle.set(0) 

    t=IntVar() 

    ###def tog(): 
     ### HELP! This is where I need the program to be looking for the EXIT button being pressed 
     ### in order to stop the program. Everything I have tried so far waits until the z value drops 
     ### to zero, basically until the program stops on its own. I need a button that interrupts 
     ### everything. 


    on = Scale(master, label="Set # Seconds Water ON:", from_=0, to=180, orient=HORIZONTAL, length=400, width=35, 
       troughcolor="red", bg="SteelBlue1", fg="black", bd=6, sliderlength=90, sliderrelief=RIDGE, 
       font = '-weight bold') 
    on.grid(column=1, row=1, columnspan=3) 

    off = Scale(master, label="Set # Seconds Water OFF:", from_=0, to=30, orient=HORIZONTAL, length=400, width=35, 
       troughcolor="yellow", bg="SteelBlue1", fg="black", bd=6, sliderlength=90, sliderrelief=RIDGE, 
       font = '-weight bold') 
    off.grid(column=1, row=2, columnspan=3) 

    cycle = Scale(master, label="Set # of Plants to Water:", from_=0, to=200, orient=HORIZONTAL, length=400, width=35, 
       troughcolor="green", bg="SteelBlue1", fg="black", bd=6, sliderlength=90, sliderrelief=RIDGE, 
       font = '-weight bold') 
    cycle.grid(column=1, row=3, columnspan=3) 


    go = Button(master, text="START", command=start, bg="SteelBlue3", width=10, height=2, font='-weight bold') 
    go.grid(column=1, row=4) 

    adios = Checkbutton(master, text="EXIT", variable=t, indicatoron=0, bd=6, bg="SteelBlue3", width=11, height=2, font='-weight bold') 
    adios.grid(column=3, row=4) 
    ###adios should have a command to run an exit program - with toggle values? or something else? 
    resetbutton = Button(master, text="RESET", command=reset, bg="SteelBlue3", width=10, height=2, font='-weight bold') 
    resetbutton.grid(column=2, row=4) 



    mainloop() 
+0

вы должны использовать 'root.after (миллисекунды, имя_функции)' вместо 'sleep()' и 'while'. '10s = 10000ms', то есть,' root.after (10000, start) 'в конце' start() 'должен снова запустить эту функцию через 10 секунд, и вам не нужно' sleep() ', и вы не нужно 'while', но' if', чтобы остановить 'after'. – furas

+0

'print (« Вкл. ») + Str (x)' не имеет смысла, потому что 'print' возвращает' None', поэтому вы делаете 'None + str (x)'. Возможно, вам нужно «print (« Вкл. », X)» или, по крайней мере, «print (« Вкл. »+ Str (x))» или «print (« On {} ». Format (x))', но первая версия Простейшие. – furas

+0

furas - спасибо за столько информации! Части print() печатают выбранные пользователем значения в командной строке; Я полагаю, что это действительно не предназначено для просмотра пользователем, так как я хочу, чтобы окно TKinter было полноэкранным. Я использую его во время отладки, и в конце концов это может быть полезно. – Stephen

ответ

0

Используйте логические переменные True/False контролировать циклы и другие элементы

Например, можно создать глобальную переменную

time_to_exit = False 

и установить его True при нажатии EXIT кнопку

и использовать в функция

while z > 0 and not time_to_exit: 

Он должен остановить цикл.

Вы можете использовать другие логические переменные, чтобы сообщить программе, если цикл был остановлен, и он может выйти.

# inform function to use global variable 
global loop_is_working 
loop_is_working = True 
while z > 0 and not time_to_exit: 

# ... 

def reset(): 
    # inform function to use global variable 
    global loop_is_working 

    loop_is_working = False 

def on_exit: 

    if loop_is_working: 
     reset() 
     # check again afte 100ms 
     after(100, on_exit) 
    else: 
     # stop `mainloop()` and close tkinter window 
     root.destroy() 

Ваша другая проблема: вы используете sleep() и while так Tkinter должен ждать, пока она не закончится.

Вы можете использовать master.after(miliseconds, function_name), а Tkinter/mainloop выполнит эту функцию, и тем временем он сможет проверять ваши кнопки.

пример

time_to_exit = False 

def start(): 
    print("Prepare to water in 10 seconds...") 

    # mainloop will run `start_loop` after `10s` 
    master.after(10000, start_loop) # 10s = 10 000ms 

def start_loop():  
    z = float(cycle.get()) 
    loop(z) 
    # or 
    #master.after(0, loop, z) 

def loop(z):  
    if z > 0 and not time_to_exit: 
     onoffcycle() 
     z -= 1 
     print("Cycles remaining:", z) 
      ###tog() 
      ###if t==1: 
       ###reset() 
     # mainloop will run `loop` again as soon as possible 
     master.after(0, loop, z) 
    else:  
     reset() 

или даже

# create global variables 
time_to_exit = False 
loop_is_working = False 

def start(): 
    # inform function to use global variable 
    global loop_is_working 

    loop_is_working = True 

    print("Prepare to water in 10 seconds...") 

    # mainloop will run `start_loop` after `10s` 
    master.after(10000, start_loop) # 10s = 10 000ms 

def start_loop():  
    z = float(cycle.get()) 
    loop(z) 
    # or 
    #master.after(0, loop, z) 

def loop(z):  

    if z > 0 and not time_to_exit and loop_is_working: 
     onoffcycle() 
     z -= 1 
     print("Cycles remaining:", z) 
      ###tog() 
      ###if t==1: 
       ###reset() 
     # mainloop will run `loop` again as soon as possible 
     master.after(0, loop, z) 
    else:  
     reset() 

def reset(): 
    # inform function to use global variable 
    global loop_is_working 

    # ... your code ... 

    loop_is_working = False 
+0

Это здорово, я попробую и дам вам знать! – Stephen

+0

Ничего себе! Кнопка сброса теперь прерывает цикл !!! Единственная проблема теперь в том, что значение «z» не уменьшается после каждой итерации «loop()». Должен ли он перемещаться или вводиться как глобальная переменная? Мне кажется, что он может получать значение от кнопки каждый раз, когда цикл воспроизводится ... – Stephen

+0

Я меняю код - теперь есть 'start_loop', которые получают' z' (после 10 секунд) и запускают 'loop (z) '. И 'after' запустите' loop' снова с новым 'z' – furas

0

Так вот новый код с отличными предложениями furas. Я также узнал, как получить значение z с каждой итерацией. Кнопка RESET остановит программу после завершения текущего цикла и дождитесь, пока вы начнете новый цикл. Это связано с тем, что у меня есть time.sleep(), обрабатывающий время включения и выключения. Я попытался использовать для этого after(), но он продолжал вести к другим большим проблемам. И на данный момент программа технически полностью пригодна для использования, поэтому я не против, чтобы она висела на один цикл; это не влияет на приложение реального мира! У меня все еще есть другие проблемы, которые заслуживают их собственных вопросов, таких как настройка сенсорного экрана для правильной калибровки и автоматический запуск программы после автоматического запуска LDXE. Я планирую правильно разместить все в class(). Также хотелось бы иметь возможность изменять текст виджетов ярлыков, застревая в заявлении if:. Всем спасибо!

#!/usr/bin/python 
    from Tkinter import * 
    import ttk 
    import RPi.GPIO as GPIO 
    import time 


    master = Tk() 
    master.title("Obligatory Title") 
    master.attributes("-fullscreen", True) 
    time_to_exit = False 
    loop_is_working = False 


    def onoffcycle(): 
     global t 
     GPIO.setwarnings(False) 
     GPIO.setmode(GPIO.BCM) 
     GPIO.setup(18, GPIO.OUT) 
     GPIO.output(18, GPIO.LOW) 
     y=float(off.get()) 
     x=float(on.get()) 
     GPIO.output(18, True) 
     print("On")+str(x) 
     t.set("Water ON") 
     t.get() 
     time.sleep(x) 
     GPIO.output(18, False) 
     print("Off")+str(y) 
     t.set("Water OFF") 
     time.sleep(y)    

    def start(): 
     global loop_is_working 
     global t 
     loop_is_working = True 
     print("Prepare to water in 10 seconds...") 
     t.set("Prepare to water in 10 seconds...") 
     master.after(10000, loop) 

    def loop(): 
     z=float(cycle.get()) 
     global loop_is_working 
     global time_to_exit 
     global t 
     t.set("Now Watering...") 
     if z > 0 and not time_to_exit and loop_is_working: 
      onoffcycle() 
      z=z-1 
      cycle.set(z) 
      print("Cycles remaining: ")+ str(z) 
      master.after(0, loop) 
     else: 
      reset() 


    def reset(): 
     global loop_is_working 

     GPIO.cleanup() 
     on.set(0) 
     off.set(0) 
     cycle.set(0) 
     t.set("Welcome! Drag the sliders to set values. Tap left or right of the sliders to fine tune values.") 
     loop_is_working = False 

    t = StringVar() 
    t.set("Welcome! Drag the sliders to set values. Tap left or right of the sliders to fine tune values.") 

    welcome = Label(master, bg="Steelblue3", bd=6, relief=RAISED, width=92, 
        height=2, textvariable=t) 
    welcome.grid(column=1, row=1, columnspan=3) 


    on = Scale(master, label="Set # Seconds Water ON:", from_=0, to=120, orient=HORIZONTAL, length=735, 
       width=50, troughcolor="steelblue", bg="SteelBlue1", fg="black", bd=6, sliderlength=90, 
       sliderrelief=RIDGE) 
    on.grid(column=1, row=2, columnspan=3) 

    off = Scale(master, label="Set # Seconds Water OFF:", from_=0, to=30, orient=HORIZONTAL, length=735, 
       width=50,troughcolor="steelblue", bg="SteelBlue1", fg="black", bd=6, sliderlength=90, 
       sliderrelief=RIDGE) 
    off.grid(column=1, row=3, columnspan=3) 

    cycle = Scale(master, label="Set # of Plants to Water:", from_=0, to=200, orient=HORIZONTAL, length=735, 
        width=50,troughcolor="steelblue", bg="SteelBlue1", fg="black", bd=6, sliderlength=90, 
        sliderrelief=RIDGE) 
    cycle.grid(column=1, row=4, columnspan=3) 

    go = Button(master, text="START", command=start, bg="green3", width=19, height=3, 
       bd=4, font='-weight bold') 
    go.grid(column=1, row=5) 

    adios = Button(master, text="EXIT", command=exit, bg="red3", width=19, height=3, 
       bd=4, font='-weight bold') 
    adios.grid(column=3, row=5) 

    resetb = Button(master, text="RESET", command=reset, bg="yellow3", width=19, 
       height=3, bd=4, font='-weight bold') 
    resetb.grid(column=2, row=5) 

    mainloop()