Wischfunktion durch Buttonklick hervorrufen

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

__deets__ hat geschrieben:"Gehen" tut vieles. Zum Beispiel ein Loch ins Knie bohren, geht auch. Gute Ideen sind das aber nicht alle. Für flüssige Animationen brauchts schon etwas mehr. Es gibt schon gute Gründe warum so etwas in entsprechend Frameworks eingebaut wird.
Einen Frame durch Setzen der Koordinaten zu verschieben, geht flüssig. Übrigens, die Höhe muss nicht angepasst werden, da der Frame nicht über die Parent Grenzen hinaus sichtbar sein kann. Und wenn da nicht gleich eine Funktion im tkinter Framework da ist, die kann man sich unter Benützung des timers selber schreiben.

Wer natürlich denkt, selber programmieren ist dasselbe wie ein Loch ins Knie bohren, der soll es halt bleiben lassen.
BlackJack

@Alfons Mittelmeyer: Hast Du ausprobiert ob das flüssig geht? Auf einem Raspiberry Pi? Denn darum geht es hier ja.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack: ja, habe mir heute einmal nach langer Zeit wieder meinen GuiDesigner angeschaut und ihn auf meinen Raspberry Pi3 geladen. Das Container Bewegen geht total flüssig. Und dazu benutze ich after, Mauspositionsabfrage und place. Das geht sicher auch ohne Verschieben mittels der Mausposition.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe heute auch deinen GUI-Designer angeschaut, und außer einer decode exceptions war da nix flüssig... ich bleibe also skeptisch was deine Tipps für modernes GUI Design angeht :lol:
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben:Ich habe heute auch deinen GUI-Designer angeschaut, und außer einer decode exceptions war da nix flüssig
Was soll dieser Unsinn?
Frame bewegen geht genauso schnell wie ein Anwendungsfenster bewegen oder ein sonstiges Betriebssystem GUI Element, wie etwa ein Icon für einen Ordner.

Und wenn Du irgendwo eine decode exception bekommst, wäre gut, den Fehler zu beschreiben und abzuklären, denn ich habe so eine exception nicht bekommen.
apple
User
Beiträge: 8
Registriert: Sonntag 16. April 2017, 15:58

@ Alfons Mittelmayer: Wenn ich das jetzt richtig verstehe, kann ich also in Tkinter (ohne GUI Designer) einen Frame erzeugen, welcher die benötigten Buttons beinhaltet. Dieser Frame ist zunächst nicht sichtbar und wird durch Betätigung eines anderen Buttons durch eine Zeitverzögerung von unten nach oben eingeblendet. Anschließend kann der Frame durch nochmaliges Betätigen des Buttons ausgeblendet werden.

Das wäre genau das was ich brauche :D

Wie kann ich eine solche zeitgesteuerte Verschiebung erzeugen?
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

@Alfons: moderne UI-Frameworks & Browser haben eine Menge Zeit und Code darin investiert, solche Animationssystem zu entwickeln. Mit GPU-Beschleunigung (gerade im dem mobilen Sektor sehr wichtig, und der PI IST ein Mobilprozessor) und entsprechenden Abstraktionen. Es gibts natürlich immer ein paar Schlaumeier die glauben, sie können alles besser als der Rest. Wohlan, mach.

Deine Exception liefere ich gerne nach.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

apple hat geschrieben:Wie kann ich eine solche zeitgesteuerte Verschiebung erzeugen?
Hier ein Beispiel. Das Beispiel zeigt das Prinzip, ist aber kein Beispiel für schöne OOP:

Code: Alles auswählen

import tkinter as tk

move_element = None
move_yi = 0
move_yn = 0
move_step = -1

def do_move():
    
    timestep=3
    global move_element
    global move_yi
    global move_yn
    global move_step
    move_yi = move_yi + move_step
    move_element.place(y=move_yi)
    if move_yn != move_yi:
        move_element.after(timestep,do_move)

def wish(element,y0,yn,step):
    global move_element
    move_element=element
    global move_yi
    move_yi=y0
    global move_yn
    move_yn=yn
    global move_step
    move_step=step
    do_move()

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.geometry("280x370") 
        self.labelFrame = Labelframe(self,**{'width': 200, 'text': 'Screen', 'bd': 0, 'relief': 'flat', 'bg': '#d949d9', 'height': 300})
        self.labelFrame.place(**{'y': '28', 'x': '43'})

class Labelframe(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.labelFrame = Labelframe_1(self,**{'width': 200, 'text': 'Noch ein Fenster', 'bd': 0, 'relief': 'flat', 'bg': '#d9d93c', 'height': 150})
        self.labelFrame.place(**{'y': '300', 'x': '0'})

    def wish_on(self):
        wish(self.labelFrame,300,140,-1)
        

class Labelframe_1(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)


Application().mainloop()
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wie versprochen, die exception. Das man die main.py nicht aufrufen ausser man steht im Verzeichnis in dem sie liegt ist sicher gewollt?

Code: Alles auswählen

dir@client2087 /tmp $ python3.5 python-gui-messaging/GuiDesigner/main.py
Couldn't open file: guidesigner/Guidesigner.py
^CTraceback (most recent call last):
  File "python-gui-messaging/GuiDesigner/main.py", line 2, in <module>
    tk.Tk().mainloop('guidesigner/Guidesigner.py')
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 901, in mainloop
    StatTkInter.Tk.mainloop(self)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1131, in mainloop
    self.tk.mainloop(n)
KeyboardInterrupt
dir@client2087 /tmp $ cd python-gui-messaging/GuiDesigner/
dir@client2087 /tmp/python-gui-messaging/GuiDesigner (master =) $ python3.5 main.py
Exception in Tkinter callback
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1550, in __call__
    return self.func(*args)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 596, in callit
    func(*args)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 2601, in DynLoad
    exec(evcode)
  File "guidesigner/Guidesigner.py", line 1, in <module>
    Toplevel("DynTkInterGuiDesigner",title="DynTkInter GuiDesigner",geometry='+30+20',link="guidesigner/Modules.py")
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 926, in __init__
    master = _initGuiContainer(kwargs,StatTkInter.Toplevel,self,myname,"toplevel",True,True,None,_TopLevelRoot._container)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 827, in _initGuiContainer
    GuiContainer.__init__(element,myname,select,mayhave_grid,isMainWindow,tkmaster,**kwargs)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 604, in __init__
    FileImportContainer(self) # if link != ""
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 788, in FileImportContainer
    DynLoad(container.link)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 2601, in DynLoad
    exec(evcode)
  File "guidesigner/Modules.py", line 57, in <module>
    LabelFrame('BaseLayout',text="""Layout""",link="guidesigner/BaseLayout.py")
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 1102, in __init__
    _initGuiContainer(kwargs,StatTkInter.LabelFrame,self,myname,"labelframe",True)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 827, in _initGuiContainer
    GuiContainer.__init__(element,myname,select,mayhave_grid,isMainWindow,tkmaster,**kwargs)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 604, in __init__
    FileImportContainer(self) # if link != ""
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 788, in FileImportContainer
    DynLoad(container.link)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 2601, in DynLoad
    exec(evcode)
  File "guidesigner/BaseLayout.py", line 7, in <module>
    LabelFrame('GridLayout',link="guidesigner/GridLayout.py")
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 1102, in __init__
    _initGuiContainer(kwargs,StatTkInter.LabelFrame,self,myname,"labelframe",True)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 827, in _initGuiContainer
    GuiContainer.__init__(element,myname,select,mayhave_grid,isMainWindow,tkmaster,**kwargs)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 604, in __init__
    FileImportContainer(self) # if link != ""
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 788, in FileImportContainer
    DynLoad(container.link)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 2599, in DynLoad
    code = handle.read()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 20599: ordinal not in range(128)
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1550, in __call__
    return self.func(*args)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 933, in destroy
    GuiElement.destroy(self)
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 298, in destroy
    if widget_exists(self): self.tkClass.destroy(self)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 2146, in destroy
    if self._name in self.master.children:
AttributeError: '_CreateTopLevelRoot' object has no attribute 'children'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "main.py", line 2, in <module>
    tk.Tk().mainloop('guidesigner/Guidesigner.py')
  File "/private/tmp/python-gui-messaging/GuiDesigner/DynTkInter.py", line 901, in mainloop
    StatTkInter.Tk.mainloop(self)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1131, in mainloop
    self.tk.mainloop(n)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1554, in __call__
    self.widget._report_exception()
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/tkinter/__init__.py", line 1297, in _report_exception
    root.report_callback_exception(exc, val, tb)
AttributeError: '_CreateTopLevelRoot' object has no attribute 'report_callback_exception'
dir@client2087 /tmp/python-gui-messaging/GuiDesigner (master =) $0
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben:Das man die main.py nicht aufrufen ausser man steht im Verzeichnis in dem sie liegt ist sicher gewollt?
Nein , ist nicht gewollt, bzw. ich war mir nicht bewusst, dass jemand nicht in das Verzeichnis selber gehen will. Danke, da kann man dann das noch in main.py einfügen:

Code: Alles auswählen

import os 
os.chdir(os.path.dirname(os.path.realpath(__file__)))
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich persoenlich wuerde niemals das Arbeitsverzeichnis veraendern, sondern meinen Code agnostisch machen. Denn ein solches Verhalten modifiziert globalen Zustand, und mehrere Teilnehmer die das tun - dann krachts.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@__deets__: besser keine Diskussion anfangen, das ist verschwendete Lebenszeit :wink: .

@Alfons Mittelmeyer: global sollte in keinem Programm vorkommen. Zumal das meiste davon unnütz ist. Wenn Du schon eine Labelframe_1-Klasse hast, dann könntest Du sie auch nutzen. Du hast immer noch diese unmöglich komplizierte Variante, Schlüsselwortargumente zu übergeben, dass man es nicht lesen möchte. So nichtsagende Namen wie Labelframe_1 sollte man auch nicht benutzen.

Soetwas einem Anfänger zu zeigen, halte ich nicht für gut, das könnte jemand für brauchbar halten.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@__deets__: os.chdir ändert nicht das Arbeitsverzeichnis für die Shell. Etwa nach Beendigung von "python3 GuiDesigner/main.py" steht die Shell wieder auf dem ursprünglichen Arbeitsverzeichnis, auch wenn innerhalb von main.py, dieses durch os.chdir geändert wurde. Python stelllt also bei Beendigung das ursprüngliche Arbeitsverzeichnis der Shell wieder her.

Meine Ansicht: eine Anwendung sollte ein Arbeitsverzeichnis haben und daher sollte man ein solches durchaus in der Anwendung setzen dürfen, anstatt etwa ständig explizit eine Variable für einen Basispfad anzugeben oder zuvor in der Shell es mit cd zu verstellen.
BlackJack

@Alfons Mittelmeyer: Python stellt gar nichts wieder her. Es wurde nichts ausserhalb des Prozesses verändert was wiederhergestellt werden müsste.

Das Arbeitsverzeichnis wird vom Aufrufer vorgegeben. Anwendungen die das anders handhaben verhalten sich anders als der Benutzer das erwartet, weil das so gut wie keine Anwendung macht. Ausnahmen sind die Anwendungen von Programmierern die immer alles anders machen wollen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben: @Alfons Mittelmeyer: global sollte in keinem Programm vorkommen. Zumal das meiste davon unnütz ist.
Das brauchst Du mir nicht sagen, sondern dem Anfänger. Dieser sollte diese Globalen Variablen und die Funktion do_move und eventuell auch wish in einer Klasse kapseln. wish kann man aber auch als Funktion implementieren.
Sirius3 hat geschrieben:Wenn Du schon eine Labelframe_1-Klasse hast, dann könntest Du sie auch nutzen.
So eine wish Methode kann man für belebige GUI Elemente verwenden, sie soll daher keineswegs nur ein Bestandteil dieser Labelframe_1-Klasse sein
Sirius3 hat geschrieben:Du hast immer noch diese unmöglich komplizierte Variante, Schlüsselwortargumente zu übergeben, dass man es nicht lesen möchte. So nichtsagende Namen wie Labelframe_1 sollte man auch nicht benutzen.
Sorry, dass ich meine Zeit nicht damit vertat, den Code für die GUI zu schreiben, sondern den Code generierte mit automatisch generierten Namen und Schlüsselwortargumenten.
Sirius3 hat geschrieben:Soetwas einem Anfänger zu zeigen, halte ich nicht für gut, das könnte jemand für brauchbar halten.
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.

Und beim Schöner machen, kannst dann Du ihm helfen.
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: 14528
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,
Antworten