Funktion im zweiten Fenster ausführen

Fragen zu Tkinter.
Antworten
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

Tag zusammen,

ich versuche seit jetzt fast drei Wochen mit Python ein paar Messgeräte anzusprechen und die Ergebnisse darzustellen. Da ich vorher meistens mit C++ und mit MS Visual Studio und nicht wie jetzt unter Linux mit Python und Tkinter programmiert habe, ist doch alles noch recht ungewohnt. Aber dank des Forums hier bin ich schon recht gut voran gekommen. Dafür erstmal vielen Dank!
Allerdings hänge ich jetzt gerade total und komm irgendwie gar nicht weiter!
Ich hab einige "Entrys" in meinem Hauptfenster (root) in die ich die Parater für die Ansteuerung der Messgeräte schreiben kann. Die Messungen werden dann in einem Textfeld dargestellt. So weit geht das auch alles noch.
Jetzt hab ich aber einen Button, wen ich den drücke geht ein anders Fenster auf, in dem ich einen Dateinamen reinschreiben kann unter dem der Inhalt des Textfeldes dann gespeichert werden soll.
Bisher sieht das so aus:
Button:

Code: Alles auswählen

save = Button(root,text='SAVE', command = save)
save.grid(row=19, column=1, sticky=E)
def save():

Code: Alles auswählen

def save():
	saves = textfenster.get(1.0, END)
	top = Toplevel()
	top.title('Save')

	label=Label(top, text=u"Dateinamen: ")
	label.grid(row=0, column=0, sticky=W)
		
	name = Entry(top, width=30,background='white')
	name.grid(row=0, column=1, sticky=W)

	ok = Button(top, text='Speichern', command = save2)
	ok.grid(row=1, column=1)

	cancel = Button(top, text='Abbruch', command = top.destroy)
	cancel.grid(row=1, column=1, sticky=E)
Aber wo muss ich denn jetzt die "def save2()" hinschreiben, damit er sie auch findet und auch das Fenster top kennt? Ich nehme mal an, ich muss da was mit Klassen und self machen, aber da weiß ich auch nicht so recht, wie und wo ich das einfügen muss...

[EDIT:] er kennt bisher in save2() das Zweitfenster top nicht

Schonmal vielen Dank für eure Hilfe!
pyStyler
User
Beiträge: 311
Registriert: Montag 12. Juni 2006, 14:24

Hallo und wilkommen,
kannst Du mal bitte etwas mehr von deinem Code zeigen?

Die save2() Funktion müsste laut deiner beschreibung oberhalb von save() definiert werden.

Gruss
pyStyler
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

pyStyler hat geschrieben:Die save2() Funktion müsste laut deiner beschreibung oberhalb von save() definiert werden.
Da steht sie mittlerweile auch und sieht nun so aus:

Code: Alles auswählen

def save2():
	speichern = 1
	datei = name.get()
	if (name.get() == ''):
		tkMessageBox.showerror('Error','Bitte einen Dateinamen eintragen!')
		speichern = 0
	
	# mit oder ohne Endung?
	elif (datei[-4] != '.'):
		datei = datei + '.dat'
		
	if (speichern == 1):
		f = open(datei, 'w')
		f.write (str(saves))
		f.close()
		
	top.destroy()
Allerdings kennt save2() die Elemente aus save() ja nicht, so dass save2() weder den Dateinamen auslesen noch das save() Fenster schließen kann. Den Inhalt des Textfeldes (im Hauptfenster) wird es wohl auch nicht auslesen können...
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

Hi Zizibee!

Das Problem an deinem Programm ist, das du nur mit Funktionen arbeitest. Wenn du in der Hauptfunktion ´t=Tk()´ schreibst, ist das Fenster nur in dieser Funktion an den Namen t gebunden. Such mal nach Namensräumen.

Um dein Script zum Laufen zu bringen, könntest du zwar an jede Funktion alle nötigen Variablen als Argumente übergeben, und sie so von einem Namensraum in den anderen schieben, am Besten wär aber alles in eine Klasse zu packen. Dann definierst du die Variablen mit ´self.var´ und kannst sie überall in der Klasse, also auch in den Methoden, aufrufen/ auf sie zugreifen. So sind auch alle Entrys etc. erreichbar.

Noch ein Tip: Wenn du Dateien auswählen willst, kannst du statt einem Entry zur direkten Pfadangabe auch die Funktion ´askopenfilename()´ oder ´askopenfile()´ des Moduls ´tkFileDialog´ benutzen. (So ähnlich wie das Modul tkMessageBox.)


Gruß, jj
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Hallo Zizibee,

du wirst auf Dauer nicht um Klassen herumkommen. Also solltest du dich jetzt schon mal daran gewöhnen :D .

Ich habe mal deine Codeschnipsel in ein kleines Klassenbeispiel gesteckt:

Code: Alles auswählen

import os
import tkMessageBox
import Tkinter as tk

class Test:
    
    def __init__(self):
        self.root = tk.Tk()
        self.text = tk.Entry(self.root)
        self.text.pack()
        self.text.insert(tk.END, 'Messergebnis')
        button = tk.Button(self.root, text='Speichern', command=self.save)
        button.pack()
        self.root.mainloop()

    def save(self): 
        self.top = tk.Toplevel() 
        self.top.title('Save') 

        label = tk.Label(self.top, text=u"Dateinamen: ") 
        label.grid(row=0, column=0, sticky=tk.W) 
            
        self.name = tk.Entry(self.top, width=30, background='white') 
        self.name.grid(row=0, column=1, sticky=tk.W) 

        ok = tk.Button(self.top, text='Speichern', command = self.save2) 
        ok.grid(row=1, column=1) 

        cancel = tk.Button(self.top, text='Abbruch', command = self.top.destroy) 
        cancel.grid(row=1, column=1, sticky=tk.E)
    
    def save2(self): 
        datei = self.name.get() 
        if datei == '': 
            tkMessageBox.showerror('Error','Bitte einen Dateinamen eintragen!') 
        else:
            # mit oder ohne Endung? 
            if os.path.splitext(datei) != '.dat': 
                datei = os.path.splitext(datei)[0] + '.dat'
                f = open(datei, 'w') 
                f.write (self.text.get()) 
                f.close() 
            
            self.top.destroy()
        
if __name__ == '__main__':
    Test()
In der __init__Funnktion wird das Hauptfenster erstellt. In der Funktion save wird das Fenster zur Eingabe des Dateinamens erstellt und mit der Funktion save2 wird das ganze in eine Datei geschrieben.
Um eine Dateiendung zu prüfen, gibt es os.path.splitext. Damit kannst du die Dateinamenerweiterung abspalten und prüfen.

Um den Dateinamen, wie von schlangenbeschwörer vorgeschlagen, direkt auszuwählen, ersetzt du die Klasse save im Beispiel mit:

Code: Alles auswählen

    def save(self):
        file_ = tkFD.asksaveasfilename(filetypes = [('Messergebnis.dat','.dat')])
        if file_:
            f = file(file_, 'w') 
            f.write (self.text.get()) 
            f.close()
        else:
            tkMessageBox.showerror('Error','Bitte einen Dateinamen eintragen!')
Davor muss natürlich ein

Code: Alles auswählen

import tkFileDialog as tkFD
stehen.
Die Klasse save2 entfällt dabei.

Das Beispiel soll nur mal als Anschauung dienen. Anpassen musst du es selbst :D

Mawilo
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

Danke hier erstmal für die Antworten! :D
Dann werd ich jetzt mal versuchen mein Programm dementsprechend umzuschreiben, falls ich nicht weiterkomme, melde ich mich wieder :wink:

Aber ist es nicht eigentlich (so rein logisch gesehen) komisch alles in eine Klasse zu packen, wenn nie eine andere Klasse dazu kommt und auch nie etwas vererbt wird?
lunar

Zizibee hat geschrieben:Aber ist es nicht eigentlich (so rein logisch gesehen) komisch alles in eine Klasse zu packen, wenn nie eine andere Klasse dazu kommt und auch nie etwas vererbt wird?
Ein Programm-Fenster ist ein sichtbares Objekt und lässt sich auch als Objekt beschreiben, ergo gehört es in eine Klasse.

Ich finde es vielmehr komisch und rein logisch unverständlich, wenn man eine GUI in einen funktionellen Ablauf packt, anstatt objekt-orientiert zu arbeiten. Nebenbei ist es auch absolut unüblich.
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

OK, ich hab jetzt mal alles umgeschrieben und es funktioniert!!! :D

Allerdings hätte ich jetzt gleich noch eine Frage, bezüglich dem self. Das bräuchte ich doch eigentlich nur vor den "Feldern" bzw. Variablen zu schreiben, die ich auch "woanderst" (z.B. in einer anderen "def xy()") wieder benutzen will, oder? Im Moment steht es fast überall :roll:

@ lunar
Ich hab es ja jetzt auch in eine Klasse geschrieben und mir leuchtet ja auch ein, dass es sich anbietet.
ABER ( :wink: ) Klassen (und damit auch Objekte und Methoden) hat man eingeführt, damit man auf sie zugreifen kann, ohne zu wissen, wie sie intern aufgebaut sind. (Korrigiert mich, falls ich falsch liege!) Ausserdem um über die Vererbung schneller und einfacher wieder "etwas neues" hinzufügen zu können.
Da wir aber nur eine Klasse besitzen und auch nicht von aussen auf sie zugreifen wollen gibt es keinen Grund sie einzusetzten...
(Ausser natürlich, das mein Programm jetzt so funktioniert :) )
BlackJack

Klassen sind primär zum Zusammenfassen von Daten und Funktionen die auf diesen Daten operieren zu Objekten gedacht. Genau das wird hier gemacht, also ist es doch auch sinnvoll. Ohne die Klasse müsstest Du mit globalen Variablen arbeiten oder immer den Zustand als Argumente herumreichen.

Und natürlich greifst Du auch von aussen auf Dein Objekt zu, Du musst ja ein Exemplar davon erstellen. Und an der Stelle wo Du das tust, interessiert Dich auch nicht wie das Objekt konkret implementiert ist.

An das Objekt braucht man nur Attribute binden, die den Zustand des gesamten Objekts ausmachen. Alles was nur innerhalb einer Methode benutzt wird braucht kein `self`.
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

Mh... wenn man das so sieht... Ihr werdet schon recht haben, da werd ich mich wohl doch mal genauer mit Klassen beschäftigen müssen... ;)
schlangenbeschwörer
User
Beiträge: 419
Registriert: Sonntag 3. September 2006, 15:11
Wohnort: in den weiten von NRW
Kontaktdaten:

Zizibee hat geschrieben:Mh... wenn man das so sieht... Ihr werdet schon recht haben, da werd ich mich wohl doch mal genauer mit Klassen beschäftigen müssen... ;)
Das solltest du eh, denn du willst ja auch nicht nur GUIs mit Funktionen hinbiegen. Bei etwas größeren Berechnungen, egal, was du machen willst, sind Klassen immer praktischer.
Benutzeravatar
Mawilo
User
Beiträge: 452
Registriert: Sonntag 22. Februar 2004, 10:58
Wohnort: Sachsen
Kontaktdaten:

Faulheit ist der Motor der Menschheit :D - schon deshalb solltest du Klassen verwenden. Denn die lassen sich sehr gut weiterverwenden. Das ist von Vorteil, wenn du mal ne Menge entwickelt hast. Musst dann nicht immer alles neu machen, sondern kannst auf einen "bunten Werkzeugkasten" zugreifen.
Zizibee
User
Beiträge: 229
Registriert: Donnerstag 12. April 2007, 08:36

Moin, nachdem ich jetzt mal darüber geschlafen habe muss ich euch wohl recht geben :)
Wenn ein Projekt größer als erwartet wird oder man etwas von einem anderen Projekt übernehmen will sind Klassen bestimmt eine feine Sache.

Dann stürz ich mich mal weiter in meinen Code, die nächsten Probleme warten schon :?
Antworten