Forum: PC-Programmierung Kopien von tkinter-Frames erstellen und auswerten (python)


von Rahul D. (rahul)


Lesenswert?

Moin,
ich habe hier einen tkinter-Frame mit mehreren Bedien- und 
Anzeigeelementen.
Den würde ich gerne per Schleife vervielfältigen und in einem anderen 
Frame verteilen.
Das geht auch soweit.

Leider kann ich aber immer nur auf den zuletzt erstellten zugreifen.

In anderen (klammerorintierten) Programmiersprachen erstellt man ja neue 
Instanzen per "new" und hat dann auch eindeutige "Ansprechpartner".
Wie geht man mit sowas in python um?

Es kommt weder ein Wechsel auf eine andere Programmiersprache, noch auf 
ein andere GUI-Framework infrage.

Vielen Dank schon mal

von Norbert (der_norbert)


Lesenswert?

Rahul D. schrieb:
> In anderen (klammerorintierten) Programmiersprachen erstellt man ja neue
> Instanzen per "new" und hat dann auch eindeutige "Ansprechpartner".
> Wie geht man mit sowas in python um?

Klasse erstellen. Instanziieren, bis der Speicher ausgeht.
1
#!/usr/bin/python3
2
# -*- coding: UTF-8 -*-
3
# vim: fileencoding=utf-8: ts=4: sw=4: expandtab:
4
5
class MyClass():
6
7
    def __init__(self, num):
8
        self._num = num**2
9
10
    def show(self):
11
        return self._num
12
13
cls = [ MyClass(i) for i in range(10) ]
14
15
for i in range(10):
16
    print(cls[i].show())

: Bearbeitet durch User
von Rahul D. (rahul)


Lesenswert?

Norbert schrieb:
> Klasse erstellen. Instanziieren, bis der Speicher ausgeht.

Danke, geht so (bis auf die Button-Aktionen, die jetzt auch umziehen).
Da stand ich wie der Ochs' vorm Berg.

von Ralf L. (ralfmitkurzschluss)


Lesenswert?

import tkinter as tk

class BedienFrame(tk.Frame):
    def __init__(self, parent, nummer):
        super().__init__(parent, borderwidth=2, relief="groove")
        self.label = tk.Label(self, text=f"Frame {nummer}")
        self.label.pack()
        self.button = tk.Button(self, text="Klick mich", 
command=self.aktion)
        self.button.pack()

    def aktion(self):
        print(f"Button in Frame {self.label.cget('text')} gedrückt")

root = tk.Tk()
hauptframe = tk.Frame(root)
hauptframe.pack()

# Hier erzeugen wir mehrere Instanzen und merken sie uns in einer Liste:
frames = []
for i in range(5):
    f = BedienFrame(hauptframe, i)
    f.grid(row=i, column=0, padx=5, pady=5)
    frames.append(f)   # WICHTIG: Referenz speichern!

# Zugriff auf eine bestimmte Instanz:
frames[2].label.config(text="Geändert!")

root.mainloop()

In Python erzeugst du Objekte einfach durch Funktions- oder 
Klassenaufruf (f = BedienFrame(...)).
  •  Du musst die Referenzen (z. B. f) in einer Liste oder einem 
Dictionary speichern, wenn du später wieder darauf zugreifen willst.
  •  Überschreibst du die Variable in der Schleife (f = ... ohne 
Speicherung), bleibt am Ende nur die letzte Instanz übrig.
  •  Zugriff erfolgt später über frames[index] oder frames_dict[name].

Wenn du lieber sprechende Namen hast, kannst du z. B. ein Dictionary 
verwenden:

frames = {}
for i in range(5):
    frames[f"frame_{i}"] = BedienFrame(hauptframe, i)
    frames[f"frame_{i}"].grid(row=i, column=0)

Dann kannst du später schreiben:

frames["frame_3"].button.config(text="Aktiviert")


In Python ersetzt du das Konzept von new durch einfaches Instanziieren 
und das Speichern der Referenzen (z. B. in einer Liste oder einem 
Dictionary).
So hast du „eindeutige Ansprechpartner“ für jede erstellte GUI-Einheit.

von Ein T. (ein_typ)


Angehängte Dateien:

Lesenswert?

Ralf L. schrieb:
>
1
> class BedienFrame(tk.Frame):
2
>     def __init__(self, parent, nummer):
3
>         # ...
4
> 
5
> root = tk.Tk()
6
> hauptframe = tk.Frame(root)
7
> hauptframe.pack()
8
>

Das verstehe ich nicht. Du weißt offensichtlich, wie Objektorientierung 
mit Tkinter geht. Warum erzeugst Du Dein Hauptfenster trotzdem 
prozedural? Das ärgert mich schon bei den 10000 Tutorials, die im Netz 
herumschwirren, dort schreibt offenbar immer wieder ein neuer Tünnes von 
den anderen ab. Meinen Beispielcode habe ich hier angehängt.

Das Einzige, was man dabei beachten muß, wird zunehmend unwichtiger: 
Tkinter ist schon recht alt und unter Python2 waren das old-style 
classes, die also nicht von object geerbt haben. Darum hat dort super() 
nicht so funktioniert, wie wir das heute kennen (und oft als 
selbstverständlich annehmen), weil die von object geerbte Infrastruktur 
nicht vorhanden war. Mit dem Ausschleichen von Python2 wird das zwar 
zunehmend unwichtig, denn seit Python3 gibt es ja nurmehr new-style 
classes, die automatisch von object erben und deshalb die notwendige 
Infrastruktur haben. Aber wenn man mal alten Code pflegen müsste und den 
aus irgendeinem Grund nicht auf Python3 portieren könnte oder wollte, 
ist es sinnvoll, das im Hinterkopf zu behalten.

>
1
> frames = []
2
> for i in range(5):
3
>     f = BedienFrame(hauptframe, i)
4
>     f.grid(row=i, column=0, padx=5, pady=5)
5
>     frames.append(f)   # WICHTIG: Referenz speichern!
6
>

Da würde ich eine List Comprehension eleganter finden:
1
#!/usr/bin/env python # for proper syntax highlighting
2
3
frames = [BedienFrame(hauptframe, i) for i in range(5)]
4
for i, frame in enumerate(frames):
5
    frame.grid(row=i, column=0, padx=5, pady=5)

>
1
> frames = {}
2
> for i in range(5):
3
>     frames[f"frame_{i}"] = BedienFrame(hauptframe, i)
4
>     frames[f"frame_{i}"].grid(row=i, column=0)
5
>

Hier gefiele mir eine Dictionary Comprehension besser:
1
#!/usr/bin/env python # for proper syntax highlighting
2
3
frames = {f'frame_{i}': BedienFrame(hauptframe, i) for i in range(5)}
4
for i, frame in enumerate(frames.values()):
5
    frame.grid(row=i, column=0)

von Rahul D. (rahul)


Lesenswert?

Ein T. schrieb:
> Das ärgert mich schon bei den 10000 Tutorials, die im Netz
> herumschwirren, dort schreibt offenbar immer wieder ein neuer Tünnes von
> den anderen ab

Dann schreib doch endlich mal eins, das dem mmodernen Standard 
entspricht und laber nicht immer in irgendwelchen Threads rum, wie du es 
machen würdest (Sätze, die mit "Ich würde.." beginnen, stellen mir die 
Nackenhaare auf, weil die implizieren, dass alle Welt das selbe weiß wie 
du).
Du könntest ja beispielsweise mal einen Artikel hier in der 
Artikelsammlung deses Forums erstellen, wenn du schon so eine Korypäe 
bist.
Dann brauchst du nur auf deinen Artikel verweisen.

Ralf L. schrieb:
> In Python erzeugst du Objekte einfach durch Funktions- oder
> Klassenaufruf (f = BedienFrame(...)).
>   •  Du musst die Referenzen (z. B. f) in einer Liste oder einem
> Dictionary speichern, wenn du später wieder darauf zugreifen willst.
>   •  Überschreibst du die Variable in der Schleife (f = ... ohne
> Speicherung), bleibt am Ende nur die letzte Instanz übrig.
>   •  Zugriff erfolgt später über frames[index] oder frames_dict[name].
>
> Wenn du lieber sprechende Namen hast, kannst du z. B. ein Dictionary
> verwenden:

Danke, mein Programm läuft inzwischen.

von Ein T. (ein_typ)


Lesenswert?

Rahul D. schrieb:
> Dann schreib doch endlich mal eins, das dem mmodernen Standard
> entspricht und laber nicht immer in irgendwelchen Threads rum, wie du es
> machen würdest (Sätze, die mit "Ich würde.." beginnen, stellen mir die
> Nackenhaare auf, weil die implizieren, dass alle Welt das selbe weiß wie
> du).

Schau doch einfach in den Anhang meines Beitrages, den Du wieder einmal 
in Deiner Dir ganz eigenen Höflichkeit beantwortet hast. Da ist sogar 
ein Link "Codeansicht", den Du bemühen kannst. Du schaffst das, ich 
glaube an Dich!

Ich wünsche Dir viel Glück und allen einen schönen Sonntag.

von Rahul D. (rahul)


Lesenswert?

Ein T. schrieb:
> Schau doch einfach in den Anhang meines Beitrages, den Du wieder einmal
> in Deiner Dir ganz eigenen Höflichkeit beantwortet hast. Da ist sogar
> ein Link "Codeansicht", den Du bemühen kannst. Du schaffst das, ich
> glaube an Dich!

Arrogant bist du gar nicht, oder?
Ich hatte dir einen Vorschlag gemacht, weil du ja immer von allem so 
genervt bist, dich alle anderen immer aufregen und dauernd mit deinem 
Wissen prahlen musst, es dann aber auch jedes Mal wieder hinbschreiben 
musst.
Wü+rdest du das in einem Artikel in der Artikelsammlung veröffentlich, 
bräuchtest du höchstens den Link dazu verteilen (das könnten dann 
überigens auch andere machen, die den Artikel vielleicht gut finden).
Warum ich deine Art nicht mag, habe ich dir geschrieben.
Wenn dich meinne "höfliche Art" stört: Bleib weg (von Threads, die ich 
eingestellt habe).
Mich interessiert deine Meinung kein Stück, und helfen konntest du ja 
auch bisher nicht.

Edit: Dein Quellcode ist ja super kommentiert. Da kann man richtig was 
lernen. NOT

: Bearbeitet durch User
von Ein T. (ein_typ)


Lesenswert?

Rahul D. schrieb:
> Arrogant bist du gar nicht, oder?

Das ist eine  außerordentlich witzige Frage von jemandem, der hier erst 
um Hilfe bittet und dann die Helfer verspottet. :-)

> Edit: Dein Quellcode ist ja super kommentiert. Da kann man richtig was
> lernen. NOT

Mein Vorposter hatte schon gezeigt, daß er das Thema grundsätzlich 
verstanden und nur eine kleine Anregung gebraucht hat. Er wird meinen 
Anhang sicher auch ohne Kommentare verstehen, und alles Übrige steht in 
meinem Beitrag.

Schau, ich habe verstanden, daß Du Python nicht leiden kannst und Du 
trotzdem von irgendwem dazu gezwungen wirst, etwas damit zu machen. Aber 
auch wenn mir das leid tut, bin ich der Falsche, um Deinen Frust darüber 
auszulassen.

von Rahul D. (rahul)


Lesenswert?

Ein T. schrieb:
> Schau, ich habe verstanden, daß Du Python nicht leiden kannst und Du
> trotzdem von irgendwem dazu gezwungen wirst, etwas damit zu machen. Aber
> auch wenn mir das leid tut, bin ich der Falsche, um Deinen Frust darüber
> auszulassen.

Das hast du falsch verstanden.
Ich mag Sätze, die mit "Ich würde..." beginnen, einfach nicht.
Soll ich deiner Meinung nach das Projekt noch mal von vorne beginnen, um 
mir vorher noch dein Wissen anzueignen?
Wie wärw denn eine Formulierung wie "Vorschlag fürs nächste Mal...".
Vielleicht übernehme ich beim nächsten Projekt ja deinen Vorschlag. Aber 
jetzt ist es dafür zu spät ("Never change a running system!").
Deine "Hilfe" mit ".after" in anderen Python-Thread war auch nicht 
wirklich hilfreich. Dafür hatte ich aber in meiner "typisch freundlichen 
Art" ("höflich" ist mMn unpassend, da durch Höflichkeitsbremser noch 
negativer belegt) schon bedankt.

von Ein T. (ein_typ)


Lesenswert?

Rahul D. schrieb:
> Ein T. schrieb:
>> Schau, ich habe verstanden, daß Du Python nicht leiden kannst und Du
>> trotzdem von irgendwem dazu gezwungen wirst, etwas damit zu machen. Aber
>> auch wenn mir das leid tut, bin ich der Falsche, um Deinen Frust darüber
>> auszulassen.
>
> Das hast du falsch verstanden.

Okay.

> Ich mag Sätze, die mit "Ich würde..." beginnen, einfach nicht.
> Wie wärw denn eine Formulierung wie "Vorschlag fürs nächste Mal...".

Naja, "ich würde" sagt und zeigt ja, wie ich das machen würde, also 
implizit mit meinem Wissen, und meiner Erfahrung. Die Formulierung, die 
Du vorschlägst, empfinde ich hingegen, als würde mein alter Meister zum 
Lehrling sprechen.

> Soll ich deiner Meinung nach das Projekt noch mal von vorne beginnen, um
> mir vorher noch dein Wissen anzueignen?

Das kommt darauf an, ehrlich gesagt. Das Ziel der Übung sollte IMHO 
bleiben, am Ende eine Codebasis zu erhalten, die natürlich erstens tun 
muß, was sie tun soll, und zweitens möglichst les- und wartbar ist. 
Dabei stehen drittens auch immer die Fragen nach Ökonomie und 
Priorisierung im Raum.

> Vielleicht übernehme ich beim nächsten Projekt ja deinen Vorschlag. Aber
> jetzt ist es dafür zu spät ("Never change a running system!").

Puh... "never change a running system" ist für einen DevOpsler schwer zu 
verknusen, denn changing running systems ist ja quasi mein Beruf.

> Deine "Hilfe" mit ".after" in anderen Python-Thread war auch nicht
> wirklich hilfreich. Dafür hatte ich aber in meiner "typisch freundlichen
> Art" ("höflich" ist mMn unpassend, da durch Höflichkeitsbremser noch
> negativer belegt) schon bedankt.

Das stimmt, allerdings... wenn Du häufiger mit Tkinter arbeitest, wirst 
Du Dich garantiert noch an diesen Rat erinnern. Spätestens dann, wenn Du 
eine Anzeige aktualisieren möchtest und sich nichts tut, obwohl Du genau 
weißt, alles richtig zu machen. ;-)

von Rahul D. (rahul)


Lesenswert?

[Offtopic]
Ein T. schrieb:
> quasi mein Beruf.

Meiner nicht. Nicht mal quasi ;)

Ein T. schrieb:
> Naja, "ich würde" sagt und zeigt ja, wie ich das machen würde, also
> implizit mit meinem Wissen, und meiner Erfahrung.
Genau so verstehe ich diese Formulierung auch.
Ein kleines Beispiel (jetzt klinge ich wie mein Vater), warum ich diese 
Aussage nicht so pralle finde:
Ich hatte mir einen Hochdachkombi in der Lieferwagen-Version gekauft, da 
ich zu dem Zeit öfter mal Gerätschaften transportiert habe.
Eine Bekannte meinte dann "Ich würde mir den ja zum Wohnmobil umbauen." 
- "Du würdest dir den nicht mal kaufen."
Oder jemand sagt "Ich würde das ja so und so machen!" - "Dann mach das 
doch selber!"

> Die Formulierung, die
> Du vorschlägst, empfinde ich hingegen, als würde mein alter Meister zum
> Lehrling sprechen.
Und? Was spricht dagegen? Solange man nicht auf dem selben Stand ist 
(dass ich keine Ahnung von python habe, hatte ich schon geschrieben), 
aber jemanden frage, der mehr Ahnung hat, ist das ein 
Meister-/Geselle-Lehrlingsverhältnis bzw. Lehrer-Schüler-Verhältnis.
[/Offtopi]

von Ein T. (ein_typ)


Lesenswert?

Rahul D. schrieb:
> Ein T. schrieb:
>> Die Formulierung, die
>> Du vorschlägst, empfinde ich hingegen, als würde mein alter Meister zum
>> Lehrling sprechen.
>
> Und? Was spricht dagegen? Solange man nicht auf dem selben Stand ist
> (dass ich keine Ahnung von python habe, hatte ich schon geschrieben),
> aber jemanden frage, der mehr Ahnung hat, ist das ein
> Meister-/Geselle-Lehrlingsverhältnis bzw. Lehrer-Schüler-Verhältnis.
> [/Offtopi]

Ich bin hier (soweit ich weiß) niemandes Chef und will das auch nicht 
sein, und ich möchte so etwas auch nicht einmal andeuten. Deine 
Formulierung gibt mir das Gefühl, ich spräche als Chef zu meinem 
Mitarbeiter, als mein "schau bitte mal auf mein Beispiel, ob Dir das 
nicht auch besser gefällt".

von Bernd W. (berndwiebus) Benutzerseite


Lesenswert?

Hallo T.

Ich bin zwar nicht Ralf, aber die Frage könnte auf mich auch passen.

Ein T. schrieb:

> Warum erzeugst Du Dein Hauptfenster trotzdem
> prozedural? Das ärgert mich schon bei den 10000 Tutorials, die im Netz
> herumschwirren, dort schreibt offenbar immer wieder ein neuer Tünnes von
> den anderen ab.

Weil es in solchen Situationen oft dazu kommt, dass man zwar ähnliche 
Inhalte, aber nicht die gleichen verwendet. Objektorientierung erzeugt 
gleiche Objekte, die aber mit einer Parametrisierung angepasst werden 
können. Theoretisch könnte man die Objekte jetzt so gestalten, das die 
Parametrisierung diese Unterschiede mit abdeckt.

Leider ist es aber oft so, dass man erst beim Erstellen eines Programmes 
die vielen kleinen Nickeligkeiten erkennt, die die Unterschiede 
verlangen. Jetzt nachträglich immer wieder das Objekt anpassen, kann 
kompliziert und vor allem langwierig werden.

Irgendwann ist dann der Punkt gekommen, wo es schneller geht, statt ein 
Objekt anzupassen, per copy und paste eine Kopie zu machen und separat 
in das Programm einzufügen.

Wenn Du hauptberuflich Python schreibst, mag das anders aussehen, aber 
gerade Python ist eine Sprache, die wegen ihrer Einfachheit von vielen 
Programmierern verwendet wird, die eher selten oder sogar nur als Hobby 
gelegentlich etwas programmieren.

Als solcher muss ich mich nach einem halben oder ganzen Jahr, wenn ich 
alles vergessen habe, wieder neu in die Details der Sprache bzw. meines 
eigenen Programmes einarbeiten, und da ist dann Objektorientierung auf 
einmal etwas umständlich. Copy and paste ist dann durchaus die 
einfachere Alternative und führt zu dem prozeduralen Stil.

Wenn Du bessere Alternativen kennst, die auch meine eigenen 
diesbezüglichen Unzulänglichkeiten abdecken, so bin ich auf Deine 
Vorschläge gespannt.

Nachtrag: Sehr viele der Eigenschaften, die Python so "einfach" machen, 
betreffen Fälle, wo durch Schleifen und Vereinbahrung vieles 
"automatisiert" werden kann. Diese Fälle sind aber nur hilfreich, wenn 
ich ständig damit arbeite. Als Gelegenheitsprogrammierer verstolpere ich 
dort nur.

Mit freundlichem Gruß:
Bernd Wiebus alias dl1eic
http://www.l02.de

: Bearbeitet durch User
von Ein T. (ein_typ)


Angehängte Dateien:

Lesenswert?

Hallo Bernd,

Bernd W. schrieb:
> Ein T. schrieb:
>> Warum erzeugst Du Dein Hauptfenster trotzdem
>> prozedural? Das ärgert mich schon bei den 10000 Tutorials, die im Netz
>> herumschwirren, dort schreibt offenbar immer wieder ein neuer Tünnes von
>> den anderen ab.
>
> Weil es in solchen Situationen oft dazu kommt, dass man zwar ähnliche
> Inhalte, aber nicht die gleichen verwendet. Objektorientierung erzeugt
> gleiche Objekte, die aber mit einer Parametrisierung angepasst werden
> können. Theoretisch könnte man die Objekte jetzt so gestalten, das die
> Parametrisierung diese Unterschiede mit abdeckt.

Ohne konkretes Beispiel ist es natürlich nicht ganz einfach, etwas in 
Code zu zeigen. Aber die Objektorientierung bietet doch neben der 
Parametrierung noch eine andere Möglichkeit zur Spezialisierung, nämlich 
die Vererbung. Vielleicht magst Du dazu einen eigenen Thread eröffnen 
und mit einem (oder gern mehreren) Codebeispielen zeigen, was genau Du 
meinst?

> Irgendwann ist dann der Punkt gekommen, wo es schneller geht, statt ein
> Objekt anzupassen, per copy und paste eine Kopie zu machen und separat
> in das Programm einzufügen.

Wenn ich so etwas klassisch prozedural schreibe, wie es zum Beispiel auf 
der (ansonsten sehr empfehlenswerten) Seite Realpython [1] gezeigt wird, 
muß ich beim Copy'n'Paste mehrere Zeilen kopieren, die Variablennamen in 
einer Reihe meiner kopierten Codezeilen anpassen, und wenn ich dabei 
eine Zeile vergesse oder einen Variablennamen nicht oder nicht richtig 
anpasse, kann ich enormes Glück haben und die Veranstaltung explodiert 
sofort. Mit ein wenig Pech habe ich einen sehr schwer zu findenden 
Fehler eingebaut.

Schau Dir auf der verlinkten Seite [1] einfach mal unten den "Text 
Editor" an (letztes Beispiel vor "Conclusion"): fünf globale Variablen 
auf nur 37 Zeilen Code in einem winzigen Projekt. Ich hab' mir mal den 
Spaß gemacht, und dieses Beispiel in zwei OO-Versionen umgebaut: einmal 
in better.py so "halbwegs OO", wie ich das in einem kleinen Projekt wie 
diesem umgesetzt hätte. Zudem einmal in best.py, wo auch für den Frame 
mit den Buttons eine eigene Klasse benutzt wird -- was bei diesem 
Winzling natürlich Overkill ist, aber in einem großen Projekt würde das 
durch die bessere Organisation des Code den Überblick, und damit die 
Les- und Wartbarkeit deutlich verbessern. Den Originalcode von der 
verlinkten Seite habe ich der Einfachheit halber als main.py angehängt.

[1] https://realpython.com/python-gui-tkinter/

Für jeden, der Interesse an der Entwicklung mit Tkinter hat, ist die 
Seite trotzdem ausgesprochen lesenswert, auch wenn ich mit dem 
prozeduralen Stil ihres Autors unglücklich bin. Dennoch ist die Seite 
großartig!

> Wenn Du hauptberuflich Python schreibst, mag das anders aussehen, aber
> gerade Python ist eine Sprache, die wegen ihrer Einfachheit von vielen
> Programmierern verwendet wird, die eher selten oder sogar nur als Hobby
> gelegentlich etwas programmieren.

Naja, auch wenn man kein hauptberuflicher Entwickler ist -- was ich 
nicht bin und auch nie gewesen bin, ich komme aus dem Operationsbereich 
-- ist es immer sinnvoll, sauberen und sauber strukturierten Code zu 
schreiben, den man auch morgen noch lesen und viel leichter anpassen, 
warten und pflegen kann. Dabei hilft die Objektorientierung auch den 
Nichtprofis enorm. Wie gesagt, schau' Dir den angehängten Code an: 
findest Du denn nicht auch, daß meine Varianten viel strukturierter und 
übersichtlicher sind?

Hinzu käme bei einem wirklich großen Projekt, daß ich meinen Code ganz 
leicht auf mehrere Dateien verteilen kann. Das ist mit dem prozeduralen 
Originalcode von Realpython leider nicht möglich.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.