GUI Designer mit Source File update?

Fragen zu Tkinter.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Alfons

Da hast du natürlich recht. Bei Verwendung nur eines nackten command für den Callback mit der zugehörigen Funktion- bzw. Medode-Referenz ist es so:

Code: Alles auswählen

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

from functools import partial

try:
    # Tkinter for Python 2.xx
    import Tkinter as tk
except:
    # Tkinter for Python 3.xx
    import tkinter as tk


class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.dyn_button = tk.Button(self, text='DynButton',
            command=self.button_callback)
        self.dyn_button.pack()
        self.dyn_button = tk.Button(self, text='DynButton',
            command=self.button_callback)
        self.dyn_button.pack()
        self.dyn_button = tk.Button(self, text='DynButton',
            command=self.button_callback)
        self.dyn_button.pack()

    def button_callback(self):
        print('Who am I? :-)')
        
Application().mainloop()
Gruss wuf :wink:
Take it easy Mates!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Hi wuf,

den command solltest Du nicht da gleich reinschreiben, denn wenn Du die GUI veränderst und neu drüberschreibst, ist der command weg. Commands also erst nach einer Leerzeile nach der init. Ich mache dann einfach bei gleichen Widgetnamen veränderte Variablennamen durch Anfügen von _nr.

Das hieße, dass ich dann die GUI des GuiDesigner auch exportieren könnte, wenn ich bei den Namen keine Leerzeichen und Operatorzeichen verwenden würde. Würde allerdings nichts bringen, da der Code nicht dazu passt.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Alfons:
Wenn man das Pseudocode-Bsp. mit der for-Schleife weiter ausbaut, sprich ich habe eine fertige Anwendung mit funktionierender Oberfläche, möchte aber Teile davon einem refactoring unterziehen, z.B. einen Button mit einer Aktion hinzufügen etc. - Wie stellst Du sicher, dass die Logik nicht verloren geht bzw. noch korrekt arbeitet? Was passiert mit dem Rest aus dem Bsp. (das `do_sumthing_with_button`)?
Auch verstehe ich diese Anmerkung nicht: "buttons are dynamically created, so the widgets shall not be saved. Only the code, which creates them shall be saved" - was meinst Du hier mit dynamisch? Deklarationen wie z.B. in C++ kennt Python nicht, so gesehen ist alles dynamisch.

Übrigens halte ich die gezeigte Tk-API für C++ für ein schönes Bsp, wie man es nicht in C++ machen sollte.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@ Hi wuf,

hab jetzt die Änderung gemacht. Jetzt werden unterschiedliche Variablennamen erzeugt:

Code: Alles auswählen

import DynTkInter as tk

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.DynButton = tk.Button((self,'DynButton'),**{'text': 'DynButton 1'})
        self.DynButton_1 = tk.Button((self,'DynButton'),**{'text': 'DynButton 2'})
        self.DynButton_2 = tk.Button((self,'DynButton'),**{'text': 'DynButton 3'})
        self.DynButton.pack()
        self.DynButton_1.pack()
        self.DynButton_2.pack()
        
        tk.gui()

Application().mainloop()
Übrigens, den GuiDesigner kann man auch mit tk.gui() laden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:@Alfons:
Wenn man das Pseudocode-Bsp. mit der for-Schleife weiter ausbaut, sprich ich habe eine fertige Anwendung mit funktionierender Oberfläche, möchte aber Teile davon einem refactoring unterziehen, z.B. einen Button mit einer Aktion hinzufügen etc. - Wie stellst Du sicher, dass die Logik nicht verloren geht bzw. noch korrekt arbeitet? Was passiert mit dem Rest aus dem Bsp. (das `do_sumthing_with_button`)?
Ok, dann schauen wir was passiert. Gehen wir davon aus:

Code: Alles auswählen

import DynTkInter as tk
 
root = tk.Tk()
 
class DynButton(tk.Button):
    def __init__(self):
        tk.Button.__init__(self,"DynButton",text='DynButton')
        self.pack()
   
def do_sumthing_with_button(dummy): pass
 
def create_fancy_dynbutton(some_argument):
    # init & return button from dyn classes
    dynbutton_object = DynButton()
    return dynbutton_object
 
for i in range(3):
    do_sumthing_with_button(create_fancy_dynbutton(i))
 
tk.gui()

root.mainloop()
Mit tk.gui() hab ich den GuiDesigner eingebunden. Mit dem GuiDesigner machte ich dan den letzten Button grün und habe es dann mit export with Names wieder abgespeichert. Diesmal auf das Originalfile (davon wird übrigens ein Backup angelegt). Und was kommt dabei heraus? Das da:

Code: Alles auswählen

import DynTkInter as tk
 
root = tk.Tk()
 
class DynButton(tk.Button):
    def __init__(self):
        tk.Button.__init__(self,"DynButton",text='DynButton')
        self.pack()
   
def do_sumthing_with_button(dummy): pass
 
def create_fancy_dynbutton(some_argument):
    # init & return button from dyn classes
    dynbutton_object = DynButton()
    return dynbutton_object
 
for i in range(3):
    do_sumthing_with_button(create_fancy_dynbutton(i))
 
tk.gui()

# =======  New GUI Container Widgets ======================

class Application(tk.Tk):

    def __init__(self,**kwargs):
        tk.Tk.__init__(self,**kwargs)
        self.DynButton = tk.Button((self,'DynButton'),**{'text': 'DynButton'})
        self.DynButton_1 = tk.Button((self,'DynButton'),**{'text': 'DynButton'})
        self.DynButton_2 = tk.Button((self,'DynButton'),**{'text': 'DynButton', 'bg': 'green'})
        self.DynButton.pack()
        self.DynButton_1.pack()
        self.DynButton_2.pack()

# =======  End New GUI Container Widgets ==================

root.mainloop()
Die Applikationklasse hattest Du nicht, also wurde sie vor mainloop angefügt. Aufgerufen wird sie auch nicht, weil ein solcher Aufruf auch nicht drin war. Am Rest ändert sich eben nichts. Hättest Du die Applikationsklasse gehabt, wäre von Klassendefinition bis erste Leerzeile nach __init__ ausgetauscht worden.

Jetzt kannst Du entscheiden, ob Du das statisch haben möchtest und Application verwendest. Dann müsstest Du, wenn du mit einer Schleife darauf zugreifen willst nach der init noch eine Liste einfügen. Oder Du verwendest den geänderten Button für alle Buttons etwa in class DynButton oder create_fancy_dynbutton oder do_sumthing_with_button.

Ach so, eine andere Möglichkeit anstatt unterschiedliche Namen wäre bei gleichnamigen Widgets auch eine Liste. Weiß aber nicht was besser ist. Evtl möchte man ja gerade indiziert darauf zugreifen? Gerade wenn man Widgets mit einer Schleife erzeugt, möchte man wohl indiziert darauf zugreifen, oder? Bei den Scripts verwende ich ja auch den Index.

Und der Index hat Vorteile. Beim GridLayout kann ich so einfach mit dem Button 'Hide' den Hintergrund löschen, bei dem alle Widgets den Namen NONAME haben.

Wobei natürlich noch unklar ist, wie das bei Container Widgets mit gleichem Namen ist. Alle dieselbe Klasse oder unterschiedlich? Frames könnten gleiche Inhalte haben aber auch unterschiedliche. Soll man bei hundert Frames gleichen Namens mit Inhalt, dann 100 Klassen erzeugen?

Nö, dynamische Inhalte sollte man vom Abspeichern ausschließen können. Sollte wohl die Methode saveOnlyCode oder Lock() auch für den export berücksichtigen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:Auch verstehe ich diese Anmerkung nicht: "buttons are dynamically created, so the widgets shall not be saved. Only the code, which creates them shall be saved" - was meinst Du hier mit dynamisch?
Die Anmerkung verstehst DFu auch nicht ganz. Sie bezieht sich auf eine Besonderheit der GUI Scripts. Markierte Source Code Teile werden, wenn man ein Script mit dem GUI Designer lädt, auch mit als Source Code geladen und beim Speichern wieder mitgespeichert.

Der Ausdruck besagt, dass hier der Sourcecode gespeichert werden soll, aber die Widgets für diesen Container nicht. Man will ja nicht dynamisch erzeugte Widgets nochmals zusätzlich als statische Widgets speichern.

Diese Funktion sollte man für den Export auch haben, denke ich, allerdings der Name paßt dann nicht ganz. Sollte man wohl umbennen. Das wäre, wenn ein Container nur dynamisch erzeugte Widgets hätte. Was allerdings bei einer Mischung von statisch und und dynamisch erzeugten Widgets sein sollte, darüber habe ich noch nicht nachgedacht. Man kann natürlich den Container markieren, dass die Widgets nicht gespeichert werden, aber die Definition der statischen Widgets durch eine Leerzeile von der __init__ trennen, damit sie erhalten bleiben.

Und wenn man sie doch mal editieren will, kann man zwischendurch mal die Erzeugung der dynamischen Widgets unterdrücken und die Sperre wieder herausnehmen oder auch die doppelten Widgets wieder löschen.

Also die Sperrfunktion sollte ich noch machen. Die saveOnlyCode speicher die Widgets nicht und Lock sorgt dafür, dass man den Container gar nicht mal betreten kann im GuiDesigner. Als Methode ist sie jetzt nicht implementiert, aber mit .isLocked = True, kann man jetzt auch den Container sperren und es wird dann auch keine Klasse für den Container erzeugt und man kann dann auch nicht in den Container mit dem GuiDesigner hinein. Sperren geht also doch schon.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Also, das mit JSON Format ist nun auch ziemlich klar. Ich halte mich einfach an die ursprüngliche Syntax und beseitige Python spezifisches. Und für jede Zeile eine Liste:

Code: Alles auswählen

# Aus
Radiobutton('button_one',**{'text': 'One', 'underline': '0', 'anchor': 'w'}).grid(**{'sticky': 'ew', 'row': '1'})

# wird
['Radiobutton',['button_one',{'text': 'One', 'underline': '0', 'anchor': 'w'}],['grid',{'sticky': 'ew', 'row': '1'}]]
Da brauche ich auch nicht zu parsen, sondern kann es direkt umsetzen.
Nö noch einfacher:

Code: Alles auswählen

['Radiobutton','button_one',{'text': 'One', 'underline': '0', 'anchor': 'w'}],
['grid',{'sticky': 'ew', 'row': '1'}]
Und was braucht man zur Umsetzung?

- Einen Dictionary Eintrag mit Referenz auf die Funktion und Anzahl und Art der Parameter
- Als Art genügt dann True oder False, ob es ein Dictionary ist, denn da muss man dann ** dazumachen.
- und dann braucht man noch das automatische goOut() am Ende einer Children Liste.

Ach so, geht Referenz auf Klasse? Muss ich ausprobieren.

Ach was goIn() und goOut() ist?

Eine Selection hat zwei Komponenten:

- container
- widget

goIn() ist:

container = widget (aber nur bei Container Widgets)

goOut() ist:

widget = container
container = container.master

natürlich noch unter Berücksichtigung der root. Da bleibt der container auf der root stehen, denn weiter hinaus geht es nicht.

Also, angenommen, der ausgewählte Gegenstand wäre ein Schrank. goIn() bedeutet, der Schrank ist ausgewählt aber von innen mit Sicht auf die darin befindlichen Kleidungsstücke. Und goOut() bedeutet, der Schrank ist ausgewählt aber von außen mit Sicht auf das Zimmer, in dem er sich befindet. Würde man ein Kleidungsstück im Schrank auswählen, dann würde goOut() auch dazu führen. dass der Schrank danach von außen ausgewählt ist, entspricht in etwa cd .. aber gekoppelt mit Selektion
Antworten