Tkinter reagiert nicht mehr

Fragen zu Tkinter.
Antworten
Broken_Mind
User
Beiträge: 1
Registriert: Freitag 4. Juli 2014, 10:01

Hallo,

ich bin noch ziemlich neu in Python und versuche mir dies momentan anzueignen. Soweit komme ich auch gut zu recht nur habe ich das Problem wenn ich meinen Button drücke bleibt dieser gedrückt und die Schleife läuft unendlich durch (was sie auch soll). Gemeint wäre der Button "E-Mail". Nur würde ich gerne diese Schleife auch unterbrechen können oder irgendwas anderes parallel ausführen können.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from Tkinter import *
from tinkerforge.ip_connection import IPConnection
from tinkerforge.bricklet_remote_switch import RemoteSwitch
from tinkerforge.bricklet_temperature import Temperature
import os
import smtplib
import time
from email.mime.text import MIMEText

# Tinkerforge

HOST = "127.0.0.1"
PORT = 4223
RemoteSwitch_UID = "jNw"
Temperature_UID = "dFu"

### E-Mail Einstellungen ###

# SMTP
smptHost = "smtp.web.de"
smtpPort = 587
smtpUser = "XXX"
smtpPassword = "XXX"
 
# E-Mail
mailSender = "XXX"
mailReceiver = "XXX"

# Warning
criticalTemperature = 30 # in degree celsius

date_mail = "%02i.%02i.%04i" % (int(time.localtime()[2]), int(time.localtime()[1]), int(time.localtime()[0]))
time_mail = "%02i:%02i:%02i" % (int(time.localtime()[3]), int(time.localtime()[4]), int(time.localtime()[5]))

root = Tk()

root.title("GUI")

WMWIDTH, WMHEIGHT, WMLEFT, WMTOP = root.winfo_screenwidth(), root.winfo_screenheight(), 0, 0
root.overrideredirect(1)
root.geometry("%dx%d+%d+%d" % (WMWIDTH, WMHEIGHT, WMLEFT, WMTOP))

# Temperature
T = None

# Definitionen

def cb_temperature(temperature):
    #print('Temperature: ' + str(temperature/100.0) + ' °C')
    T.delete(1.0, END)
    T.insert(END, 'Temperature: ' + str(temperature/100.0) + ' °C\n')

def shutdown():
    os.system("sudo shutdown -h now")

def reboot():
    os.system("sudo shutdown -r now")

def light_on():
    rs.switch_socket_b(1, 1, RemoteSwitch.SWITCH_TO_ON)

def light_off():
    rs.switch_socket_b(1, 1, RemoteSwitch.SWITCH_TO_OFF)

def mail():
	while True:
		def getCPUtemperature():
			res = os.popen('vcgencmd measure_temp').readline()
			return(res.replace("temp=","").replace("'C\n",""))
 
		tempFloat = float(getCPUtemperature())
 
		if (tempFloat > criticalTemperature):
			server = smtplib.SMTP(smptHost, smtpPort)
			server.starttls()
			server.login(smtpUser, smtpPassword)
 
			value = "Die aktuelle Temperatur des Raspberry Pi liegt bei " + str(tempFloat) + " Grad Celsius." + "\n" + "Zeit: " + time_mail + " am " + date_mail + "\n" + " --- Raspberry Pi --- "
			msg = MIMEText(value)
			msg['Subject'] = "[Warnung] Rasperry Pi Temperatur " + str(tempFloat) + " Grad!"
			msg['From'] = mailSender
			msg['To'] = mailReceiver
			server.sendmail(mailSender, mailReceiver, msg.as_string())
			server.quit()
		time.sleep(30)

def netio():
    os.system("sudo python /home/pi/GUI/netio/tinkerforge listen --enable-execute")

def exit():
    sys.exit()	
	
# Create UI
logo = PhotoImage(file="/home/pi/GUI/images/rpi_inside.gif")
w1 = Label(root, image=logo).grid(row=8, column=8)

ShutdownButton = Button(root, text="Shutdown", width=10, command=shutdown)
ShutdownButton.grid(row=1, column=0)

RebootButton = Button(root, text="Reboot", width=10, command=reboot)
RebootButton.grid(row=2, column=0)

ExitButton = Button(root, text="Exit", width=10, command=exit)
ExitButton.grid(row=3, column=2)

LightButton1 = Button(root, text="Licht an", width=10, command=light_on)
LightButton1.grid(row=4, column=1)

LightButton2 = Button(root, text="Licht aus", width=10, command=light_off)
LightButton2.grid(row=5, column=1)

MailButton = Button(root, text="E-Mail", width=10, command=mail)
MailButton.grid(row=6, column=1)

NetIOButton = Button(root, text="NetIO", width=10, command=netio)
NetIOButton.grid(row=7, column=1)

T = Text(root, height=1, width=25)
T.grid(row=6, column=3)

# Create Tinkerforge objects
ipcon = IPConnection()
rs = RemoteSwitch(RemoteSwitch_UID, ipcon)
t = Temperature(Temperature_UID, ipcon)

ipcon.connect(HOST, PORT)

t.register_callback(t.CALLBACK_TEMPERATURE, cb_temperature)
t.set_temperature_callback_period(1000)

# Start mainloop
root.mainloop()
Ich weiß der Code ist noch recht unordentlich aber ich versuche und probiere viel aus.
Ich hoffe jemand kann mir dabei einen Tipp geben.

Grüße
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo und willkommen im Forum!

Du musst die mail-Funktion einfach als Thread starten. Sei aber vorsichtig bei der Verbindung von GUIs und Threads, aus einem Thread kannst du nicht einfach auf die Elemente der GUI zugreifen. Zu dem Thema gibt es aber genug Beiträge hier im Forum.

Und dann gleich noch einige Verbesserungsvorschläge zu deinem Code:

Mache keine *-Importe. Das müllt dir den gesamten Namensraum zu und überschreibt möglicherweise andere Werte aus deinem Namensraum. Da sich Module auch gerne mal verändern, sind die Seiteneffekte schwer zu überblicken. Damit kannst du dir prima dein ganzen Programm zerschießen. Bei Tkinter ist es üblich, es mittels

Code: Alles auswählen

import Tkinter as tk
zu importieren. Dann kannst du qualifiziert auf die Objekte des Moduls zurückgreifen:

Code: Alles auswählen

tk.Button(...)
Achte auf deine Namen. Python ist nicht Java und hat seine eigenen "Regeln" für Namen. Dazu solltest du einen Blick in PEP 8 werfen. Gerade wenn du Code veröffentlichst oder andere um Hilfe fragst, sollte sich dein Programm an diese Regeln halten. Dann sind sie wesentlich leichter lesbar. Konstanten sollten durchgängig IN_GROSSBUCHSTABEN_MIT_UNTERSTRICHEN geschrieben werden, Variablen- und Funktionsnamen klein_mit_unterstrichen und TypenAlsCamelCase. Auch sollten Zeilen nicht länger als 80 Zeichen sein.

Auch sollte dein Programm nicht auf modulebene stehen, dann es es nämlich nicht wiederverwendbar. Nur Importe, Konstanten und Definitionen von Funktionen und Klassen gehören auf Modulebene. Alles andere in Funktionen/Methoden. In Python hat man dafür üblicherweise eine main-Funktion:

Code: Alles auswählen

def main():
    ...


if __name__ == "__main__":
    main()
Die letzten beiden Zeilen sorgen dann dafür, dass die main-Funktion nur ausgeführt wird, wenn dein Programm auch direkt gestartet wurde. Wird dein Modul importiert, so wird die main-Funktion nicht gestartet.

Zeile 35 und 36 kann man viel schöner ausdrücken. Python bietet Module zur Verarbeitung von Zeitangaben, damit lässt sich die Formatierung viel eleganter lösen. Dann solltest du auch gleich vergessen, dass es so etwas wie globale Variablen, wie in Zeile 47, gibt. Die benötigt man in extrem seltenen Fällen. Werte betreten eine Funktion über dessen Parameter und verlassen sie über Rückgabewerte. Ansonsten gibt es auch noch Klassen, in denen man den Zustand kapseln kann, dann fliegt dieser nicht überall rum.

Zur String-Formatierung, wie in Zeile 54, gibt es in Python jede Menge Unterstützung. Such einfach mal nach "String-Formatting". Zusammensetzen von Strings mittels "+" ist eine unschöne Lösung. Besonder dann, wenn dazwischen noch Berechnungen stattfinden. Das ganze gilt natürlich auch für die mail-Funktion.

Statt "os.system" solltest du besser das subprocess-Modul verwenden.

Deine "light_on"- und "light_off"-Funktionen sind quasi identisch, die solltest du zusammenfassen.

Die "getCPUtemperature", was besser "get_cpu_temperature" heißen solte, hat auf der ebene nichts zu suchen. Die ist so allgemein, die solltest du nicht in der mail-Funktion definieren. Schon gar nicht in der while-Schleife. Das bedeutet nämlich, dass für jeden Durchlauf eine neue Funktion ersstellt wird, was wahrscheinlich nicht deine absicht ware. Auch solltest du hier wieder das subprocess-Modul verwenden statt "os.popen".
Das Leben ist wie ein Tennisball.
Antworten