Bilder mit while Schleife anzeigen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
badi113
User
Beiträge: 22
Registriert: Mittwoch 27. März 2019, 14:53

Hy Leute,

nun bin Ich bei mehrere Bilder mit Buttons zu erstellen, was auch zumindest halbwegs funktioniert.
Leider wird mir aber nur das letzte Bild angezeigt und die anderen nicht.

Code: Alles auswählen

root1 = Frame(frame)
root1.pack(anchor="nw", fill="x")


dirList = os.listdir(sql_bilder + "icons/")
dirList.sort()

icons = []
for sFile in dirList:
    if sFile.find('.gif') == -1:
        continue

    icons.append(sFile)

#####Zähler für Icons in Ordner    
counter=0
for zaehler in icons:
    counter=counter+1
 
a=0
while a < counter:
    
    print(icons[a])
    
    root2 = Frame(root1)
    root2.pack(anchor="nw", fill="x")
    
    logo = PhotoImage(file=sql_bilder + "icons/" + icons[a])
    Bild = Label (root2, image=logo)
    Bild.grid(row="0", column="0")

    button = Button(root2,  text="...", border=1)
    button["font"] = schrift# Schriftart und Größe
    button["borderwidth"] = 1 #Randbreite
    button.grid(row="1", column="0")
    a= a+1
__deets__
User
Beiträge: 14543
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ok, zu dem Code gibt es sehr viel zu sagen:

- warum baust du eine Schleife um die Laenge von icons zu bestimmen? len(icons) tut das fuer dich.
- warum baust du muehselig eine while-Schleife, wenn du doch gleich for a in range(len(icons)) machen kannst?
- wo wir schon dabei sind: warum ueberhautp einen Zaehler? Du hast doch schon gezeigt, dass du weisst, dass man direkt ueber eine Liste iterieren kann und die Elemente bekommt.

Code: Alles auswählen

for icon in icons:
      pfad = sql_bilder + "icons/" + icon
wuerde dann doch schon alles erledigen.

- warum verteilst du das Wissen, dass die Pfade relativ zu sql_bilder & "icons" sind, statt einfach gleich beim erstellen der Liste icons alle Pfade komplett zu erzeugen?

Und zu guter Letzt zu deinem eigentlichen Problem: PhotoImage muss unbedingt "aufgehoben" werden. Sonst wird es garbage collected, und damit verschwindet das Bild. Es gibt diverse Moeglichkeiten das zu tun, die fuer dich wohl einfachste waere

Bild.logo = logo

zu benutzen. Uebrigens schreibt man in Python Namen von Variablen klein, was du auch fast ueberall machst, aber "Bild" hat ploetzlich ein grosses B.
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@badi113: Du musst eine Referenz auf Python-Seite auf die Bilder behalten. Wenn Du das nicht tust, dann gibt Python den Speicher für das Bild wieder frei, und Tk hat nichts zum Anzeigen.

Zu dem Code gibt's aber auch sonst noch einiges zu sagen. Das scheint auf Modulebene zu stehen – da gehört das nicht hin. Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Und man verwendet keine globalen Variablen. Alles was eine Funktion oder Methode ausser Konstanten benötigt bekommt sie als Argument(e) übergeben. Was bei jedem nicht-trivialen GUI-Programm zur Folge hat, das man objektorientierte Programmierung braucht, also selber Klassen schreiben muss.

Wenn man anfängt Namen zu nummerieren macht man in aller Regel etwas falsch. Entweder will man sich bessere Namen ausdenken, oder aber gar keine Einzelnamen sondern eine Datenstruktur verwenden. Oft eine Liste. Im Fall von `root1` ist aber der Name falsch. Es kann bei einem Baum nur *eine* Wurzel geben, und bei Tk ist das der Name für das `Tk`-Objekt. Einen `Frame` würde man einfach `frame` nennen. Eventuell mit einen Präfix der etwas über die Bedeutung des Inhalts des `Frame` aussagt, falls das interessant für den Leser sein sollte.

Das Du `Frame` einfach so benutzt lässt befürchten das aus `tkinter` einfach alles mit einem * importiert wurde. Nicht machen! Das ist Böse™. Damit holt man sich über 150 Namen ins Modul von denen nur ein Bruchteil tatsächlich benötigt wird. Und nicht nur Namen die im `tkinter`-Modul selbst definiert werden, sondern auch welche die das Modul seinerseits von woanders her importiert hat. Man hat da wenig Kontrolle was man sich da alles einfängt.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). Also `dir_list` statt `dirList`. Wobei da das `list` nicht in den Namen gehört. Wenn man Grunddatentypen in Namen schreibt, bekommt man regelmässig das Problem das man während der Entwicklung den Datentyp auf einen spezialisierteren ändert, und dann entweder überall im Programm die betroffenen Namen ändern muss, oder falsche, irreführende Namen im Programm hat.

Pfadteile setzt man nicht mit ``+`` zusammen, sondern mit `os.path.join()`.

Kryptische Abkürzungen und Prä- und Suffixe sollte man vermeiden. Der Leser soll am Namen die Bedeutung des Wertes ablesen können und nicht raten müssen wöfür das `s` bei `sFile` wohl stehen mag. Wobei `file` ein guter Name für ein Dateiobjekt ist, aber ein schlechter für einen Datei*namen*. Bei `file` erwartet der Leser ein Objekt mit Methoden wie `read()`/`write()`/`close()`/… und keine Zeichenkette (oder ein `pathlib.Path`-Objekt).

Die `find()`-Methode von Sequenztypen sollte man eher nicht verwenden weil auch der Wert -1 für „nicht gefunden“ ein gültiger Index ist. Wenn man tatsächlich den Index benötigt, sollte man die `index()`-Methode nehmen. Aber der Index spielt hier ja gar keine Rolle, Du willst ja wissen ob '.gif' in dem Namen vorkommt oder nicht – dafür gibt es den ``in``-Operator: ``if '.gif' not in sFile:``. Aber halt: Das willst Du gar nicht wissen, denn '.gif' soll ja nicht *irgendwo* vorkommen, sondern am *Ende*. Das kann man mit ``if not sFile.endswith('.gif'):`` testen.

Das ist letztlich aber alles zu umständlich: es gibt das `glob`-Modul.

`icons` ist als Name wieder ungenau, denn in der Liste sind keine Icons sondern Pfade zu Icons.

So wie Du `counter` bestimmst, solltest Du noch mal ein Grundlagentutorial durcharbeiten. Das man die Anzahl von Elementen in Containertypen mit der `len()`-Funktion abfragen kann, sollte man wissen:

Code: Alles auswählen

    #####Zähler für Icons in Ordner    
    counter = 0
    for zaehler in icon_paths:
        counter = counter + 1

    # ->
    
    counter = len(icon_paths)
Dann ist die ``while``-Schleife eigentlich eine ``for``-Schleife:

Code: Alles auswählen

    counter = len(icon_paths)     
    a = 0
    while a < counter:
        print(icon_paths[a])
        # ...
        a = a + 1
        
    # ->
    
    for a in range(len(icon_paths)):
        print(icon_paths[a])
        # ...
*Das* ist in Python aber ein starkes „anti pattern“, denn `a` wird bei Dir nur als Index in die Liste verwendet. Man kann aber stattdessen *direkt* über die Elemente von Sequenzen iterieren, ohne den Umweg über einen Index:

Code: Alles auswählen

    for icon_path in icon_paths:
        print(icon_path)
        # ...
`row` und `column` bei `grid()` als Zeichenketten anzugeben ist schräg. Warum?

Warum setzt Du bei dem `Button` direkt nach dem Erstellen Optionen, die man auch beim erstellen schon angeben kann? Wenn man das nicht machen würde, bräuchte man den `Button` nicht einmal an einen Namen binden.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import os
import tkinter as tk
from glob import glob


def main():
    # ...
    
    icon_paths = glob(os.path.join(sql_bilder, 'icons', '*.gif'))
    icon_paths.sort()

    icons_frame = tk.Frame(frame)
    icons_frame.pack(anchor=tk.NW, fill=tk.X)
    for icon_path in icon_paths:
        print(icon_path)
        icon_frame = tk.Frame(icons_frame)
        icon_frame.pack(anchor=tk.NW, fill=tk.X)
        logo = PhotoImage(file=icon_path)
        icon_frame.image = logo
        tk.Label(icon_frame, image=logo).grid(row=0, column=0)
        tk.Button(
            icon_frame, text='…', border=1, borderwidth=1, font=schrift
        ).grid(row=1, column=0)

    # ...


if __name__ == '__main__':
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
badi113
User
Beiträge: 22
Registriert: Mittwoch 27. März 2019, 14:53

@_deets_: Hy wollte es grade noch mal ändern mein beitrag

Code: Alles auswählen

root1 = Frame(frame)
root1.pack(anchor="nw", fill="x")


dirList = os.listdir(sql_bilder + "icons/")

icons = []
for sFile in dirList:
    if sFile.find('.gif') == -1:
        continue

    icons.append(sFile)
    icons.sort()
 
for a in icons:
    print(a)
    
    root2 = Frame(root1)
    root2.pack(anchor="nw", fill="x")
    
    logo = PhotoImage(file=sql_bilder + "icons/" + a)
    Bild = Label (root2, image=logo)
    Bild.grid(row="0", column="0")
    logo.config(file=sql_bilder + "icons/" + a)

    button = Button(root2,  text="...", border=1)
    button["font"] = schrift# Schriftart und Größe
    #button["width"] = 4 #Länge
    button["borderwidth"] = 1 #Randbreite
    button.grid(row="1", column="0")
    fenster31.update()
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@badi113: Ähm, ``fenster31.update()`` – NEIN! Weder sollte ein Name eine 31 als Präfix haben, noch solltest Du `update()` aufrufen.

`icons` nach jedem hinzufügen eines Elements zu sortieren ist auch total unsinnig.

`a` ist ein schlechter Name für so ziemlich alles.

Der `logo.config()`-Aufruf macht ebenfalls überhaupt gar keinen Sinn.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Variablennamen schreibt man klein_mit_unterstrich. Wenn man anfängt an Namen Nummern anzuhängen, dann macht man etwas falsch. Hier solltest Du also bessere Namen vergeben.
Was soll denn das s bei sFile? In Wirklichkeit hast Du ein filename.
Auch bei dirList sollte nicht der Datentyp im Variablennamen stehen. Dass das eine Liste ist, ist doch gar nicht relevant. Ein guter Name wäre filenames.
str.find benutzt man nur, wenn man auch die Position wissen will, und wenn man weiß, dass -1 ein schlechter Rückgabewert ist. Du suchst hier wahrscheinlich endswith, oder Du benutzt gleich glob.glob oder pathlib, denen man auch ein Pattern angeben kann.

Code: Alles auswählen

icons = glob.glob(os.path.join(sql_bilder, "icons", "*.gif"))
Wenn man die Länge einer Liste wissen will, dann gibt es dafür `len`. Die willst Du aber gar nicht wissen, denn statt der while-Schleife willst Du ja eine for-Schleife über die `icons` (wie Du drei Zeilen davor schon bewiesen hast, dass Du das Konstrukt kennst).

Bleibt also:

Code: Alles auswählen

root = tk.Frame(frame)
root.pack(anchor="nw", fill="x")

icons = glob.glob(os.path.join(sql_bilder, "icons", "*.gif"))
for icon in icons:
    frame = tk.Frame(root)
    frame.pack(anchor="nw", fill="x")
    logo = tk.PhotoImage(icon)
    bild = tk.Label(frame, image=logo)
    bild.image = logo # keep reference to PhotoImage-instance
    bild.grid(row=0, column=0)
    tk.Button(frame, text="...", border=1, font=schrift, borderwidth=1).grid(row=1, column=0)
badi113
User
Beiträge: 22
Registriert: Mittwoch 27. März 2019, 14:53

@_blackjack_:
- In der variable icons sind keine pfade nur die namen des Bildes hinterlegt. Für den Pfad ist sql_bilder zuständig.
- Mit row und column möchte ich später die anordnung machen z.B. nur 3 Spalten und dan in die nächste Zeile.
- Label image habe ich jetzt komplett rausgenommen und die Bilder als Button erstellt.(button haben noch keine funtion^^)
- _main_ benutze ich nicht.
und ja ich hab das * beim import.

Danke noch mal für die Antworten, da hab ich noch einiges zu lernen XD. Ich werd mal versuchen alles was du geschrieben hast umzusetzen.

Code: Alles auswählen

root1 = Frame(frame)
root1.pack(anchor="nw", fill="x")


dirList = os.listdir(sql_bilder + "icons/")
dirList.sort()

icons = []
for sFile in dirList:
    if sFile.find('.gif') == -1:
        continue

    icons.append(sFile)
    icons.sort()

#####Zähler für Icons in Ordner    
counter=0
for zaehler in icons:
    counter=counter+1
 
for a in icons:
    print(a)
    
    root2 = Frame(root1)
    root2.pack(anchor="nw", fill="x")
    
    logo = PhotoImage(file=sql_bilder + "icons/" + a)
    
    button = Button(root2,  image=logo, border=1)
    button["font"] = schrift# Schriftart und Größe
    button["borderwidth"] = 1 #Randbreite
    button.grid(row="1", column="0")
    
    button.logo = logo
    
    fenster31.update()
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@badi113: Wenn in `icons` Dateinamen statt Icons hinterlegt sind, ändert das ja nichts daran das da eben keine Icons drin sind. Dann wäre der Name eben `icon_filenames` statt `icon_paths` – allerdings sind in meinem Code ja Pfade hinterlegt, denn `glob()` liefert den gesamten Pfad und nicht nur den Dateinamen. Und zwar den Pfad zur Datei. Ein Pfad kann ein Verzeichnis oder eine Datei beschreiben, nicht nur Verzeichnisse.

Was Du mir da mit `row` und `column` sagen willst, verstehe ich nicht. Ich weiss wofür diese Argumente da sind. Bezieht sich das auf die Frage warum Du diese Werte als *Zeichenketten* angibst, wo das doch *Zahlen* sind? Darauf bezog sich das „Warum?“

Den Test auf '__main__' und eine Hauptfunktion solltest Du aber benutzen.

Und einen *-Import solltest Du *nicht* benutzen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
badi113
User
Beiträge: 22
Registriert: Mittwoch 27. März 2019, 14:53

@_blackjack_:
Hab jetzt Icon in pic_name geändert 😀.
Ich wollte ja das der Pfad nicht komplett ist da sich der Pfad wahrscheinlich irgend wann mal ändert , deswegen gibt es ja sql_bilder (//192.168.usw.) und pic_name ( klemmhebel.gif)

PS. Ich glaub würdest du noch die andern Scripte sehen die dazu gehören .... . Naja bin halt ein Leihe und Versuche immer ein Stück besser zu werden.
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@badi113: Der Pfad ändert sich nicht. Wie sollte das denn auch gehen? Klar kannst Du `sql_bilder` ändern – was ich ja auch als Pfad zu den Bildern verwende – aber doch nicht während das Programm läuft. Beziehungsweise würden dann ja auch die Dateinamen in der Liste nicht mehr stimmen und man müsste die sowieso neu einlesen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Dass sich der Pfad ändern kann, ist ja gerade ein Grund dafür, immer mit dem kompletten Pfad zu arbeiten, sonst mußt Du an jeder Stelle, wo Du auf das Bild zugreifen willst, auch wissen, wie der Pfad denn gerade heißt.
badi113
User
Beiträge: 22
Registriert: Mittwoch 27. März 2019, 14:53

Naja der Pfad kann sich ändern da ja niemand sagen kann wo man es installiert. zB. Wen ich in einen anderen Netzwerk mich anmelde ist ja auch die IP vom Server anders, da die Server nur im Intranet benutzt werden.
Zum Einstellen der Variable gibt es ja noch ein anderes Fenster wo man die IP einträgt und speichert.

Und ein Teilstück vom Pfad + Name des bildes wird in die Datenbank geschrieben, wen man ein Bild auswählt.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@badi113: mag ja alles sein, aber wenn man mit einem Dateinamen arbeitet, setzt man den an der Stelle zu einem absoluten Pfad zusammen, wo die Information dazu vorliegt, bei Deinem Beispiel also bei listdir. Dann hat man nur ein Ding (absoluter Dateiname), das man herumreichen kann und nicht mehrere (Pfad und Name).
Benutzeravatar
__blackjack__
User
Beiträge: 13114
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Wobei ich hier noch mal anmerken möchte das man hier gar nicht `listdir()` will sondern `glob()`, womit sich diese Frage ja automatisch erledigt. Man muss sich das ja nicht alles unnötig kompliziert machen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten