Seite 1 von 2

Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 09:29
von Eisbär
Hallo!

Ich bin absoluter Python-Anfänger... vielleicht kann mir daher jemand bei meinem Problem helfen:

Ich habe einen Code, bei dem jeweils zwei Bilder (Vereinswappen) von Sportmannschaften miteinander verglichen werden, wobei der Nutzer auswählen muss, welche Mannschaft besser ist. Dabei tritt jede Mannschaft gegen jede andere an und der Sieger bekommt einen Punkt. Nachdem alle Duelle abgeschlossen sind, wird eine Rangliste anhand der erzielten Punkte ausgegeben.

Das funktioniert bei wenigen Mannschaften ganz gut, doch wenn es mehr werden, kommt es zu einer Anzahl an Duellen, die für einen Nutzer nicht mehr zu bewerkstelligen ist. Daher möchte ich den Code so ändern, dass eine Art Logik integriert wird, dass "unnötige" Duelle, bei denen der Sieger anhand früherer Ergebnisse schon feststeht, ausgelassen werden und der bereits feststehende Sieger automatisch einen Punkt erhält.

Zum Beispiel: Mannschaft 1 gewinnt gegen Mannschaft 2, Mannschaft 3 gewinnt gegen Mannschaft 4 und Mannschaft 1 gewinnt gegen Mannschaft 3. Also ist klar, dass Mannschaft 1 auch gegen Mannschaft 4 gewinnt, weshalb dieses Duell ausgelassen werden soll. Das Ziel ist es, mit so wenigen Duellen wie möglich eine Rangliste zu erstellen. Kann mir bitte jemand helfen, den Code entsprechend zu ändern?

Mein Code lautet wie folgt:


import os
import random
from itertools import combinations
from tkinter import *
from PIL import ImageTk, Image

root = Tk()
root.title("Mannschaftsvergleich")

# Pfad zum Ordner mit den Bildern
folder_path = "D:/Mannschaftsvergleich/Vereinswappen"

# Liste der Bilddateien im Ordner
image_files = os.listdir(folder_path)

# Punkteliste für die Bilder
scores = {}
for image in image_files:
scores[image] = 0

# Erstellung der Label-Objekte
label1 = Label(root)
label2 = Label(root)

# Erstellung einer Liste aller möglichen Bildpaare
all_image_pairs = []
for i in range(len(image_files)):
for j in range(i+1, len(image_files)):
all_image_pairs.append((image_files, image_files[j]))

# Erstellung einer Kopie der Liste aller Bildpaare
image_pairs = all_image_pairs.copy()

# Fortschrittsanzeige definieren
total_pairs = len(all_image_pairs)
progress_label = Label(root, text="Fortschritt: 0/" + str(total_pairs) + " (0%)", font=("Arial", 12))
progress_label.pack()

# Funktion, die beim Klicken auf ein Bild aufgerufen wird
def select_image(event, selected_image):
global selected_images
scores[selected_image] += 1
# Rufe die Funktion zur Anzeige des nächsten Bildpaars auf
show_next_images()

# Funktion, die das nächste Bildpaar anzeigt
def show_next_images():
global label1, label2, selected_images
# Löschen der Label-Objekte
label1.destroy()
label2.destroy()
if not all_image_pairs:
print("Alle Bildpaare wurden geladen")
return
# Fortschrittsanzeige aktualisieren
progress = total_pairs - len(all_image_pairs)
progress_percent = int((progress / total_pairs) * 100)
progress_label.config(text="Fortschritt: " + str(progress) + "/" + str(total_pairs) + " (" + str(progress_percent) + "%)")
selected_images = random.sample(all_image_pairs, 1)[0]
all_image_pairs.remove(selected_images)
image1 = Image.open(folder_path + "/" + selected_images[0])
image2 = Image.open(folder_path + "/" + selected_images[1])
photo1 = ImageTk.PhotoImage(image1)
photo2 = ImageTk.PhotoImage(image2)
label1 = Label(root, image=photo1)
label2 = Label(root, image=photo2)
label1.image = photo1
label2.image = photo2
label1.pack(side=LEFT)
label2.pack(side=RIGHT)
label1.unbind("<Button-1>")
label2.unbind("<Button-1>")
root.bind("<Left>", lambda event: select_image(event, selected_images[0]))
root.bind("<Right>", lambda event: select_image(event, selected_images[1]))

all_image_pairs = []
for i in range(len(image_files)):
for j in range(i+1, len(image_files)):
all_image_pairs.append([image_files, image_files[j]])

show_next_images()
root.mainloop()

# Ausgabe der Punkteliste
print("Punkteliste:")
for image, score in sorted(scores.items(), key=lambda x: x[1], reverse=True):
print(image + ": " + str(score))

Re: Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 11:12
von Sirius3
*-Importe benutzt man nicht, weil so nicht kontrollierbar ist, welche Namen tatsächlich in den Namensraum eingebunden werden.
Über einen Index iteriert man nicht, wenn man alles Kombinationspaare haben möchte, dann gibt es dafür bereits eine Funktion in itertools, die Du ja einbindest, aber nicht benutzt. Warum?
`all_image_pairs` werden im Code zweimal erzeugt. Was soll die Kopie image_pairs, die gar nicht benutzt wird?
Strings setzt man nicht mit + zusammen, sondern benutzt Formatstrings. Pfade sind keine Strings, werden also mit den passenden Funktionen (pathlib.Path) bearbeitet und nicht mit + oder Formatstrings.
Globale Variablen darf man nicht benutzen. So wie Du das Keyword `global` benutzt, ist das eh nicht sinnvoll. Vergiss einfach gleich wieder, dass es `global` überhaupt gibt.
Labels verändert man, statt sie zu zerstören und neu zu bauen.
Wenn Du eh nur ein zufälliges Element willst, dann nutz doch random.choice.

Für GUIs braucht man zwangsläufig Klassendefinitionen. Das ganze könnte also ungefähr so aussehen:

Code: Alles auswählen

import random
from itertools import combinations
import tkinter as tk
from pathlib import Path
from PIL import ImageTk, Image

# Pfad zum Ordner mit den Bildern
FOLDER_PATH = Path("D:/Mannschaftsvergleich/Vereinswappen")


class GUI:
    def __init__(self):
        image_files = list(FOLDER_PATH.iterdir())
        self.image_pairs = list(combinations(image_files, 2))
        random.shuffle(self.image_pairs)
        self.scores = {}
        for image in image_files:
            scores[image.name] = 0
        self.selected_images = None

        self.root = tk.Tk()
        self.root.title("Mannschaftsvergleich")
        self.label1 = tk.Label(self.root)
        self.label2 = tk.Label(self.root)
        self.label1.pack(side=LEFT)
        self.label2.pack(side=RIGHT)

        self.total_pairs = len(image_pairs)
        self.progress_label = tk.Label(self.root, text=f"Fortschritt: 0/{self.total_pairs} (0%)", font=("Arial", 12))
        self.progress_label.pack()

        self.root.bind("<Left>", lambda event: select_image(0))
        self.root.bind("<Right>", lambda event: select_image(1))
        self.show_next_images()

    def select_image(self, number):
        self.scores[self.selected_image[number].name] += 1
        self.show_next_images()

    def show_next_images(self):
        if not self.image_pairs:
            self.label1['image'] = None
            self.label2['image'] = None
            print("Alle Bildpaare wurden geladen")
            return

        self.selected_images = self.image_pairs.pop()
        progress = total_pairs - len(self.image_pairs)
        progress_label.config(text=f"Fortschritt: {progress}/{total_pairs} ({progress / total_pairs:.0%})")
        self.load_image(self.label1, self.selected_images[0])
        self.load_image(self.label2, self.selected_images[1])

    def load_image(self, label, imagefile):
        image = Image.open(selected_images[0])
        photo = ImageTk.PhotoImage(image)
        label['image'] = photo
        label.image = photo


def main():
    gui = GUI()
    gui.root.mainloop()

    # Ausgabe der Punkteliste
    print("Punkteliste:")
    for image, score in sorted(scores.items(), key=lambda x: x[1], reverse=True):
        print(f"{image}: {score}")
        
if __name__ == "__main__":
    main()

Re: Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 13:02
von __blackjack__
Anmerkung: `self.scores` kann man auch mit `dict.fromkeys()` erstellen:

Code: Alles auswählen

        self.scores = {}
        for image in image_files:
            self.scores[image.name] = 0

        # Alternative:
        
        self.scores = dict.fromkeys((image.name for image in image_files), 0)

        # Oder als „dict compehension“:
        
        self.scores = {image.name: 0 for image in image_files}

Re: Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 14:51
von Eisbär
Vielen Dank erstmal! Wie gesagt, ich bin absoluter Neuling. Dein Code funktioniert leider nicht, es werden die folgenden Fehlermeldungen angezeigt:

File "D:\Mannschaftsvergleich\Mannschaftsvergleich.py", line 71, in <module>
main()
File "D:\Mannschaftsvergleich\Mannschaftsvergleich.py", line 61, in main
gui = GUI()
File "D:\Mannschaftsvergleich\Mannschaftsvergleich.py", line 18, in __init__
scores[image.name] = 0
NameError: name 'scores' is not defined

Process finished with exit code 1

Re: Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 15:03
von __blackjack__
@Eisbär: Den Fehler hatte ich in meiner Anmerkungen ja schon korrigiert. Der ist ja recht offensichtlich und dessen Lösung auch.

Re: Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 15:33
von Eisbär
Ich bin blutiger Anfänger, daher ist für mich gar nichts offensichtlich... :D

Nun habe ich

self.scores = {}
for image in image_files:
scores[image.name] = 0

durch

self.scores = dict.fromkeys((image.name for image in image_files), 0)

ersetzt. Es werden dennoch Fehler angezeigt:

line 69, in <module>
main()

line 59, in main
gui = GUI()

line 23, in __init__
self.label1.pack(side=LEFT)
NameError: name 'LEFT' is not defined

Re: Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 16:01
von __blackjack__
@Eisbär: Das ist eigentlich auch offensichtlich, genau wie die Lösung dafür. Wo ist denn `LEFT` definiert? Also laut Fehlermeldung gar nicht, aber es gibt ja so eine Konstante in einem Modul. Über das Modul muss man die dann halt auch ansprechen, wie das mit vielen anderen Namen aus diesem Modul in dem Quelltext ja auch getan wird.

Re: Integration von Logik in Code

Verfasst: Dienstag 9. Mai 2023, 21:48
von Eisbär
Wie würde der Code aussehen, damit er funktioniert?

Sorry, aber für mich als absoluter Laie ist überhaupt nichts offensichtlich...

Re: Integration von Logik in Code

Verfasst: Mittwoch 10. Mai 2023, 00:56
von __deets__
Das Modul ist tk. Du musst also tk.LEFT benutzen.

Re: Integration von Logik in Code

Verfasst: Mittwoch 10. Mai 2023, 10:52
von Eisbär
Ich habe es nun irgendwie hinbekommen... aber die beschriebene Logik, wonach es kein Duell zweier Mannschaften geben soll, bei denen der Sieger logischer weise feststeht, wird nicht angewendet. Es werden nach wie vor alle möglichen Duelle angezeigt.

Re: Integration von Logik in Code

Verfasst: Mittwoch 10. Mai 2023, 13:26
von __blackjack__
Letztendlich läuft das auf einen Sortieralgorithmus hinaus bei dem die Vergleichsfunktion zwischen zwei Werten eine Bewertung vom Benutzer ist.

Re: Integration von Logik in Code

Verfasst: Mittwoch 10. Mai 2023, 13:47
von Eisbär
Ja, genau. Aber wenn es sehr viele Duelle gibt, sollen eben jene ausgeschlossen werden, bei denen der Sieger ohnehin feststeht. Wie ich zu Beginn geschrieben habe:

"Mannschaft 1 gewinnt gegen Mannschaft 2, Mannschaft 3 gewinnt gegen Mannschaft 4 und Mannschaft 1 gewinnt gegen Mannschaft 3. Also ist klar, dass Mannschaft 1 auch gegen Mannschaft 4 gewinnt, weshalb dieses Duell ausgelassen werden soll. Das Ziel ist es, mit so wenigen Duellen wie möglich eine Rangliste zu erstellen."

Re: Integration von Logik in Code

Verfasst: Mittwoch 10. Mai 2023, 14:25
von __deets__
Ich betrachte das als Graphen-Erreichbarkeitsproblem. Wenn eine Mannschaft transitiv erreichbar ist aus der Liste der Gegner, gegen die man gewonnen hat, dann ist sie eben auch direkt erreichbar. Aus dem Kopf, ohne Garantie auf Funktion, aber macht das richtige für dein konkretes Beispiel:

Code: Alles auswählen

from collections import defaultdict

GAMES = [
    (1, 2),
    (3, 4),
    (1, 3),
    ]
    
CYCLIC_GAMES = [
    (1, 2),
    (3, 4),
    (1, 3),
    (3, 1), # should throw, it's a cycle
    ]
    
def build_graph(games):
    g = defaultdict(set)
    for winner, loser in games:
        g[winner].add(loser)
    return g
    
  
class CyclicGraphException(Exception):
    pass 
    
    
def depth_search(team, g):
    """"
    Takes a team and finds all teams that lost to it transitively. 
    Raises exception on a cycle.
    """
    visited = set()
    work = g[team]
    while work:       
        more_work = set()
        for loser in work:
            if loser not in visited:
                more_work |= g[loser]
            visited.add(loser)
        work = more_work
        if team in visited:
            raise CyclicGraphException
    return visited
    
def transitive_graph_completion(g):
    worked = True
    while worked:
        worked = False
        for winner in list(g): # allow changing of g
            downstream_losers = depth_search(winner, g)
            worked |= downstream_losers != g[winner]
            g[winner] = downstream_losers
    return g
            
            
def games_from_graph(g):
    return sorted([(winner, loser) for winner in g for loser in g[winner]])
    
print(games_from_graph(transitive_graph_completion(build_graph(GAMES))))
#print(games_from_graph(transitive_graph_completion(build_graph(CYCLIC_GAMES))))
Die letzte Zeile ist nur ein Test, ob die Logik korrekt ist.

Zum Einbau musst du lediglich nach jedem neuen Spiel die Liste der Spiele durch den Algorithmus erweitern lassen.

Re: Integration von Logik in Code

Verfasst: Donnerstag 11. Mai 2023, 10:01
von Eisbär
Sie sieht dann der gesamte Code mit dieser Ergänzung aus?

Re: Integration von Logik in Code

Verfasst: Donnerstag 11. Mai 2023, 10:14
von __deets__
Den zu integrieren ist schon deine Aufgabe. Du hast den anderen Code ja auch hinbekommen, oder nicht? Denn Hilfe ist etwas anderes als “mach meine Arbeit für mich”, da sind wir uns doch hoffentlich einig?

Re: Integration von Logik in Code

Verfasst: Donnerstag 11. Mai 2023, 10:50
von Eisbär
Wie ich bereits gesagt habe, bin ich absoluter Neuling. Ich kann das nicht.

Ich bin dir wirklich sehr dankbar für die Mühe, die du dir bisher gemacht hast, aber wenn es nun an dieser Kleinigkeit scheitern soll, war alles umsonst. :|

Re: Integration von Logik in Code

Verfasst: Donnerstag 11. Mai 2023, 13:10
von __deets__
Dann war es wohl umsonst. Hilfe zur Selbsthilfe immer gerne, Programmieren auf Zuruf gibt es bei upwork oder Guru.com.

Re: Integration von Logik in Code

Verfasst: Donnerstag 11. Mai 2023, 13:20
von ThomasL
Umsonst ist nichts. Es wäre allerdings vertane Zeit der Helfenden wenn du dich jetzt weigerst mal dein Hirn etwas anzustrengen und es ablehnst mit den dir hier gelieferten Informationen weiter zu arbeiten.

Es stellt sich die Frage ob der erste Code überhaupt von dir persönlich ist oder von irgendwo her kopiert wurde,
denn wenn er von dir wäre, dann könntest du weiter machen.

Ansonsten kann ich dir diese Python Tutorials empfehlen. https://wiki.python.org/moin/BeginnersGuide/Programmers

Re: Integration von Logik in Code

Verfasst: Donnerstag 11. Mai 2023, 13:55
von Eisbär
@ deets
Ich habe in jedem Beitrag darauf hingewiesen, dass ich absoluter Anfänger bin, weshalb das, was für dich vielleicht offensichtlich ist, für mich alles andere als offensichtlich ist. Ich finde es ehrlich gesagt ziemlich infantil, so zu reagieren. Du hast dir so viel Mühe gegeben mit dem Code (was ich sehr zu schätzen weiß) und dann das. Ich hätte mich auch gerne finanziell erkenntlich gezeigt, da es für mich nicht selbstverständlich ist, dass andere ihre Zeit für mich opfern... auch nicht, um "nur" zu helfen.

@ Thomas
Pazifismus sieht für mich anders aus. :wink:
Nein, ich habe den Code nicht selbst geschrieben, da ich, wie bereits gesagt, wenig bis gar nichts davon verstehe. Ich bin nun mal kein Programmierer, weshalb es auch nichts nützen würde, mein Hirn einzuschalten.

Trotzdem danke für eure Mühe, auch wenn ich nun nicht klüger bin als zuvor!

Re: Integration von Logik in Code

Verfasst: Donnerstag 11. Mai 2023, 14:09
von __deets__
@eisbär: du beschreibst dich selbst in deinem ersten Post hier als “Python Anfänger”. Also jemand, der mit Python anfangen will. Davon ist aber nichts zu sehen. Offensichtlich hast du irgendeinen Code irgendwoher, und du willst, das man dir den nach deinen Vorgaben verändert. Statt eben zu lernen, wie das geht. Darauf hast du nicht hingewiesen.

Der Algorithmus zur Bestimmung der Abhängigkeiten ist nicht komplex, aber klar zu viel für Anfänger. Darum habe ich den beigetragen. Der Einbau in ein Programm, dass jemand vermeintlich geschrieben hat, ist hingegen machbar. Das du deine Umstände hier falsch dargestellt hast, ist nicht mein Problem.

Und wenn du meine Reaktion infantil findest, bitte, gerne. Ich habe nicht mit angesäuertem “ aber wenn es nun an dieser Kleinigkeit scheitern soll, war alles umsonst. :|” angefangen. Von dem hohen Ross darfst du also gerne runter.

Frag wen auch immer den code ursprünglich geschrieben hast um Hilfe bei der Integration. Dann war’s auch alles nicht umsonst.