@siver: Das konkrete Problem liegt daran das man bei `tkinter` auf Python-Seite eine Referenz auf Bildobjeke behalten muss, sonst werden die von der Speicherbereinigung beseitigt und stehen damit auf der `Tk`-Seite nicht mehr zur Anzeige zur Verfügung. Normalerweise bindet man das Bild dafür einfach als Attribut an das Exemplar von der Klasse die man für die GUI geschrieben hat. Denn für so ziemlich jede nicht-triviale GUI braucht man objektorientierte Programmierung. Alternativ kann man das Bildobjekt auch an irgend ein anderes Objekt als Attribut zuweisen – beispielsweise an einem `Label` in dem es angezeigt wird.
An dem Quelltext ist so einiges überarbeitungswürdig.
Sternchen-Importe sind Böse™. Damit holt man sich den gesamten Inhalt eines Moduls in den Namensraum. Bei `tkinter` sind das deutlich mehr als 100(!) von denen nur ein kleiner Bruchteil verwendet wird. Nicht nur Namen die in dem Modul definiert sind, sondern auch Namen die das Modul selbst von woanders importiert. Es besteht zudem die Gefahr von Namenskollisionen. `tkinter` und `PIL` definieren beispielsweise beide eine `Image`-Klasse.
`tkinter` wird typischerweise als ``import tkinter as tk`` importiert und auf die Namen aus dem Modul dann explizit beispielsweise per `tk.Label` zugegriffen.
Der Sternchen-Import aus `PIL` macht noch nicht einmal Sinn, weil die beiden Namen die aus dem Modul verwendet werden, vorher schon explizit importiert wurden‽
Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.
Funktionen und Methoden bekommen alles was sie ausser Konstanten verwenden als Argument(e) übergeben. Sie greifen nicht auf globale Variablen zu. Und verändern die schon mal gar nicht. Vergiss das es ``global`` überhaupt gibt. Zumal da sowieso nur `i` so hätte deklariert werden müssen, und das noch nicht einmal in jeder Funktion, sondern nur in `weiter()` und `zurück()`.
Das was Du mit `fnmatch.filter()` und `os.listdir()` machst, gibt es schon fertig im `glob`-Modul – mit dem zusätzlichen Vorteil, dass dabei schon komplette Pfade bei herum kommen. Alternativ kann man mal über das `pathlib`-Modul nachdenken. Auf keine Fall setzt man Pfadteile mit ``+`` zusammen. Dafür ist `os.path.join()` oder eben auch `pathlib` da.
Was soll die 1 bei `label1`? Und die ganzen durchnummerierten `button`-Namen? Wenn man Namen durchnummeriert, dann will man entweder bessere Namen verwenden oder gar keine einzelnen Namen für die Werte sondern eine Datenstruktur, meistens eine Liste. In diesem Fall sind die Namen einfach schlecht. Beim `label1` sollte man einen Namen wählen bei dem der Leser weiss *was* der Zweck von dem `Label` ist. Bei den `Button`\s ist der Name egal solange man ihn nach dem Erstellen und Anordnen nicht noch für irgend etwas braucht. Man kann natürlich trotzdem einen passenden Namen wählen, damit man die Stelle wo ein `Button` für einen bestimmten Zweck erstellt wird, leichter finden kann.
`bind()` ist der falsche Weg im Aktionen an `Button`\s zu binden. Die haben dafür das `command`-Argument, und nur dann verhält sich die Schaltfläche so wie der Benutzer das erwartet und von anderen Schaltflächen gewohnt ist.
Funktions- und Methodennamen reflektieren üblicherweise die Tätigkeit, die sie durchführen. Keiner der Funktionsnamen tut das tatsächlich.
Statt `bild()` nur zum Laden zu verwenden, sollte da auch gleich die GUI aktualisiert werden, denn den Code hast Du momentan drei mal im Code stehen, jeweils nach dem laden.
`os.system()` sollte man nicht verwenden, das steht auch in der Dokumentation. Zum Kopieren von Dateien ist es aber auch gar nicht notwendig ein externes Programm aufzurufen. Entsprechende Funktionen findet man im `shutil`-Modul in der Standardbibliothek.
Die drei `kind*()`-Funktionen machen im Grunde das selbe, das sollte nur eine Funktion sein, mit dem Zielordner als Argument.
Ungetesteter Zwischenstand:
Code: Alles auswählen
#!/usr/bin/env python3
from functools import partial
import glob
import os
import shutil
import tkinter as tk
from PIL import Image, ImageTk
ORDNER = 'W:\\2019'
#
# TODO Eine Klasse für die GUI schreiben und Schaltflächen die aktuell nichts
# bewirken können, deaktivieren.
#
def show_next_image(dateinamen, dateinamenindex_var, image_label):
#
# FIXME Index kann grösser werden als Anzahl der Dateinamen.
#
dateinamenindex_var.set(dateinamenindex_var.get() + 1)
update_image_label(dateinamen, dateinamenindex_var, image_label)
def show_previous_image(dateinamen, dateinamenindex_var, image_label):
#
# FIXME Index kann kleiner werden als Anzahl der Dateinamen.
#
dateinamenindex_var.set(dateinamenindex_var.get() - 1)
update_image_label(dateinamen, dateinamenindex_var, image_label)
def copy_file(dateinamen, dateinamenindex_var, target):
shutil.copy2(
dateinamen[dateinamenindex_var.get()], os.path.join(ORDNER, target)
)
def update_image_label(dateinamen, dateinamenindex_var, image_label):
image = ImageTk.PhotoImage(
Image.open(dateinamen[dateinamenindex_var.get()]).resize((1024, 800))
)
image_label.image = image
image_label['image'] = image
def main():
root = tk.Tk()
root.title('Fotos')
root.geometry('1500x800')
image_label = tk.Label(root)
image_label.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
dateinamenindex_var = tk.IntVar(value=0)
dateinamen = glob.glob(os.path.join(ORDNER, '*.jpg'))
update_image_label(dateinamen, dateinamenindex_var, image_label)
previous_button = tk.Button(
root,
height=10,
width=50,
text='zurück',
command=partial(
show_previous_image, dateinamen, dateinamenindex_var, image_label
),
)
previous_button.pack(side=tk.BOTTOM)
for i in range(1, 4):
tk.Button(
root,
height=10,
width=50,
text=f'kind{i}',
command=partial(
copy_file, dateinamen, dateinamenindex_var, f'Kind{i}'
),
).pack(side=tk.BOTTOM)
next_button = tk.Button(
root,
height=10,
width=50,
text='weiter',
command=partial(
show_next_image, dateinamen, dateinamenindex_var, image_label
),
)
next_button.pack(side=tk.BOTTOM)
root.mainloop()
if __name__ == '__main__':
main()
Bei den Vor-/Zurück-Schaltflächen wird der Index verändert ohne zu prüfen ob der noch innerhalb der Grenzen liegt. Benutzerfreundlich wäre zudem wenn man eine Schaltfläche deaktiviert wenn sie keine sinnvolle Aktion durchführen kann (`state`-Option von Widgets und `tk.DISABLED` und `tk.NORMAL`). Das aber erst nach dem eine Klasse für die GUI existiert, denn hier kommt `partial()` an die Grenzen eines sinnvollen Einsatzes.