Wischfunktion durch Buttonklick hervorrufen

Fragen zu Tkinter.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: Du hast Den Code geschrieben, und deswegen sage ich es Dir. Wie soll jemand, der noch am Anfang steht, wissen wie es besser geht. Dass Dein GUI-Designer schrecklichen Code schreibt, hatten wir schon an anderer Stelle. Wenn man Code generiert, den man danach ändert, sollte der so lesbar sein, wie es eben geht, da es ja kein Problem für einen Code-Generierer ist, auch schöne Keyword-Argumente zu schreiben. Ansonsten ist der Code ja so simple, dass man dafür keinen Designer braucht.

Der selbe Code, nur ohne global, aber immer noch mit zu vielen festen Fenstergrößen und Positionen.

Code: Alles auswählen

import tkinter as tk
 
class Mainframe(tk.LabelFrame):
    def __init__(self,master,**kwargs):
        tk.LabelFrame.__init__(self,master,**kwargs)
        tk.Button(self, text='wish on', command=self.wish_on).place(y=243,x=50)
        self.button_frame = ButtonFrame(self, width=200, text='Noch ein Fenster', bd=0, relief='flat', bg='#d9d93c', height=150)
        self.button_frame.place(y=300, x=0)
 
    def wish_on(self):
        self.button_frame.wish(300,140,-1)
 
class ButtonFrame(tk.LabelFrame):
    def __init__(self, master, **kwargs):
        tk.LabelFrame.__init__(self, master, **kwargs)
        tk.Button(self, text='wish off', command=self.wish_off).place(y=83,x=50)
 
    def wish_off(self):
        self.wish(140,300,1)

    def wish(self, y0, yn, step):
        def do_move(y):
           y += step
           self.place(y=y)
           if y != yn:
               self.after(3, do_move, y)
        do_move(y0)

def main():
    app = tk.Tk()
    app.geometry("280x370")
    mainframe = Mainframe(app, width=200, text='Screen', bd=0, relief='flat', bg='#d949d9', height=300)
    mainframe.place(y=28, x=43)
    app.mainloop()
    
if __name__ == '__main__':
    main()
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Alfons Mittelmeyer hat geschrieben:Ich denke, es ist gut, dem Anfänger das Prinzip zu zeigen, wie etwas funktioniert. Und unschön anzusehender Code ist da gar nicht einmal so schlecht, da der Anfänger es dann schöner machen will und selber machen will, anstatt einfach nur die Vorlage zu kopieren.
Genau. Weil der Anfaenger, der bis dato keine Ahnung davon hat, wie man etwas macht, urploetzlich nicht nur aufhoert Code zu kopieren (was er bis jetzt immer getan hat), sondern auch noch einen Sinn fuer Code-Aesthetik entwickelt - unabhaengig von Dingen, die man ihm zeigt. :twisted:

"Do as I say, not as I do" hat noch in keinem Kontext funktioniert. Gerade an den vielen Projekten und Codeschnipseln rund um den Pi sieht man, dass es keine Grenze nach unten gibt... aber fuer mich ist hier EOD. Ich bleibe bei meiner Einschaetzung, dass fuer eine moderne UI ein modernes UI-Framework notwendig ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Python stellt gar nichts wieder her. Es wurde nichts ausserhalb des Prozesses verändert was wiederhergestellt werden müsste.
Also hier wird das Arbeitsverzeichnis schon wieder hergestellt, Dazu die Datei test.py in ein Unterverzeichnis subdir legen:

Code: Alles auswählen

import os 
print("Workdir original:"+os.getcwd())
os.chdir(os.path.dirname(os.path.realpath(__file__)))
print("Workdir changed :"+os.getcwd())
Dann dieses Programm zweimal hintereinander aufrufen:

python3 subdir/test.py
python3 subdir/test.py

Beim zweiten Mal läßt sich das auch aufrufen, und wenn man es zum zweiten Mal aufruft, werden dieselben Verzeichnisse angezeigt, wie beim ersten Aufruf. Nichts wurde also verstellt, bzw es wurde nach Beendigung des ersten Aufrufes wieder zurückgesetzt.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: jeder Prozess hat sein eigenes Arbeitsverzeichnis. So dass wenn ein Prozess sein Verzeichnis verändert, hat das keine Auswirkung auf andere Prozesse. Du startest nacheinander zwei Prozesse, jedes startet mit dem ursprünglichen Pfad des aufrufenden Prozesses. Da wird wirklich nichts zurückgesetzt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben:Ich bleibe bei meiner Einschaetzung, dass fuer eine moderne UI ein modernes UI-Framework notwendig ist.
Ja, beim Raspberry Pi, wahrscheinlich auch auf dem PC mit Linux, sind solche Animationen mit after sehr flüssig. Aber unter Windows ist dieses nicht der Fall - zumindest nicht mit meinem Windows7 Ultimate 32 Bit. Wenn man solche Animationen auch für Windows programmieren möchte, sollte man ein entsprechendes modernes UI-Framework verwenden,
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: jeder Prozess hat sein eigenes Arbeitsverzeichnis. So dass wenn ein Prozess sein Verzeichnis verändert, hat das keine Auswirkung auf andere Prozesse. Du startest nacheinander zwei Prozesse, jedes startet mit dem ursprünglichen Pfad des aufrufenden Prozesses. Da wird wirklich nichts zurückgesetzt.
Naja, jedenfalls sorgt python dafür, dass der aktuelle Pfad der Shell unverändert bleibt - ob durch Zurücksetzen oder durch Verwendung eines Prozesses ist an sich schnuppe.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: Python sorgt für gar nichts. Man kann nur sein eigenes Arbeitsverzeichnis ändern, nicht das eines anderen Prozesses, das geht gar nicht. Und dass ein Modul von sich aus den Pfad ändert ist einfach nur unerwartet, damit rechnet niemand und stell Dir zwei Module vor, die das machen, dann kann man die nicht zusammen benutzen. Daher ist es höchst unangebracht, das zu tun.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Sirius3: Wichtig ist eine flexible Programmierung, das heißt, man soll allgemein verwendbare Funktionen auch allgemein verwenden können. Die wish-Funktion als Methode für ein einziges GUI Element zu definieren, sollte nicht sein.

Ein Anwendung kann wachsen und am Ende viele unterschiedliche Fenster haben, die mit wish verschoben werden. Daher ist die wish Funktion gesondert zu implementieren. Hier einmal nicht mit globalen Variablen:

Code: Alles auswählen

import tkinter as tk

# ---- Definition of wish function ---------------

def wish(element,y0, yn, step,timestep):
    def do_move(y):
        y += step
        element.place(y=y)
        if y != yn:
            element.after(timestep, do_move, y)
    do_move(y0)

# ---- GUI ---------------------------------------
 
class Mainframe(tk.LabelFrame):
    def __init__(self,master,**kwargs):
        tk.LabelFrame.__init__(self,master,**kwargs)
        tk.Button(self, text='wish on', command=self.wish_on).place(y=243,x=50)
        self.button_frame = ButtonFrame(self, width=200, text='Noch ein Fenster', bd=0, relief='flat', bg='#d9d93c', height=150)
        self.button_frame.place(y=300, x=0)

    def wish_on(self):
        wish(self.button_frame,300,140,-1,3)
 
class ButtonFrame(tk.LabelFrame):
    def __init__(self, master, **kwargs):
        tk.LabelFrame.__init__(self, master, **kwargs)
        tk.Button(self, text='wish off', command=self.wish_off).place(y=83,x=50)
 
    def wish_off(self):
        wish(self,140,300,1,3)
 
def main():
    app = tk.Tk()
    app.geometry("280x370")
    mainframe = Mainframe(app, width=200, text='Screen', bd=0, relief='flat', bg='#d949d9', height=300)
    mainframe.place(y=28, x=43)
    app.mainloop()
   
if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: zum Wiederverwenden von Methoden hat man was ganz tolles erfunden: Objektorientierte Programmierung. Kannst Du Dir mal bei Gelegenheit anschauen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: zum Wiederverwenden von Methoden hat man was ganz tolles erfunden: Objektorientierte Programmierung. Kannst Du Dir mal bei Gelegenheit anschauen.
Jedem wie es beliebt, wenn das Dich glücklicher macht:

Code: Alles auswählen

import tkinter as tk

# ---- Definition of wish class ---------------

class WishClass():
    def wish(self,element,y0, yn, step,timestep):
        def do_move(y):
            y += step
            element.place(y=y)
            if y != yn:
                element.after(timestep, do_move, y)
        do_move(y0)

# ---- GUI ---------------------------------------
 
class Mainframe(tk.LabelFrame):
    def __init__(self,master,**kwargs):
        tk.LabelFrame.__init__(self,master,**kwargs)
        tk.Button(self, text='wish on', command=self.wish_on).place(y=243,x=50)
        self.button_frame = ButtonFrame(self, width=200, text='Noch ein Fenster', bd=0, relief='flat', bg='#d9d93c', height=150)
        self.button_frame.place(y=300, x=0)
        self.wish_object=WishClass()

    def wish_on(self):
        self.wish_object.wish(self.button_frame,300,140,-1,3)
 
class ButtonFrame(tk.LabelFrame):
    def __init__(self, master, **kwargs):
        tk.LabelFrame.__init__(self, master, **kwargs)
        tk.Button(self, text='wish off', command=self.wish_off).place(y=83,x=50)
        self.wish_object=WishClass()
 
    def wish_off(self):
        self.wish_object.wish(self,140,300,1,3)
 
def main():
    app = tk.Tk()
    app.geometry("280x370")
    mainframe = Mainframe(app, width=200, text='Screen', bd=0, relief='flat', bg='#d949d9', height=300)
    mainframe.place(y=28, x=43)
    app.mainloop()
   
if __name__ == '__main__':
    main()
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Alfons Mittelmeyer hat geschrieben:Die wish-Funktion als Methode für ein einziges GUI Element zu definieren, sollte nicht sein.
Das ist ganz richtig erkannt und somit ist die Funktion ein heißer Kandidat als Methode einer Superklasse.
BlackJack

@Alfons Mittelmeyer: Die `WishClass` ist semantisch keine Klasse. Zudem ist der Name ziemlich schlecht. Das `Class` hat da nichts zu suchen und mit Wünschen hat das auch alles nichts zu tun. :-)
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: das macht mich nicht glücklich, weil Du ja jetzt nur die Funktion eine Ebene tiefer geschoben hast und class drumrum geschrieben. Wahrscheinlich braucht man die wish-Methode (eigentlich swipe) nur in Frames, so dass man am einfachsten eine SwipeFrame-Klasse von Frame ableitet und davon wieder konkrete Frames.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Die `WishClass` ist semantisch keine Klasse.
Sementisch war das schon eine Klasse, weil sie mit class definiert war. Allerdings wie es codiert war, ist zum Haareausraufen. Die Funktion war von Sirius3.

@Siorius3: In einer anderen Programmiersprache würde man so etwas nie schreiben, und auch in Python sollte man solches nicht tun. Normalerweise sollte man davon ausgehen, dass nach Beendigung der Methode "wish" die Parameter nicht mehr vorhanden sein sollten und deshalb weitere Aufrufe mittels after von do_move die Werte eigentlich nicht mehr finden sollten. Wenn es in Python trotzdem geht, sollte kein Grund sein, so etwas zu codieren.

Eine richtige Implementierung ist die Verwendung von Objektattributen. Durch die Verwendung von Objektattributen ist es auch möglich, mehrere Fenster mehr oder minder gleichzeitig zu verschieben, da jedes Objekt dann die eigenen Werte hat. Die WishKlasse mehr oder minder richtig implementiert, wäre dann:

Code: Alles auswählen

class WishClass():

    def wish(self,element,y0, yn, step,timestep):
        self.element = element
        self.yi = y0
        self.yn = yn
        self.step=step
        self.timestep = timestep
        self.do_move()
        
    def do_move(self):
        self.yi += self.step
        self.element.place(y=self.yi)
        if self.yi != self.yn:
            self.element.after(self.timestep, self.do_move)

Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: ...Wahrscheinlich braucht man die wish-Methode (eigentlich swipe) nur in Frames, so dass man am einfachsten eine SwipeFrame-Klasse von Frame ableitet und davon wieder konkrete Frames.
Richtig, das wäre auch mein Vorschlag:

Code: Alles auswählen

import tkinter as tk

# ---- Definition of SwipeFrame ---------------

class SwipeFrame(tk.Frame):
    def __init__(self,master, **kwargs):
        tk.Frame.__init__(self, master, **kwargs)

    def wish(self,y0, yn, step,timestep):
        self.yi = y0
        self.yn = yn
        self.step=step
        self.timestep = timestep
        self.do_move()
        
    def do_move(self):
        self.yi += self.step
        self.place(y=self.yi)
        if self.yi != self.yn:
            self.after(self.timestep, self.do_move)

# ---- GUI ---------------------------------------
 
class Mainframe(tk.LabelFrame):
    def __init__(self,master,**kwargs):
        tk.LabelFrame.__init__(self,master,**kwargs)
        tk.Button(self, text='wish on', command=self.wish_on).place(y=243,x=50)
        self.button_frame = ButtonFrame(self, width=200, bd=0, relief='flat', bg='#d9d93c', height=150)
        self.button_frame.place(y=300, x=0)

    def wish_on(self):
        self.button_frame.wish(300,140,-1,3)
 
class ButtonFrame(SwipeFrame):
    def __init__(self, master, **kwargs):
        SwipeFrame.__init__(self, master, **kwargs)
        tk.Label(self, text='Swipe Window').place(y=0,x=0)
        tk.Button(self, text='wish off',command=self.wish_off).place(y=83,x=50)
 
    def wish_off(self):
        self.wish(140,300,1,3)
 
def main():
    app = tk.Tk()
    app.geometry("280x370")
    mainframe = Mainframe(app, width=200, text='Screen', bd=0, relief='flat', bg='#d949d9', height=300)
    mainframe.place(y=28, x=43)
    app.mainloop()
   
if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: das was Du da so verteufelst, weil Du es nicht kennst, nennt sich closure und existiert so in fast jeder modernen Programmiersprache. So lange eine Funktion schön kurz ist, hilft es, umständliches Kopieren von Argumenten in Attribute, die man eigentlich nicht in einer Funktion neu einführen sollte, zu verhindern.
BlackJack

@Alfons Mittelmeyer: Das ist semantisch keine Klasse. Das ist nur formal Eine.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Das ist semantisch keine Klasse. Das ist nur formal Eine.
Wieso ist das semantisch keine Klasse? Es ist natürlich eine Klasse. Nur macht so eine Klasse wenig Sinn, wenn man von dieser nur eine einzige Methode benützt. Dann kann man genauso gut eine Funktion nehmen.

Aber wenn man davon ausgeht, dass man das eh nur für einen SwipeFrame braucht, dann ist so ein SwipeFrame natürlich ein Klasse. Wenn man weiter davon ausgeht, dass diese Swipe Fenster alle gleich groß sind, dann kann man auch Größenangaben und Koordinatenangaben in diesen SwipeFrame verlagern.

Also SwipeFrame ist bestimmt eine Klasse:

Code: Alles auswählen

import tkinter as tk

# ---- Definition of SwipeFrame ---------------

class SwipeFrame(tk.Frame):
    def __init__(self,master, **kwargs):
        kwargs['width']=200
        kwargs['height']=150
        tk.Frame.__init__(self, master, **kwargs)
        self.ydown=300
        self.yup=150
        self.timestep=3
        self.place(y=self.ydown,x=0)

    def wish_on(self):
        self.wish(self.ydown,self.yup,-1,self.timestep)

    def wish_off(self):
        self.wish(self.yup,self.ydown,1,self.timestep)

    def wish(self,y0, yn, step,timestep):
        def do_move(y):
            y += step
            self.place(y=y)
            if y != yn:
                self.after(timestep, do_move,y)
        do_move(y0)
        
# ---- GUI ---------------------------------------
 
class Mainframe(tk.Frame):
    def __init__(self,master,**kwargs):
        tk.Frame.__init__(self,master,**kwargs)
        tk.Label(self, text='Screen',bg=self['bg']).place(y=0,x=0)
        tk.Button(self, text='wish on', command=self.wish_on).place(y=243,x=50)
        self.button_frame = ButtonFrame(self,bg='#d9d93c')

    def wish_on(self):
        self.button_frame.wish_on()
 
class ButtonFrame(SwipeFrame):
    def __init__(self, master, **kwargs):
        SwipeFrame.__init__(self, master, **kwargs)
        tk.Label(self, text='Swipe Window',bg=self['bg']).place(y=0,x=0)
        tk.Button(self, text='wish off',command=self.wish_off).place(y=93,x=50)
 
def main():
    app = tk.Tk()
    app.geometry("280x370")
    mainframe = Mainframe(app, width=200, bg='#d949d9', height=300)
    mainframe.place(y=28, x=43)
    app.mainloop()
   
if __name__ == '__main__':
    main()
asse:
BlackJack

@Alfons Mittelmeyer: Es ist semantisch keine Klasse. Die Begründung hast Du ja selbst schon geschrieben.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Es ist semantisch keine Klasse. Die Begründung hast Du ja selbst schon geschrieben.
Die Klasse ist syntaktisch richtig. Auch semantisch stimmt sie. Nur pragmatisch ist diese Klasse nicht sinnvoll.

Syntaktisch richtig wäre etwa dieses Datum: 29. Februar 2017
Semantisch ist dieses Datum allerdings falsch.
Pragmatisch unklug wäre es, stets nur am 29. Februar jährlich fällige Zahlungen zu leisten.
Antworten