tkinter in einem thread nur bei bedarf starten

Fragen zu Tkinter.
Antworten
sf169
User
Beiträge: 1
Registriert: Dienstag 25. August 2015, 17:29

Hallo zusammen,

ich suche nun schon seit tagen und komme nicht wirklich weiter. Dazu muss ich sagen, daß ich ein Python-Frischling bin.

Ich bin dabei ein kleines Programm zu schreiben, welches über eine Raspberry Pi2 mit Python 3.4.3 die Impulse eines Messgerätes zählt.
Der Part mit dem GPIO und Abfrage der Impulse (möglichst viele - ca. max. 5000/min gibt das Messgerät - da kann der Raspi nicht mit, macht aber nichts, ca. 500/min bekomme ich maximal mit) ist nun nicht das Problem. Möchte aber gern die Verzögerungen so gering als möglich halten.

Angefangen habe ich mit einer GUI mit TKinter. Da die Software in Zukunft aber nur optional mit einer Grafischen Oberfläche laufen soll, möchte ich Tkinter nur modular laden, wenn das Programm über VNC oder direkten Anschluss an Monitor/Tastatur/Maus aufgerufen wird.

Die anderen Optionen wären dann ein Aufruf über den GPIO (einen Taster drücken) mit Darstellung der Werte über ein LCD-Display oder der Aufruf über SSH und Steuerung über eine Handy-App.

Da alle optionen zusammen na klar Rechenleistung erfordern, würd ich gern nur die Option(en) laden, welche auch benötigt werden bzw. auch verbaut sind.

Aktuell hab ich halt das Problem, daß ich Tkinter nur optional in einem Thread aufrufen möchte und die Daten dann per Queue austauschen möchte:
Der User sagt: mach mir eine Summe über 1 / 3 oder 5 Minuten, Starte die Messung, Beende die Messung würd von der GUI kommen.
Das eigendliche Programm gibt dann halt alle 1 / 3 oder 5 Minuten zurück: Summe X / Durchschnitt vorher.
Diese Informationen werden dann alternativ / optional auch auf einem LCD-Display bereitgestellt und in Zukunft auch über eine Verbindung zum Handy.

Evtl. hat da jemand einen Tip für mich.

Der aktuelle Code funktioniert nicht. Ich hab mal eine alte Version angehangen. Tkinter soll halt in einen eigenen Thread abgeteilt werden. Über Kritik würde ich mich auch sehr freuen.

Code: Alles auswählen

#!/usr/bin/env python3.4
from tkinter import *
import datetime
import time
import pigpio
import threading
import sys
import mysql.connector
import LCD_PIGPIO
import os

Hauptfenster = Tk()
Hauptfenster.title("Sferics-Test ")
Hauptfenster.geometry("300x400+30+30")

Anzeigefeld = Label(Hauptfenster, text="Herzlich Willkommen", bg="light grey")
Anzeigefeld.pack(pady=10)

FrequenzAbfrage = Entry(Hauptfenster)
FrequenzAbfrage.pack(pady=10)

ReadFreqButton = Button(Hauptfenster, text="Abfragezeit in Minuten angeben")
ReadFreqButton.pack(pady=10)

DoitButton = Button(Hauptfenster, text="Warte auf Eingabe der Abfragezeit", state=DISABLED)
DoitButton.pack(pady=10)

Wertefeld = Label(Hauptfenster, text="", bg="light grey")
Wertefeld.pack(pady=10)

ShutdownButton = Button(Hauptfenster, text="Herunterfahren")
ShutdownButton.pack(pady=10)

#LED_PIN = 5
OPTO_PIN = 6
ZAEHLTAST_PIN = 12
STARTTAST_PIN = 13
SHUTDOWNTAST_PIN = 16

Aktiv = 0
AbfrageFrequenz = 0
AktuelleSumme = 0
LetzteSumme = 0
LetzteSumme2 = 0
LetzteSumme3 = 0
#LedAn = 0
Tabellenname = "Werte"
StartTastAktiv = 0
ShutdownTastAktiv = 0
ZaehlTastAktiv = 0
OptoAktiv = 0
TICK = 0

def usleep(microseconds):
    """Sleep the specified amount of microseconds."""
    time.sleep(microseconds / 1000000.0)

def Datenbank_aktivieren():
  global Datenbank
  global Datentabelle
  global Tabellenname

  Tabellenname = "Werte-" + time.strftime("%Y%m%d")

  Datenbank = mysql.connector.connect(host='localhost', user='root', password='Metwurst', db='Messwerte',autocommit=True)
  Datentabelle = Datenbank.cursor()

  Tabellenstring =  """\
         CREATE TABLE IF NOT EXISTS `""" + Tabellenname  + """`(
         ID INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
         Unixzeit DOUBLE(18,7) NOT NULL,
         Wert INTEGER 
         )"""
  Datentabelle.execute(Tabellenstring)

def Datenbank_schreiben():
   global Datenbank
   global Datentabelle
   global Tabellenname
   global AktuelleSumme
   global LetzteSumme
   global LetzteSumme2
   global LetzteSumme3
   global LCD
   global Startzeit

   LetzterDurchschnitt = 0

#   unixzeit = float(time.time())
#   windowszeit= ((unixzeit/86400)+25569)
   Anzeigestring = "Summe: " + str(AktuelleSumme) + " um " + time.strftime("%H:%M:%S")
   LCDString = "Summe: " + str(AktuelleSumme)
   Anzeigestring += chr(13) + chr(13)
   Anzeigestring += "Vorherige Summe: " + str(LetzteSumme) + chr(13) + chr(13) + "Tendenz: "
   LCD2String = "Vorher:" + str(LetzteSumme) + " T:"
   Gesamtsumme = LetzteSumme + LetzteSumme2 + LetzteSumme3
   if LetzteSumme3 > 0:
     LetzterDuchschnitt = Gesamtsumme / 3
   else
     if letzteSumme2 > 0:
       LetzterDuchschnitt = Gesamtsumme / 2
     else  
       LetzterDurchschnitt = Gesamtsumme
   if LetzterDurchschnitt == AktuelleSumme:
       Anzeigestring += "gleichbleibend"
       LCD2String += " ="
   elif LetzterDurchschnitt > AktuelleSumme:
       Anzeigestring += "Fallend"
       LCD2String += " <"
   elif LetzterDurchschnitt < AktuelleSumme:
       Anzeigestring += "Steigend"
       LCD2String += " >"
   Wertefeld["text"] = Anzeigestring 
   Tabellenstring =  """\
         INSERT INTO `""" + Tabellenname + """`(
         Unixzeit,
         Wert
         ) VALUES ( """ + str(Startzeit + (AbfrageFrequenz*60)) + ", " + str(AktuelleSumme) + ")"
   LCD.clear()
   LCD.write_string(LCDString + '\n\r' + LCD2String)
   Datentabelle.execute(Tabellenstring)
   LetzteSumme3 = LetzteSumme2
   LetzteSumme2 = LetzteSumme
   LetzteSumme = AktuelleSumme
   AktuelleSumme = 0
   
   
def Messwert_da():
   global Startzeit
   global AktuelleSumme
   global AbfrageFrequenz
   #LED_steuern()
   if (Startzeit + (AbfrageFrequenz*60)) <=  float(time.time()):
      #Startseit muss noch neu berechnet werden - falls mal eine Minute nix passiert
      Startzeit = float(time.time())
      Datenbank_schreiben()
   AktuelleSumme = AktuelleSumme + 1
 

#def LED_steuern():
#    global LedAn
#    global LED_PIN
#    global gpio
    #if LedAn == 0: 
#    gpio.write(LED_PIN, 0)
    #   LedAn = 1
    #else:
#    Hauptfenster.update()
#    gpio.write(LED_PIN, 1)
#    LedAn = 0

#def Impuls_erkannt(gpio, level, tick):
#    LED_steuern()
#    Messwert_da()

def Messung_starten_beenden():
  global Aktiv
  global AbfrageFrequenz
  global LCD
  global Startzeit
  #global LED_PIN
  #global gpio
  #global OPTO_PIN
  #global Starttast
  #global STARTTAST_PIN

  #if not gpio:
  #  GPIO_aktivieren()

  Startzeit = float(time.time())
  
  if Aktiv == 0:
    Aktiv = 1
    Datenbank_aktivieren()
    Zaehltast_deaktivieren()
    Shutdown_deaktivieren()
    ReadFreqButton["state"] = "disabled"
    ShutdownButton["state"] = "disabled"
    FrequenzAbfrage["state"] = "disabled"
    DoitButton["text"] = "beenden"
    Anzeigefeld["text"] = "Abfrage eingestellt auf " + str(AbfrageFrequenz) + " Minuten" + chr(13) + "Messung gestartet"
    Anzeigefeld["bg"] = "red"
    LCD.clear()
    LCD.write_string("Messung start\n\r  keine Werte")
    Startzeit = float(time.time())
    Opto_aktivieren()
    #Zaehltast_aktivieren()
    #Shutdown_aktivieren()
  else:
    Aktiv = 0
    Opto_deaktivieren()
    Datenbank.close()
    ReadFreqButton["state"] = "active"
    ShutdownButton["state"] = "active"
    FrequenzAbfrage["state"] = "normal"
    DoitButton["text"] = "starten"
    Anzeigefeld["text"] = "Abfrage eingestellt auf " + str(AbfrageFrequenz) + " Minuten" + chr(13) + "Messung beendet"
    Anzeigefeld["bg"] = "light grey"
    LCD.clear()
    LCD.write_string("Messung stop\n\r" + time.strftime("%H:%M:%S"))
    Zaehltast_aktivieren()
    Shutdown_aktivieren()

  #Startzeit = float(time.time())

  #Opto_aktivieren()

  #gpio.set_pull_up_down(OPTO_PIN, pigpio.PUD_UP)
  #Opto1 = gpio.callback(OPTO_PIN, pigpio.FALLING_EDGE, Callback_detected)
  #Opto1 = gpio.callback(OPTO_PIN, pigpio.FALLING_EDGE, Impuls_erkannt)
  #gpio.set_pull_up_down(STARTTAST_PIN, pigpio.PUD_UP)
  #Starttast = gpio.callback(STARTTAST_PIN, pigpio.FALLING_EDGE, Callback_detected)
  #usleep(1) 
  #LCD.clear()
  #LCD.write_string("Messung start\n\r  keine Werte")
  #while Aktiv==1:
  #   Hauptfenster.update()
  #   usleep(1)
  #   if (Startzeit + (AbfrageFrequenz*60)) <=  float(time.time()):
  #      Datenbank_schreiben()
  #      Startzeit = float(time.time())   
  #else:
          #Opto1.cancel()
  #        Opto_deaktivieren()
  #        LCD.clear()
  #        LCD.write_string("Messung beendet\n\r  Tanke")
  #        gpio.write(LED_PIN, 1)
  #        gpio.set_pull_up_down(OPTO_PIN, pigpio.PUD_OFF)
  #        Datenbank.close()
         

def FrequenzLesen():
   global AbfrageFrequenz
   global StartTastAktiv
   try:
    AbfrageFrequenz = float(FrequenzAbfrage.get())
    Anzeigefeld["text"] = "Abfrage eingestellt auf " + str(AbfrageFrequenz) + " Minuten"
    Anzeigefeld["bg"] = "light grey"
    DoitButton["text"] = "Starten"
    DoitButton["state"] = "active"
    LCD.clear()
    LCD.write_string('Alle ' + str(AbfrageFrequenz) + ' min\n\r  Start klicken')
    if StartTastAktiv == 0:
      StartTastAktiv = 1
      Starttast_aktivieren()
   except ValueError:
     Anzeigefeld["text"] = "Bitte eine gueltige Zahl eingeben"
     Anzeigefeld["bg"] = "red"
     DoitButton["text"] = "Warte auf Eingabe der Abfragezeit"
     DoitButton["state"] = "disabled"
     StartTastAktiv = 0
     Starttast_deaktivieren()
     print('Mist')
   return(0)

def Callback_detected(cb_gpio, level, tick):
   global gpio
   global TICK
   global ZAEHLTAST_PIN
   global STARTTAST_PIN
   global SHUTDOWNTAST_PIN
   global OPTO_PIN
   global ZaehlTastAktiv
   global StartTastAktiv
   global ShutdownTastAktiv
   global OptoAktiv

   if cb_gpio == OPTO_PIN:
     if OptoAktiv == 1:
       #print('opto ', tick)
       Messwert_da()
   else:
     if (TICK + 200000) < tick:
       TICK = tick  
       print('GPIO:', cb_gpio, '\n Level:' , level, '\n tick:', tick)
       if level == 0:
         if cb_gpio == ZAEHLTAST_PIN:
           if ZaehlTastAktiv == 1:
             #print('Zaehler hoch')
             Frequenz_einstellen()
         elif cb_gpio == STARTTAST_PIN:
           if StartTastAktiv == 1: 
              #print('Startstop gedrueckt')
              Messung_starten_beenden()
         elif cb_gpio == SHUTDOWNTAST_PIN:
           if ShutdownTastAktiv == 1: 
             #print('Runterfahren gedrueckt')
             Herunterfahren()
       elif level == 1:
         print('falscher Level ', str(level))
         if cb_gpio == ZAEHLTAST_PIN:
           print('Zaehler hoch')
           #Frequenz_einstellen()
         elif cb_gpio == STARTTAST_PIN:
           print('Startstop gedrueckt')
           #Messung_Start_aktivieren()
         elif cb_gpio == SHUTDOWNTAST_PIN:
           print('Runterfahren gedrueckt')
       else:
         print('falscher Level ', str(level))
         if cb_gpio == ZAEHLTAST_PIN:
           print('Zaehler hoch')
           #Frequenz_einstellen()
           Zaehltast_deaktivieren()  
         elif cb_gpio == STARTTAST_PIN:
           print('Startstop gedrueckt')
           #gpio.set_watchdog(STARTTAST_PIN,10)
           #Messung_Start_aktivieren()
           Starttast_deaktivieren()
         elif cb_gpio == SHUTDOWNTAST_PIN:
           print('Runterfahren gedrueckt')
           Shutdown_deaktivieren()
         #Zaehltast_deaktivieren()
         #Starttast_deaktivieren()
         #Opto_deaktivieren()
         stop()  

#def Messung_Start_aktivieren():
#  global Aktiv
#  global gpio
#  global LCD

  
  #if not gpio:
  #  print("Kein GPIO - also GPIO-aktivieren")  
  #  GPIO_aktivieren()

#  if Aktiv == 0:
#    Zaehltast_deaktivieren()
#    Shutdown_deaktivieren()
#    ReadFreqButton["state"] = "disabled"
#    ShutdownButton["state"] = "disabled"
#    FrequenzAbfrage["state"] = "disabled"
#    DoitButton["text"] = "beenden"
#    Anzeigefeld["text"] = "Abfrage eingestellt auf " + str(AbfrageFrequenz) + " Minuten" + chr(13) + "Messung gestartet"
#    Anzeigefeld["bg"] = "red"
#    Aktiv = 1
#    Messung_starten()
#    LCD.clear()
#    LCD.write_string("Messung stop\n\r" + time.strftime("%H:%M:%S"))
    #Zaehltast_aktivieren()
    #Shutdown_aktivieren()
#  else:
#    ReadFreqButton["state"] = "active"
#    ShutdownButton["state"] = "active"
#    FrequenzAbfrage["state"] = "normal"
#    DoitButton["text"] = "starten"
#    Anzeigefeld["text"] = "Abfrage eingestellt auf " + str(AbfrageFrequenz) + " Minuten" + chr(13) + "Messung beendet"
#    Anzeigefeld["bg"] = "light grey"
#    Aktiv = 0
    #Zaehltast_aktivieren()
    #Shutdown_aktivieren()
#  return(0)

def GPIO_aktivieren():
  global gpio
  gpio = pigpio.pi()
  gpio.set_mode(LED_PIN, pigpio.OUTPUT)
  gpio.write(LED_PIN, 1)

def LCD_aktivieren():
  global LCD
  global gpio
  LCD = LCD_PIGPIO.CharLCD (MYGPIO=gpio, cols=16, rows=2)
  LCD.clear()
  LCD.write_string('Hallo Jens\n\r viel Spass')

def Herunterfahren():
  global gpio
  global LCD
  Zaehltast_deaktivieren()
  Starttast_deaktivieren() 
  LCD.clear()
  LCD.write_string('Herunterfahren')
  gpio.stop()
  os.system("sudo shutdown -h now")
  Hauptfenster.destroy()

def Frequenz_einstellen():
  global AbfrageFrequenz
  if AbfrageFrequenz == 0:
      AbfrageFrequenz = 1  
  elif AbfrageFrequenz < 3:
      AbfrageFrequenz = 3 
  elif AbfrageFrequenz < 5:
      AbfrageFrequenz = 5
  elif AbfrageFrequenz < 10:
      AbfrageFrequenz = 10 
  elif AbfrageFrequenz < 15:
      AbfrageFrequenz = 15 
  elif AbfrageFrequenz == 15: 
      AbfrageFrequenz = 1
  elif AbfrageFrequenz > 15: 
      AbfrageFrequenz = 1
  print(AbfrageFrequenz)    
  FrequenzAbfrage.delete(0,10)
  FrequenzAbfrage.insert(1,str(AbfrageFrequenz))
  FrequenzLesen()

def Shutdown_aktivieren():
  global gpio
  global SHUTDOWNTAST_PIN
  global Shutdowntast
  global ShutdownTastAktiv
  gpio.set_pull_up_down(SHUTDOWNTAST_PIN, pigpio.PUD_UP)
  Shutdowntast = gpio.callback(SHUTDOWNTAST_PIN, pigpio.FALLING_EDGE, Callback_detected)
  ShutdownButton["state"] = "active"
  usleep(2000)
  ShutdownTastAktiv = 1
  
  
def Shutdown_deaktivieren():
  global gpio
  global SHUTDOWNTAST_PIN
  global Shutdowntast
  global ShutdownTastAktiv
  ShutdownTastAktiv = 0
  Shutdowntast.cancel()
  gpio.set_pull_up_down(SHUTDOWNTAST_PIN, pigpio.PUD_OFF)
  

def Zaehltast_aktivieren():
  global gpio
  global ZAEHLTAST_PIN
  global Zaehltast
  global ZaehlTastAktiv
  gpio.set_mode(ZAEHLTAST_PIN, pigpio.INPUT)
  gpio.set_pull_up_down(ZAEHLTAST_PIN, pigpio.PUD_UP)
  Zaehltast = gpio.callback(ZAEHLTAST_PIN, pigpio.FALLING_EDGE, Callback_detected)
  usleep(20000)
  ZaehlTastAktiv = 1
  
def Zaehltast_deaktivieren():
  global gpio
  global ZAEHLTAST_PIN
  global Zaehltast
  global ZaehlTastAktiv
  ZaehlTastAktiv = 0
  Zaehltast.cancel()
  gpio.set_pull_up_down(ZAEHLTAST_PIN, pigpio.PUD_OFF)

def Starttast_aktivieren():
  global gpio
  global STARTTAST_PIN
  global Starttast
  global StartTastAktiv
  gpio.set_mode(STARTTAST_PIN, pigpio.INPUT)
  gpio.set_pull_up_down(STARTTAST_PIN, pigpio.PUD_UP)
  Starttast = gpio.callback(STARTTAST_PIN, pigpio.FALLING_EDGE, Callback_detected)
  usleep(20000)
  StartTastAktiv = 1
  
def Starttast_deaktivieren():
  global gpio
  global STARTTAST_PIN
  global Starttast
  global StartTastAktiv
  if StartTastAktiv == 1:
    StartTastAktiv = 0
    Starttast.cancel()
    gpio.set_pull_up_down(STARTTAST_PIN, pigpio.PUD_OFF)

def Opto_aktivieren():
  global gpio
  global OPTO_PIN
  global Opto1
  global OptoAktiv
  gpio.set_pull_up_down(OPTO_PIN, pigpio.PUD_UP)
  Opto1 = gpio.callback(OPTO_PIN, pigpio.FALLING_EDGE, Callback_detected)
  usleep(20000)
  OptoAktiv = 1

def Opto_deaktivieren():
  global gpio
  global OPTO_PIN
  global Opto1
  global OptoAktiv
  OptoAktiv = 0
  Opto1.cancel()
  gpio.set_pull_up_down(OPTO_PIN, pigpio.PUD_OFF)


ShutdownButton["command"] = Herunterfahren
ReadFreqButton["command"] = FrequenzLesen
DoitButton["command"] = Messung_starten_beenden
GPIO_aktivieren()
LCD_aktivieren()
Shutdown_aktivieren()
Zaehltast_aktivieren()


mainloop()
BlackJack

@sf169: Jetzt muss sich nur noch jemand finden der sich ein 400+-Zeilen Quelltext mit so vielen ``global``\s anschauen möchte. Mach das erst einmal ordentlich, also kein ``global`` und kein Code auf Modulebene der nicht Konstanten, Funktionen, oder Klassen definiert. Und bitte auch mit der üblichen Einrückung von vier Leerzeichen pro Ebene und mit Namen die dem Style Guide for Python Code entsprechen.

Edit: Zur eigentlichen Frage: Ich würde GUIs nur im Hauptthread laufen lassen und dann halt bei Bedarf den Code der die Hardware anspricht in einem Thread. Die Geschäftslogik muss so gekapselt werden das man unterschiedliche GUIs oder Ein- und Ausgabemöglichkeiten darauf aufsetzen kann.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Hab hier ein Beispiel mit tkinter über einen Thread und eine Queue.
Schau Dir mal an, ob das Dir etwas nützt: http://www.python-forum.de/viewtopic.ph ... 8&start=30
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@sf169: Sternchenimporte solltest Du vermeiden, und Dich an die Namenskonventionen halten; in Tabellennamen sollten keine Parameter (Datum) kodiert sein. Statt Strings mit + zusammenzusetzen nimm Stringformatierung. chr(13) wird als '\n' geschrieben. Werte in SQL-Statements werden nie in den String hineinformatiert sondern immer über zusätzliche Argumente. Variablen betreten eine Funktion über ihre Argumente und verlassen sie über Rückgabewerte. Einrückungen sollten einheitlich 4 Leerzeichen pro Ebene sein.
Antworten