Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
Actualizar etiqueta label desde otro hilo (tkinter)
#1
hola a todos, estoy teniendo problemas para que una etiqueta actualice su texto en función del contenido de una variable que está en otro script de python.
la variable sería un contador para cuando se ejecuta una función.
hasta ahora la etiqueta en la ventana inicia con un texto y cuando se ejecuta el botón para que se inicie la función , esta cambia su texto y toma el valor por defecto de la variable, pero no toma el incremento

El código del programa gui_tkinter.py
Código:
import sys
import threading
import tkinter as tk
from tkinter import *
from recognize_google_ import *
class ventana():
    def __init__(self):
        self.root = tk.Tk()
        self.root.title(" ")
        self.root.geometry("250x100")
        self.root.resizable(False, False)
        self.texto = tk.StringVar()
        self.texto.set("hola mundo")
        self.lblMensaje = tk.Label(self.root, textvariable=self.texto).pack()
        self.btnMiboton = tk.Button(self.root, text="inicio", command=self.inicio, background="dark green", activebackground="green").pack(pady=2, padx=1)
        self.btnMiboton2 = tk.Button(self.root, text="salir", command=sys.exit, background="dark red", activebackground="red").pack(pady=2, padx=1)

        self.root.mainloop()

    def changeText(self):
        self.texto.set("nuevo mensaje %s" % cont)
    def inicio(self):
        t0 = threading.Thread(target=main, daemon=True)
        t0.start()
        self.changeText()
if __name__ == "__main__":
    ventana()
   

el código del programa que tiene el contador
Código:
import sys
import pyttsx3
import speech_recognition as sr
from fouier_ import *


listener = sr.Recognizer()

engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty('rate', 165)
# engine.setProperty('voice', voices[2].id) #
engine.setProperty('voice', 'spanish-latin-am+f2') # linux

cont = 0

def talk(text):
    engine.say(text)
    engine.runAndWait()

def listen():
    try:
        with sr.Microphone() as source:
            print("Escuchando...")
            listener.adjust_for_ambient_noise(source, duration=0.5)
            audio = listener.listen(source)
    except:
        pass
    return audio


def recognize_audio(audio):
    print("Procesando...")
    rec = listener.recognize_google(audio, language='es-AR')
    rec = rec.lower()
    # print(rec)
    return rec


def main():
    global cont
    while True:
        try:
            response = recognize_audio(listen())

            if "cambiar" in response:
                sonido()
                cont = cont+1

            elif "cerrar" in response or "apagar" in response:
                break

            talk(response)
            print(cont)

        except:
            pass
    sys.exit()

if __name__ == "__main__":
    main()

[Imagen: Captura-de-pantalla-de-2022-12-27-12-27-06.png][Imagen: Captura-de-pantalla-de-2022-12-27-12-27-34.png]
agradecería si me dieran una mano para poder lograr que la etiqueta se actualice
gracias...
Responder
#2
Hola, ¿cómo estás?

El problema no es tanto que tengas la variable en otro archivo, sino que tu función se ejecuta en otro hilo. Es un problema típico en las aplicaciones de escritorio. Si ejecutás un código en otro hilo, no podés hacer cambios directamente en la interfaz gráfica (por ejemplo, actualizar el texto de una etiqueta). Te dejo un artículo sobre el tema para que entiendas el problema y cómo solucionarlo: Tareas en segundo plano con Tcl/Tk (tkinter).

Leído ese artículo, ahora te dejo este código de ejemplo que actualiza una etiqueta con un contador desde otro hilo. Como los controles de Tk no se pueden alterar desde el hilo secundario, se usa una cola (queue.Queue), que es un tipo de dato que permite transmitir información entre hilos, para enviar el contador actualizado al hilo principal desde donde se hace el cambio a la etiqueta.

Código:
import threading
import queue
import time
import tkinter as tk
from tkinter import ttk


# Esta función corre en el hilo secundario.
def worker(q):
    cont = 0
    while True:
        cont += 1
        # Poner el nuevo contador en la cola.
        q.put_nowait(cont)
        time.sleep(1)


def actualizar_etiqueta(q):
    try:
        cont = q.get_nowait()
    except queue.Empty:
        # Todavía no se recibió ningún dato del hilo.
        pass
    else:
        label["text"] = f"Nuevo mesaje {cont}"
    # Reprogramar esta función para dentro de 1 segundo.
    root.after(1000, actualizar_etiqueta, q)


root = tk.Tk()
label = ttk.Label()
label.pack()
# Cola para mandar información de un thread a otro.
q = queue.Queue()
t = threading.Thread(target=worker, args=(q,))
t.start()
root.after(1, actualizar_etiqueta, q)
root.mainloop()

A partir de esto deberías poder implementar algo similar para tu código. Si la función del hilo está en otro archivo o no es irrelevante para este caso.

Avisame cómo te fue.

Saludos
Responder
#3
Thumbs Up 
hola francisco, leo el articulo, veo como implementarlo y aviso
gracias
Responder
#4
hola francisco, logré adaptar parte del código que pasaste y ahora el contador funciona

[Imagen: Captura-de-pantalla-de-2022-12-28-21-30-36.png]


para no hacerme lío simplifique el código para tkinter sacando la clase, después veré como lo adapto
acá dejo como quedó

GUI_simple_tkinter.py
Código:
import sys
import threading
import tkinter as tk
from tkinter import ttk
from recognize_google_ import *

def inicio():
    t0 = threading.Thread(target=main, daemon=True)
    t0.start()
def actualizar_etiqueta(q):
    try:
        cont = q.get_nowait()
    except queue.Empty:
        # Todavía no se recibió ningún dato del hilo.
        pass
    else:
        label["text"] = f"Nuevo mensaje {cont}"
    # Reprogramar esta función para dentro de 1 segundo.
    root.after(1000, actualizar_etiqueta, q)
root = tk.Tk()
root.title(" ")
root.geometry("250x100")
root.resizable(False, False)
label = ttk.Label(root, text=f"Nuevo mensaje {cont}", background="#3B3E3F", foreground="white")
label.pack(pady=6, padx=1)
btnMiboton = ttk.Button(root, text="inicio", command=inicio)
btnMiboton.pack(pady=2, padx=1)
btnMiboton2 = ttk.Button(root, text="salir", command=sys.exit )
btnMiboton2.pack(pady=2, padx=1)
root.after(1, actualizar_etiqueta, q)
root.mainloop()

recognize_google_.py
Código:
import sys
import queue
import pyttsx3
import speech_recognition as sr
from fouier_ import *

#from playsound import playsound



listener = sr.Recognizer()

engine = pyttsx3.init()
voices = engine.getProperty('voices')
engine.setProperty('rate', 165)
# engine.setProperty('voice', voices[2].id) #
engine.setProperty('voice', 'spanish-latin-am+f2')  # linux

'''for voice in voices:
    print(voice)'''

#filename = '01-Audio 1.wav'

cont = 0
q = queue.Queue()


'''def _sonido():
    playsound(filename)'''


def talk(text):
    engine.say(text)
    engine.runAndWait()


def listen():
    try:
        with sr.Microphone() as source:
            print("Escuchando...")
            listener.adjust_for_ambient_noise(source, duration=0.5)
            audio = listener.listen(source)
    # except Exception as e:
        # print(e)
    except:
        pass
    return audio


def recognize_audio(audio):
    print("Procesando...")
    rec = listener.recognize_google(audio, language='es-AR')
    rec = rec.lower()
    # print(rec)
    return rec


def main():
    global cont
    global q

    while True:
        try:
            response = recognize_audio(listen())

            if "cambiar" in response:
                # sonido()
                sonido_()
                cont = cont+1
               
            elif "cerrar" in response or "apagar" in response:
                break

            talk(response)
            print(response)
            print(cont)
            q.put_nowait(cont)
        except:
            pass
    sys.exit()


if __name__ == "__main__":
    main()

tengo un tercer script que reproduce un sonido y se comunica con un arduino para que este aumente o disminuya la intensidad de una luz al estilo de una especie de audio rítmico (aunque todavía no lo he probado)

saludos y gracias
PD: felices fiestas
Responder
#5
¡Excelente! Usando clases la lógica es la misma, solo habría que cambiar un poco las cabeceras de las funciones para que acepten el primer argumento self.

¡Felices fiestas!
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)