xKNX und Tkinter

Fragen zu Tkinter.
Antworten
harald.wurzer
User
Beiträge: 2
Registriert: Dienstag 12. Oktober 2021, 10:34

Mittwoch 13. Oktober 2021, 15:51

Hallo, ich bin Harald, Systemintegrator (Gebäudeleittechnik) und "ziemlicher" Python Anfänger.
Ich plage mich gerade ein wenig für mein Projekt mit Python. Es macht Spaß, aber ich komme nicht so richtig weiter.
Ich möchte zwei Werte welche ich von 2 Button in der GUI aufrufe ein Licht über XKNX schalten. Über die Python (input) Eingabeaufforderung kann ich das "Licht" bereits schalten.

Über die GUI geht es "nur" in dem ich den Button drücke. Der Wert wird aber leider erst gesendet wenn ich die GUI schließe. Leider komme ich nicht drauf .... das das Licht bereits beim Drücken des Button schaltet.

Anbei mein "zusammengebastelter" Code.

Code: Alles auswählen

import asyncio
import logging

from xknx import XKNX
from xknx.devices import Switch
from xknx.io import ConnectionConfig, ConnectionType
from tkinter import *


# Die folgende Funktion soll ausgeführt werden, wenn
# der Benutzer den Button anklickt
def button_action_ein():
    global a_1
    a_1 = (1)
    print(a_1)
    # anweisungs_label.config(text="Ich wurde geändert!")

def button_action_aus():
    global a_1
    a_1 = (0)
    print(a_1)

# Ein Fenster erstellen
fenster = Tk()
# Den Fenstertitle erstellen
fenster.title("kleine KNX Visu")
# Die Größe des Fenster fix einstellen
fenster.geometry('350x145')
# Label und Buttons erstellen.
change_button = Button(fenster, text="Küchenlicht EIN", command=button_action_ein)
change_button.pack()
change_button = Button(fenster, text="Küchenlicht AUS", command=button_action_aus)
change_button.pack()

fenster.mainloop()

logging.basicConfig(level=logging.INFO)
logging.getLogger("xknx.log").level = logging.DEBUG
logging.getLogger("xknx.knx").level = logging.DEBUG

async def main():
    connection_config = ConnectionConfig(
        connection_type=ConnectionType.TUNNELING,
        gateway_ip="10.0.0.126",    )

    #a_1 = int(input("Licht ein:"))
    print (a_1)
    if a_1 == 1:
        print("Licht EIN!")
        xknx = XKNX(connection_config=connection_config)
        await xknx.start()
        Switch(xknx, name="TestOutlet", group_address="0/0/10")

        await xknx.devices["TestOutlet"].set_on()

        await xknx.stop()
    else:
        print("Licht AUS!")
        xknx = XKNX(connection_config=connection_config)
        await xknx.start()
        Switch(xknx, name="TestOutlet", group_address="0/0/10")

        await xknx.devices["TestOutlet"].set_off()

        await xknx.stop()

asyncio.run(main())
Über einen Tipp und ein paar erklärenden Worten, würde ich mich freuen. (Sorry für den Code ... wie gesagt ziemlicher Anfänger) - :o

Danke
Harald
__deets__
User
Beiträge: 10485
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 13. Oktober 2021, 16:14

Nach der Anweisung "mainloop" befindet sich tkinter in seiner Ereignisschleife. Ab dem Zeitpunkt kann und wird kein Code mehr danach ausgefuehrt.

Eine Integration von tkinter und asyncio ist nicht trivial. Wahrscheinlich ist das beste, die KNX-main in einen Thread zu verlagern, und aus der GUI mittels loop.call_soon_threadsafe aufzuwecken, wenn sich etwas tut.
__deets__
User
Beiträge: 10485
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 13. Oktober 2021, 17:05

Weil mich das mal interessiert hat, habe ich das kurz skizziert:

Code: Alles auswählen

import asyncio
import tkinter as tk
import threading


class AsyncioBackgroundThread:

    def __init__(self):
        self._event = threading.Event()
        self._loop = None
        t = threading.Thread(target=self._run_loop)
        t.daemon = True
        t.start()
        # Ensure we really have a asyncio loop running
        self._event.wait()

    def _run_loop(self):
        self._loop = asyncio.new_event_loop()
        self._event.set()
        self._loop.run_forever()

    def run_in_loop(self, callable, *args, **kwargs):
        self._loop.call_soon_threadsafe(
            callable, *args, **kwargs
        )


async def ten_times_printer():
    for i in range(10):
        print("Run", i)
        await asyncio.sleep(1)


def run_a_task():
    loop = asyncio.get_event_loop()
    loop.create_task(ten_times_printer())


def main():
    background_thread = AsyncioBackgroundThread()
    root = tk.Tk()
    button = tk.Button(
        root,
        text="Push to background loop",
        command=lambda: background_thread.run_in_loop(run_a_task)
    )
    button.pack()
    root.mainloop()


if __name__ == '__main__':
    main()
Das waere erstmmal das Geruest, um einen Thread, der einen asyncio-Loop laufen laesst, mit Aufgaben zu bestuecken. Dinge wie deine KNX-Connection solltest du uebrigens nur *einmal* starten, und dann einfach immer wieder verwenden.
harald.wurzer
User
Beiträge: 2
Registriert: Dienstag 12. Oktober 2021, 10:34

Freitag 15. Oktober 2021, 07:26

Hallo,

Danke für deine Antwort. Leider "blicke" ich hier noch nicht ganz durch. Wo bzw. wie würde ich dann die KNX-Connection und die Button für ein/aus einfügen?

Danke .

MfG
Harald
__deets__
User
Beiträge: 10485
Registriert: Mittwoch 14. Oktober 2015, 14:29

Freitag 15. Oktober 2021, 10:31

Das geht an sich analog dazu, wie ich jetzt auch einen Task erstellt habe, nur eben mir der asynchronen KNX main.

Ich würde dir aber empfehlen, dich mit anderen Systemen zur Heimautomation wie FHEM oder einer anderen Programmierumgebung wie CodeSYS zu beschäftigen. GUI und asyncio sind fortgeschritten, und dazu fehlen dir im Moment die Voraussetzungen.
Antworten