1

New to python3 and started my first project of using a raspberry pi 3 to create an interface to monitor and control elements in my greenhouse. Currently the program reads Temperature and Humidity via a DHT11 sensor, and controls a number of relays and servo via the GPIO pins.

I created a GUI to display the Temperature and Humidity that reads and updates every 250ms. There is also a number of buttons that control the specific relays/servo.

I am now running into some issues with the tkinter GUI freezing on a button press. I have looked on the forum a bit but don't understand how to implement threading or a check function to keep my GUI from freezing.

Code Below:

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

#Logic Setup

temp = 0
humd = 0

#GPIO Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(16, GPIO.OUT) #Water Pump
GPIO.setup(18, GPIO.IN)  #Tank Float Switch
GPIO.output(16, GPIO.LOW)

#Window Setup

win = Tk()
win.title("Test")
win.geometry("200x300+0+0")

#Label Setup

Label (win, text="Temperature", fg="red", bg="black", font="24").grid(row=0, column=0)
Label (win, text="Humidity", fg="red", bg="black", font="24").grid(row=0, column=2)
Label (win, text="Water System", fg="red", bg="black", font="24").grid(row=3, column=0)

TEMP = Label (win, text="", fg="black", bg="white", font="36")
TEMP.grid(row=1, column=0)

HUMD = Label (win, text="", fg="black", bg="white", font="36")
HUMD.grid(row=1, column=2)

#Functions

def wait(time_lapse):
    time_start = time.time()
    time_end = (time_start+time_lapse)

    while time_end >= time.time():
        pass

def RTEMP ():
    global temp
    humidity, temperature = Adafruit_DHT.read_retry(11, 27)
    temp = temperature * 9/5.0 + 32
    TEMP.configure(text=str(temp))

def RHUMD ():
    global humd
    humidity, temperature = Adafruit_DHT.read_retry(11, 27)
    humd = humidity
    HUMD.configure(text=str(humd))        

def READ ():
    RTEMP()
    RHUMD()
    win.after(250, READ)

def PUMP ():
    if GPIO.input(18):
        WTR.config(bg="green")
        GPIO.output(16, GPIO.HIGH)
        wait (10)
        GPIO.output(16, GPIO.LOW)
        WTR.config(text="Water", bg="grey")
    else:
        GPIO.output(16, GPIO.LOW)
        WTR.config(text="LOW WATER", bg="red")

#Buttons

WTR = Button(win, text="Water", bg="grey", command = PUMP, height = 2, width = 8)
WTR.grid(row=4, column=0) #Water Pump Control

#Function Calls

READ()

mainloop()
  • That's too much code for a question. Can you cut that down to something that still reproduces your problem but has considerably fewer lines of code? For example, if the problem is with the GUI freezing when you click a button, you only need the code for that one button. See http://www.stackoverflow.com/help/mcve – Bryan Oakley Jun 10 '16 at 15:11
  • @BryanOakley OK i trimmed it down. So now when running, the GUI freezes during the 10 second wait() function. I also notice it does not change the button color when clicked, though... – Adam Alexander Jun 10 '16 at 16:20
  • You are right about needing to implement threads. I suggest `pyQT`. To save you some time - You are going to search for a very simplistic version of how to use threads for a very long time on Google before realizing that you really have to go through a few complex looking scripts before you understand it. Here are some stackoverflow questions that I have found helpful to get you started: [1](http://stackoverflow.com/questions/15698251/multiprocessing-gui-schemas-to-combat-the-not-responding-blocking), [2](https://joplaete.wordpress.com/2010/07/21/threading-with-pyqt4/) – chase Jun 10 '16 at 16:31
  • What is the purpose of `wait`: is it really just waiting, or are you attempting to simulate some other process that actually takes time to complete? That's an important distinction, because waiting is quite different from doing actual work. – Bryan Oakley Jun 10 '16 at 16:44
  • I simply wrote a wait function as time.sleep() would stop the script . It's purpose was to just wait without pausing the script. – Adam Alexander Jun 10 '16 at 18:09
  • @chase Thank you for the links to the questions. I think i need to refresh and learn a bit more about threading. Eventually I'd like to have a few processes running simultaneously (GPIO Control of A few relays, the sensor, Updating the GUI etc). – Adam Alexander Jun 10 '16 at 18:16

1 Answers1

1

Tkinter GUIs (as well as most other GUIs) are in a perpetual wait state. There's no reason to introduce explicit waiting for events.

If you need to run some function, and then run some other function 10ms later, you would schedule the other code to run with after. For example:

    GPIO.output(16, GPIO.HIGH)
    win.after(10, GPIO.output, 16, GPIO.LOW)

Naturally, if you want to do more than one thing you can use lambda or write another function.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Is he not using the wait function here for simplifying the code purposes? In other words, the `wait` function just takes the place of a process which will take several seconds to complete on the hardware? He mentions that he is trying to structure his program to incorporate threads - which would be a much more robust solution, even if much more challenging to implement. – chase Jun 10 '16 at 16:35
  • @chase: that's a good question. Since there was code after the call to `wait`, I assumed it was solely for waiting for some amount of time. – Bryan Oakley Jun 10 '16 at 16:43
  • @BryanOakley ok I did not know about the perpetual wait state. using the win.after makes sense. I shall implement this. However, i use the win.after(250, READ) to refresh the temp and humd variables on the GUI. I notice there is still some lag/freezing during these as well. I'm guess my next step is to dive into threading a bit more and experiment? – Adam Alexander Jun 10 '16 at 18:14
  • If you have functions that block (ie: you read from a sensor and it waits for data to be available) or if you call a function that takes more than a few hundred ms before returning, then threads or multiprocessing is a solution. At the end of the day, a tkinter program runs in a single thread and thus can only do one thing at a time. If any of those "things" takes a long time, the GUI will be unresponsive until those "things" finish. However, if checking a sensor takes just a few ms, you can easily check dozens without threads. – Bryan Oakley Jun 10 '16 at 18:33