@barisoezcan: `getPhotoImages()` ist eine Funktion und keine Methode.
Namen sollte man nicht ohne Not abkürzen und ein- oder zweibuchstabige Namen wie `im`, `l`, `l1`, `l2` gehen in lokal sehr beschränkten Gültigkeitsbereichen wie „list comprehensions”, Generatorausdrücken, oder anonymen Funktionen; aber allgemein sollte man sie vermeiden.
Wo kommt in `showNodes()` diese „magische” 5 beim `row`-Argument für das Textlabel her? Gleiche Frage zum Faktor 4 in `showMatchingPicturesToNode()`.
Was soll die 2 bei `root2` in `showMatchingPicturesToNode()` bedeuten?
Das am Ende der Methode *nochmal* eine Hauptschleife auf dem `Toplevel`-Exemplar aufgerufen werden muss, liegt nur daran, dass das lokale `photo_imgs` „am Leben” gehalten werden muss, damit die Bilder angezeigt werden können. Das ist aber IMHO unschön gelöst.
Ich komme dann ungefähr bei so etwas raus (ungetestet):
Code: Alles auswählen
try:
import Tkinter as tk
except ImportError:
import tkinter as tk
from functools import partial
import numpy
from PIL import Image, ImageTk
TRAINED_MAP_FILENAME = 'trained_map.npy'
def get_photo_images(nodes=None, filename=TRAINED_MAP_FILENAME):
if nodes is None:
nodes = (numpy.load(filename) * 255).astype(numpy.uint8)
return [
ImageTk.PhotoImage(Image.fromarray(node, 'RGB'))
for node in row for row in nodes
]
class Map(object):
def __init__(self, nodes_matching_pictures, dimensions, size):
self.nodes_matching_pictures = nodes_matching_pictures
self.dimensions = dimensions
self.size = size
def show_nodes(self):
root = tk.Tk()
root.title('Nodes')
photo_images = get_photo_images()
for n, (image, nodes) in enumerate(
zip(photo_images, self.nodes_matching_pictures)
):
image_label = tk.Label(root, image=image)
image_label.grid(column=n // self.size[0], row=n % self.size[0])
image_label.bind(
'<Button-1>', partial(self.show_matching_pictures_to_node, n)
)
text_label = tk.Label(root, text=str(len(nodes)))
#
# XXX: Magic 5.
#
text_label.grid(column=n// self.size[0], row=(n % self.size[0]) + 5)
root.mainloop()
def show_matching_pictures_to_node(self, selected_node, _event):
root = tk.Toplevel()
root.title(
'Matching pictures to node: column: {0}, row: {1}'.format(
selected_node // self.size[0] + 1,
selected_node % self.size[1] + 1
)
)
pictures = (
(im * 255)
.astype(numpy.uint8)
.reshape(1, self.dimensions[0], self.dimensions[1], 3)
for im in self.nodes_matching_pictures[selected_node]
)
photo_images = get_photo_images(pictures)
for n, image in enumerate(photo_images):
column, row = divmod(n, self.size[0] * 4) # XXX: Magic 4.
tk.Label(root, image=image).grid(column=column, row=row)
#
# FIXME: Strange and only necessary to keep local `photo_images` alive.
#
root.mainloop()
Zum Klassenentwurf: Es ist im Grunde eine Datenklasse, die Methoden hat, in denen GUIs erzeugt werden. Das würde man eigentlich trennen in Programmlogik, also eine Datenklasse die man nach den benötigten Daten befragen kann und die Operationen auf den Daten zur Verfügung stellt und GUI-Code der das dann benutzt. Also eigentlich umgekehrt zum jetzigen Entwurf.
Wenn man es aus Sicht der Daten betrachtet scheint es einen Zusammenhang zwischen den geladenen `photo_images()` und `self.nodes_matching_pictures` zu geben, der aber nicht wirklich abgebildet wird. Zum Beispiel in dem beides als Attribute auf dem Objekt gehalten wird und vielleicht auch irgendwo mal sichergestellt wird, dass die wenigstens von der Länge her zusammen passen.