Raspberry pi, tkinter and threading - multithreading

I am developing an app to monitor the office light system. I am using Tkinter to develop GUI, I try to run a loop within a button command and it freezes my GUI. So I read a little about the treading module.
I want to use the value returning from my variable var in a loop. I try as you can see below, but the function enable_automatico doesn't call my thread function acionamento_automatico. I don't get any error>
#!/usr/bin/python3
# Importando os pacotes
import sys
import time
import threading
import RPi.GPIO as GPIO
from tkinter import ttk
from tkinter import*
import tkinter as tk
from tkinter import messagebox
#Configurando a I/O
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#Configurando os pinos de Entrada
GPIO.setup(18, GPIO.IN) #Sensor de Presença Vendas
GPIO.setup(23, GPIO.IN) #Sensor de Luminosidade Vendas
GPIO.setup(24, GPIO.IN) #Sensor de Presença Engenharia
GPIO.setup(25, GPIO.IN) #Sensor de Luminosidade Engenharia
GPIO.setup(12, GPIO.IN) #Sensor de Presença Compras
GPIO.setup(16, GPIO.IN) #Sensor de Luminosidade Compras
GPIO.setup(20, GPIO.IN) #Sensor de Presença Marketing
GPIO.setup(21, GPIO.IN) #Sensor de Luminosidade Marketing
#Configurando os pinos de Saída
GPIO.setup(17, GPIO.OUT)#Luminária A Vendas
GPIO.setup(27, GPIO.OUT)#Luminária B Vendas
GPIO.setup(22, GPIO.OUT)#Luminária A Engenharia
GPIO.setup(5, GPIO.OUT) #Luminária B Engenharia
GPIO.setup(6, GPIO.OUT) #Luminária A Compras
GPIO.setup(13, GPIO.OUT)#Luminária B Compras
GPIO.setup(19, GPIO.OUT)#Luminária A Marketing
GPIO.setup(26, GPIO.OUT)#Luminária B Marketing
#Configurando os pinos de saída para o modo de operação manual
GPIO.output(17, GPIO.LOW)#Luminária A Vendas
GPIO.output(27, GPIO.LOW)#Luminária B Vendas
GPIO.output(22, GPIO.LOW)#Luminária A Engenharia
GPIO.output(5, GPIO.LOW) #Luminária B Engenharia
GPIO.output(6, GPIO.LOW) #Luminária A Compras
GPIO.output(13, GPIO.LOW)#Luminária B Compras
GPIO.output(19, GPIO.LOW)#Luminária A Marketing
GPIO.output(26, GPIO.LOW)#Luminária B Marketing
#Definição da fonte de texto
LARGE_FONT = ("Verdana", 22)
MEDIUM_FONT = ("Verdana", 16)
SMALL_FONT = ("Verdana", 12)
#A class thread
class MyThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
selfname = name
self.counter=counter
def acionamento_automatico(thread1, counter):
while (app.frames[Acionamento].var.get()==2):
if ((GPIO.input(18)==False) and (GPIO.input(23)==False)):
GPIO.output(17, GPIO.HIGH)
GPIO.output(27, GPIO.LOW)
app.frames[Acionamento].vendasFrame.luminaria_esquerdaFramev.label6.configure(text="Ligado")
app.frames[Acionamento].vendasFrame.luminaria_direitaFramev.label7.configure(text="Desligado")
if ((GPIO.input(18)==True) and (GPIO.input(23)==False)):
GPIO.output(17, GPIO.LOW)
GPIO.output(27, GPIO.LOW)
app.frames[Acionamento].vendasFrame.luminaria_esquerdaFramev.label6["text"]="Desligado"
app.frames[Acionamento].vendasFrame.luminaria_direitaFramev.label7["text"]="Desligado"
if ((GPIO.input(18)==False) and (GPIO.input(23)==True)):
GPIO.output(17, GPIO.HIGH)
GPIO.output(27, GPIO.HIGH)
app.frames[Acionamento].vendasFrame.luminaria_esquerdaFramev.label6["text"]="Ligado"
app.frames[Acionamento].vendasFrame.luminaria_direitaFramev.label7["text"]="Ligado"
if ((GPIO.input(18)==True) and (GPIO.input(23)==False)):
GPIO.output(17, GPIO.LOW)
GPIO.output(27, GPIO.LOW)
app.frames[Acionamento].vendasFrame.luminaria_esquerdaFramev.label6["text"]="Desligado"
app.frames[Acionamento].vendasFrame.luminaria_direitaFramev.label7["text"]="Desligado"
thread1.exit()
#Habilitar modo automático
def enable_automatico():
automatico_message = messagebox.showinfo(title="Modo Automático", message = "O acionamento das luminárias será feito conforme luminosidade e pessoas no setor, resultando num menor consumo de energia")
app.frames[Acionamento].vendasFrame.luminaria_esquerdaFramev.lev_button.configure(state=DISABLED)
app.frames[Acionamento].vendasFrame.luminaria_direitaFramev.ldv_button.configure(state=DISABLED)
app.frames[Acionamento].engenhariaFrame.luminaria_esquerdaFramee.lee_button.configure(state=DISABLED)
app.frames[Acionamento].engenhariaFrame.luminaria_direitaFramee.lde_button.configure(state=DISABLED)
app.frames[Acionamento].comprasFrame.luminaria_esquerdaFramec.lec_button.configure(state=DISABLED)
app.frames[Acionamento].comprasFrame.luminaria_direitaFramec.ldc_button.configure(state=DISABLED)
app.frames[Acionamento].marketingFrame.luminaria_esquerdaFramem.lem_button.configure(state=DISABLED)
app.frames[Acionamento].marketingFrame.luminaria_direitaFramem.ldm_button.configure(state=DISABLED)
app.frames[Acionamento].vendasFrame.luminaria_esquerdaFramev.lev_button.update()
app.frames[Acionamento].vendasFrame.luminaria_direitaFramev.ldv_button.update()
app.frames[Acionamento].engenhariaFrame.luminaria_esquerdaFramee.lee_button.update()
app.frames[Acionamento].engenhariaFrame.luminaria_direitaFramee.lde_button.update()
app.frames[Acionamento].comprasFrame.luminaria_esquerdaFramec.lec_button.update()
app.frames[Acionamento].comprasFrame.luminaria_direitaFramec.ldc_button.update()
app.frames[Acionamento].marketingFrame.luminaria_esquerdaFramem.lem_button.update()
app.frames[Acionamento].marketingFrame.luminaria_direitaFramem.ldm_button.update()
global thread1
thread1 = MyThread(1, "Thread-1", 1)
thread1.start()

Thread runs code which is in its function run() but you don't have it
from threading import Thread
import time
# -----
def my_function(name, counter):
for x in range(counter):
print(name, x)
time.sleep(0.5)
# -----
class MyThread(Thread):
def run(self):
my_function("Hello", 10)
# -----
thread = MyThread()
thread.start()
Or you can do it shorter
from threading import Thread
import time
# -----
def my_function(name, counter):
for x in range(counter):
print(name, x)
time.sleep(0.5)
# -----
thread = Thread( target=my_function, args=("World", 10) )
thread.start()
EDIT: example how to control thread and stop it.
BTW: In some situations you should use queue to communicate with thread. Only main thread should use print() and works with GUI like tkinter
from threading import Thread
import time
# -----
def my_function(name, counter):
for x in range(counter):
if stop:
print('STOP')
break
print(name, x)
time.sleep(0.5)
# -----
stop = False
thread = Thread( target=my_function, args=("World", 20) )
print("alive 0:", thread.is_alive()) # False
thread.start()
print("alive 1:", thread.is_alive()) # True
time.sleep(2)
print("alive 2:", thread.is_alive()) # True
time.sleep(2)
print("alive 3:", thread.is_alive()) # True
stop = True
print("alive 4:", thread.is_alive()) # True
time.sleep(2)
print("alive 5:", thread.is_alive()) # False
# wait till thread ends
thread.join()
print("alive 5:", thread.is_alive()) # False

Related

Show Raspberry Pi GPIO readings with PyQt [duplicate]

My python script need to change one object lcd_p1 every time the function wait_thread_v1 is call every second by a thread t1, but how do this? I don't know how to access this object inside the function? Anyone can help?
vazao1 = 12
global pulses_v1
pulses_v1 = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(vazao1, GPIO.IN)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setGeometry(50, 50, 640, 480)
self.setWindowTitle("Programa")
self.initUI()
def initUI(self):
self.lcd_v1 = QLCDNumber(self)
self.lcd_v1.display(0)
self.lcd_v1.setDigitCount(4)
self.lcd_v1.setFixedWidth(180)
self.lcd_v1.setFixedHeight(80)
self.lcd_v1.move(60,320)
def count_pulses_v1(channel):
global pulses_v1
pulses_v1 += 1
def wait_thread_v1():
global pulses_v1
while True:
time.sleep(1)
print("Pulsos total de vazão 1: "+str(pulses_v1))
#Window.initUI.self.lcd_p1.display(100)
pulses_v1 = 0
GPIO.add_event_detect(vazao1, GPIO.FALLING, callback=count_pulses_v1)
t1 = Thread(target=wait_thread_v1, name='thread_01', args=())
t1.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
You cannot and should not modify the GUI from a secondary thread as indicated by the docs since Qt does not guarantee that it works besides that it is unnecessary to create a new thread. In this case it is better to create a QObject that emits the signal when the callback is invoked since it is thread-safe, and then connect that signal to a slot that increases the pulses, then use a QTimer to implement the logic of the thread.
import sys
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer
from PyQt5.QtWidgets import QApplication, QLCDNumber, QWidget
import RPi.GPIO as GPIO
class Window(QWidget):
def __init__(self):
super().__init__()
self.pulses = 0
self.setGeometry(50, 50, 640, 480)
self.setWindowTitle("Programa")
self.initUI()
timer = QTimer(self, timeout=self.on_timeout, interval=1000)
timer.start()
def initUI(self):
self.lcd_v1 = QLCDNumber(self)
self.lcd_v1.display(0)
self.lcd_v1.setDigitCount(4)
self.lcd_v1.setFixedWidth(180)
self.lcd_v1.setFixedHeight(80)
self.lcd_v1.move(60, 320)
#pyqtSlot()
def falling_slot(self):
self.pulses += 1
#pyqtSlot()
def on_timeout(self):
self.lcd_v1.display(self.pulses)
self.pulses = 0
class GPIOManager(QObject):
fallingSignal = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
pin = 12
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.IN)
GPIO.add_event_detect(pin, GPIO.FALLING, callback=self.falling_callback)
def falling_callback(self, channel):
# This method is called in the thread where GPIOs are monitored.
self.fallingSignal.emit()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
manager = GPIOManager()
manager.fallingSignal.connect(window.falling_slot)
sys.exit(app.exec())

print but not setText

in my problem I have 2 windows. I'm putting entries in a table in the first one and I would like to display it in the second one. There is 2 column in the table. The first one is used for the combobox in window2. The second one is the one I would like to display (if row 1 is selected in combobox, print row 1 (column 2) if row 2 is selectd in combobox, print row 2 (column 2))
I don't have any error message or anything, I can print the right text, but somehow, I can't label il (settext) Any idea of my mistake ?
import sys
from PyQt5 import QtCore
from PyQt5.QtWidgets import QTableWidgetItem, QComboBox
from PyQt5.QtWidgets import QApplication, QPushButton, QDialog, QGroupBox,QVBoxLayout, QGridLayout, QLabel, QTableWidget, QScrollArea
class Window(QDialog):
def __init__(self):
super().__init__()
self.title = "XXXX"
self.top = 50
self.left = 50
self.width = 1250
self.height = 650
self.setStyleSheet("background-color:white")
self.InitWindow()
def InitWindow(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
groupBox = QGroupBox()
gridLayout = QGridLayout()
#TABLEAU
Window.table10 = QTableWidget(self)
Window.table10.setRowCount(3) # set row count
Window.table10.setColumnCount(2) # set column count
Window.table10.setMaximumHeight(250)
#entête
Window.table10.setItem(0,0, QTableWidgetItem("Equipement"))
Window.table10.setItem(0,1, QTableWidgetItem("Value"))
gridLayout.addWidget(Window.table10, 1 , 2 , 3, 6)
button11 = QPushButton("générer le profil de charge",self)
button11.setStyleSheet("background-color:rgb(2,154,132); color: white")
button11.setMinimumHeight(20)
button11.setMinimumWidth(80)
button11.clicked.connect(self.charge)
gridLayout.addWidget(button11, 5 , 1)
groupBox.setLayout(gridLayout)
scroll = QScrollArea()
scroll.setWidget(groupBox)
scroll.setWidgetResizable(True)
vbox = QVBoxLayout()
vbox.addWidget(scroll)
self.setLayout(vbox)
self.show()
def charge(self):
"""Lance la 2ème fenêtre en cas de clic sur le bouton
"""
# crée la 2ème fenêtre
self.profilcharge = ProfilCharge()
# rend modale la 2ème fenêtre (la 1ère fenêtre sera inactive)
self.profilcharge.setWindowModality(QtCore.Qt.ApplicationModal)
# affiche la 2ème fenêtre
self.profilcharge.show()
class ProfilCharge(Window):
# crée un signal pour envoyer une chaine à la fermeture de la fenêtre
fermetureprofilcharge = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super().__init__()
self.title = "profil de charge"
self.top = 50
self.left = 50
self.width = 300
self.height = 400
self.InitWindow()
def InitWindow(self):
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
groupBox = QGroupBox()
self.gridLayout = QGridLayout()
self.label1 = QLabel("chose an equipment",self)
self.gridLayout.addWidget(self.label1, 2 , 1)
self.combo = QComboBox()
i=1
while (i<Window.table10.rowCount()):
self.combo.addItem(str(Window.table10.item(i,0).text()),i)
i=i+1
self.gridLayout.addWidget(self.combo, 0 , 1 )
self.combo.activated[str].connect(self.nbheures)
groupBox.setLayout(self.gridLayout)
scroll = QScrollArea()
scroll.setWidget(groupBox)
scroll.setWidgetResizable(True)
vbox = QVBoxLayout()
vbox.addWidget(scroll)
self.setLayout(vbox)
self.show()
def nbheures(self,text):
for i in range(Window.table10.rowCount()):
if text == str(Window.table10.item(i,0).text()):
value=str(Window.table10.item(i,1).text())
break
print(value)
self.label1.setText(value)
if __name__ =="__main__":
App = QApplication(sys.argv)
App.aboutToQuit.connect(App.deleteLater)
window = Window()
App.exec()
You are calling self.InitWindow() twice (one in Window.__init__, one in ProfilCharge.__init__). The second time, you try to set a new layout to widget which already has one and the operation is discarded.
The current value of self.label1 is changed the second time. But, as you still see the first instance of your label, you will not see any changes.
ProfilCharge should not inherit from Window.

AttributeError: 'loginUser' object has no attribute 'var'

I am trying to limit characters, but I find this error. Someone to help?
And would it be able to limit characters from type A to D and from 0 to 9?
In the initializer I already got it, now I want to try out the initializer, but I could not understand this error, I'm new at it.
class loginUser:
def __init__(self, window, master=None):
self.wind = window
self.wind.title("System F2T")
#Definicoes de fonte p/ o layout de login
self.fonteTitulo = ("Arial","10","bold")
self.fontePadrao = ("Arial", "10")
#Labels e campos de texto do sistema de login
self.userLabel = Label(text="Digite seu usuário:", font=self.fontePadrao,bg="#000",fg="#FFF").place(x=27,y=60)
self.user = Entry(textvariable=self.var, font=self.fontePadrao,bg="#FFF",fg="#000")
self.user.place(x=140,y=60,width=110)
self.senhaLabel = Label(text="Digite sua senha:", font=self.fontePadrao,bg="#000",fg="#FFF").place(x=29,y=90)
self.senha = Entry(textvariable=self.var2, font=self.fontePadrao,show="*",bg="#FFF",fg="#000")
self.senha.place(x=140,y=90,width=110)
self.max_user = 1
self.var = StringVar()
self.var.trace("w", loginUser.limiteUsuario)
self.max_senha = 4
self.var2 = StringVar()
self.var2.trace("w", loginUser.limiteSenha)
def limiteUsuario(self,*args):
u = self.var.get()
if len(u) == 1 and not 65<=ord(u)<=68: # you can also use *if not u in "ABCD"*
self.var.set("")
elif len(u) > 1:
if not 65<=ord(u[-1])<=68: # retirar ultimo caracter caso nao seja digito
self.var.set(u[:-1])
else: # aproveitar apenas os primeiros 5 chars
self.var.set(u[:self.max_user])
def limiteSenha(self,*args):
s = self.var2.get()
if len(s) > 4:
if not s[-1].isdigit(): # retirar ultimo caracter caso nao seja digito
self.var2.set(s[:-1])
else: # aproveitar apenas os primeiros 5 chars
self.var2.set(s[:self.max_senha])
if __name__ == "__main__":
root = Tk()
root['bg'] = "#000"
loginUser(root)
#Tamanho da janela
root.geometry("330x200")
root.mainloop()
As Bryan pointed out, your self.var is being set as a text variable before it has been created. Simply shuffle where you defined them to fix.
class loginUser:
def __init__(self, window, master=None):
self.wind = window
self.wind.title("System F2T")
...
self.var = StringVar() #create the var first before you assign them
self.var2 = StringVar()
#Labels e campos de texto do sistema de login
self.user = Entry(textvariable=self.var, font=self.fontePadrao,bg="#FFF",fg="#000")
...
self.senha = Entry(textvariable=self.var2, font=self.fontePadrao,show="*",bg="#FFF",fg="#000")
On how to limit your the input from A-D, you can add an additional check in your limiteUsuario method. Note that I also fixed the potential errors from your class method.
def limiteUsuario(self,*args):
u = self.var.get()
if len(u) == 1 and not 65<=ord(u)<=68: # you can also use *if not u in "ABCD"*
self.var.set("")
elif len(u) > 1:
if not 65<=ord(u[-1])<=68: # retirar ultimo caracter caso nao seja digito
self.var.set(u[:-1])
else: # aproveitar apenas os primeiros 5 chars
self.var.set(u[:self.max_user])
You can use the similar logic for number 0-9.

AttributeError: 'StringVar' object has no attribute 'tk'

I'm trying to limit characters, but I got this error and I have no idea how to get out, I'm not very experienced in python, I caught up shortly in language.
And already enjoying the crash wanted to know how I do to limit a user to register with the same user. Example:
It has been registered "A", and in the next one I will not be able to register it anymore
from tkinter import *
from tkinter import ttk
import tkinter as tk
class loginUser:
def __init__(self, window, master=None):
self.wind = window
self.wind.title("System F2T")
#Definicoes de fonte p/ o layout de login
self.fonteTitulo = ("Arial","10","bold")
self.fontePadrao = ("Arial", "10")
self.var = tk.StringVar()
self.var2 = tk.StringVar()
self.userLabel = Label(text="Digite seu usuário:", font=self.fontePadrao,bg="#000",fg="#FFF").place(x=27,y=60)
self.user = Entry(textvariable=self.var, font=self.fontePadrao,bg="#FFF",fg="#000")
self.user.place(x=140,y=60,width=110)
self.senhaLabel = Label(text="Digite sua senha:", font=self.fontePadrao,bg="#000",fg="#FFF").place(x=29,y=90)
self.senha = Entry(self.var2, font=self.fontePadrao,show="*",bg="#FFF",fg="#000")
self.senha.place(x=140,y=90,width=110)
def limiteUsuario(self,*args):
u = self.var.get()
if len(u) == 1 and not 65<=ord(u)<=68: # you can also use *if not u in "ABCD"*
self.var.set("")
elif len(u) > 1:
if not 65<=ord(u[-1])<=68: # retirar ultimo caracter caso nao seja digito
self.var.set(u[:-1])
else: # aproveitar apenas os primeiros 5 chars
self.var.set(u[:self.max_user])
def limiteSenha(self,*args):
s = self.var2.get()
if len(s) > 4:
if not s[-1].isdigit(): # retirar ultimo caracter caso nao seja digito
self.var2.set(s[:-1])
else: # aproveitar apenas os primeiros 5 chars
self.var2.set(s[:self.max_senha])
if __name__ == "__main__":
root = Tk()
root['bg'] = "#000"
loginUser(root)
#Tamanho da janela
root.geometry("330x200")
root.mainloop()

Tkinter GUI Freezes - Tips to Unblock/Thread?

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()
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.

Resources