树莓派、tkinter 和线程
Posted
技术标签:
【中文标题】树莓派、tkinter 和线程【英文标题】:Raspberry pi, tkinter and threading 【发布时间】:2016-11-05 17:40:16 【问题描述】:我正在开发一个应用程序来监控办公室照明系统。我正在使用 Tkinter 开发 GUI,我尝试在按钮命令中运行一个循环,它冻结了我的 GUI。所以我读了一些关于踩踏模块的信息。
我想在循环中使用从我的变量 var 返回的值。我尝试如下所示,但函数enable_automatico
没有调用我的线程函数acionamento_automatico
。我没有收到任何错误>
#!/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()
【问题讨论】:
也许使用root.after(miliseconds, function_name)
定期执行函数,你将不需要你的循环和线程。
对我来说acionamento_automatico
不是Thread
类的一部分,所以enable_automatico
无法启动它。您必须使用名称run()
。
我将在这个应用程序上有更多线程。你能举一个在我的代码上使用线程的实际例子吗?我很迷茫......
【参考方案1】:
线程运行它的函数run()
中的代码,但你没有它
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()
或者你可以做得更短
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()
编辑: 示例如何控制线程并停止它。
顺便说一句:在某些情况下,您应该使用queue
与线程通信。只有主线程应该使用print()
并使用像tkinter
这样的GUI
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
【讨论】:
Furas 谢谢。效果很好,我只收到一个错误,说我的线程不在 tkinter 的主循环中。我该如何处理这个异常? 显示此执行 - 全文。必须在root.mainloop()
之前创建/启动线程。顺便说一句:我在您的代码中看不到 mainloop()
- 没有它 tkinter 就无法运行(除非您在使用 tkinter
并运行 mainloop()
的 IDLE 中运行)。而且您必须在关闭tkinter
窗口之前停止线程。因为线程会尝试使用关闭窗口时删除的 tkinter 元素。
好的传奇,得到它并修复它。谢谢!!
你好 Furas,一旦我使用 Thead 子包启动线程,如何查看当前线程状态并停止它?
@armf1993 你只有thread.is_alive()
来检查线程是否正在运行。要查看更多内容并阻止它,您必须使用自己的变量,即。 stop = False
并将其在线程内检查到if stop: break
。请参阅答案中的新示例。以上是关于树莓派、tkinter 和线程的主要内容,如果未能解决你的问题,请参考以下文章