QT GroupBoxes

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Ouvert
User
Beiträge: 7
Registriert: Dienstag 20. September 2016, 09:02

Hallo Python Community,

ich hab mal einige Fragen, die mir ein erfahrener Benutzer von euch sicherlich erklären kann.

Wir haben im Informatik Unterricht eine kleine Programmieraufgabe bekommen und da ich schon 3 Jahre mit Csharp und Unity gearbeitet habe, ich aber relativ neu in Python bin wollte ich mal fragen..
Ein Freund(eher Designer) und ich programmieren ein kleines Kartenspiel. Die Karten werden bereits über eine xml Datei als Objekte in eine Liste geladen, die das Blatt und das Deck des Spielers darstellt, sowie eine weitere Liste die das Blatt und Deck der AI darstellt.
Nachdem diese ganzen Funktionen bereits funktionieren, haben wir mit dem GUI angefangen. Dafür haben wir 5 GroupBox's benutzt und in dieser sind verschiedene Label.
Wenn man auf 'Spiel Starten' drückt, dann werden in die GroupBox's 1-5 die Karten die man auf der Hand(Blatt) hat geladen.. Also Leben, Angriff, Kosten und Name...
Jetzt kommt das aber :D es klappt nicht, dass man den Pfad des Bildes der Karte eingeben kann und die Karten sind auch etwas zu groß und ich weiß nicht genau wie man das per Code kleiner skalieren kann. Ebenso benötige ich eine Drag Funktion um die Karten auf das Spielfeld zu ziehen, geht das überhaupt?

PS. Der Zugriff auf die Attribute der Karten erfolgt beispielsweise so:
labelAD.setText(str(Kartenspiel.blatt.karten[0][2]))

Dabei steht Kartenspiel für das Main Game, blatt für das Objekt der Klasse Blatt und karten für die Liste, in der die Karten gespeichert werden. Davon ist [0] die allererste Karte im Blatt und [2] spiegelt das Attribut wieder, in diesem Fall Angriff.

Des Weiteren ist komisch, dass dies nur funktioniert, wenn man die Liste als String konvertiert, obwohl das Attribut der Karte einen String zurückgibt.. Es wird also dann im Label ['NameDerKarte'] angezeigt, anstatt ohne [' '].. :D

Ich hoffe mir kann jemand weiterhelfen:)
BlackJack

@Ouvert: Zu den Bildern kann man so nicht viel sagen, ausser das `QImage` beispielsweise Methoden wie `scaled()`, `scaledToWidth()`, und `scaledToHeight()` besitzt.

Drag and Drop geht mit Qt, da gibt's ein eigenes Kapitel in der Qt-Dokumentation mit dem Titel „Drag and Drop“ (wer hätte das gedacht), das beschreibt wie man ganz allgemein mit `QDrag`-Objekten hantiert.

Der Zugriff auf die Karteninformation ist ganz schön lang. Und der erste Index ist ja noch in Ordnung, weil es sich um eine Sequenz von Karten handelt, aber Attribute mit magischen Indexwerten sind nicht gut. Da sollte ``.angriff`` oder so stehen statt ``[2]``. Und dann solltest Du die Attribute nicht in extra Listen verpacken. Du behauptest das wäre nicht so, aber offensichtlich ist es doch so, sonst müsstest Du eine Zeichenkette nicht in eine Zeichenkette umwandeln.

Da `Kartenspiel` hoffentlich keine Klasse ist, sollte der Name klein geschrieben werden. Siehe auch den Style Guide for Python Code.
Ouvert
User
Beiträge: 7
Registriert: Dienstag 20. September 2016, 09:02

Okay erstmal danke für die schnelle Antwort, das mit dem QtDrag werde ich mal raussuchen :)

Also meinst du blatt.karten[0].angriff ?
Wie könnte man das am besten Umsetzen?
Karte ist eine eigene Klasse.. In einer anderen Klasse namens Paket werden beim Start die Karten aus der xml reingeladen mit den Attributen die durch den Konstruktor der Klasse Karten übergeben werden..
Hast du da einen Vorschlag wie man das besser lösen könnte;D

Naja um ehrlich zu sein ist Angriff als int aber ich habe es auch mit dem Namen probiert und da stand dass man es von List nicht in str konvertieren kann als Fehlermeldung :D
BlackJack

@Ouvert: Eine einzelne Karte müsste halt ein Objekt mit Attributen sein, statt einer Sequenz. Wenn sich die Werte eines Kartenobjekts nie ändern, könnte man sich den Datentyp dazu mit `collections.namedtuple()` erstellen. Von den Typen die damit erstellt werden, kann man auch erben, falls man dem Objekt noch irgendwelche zusätzlichen Methoden verpassen möchte.

Wenn `Karte` eine Klasse ist, warum greift man dann auf die Elemente mittels Zahlen und Indexzugriff zu?

Nur so sicherheitshalber: Ihr wisst das man in Python nicht einfach alles in Klassen steckt? Es gibt auch Funktionen.

Den letzten Satz verstehe ich nicht. Ich gehe immer noch davon aus das die Vorstellung die Du von der Datenstruktur hast, und wie die tatsächlich aussieht, sich unterscheiden. Ohne den Code zu kennen, kann man hier aber nur raten.
Ouvert
User
Beiträge: 7
Registriert: Dienstag 20. September 2016, 09:02

Ich bin gleich zuhause und dann schreib ich nochmal ein bisschen was über den Code, da ich bis jetzt Unterwegs war :D
Ouvert
User
Beiträge: 7
Registriert: Dienstag 20. September 2016, 09:02

Alsooo... Die Karten stehen in einer XML Datei wie folgt:

[codebox=xml file=Unbenannt.xml]<?xml version="1.0" encoding="UTF-8" ?>
<catalog>
<card>
<name>Der Terrorist</name>
<klasse>Magier</klasse>
<ad>1000</ad>
<health>1</health>
<cost>1</cost>
<pfad>Karten/Karten_images/1.png</pfad>
</card>..[/code]
und dann die weiteren Karten..
dann bei Spielstart bekommen die Spieler ein Deck aus allen Karten gemischt und die Karten werden wie folgt initalisiert:

Code: Alles auswählen

class Paket:
    def __init__(self):
        self.karten = []
        for c in cards:
            nname = c.find("name").text
            nklasse = c.find("klasse").text
            nad = c.find("ad").text
            nhealth = c.find("health").text
            ncost = c.find("cost").text
            npfad = c.find("pfad").text

            self.karten.append([[nname],[nklasse],[int(nad)],[int(nhealth)],[int(ncost)], [npfad]])
aber mir fällt gerade auf, dass ich wahrscheinlich bei self.karten.append(Karte(werte)) die Karte vergessen habe xD habe ja extra die Klasse Karte:

Code: Alles auswählen

class Karte:                                                   ###Beschreibt EINE Karte mit Attributen, Später durch eine Datei mit Name, (Klasse), Ad, Health und Kosten
    #klassenliste= ["Basis", "Magier", "Angriff"]               ### wird nun nicht mehr benötigt, da meine xml datei eine Liste mit 5 Dimensionen erzeugt 
   # adliste= ["1", "2","3","4", "5"]                          
    #healthliste= ["1", "2","3","4", "5"]                      
    def __init__(self, name, klasse, ad, health, cost):            #Name, (Klasse), Ad, Health, Kosten
        self.name = name
        self.klasse = klasse
        self.ad = ad
        self.health = health
        self.cost = cost

    def __str__(self):
        return  (self.name + " " + self.klasse + " " + str(self.ad) + " " + str(self.health) + " " + str(self.cost))
Zuletzt geändert von Anonymous am Dienstag 20. September 2016, 22:30, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Ouvert
User
Beiträge: 7
Registriert: Dienstag 20. September 2016, 09:02

Sorry dass ich so viel schreibe :D ich habe jetzt die eine Zeile zu

Code: Alles auswählen

self.karten.append(Karte(nname,nklasse,nad,nhealth,ncost))
geändert und nun kann ich

Code: Alles auswählen

 self.Card5Angriff.setText(str(main.blatt.karten[4].ad))
machen, und es klappt. Vielen Dank für deine Hilfe :)
BlackJack

@Ouvert: Ist XML eine feste Entscheidung? JSON hätte zum Beispiel den Vorteil etwas weniger Auzeichnung zu haben, also leichter per Hand in einem Editor berarbeitbar zu sein, und es gibt schon ein paar Grunddatentypen wie Zahlen. Die braucht man dann nicht mehr selber zu parsen.

Was macht `Paket` denn noch? So wie die Klasse da steht ist die ziemlich sinnlos.

Wo kommt `cards` her? Funktionen und Methoden sollten keine Werte (ausser Konstanten) verwenden, die nicht als Argumente übergeben wurden.

`c` ist als Name ein bisschen kurz, hier wäre `card` verständlicher.

Was soll das 'n' bei den ganzen Namen am Anfang bedeuten? Kryptische Abkürzungen und Namenszusätze sollte man vermeiden.

Warum wird jeder Wert nochmal in eine Liste gesteckt? Das macht a) keinen Sinn und ist wohl b) genau das Problem das später jeder Einzelwert in einer Liste steckt.

Die Mischung von deutschen und englischen Namen ist nicht gut. Entscheide Dich für eine Sprache. Englisch ist IMHO sinnvoller weil ansonsten auch alles auf Englisch ist und man im Deutschen schneller Probleme bei Datenstrukturen hat wenn man die in der Mehrzahl benennen möchte, weil es im Deutschen wesentlich häufuger vorkommt, das Einzahl und Mehrzahl gleich geschrieben wird.

Klassenattribute sind in aller Regel Konstanten und sollten entsprechend komplett in Grossbuchtaben benannt werden. Variable Klassenattribute sind ein Warnzeichen das man ziemlich sicher etwas falsch macht.

Grundatenstrukturen haben in Namen nichts zu suchen. Wenn man den Typ mal ändert, und das kommt häufiger vor, dann hat man entweder falsche, irreführende Namen oder man muss die überall anpassen. Für Sequenztypen (beispielsweise Listen) oder Iteratoren/Generatoren verwendet man üblicherweise die Mehrzahl des Begriffs der für ein einzelnes Element passen würde. So wie Du das bei `karten` schon gemacht hast.

Kommentare sollten einen Mehrwert zum Code bieten und nicht einfach nur das wiederholen was als Code eh schon da steht.

In dem einen Kommentar steht etwas von einer Liste mit 5 Dimensionen — ich glaub's ja eher nicht. Und falls Du wirklich mal eine Listenstruktur mit 5 Dimensionen hast, solltest Du das schleunigst ändern. :-)

Zeichenketten und Werte mit ``+`` und `str()` zusammensetzen ist kein Python sondern eher Basic. Python kennt die `format()`-Methode auf Zeichenketten für so etwas.

``self.Card5Angriff.setText(str(main.blatt.karten[4].ad))`` ist in sofern nicht gut als das da eine Nummerierung im Namen des Labels und eine hart kodierte 4 als Index im Code steht. Du hast das sicher 5 mal im Grunde die gleiche Zeile stehen die sich nur an zwei Stellen durch Ziffern unterscheidet. Und das wahrscheinlich für alle Karten und Attribute von einer Karte wiederholt. So etwas vermeidet man als Programmierer. Nummerierte Namen sind in der Regel ein Zeichen, dass man eigentlich eine Datenstruktur verwenden möchte. Meistens eine Liste. Dann kann man eine Schleife über Paare von Karten-Anzeige-Widget und Karten-Objekt schreiben und muss die ganzen `set*()`-Aufrufe für eine Karte nur *einmal* im Code stehen haben.
Ouvert
User
Beiträge: 7
Registriert: Dienstag 20. September 2016, 09:02

Ja ich finde XML ganz angemessen, da es gut damit funktioniert :)

Paket im allgemein erstellt das komplette Kartendeck, mischt das Deck, entfernt Karten aus dem Deck um sie in Blatt zu verlagern, also sozusagen die Hauptklasse für die Decks.

'cards' ist einfach nur eine kleine Variable und gibt die Anzahl der Elemente wieder, die in der XML sind glaube ich :D

das 'c' galt wieder nur als kleine Variable, kann man ja noch ändern :)

genauso wie die vielen n's :D

Ja das wollte ich mit meiner Antwort doch ausdrücken, ich habe vergessen, die KartenObjekte zu initialisieren, stattdessen habe ich der Liste mit karten.append([][][]) die Einzelwerte als eigene Listen nochmal angehängt was total sinnlos ist :D
Daher habe ich das ja in karten.append(Karte(nname,nklasse,nad,nhealth,ncost,npfad)) geändert.. Somit ist die Liste nur noch eine Sequenz von Objekten wie es eigentlich sein sollte :)

Dadurch habe ich auch die 5 Dimensionen verworfen xD

Genau das ist das Problem, ich habe diese Zeilen für die 4 Label 5 mal da stehen, aber ich wusste nicht genau wie man es in eine Schleife schreibt, da man ja wenn man die Karte auf das Spielfeld zieht direkt auf die GroupBox zugreift und diese updatet ein klares Objekt übergeben muss.. Also wenn ich meine 5 Karten auf der Hand habe befinden die sich in der Liste 'handDesSpielers' und dann kann ich davon ein Element auswählen und in die Liste 'spielfeldDesSpielers' legen und somit die Karte ins Spiel zu bringen oder funktioniert das anders?
BlackJack

@Ouvert: Mit JSON statt XML würde es auch gut funktionieren.

Also wäre ein passenderer Name für `Paket` eher `Deck`‽

Du hast immer noch nicht erklärt wo `cards` her kommt. Beziehungsweise ist das eigentlich egal, denn so wie es da steht sollte es nicht sein, weil es nicht als Argument übergeben wird sondern ”magisch” irgendwo existiert.

Ich würde das Parsen von XML auch nicht in die `__init__()` der `Paket`-Klasse schreiben, denn dann kann man so ein Paket nur aus einem `ElementTree`-Objekt erzeugen. Das ist eine unnötige Kopplung des externen Datenformats an die interne Repräsentation. Es erschwert beispielsweise Unit Tests zu schreiben, weil man dafür die Daten dann auch mindestens in ein `ElementTree`-Objekt stecken muss, auch wenn man diesen Aspekt gar nicht testen möchte.

Du hattest ganz sicher nirgends 5 *Dimensionen*! Du verwechselt hier wohl Dimension mit Länge.

Beim letzten Absatz fehlen zu viele Informationen um da etwas zu sagen zu können. Sowohl was den vorhandenen Code angeht, als auch was da vom Spiel her eigentlich passieren soll. Grundsätzlich würde man eine Widget-Klasse für die Darstellung einer Karte erstellen und der Methoden zum setzen, holen, und entfernen der Karte geben. Und die fasst man dann in einer Widget-Klasse zusammen die eine Hand darstellt, beispielsweise mit einer Methode um die Hand zu setzen.
Ouvert
User
Beiträge: 7
Registriert: Dienstag 20. September 2016, 09:02

Ich werde mal einen Blick drauf werfen ;)

Ja man könnte es auch einfach Deck nennen :)

Ja das parsen steht auch eigentlich außerhalb der Klasse aber ich hab es zu testzwecken mal darein geschrieben ;D

Das mit der Widgetklasse werde ich mal versuchen umzusetzen, vielen Dank für den Tipp :)
Antworten