Mehrere Objekte in einem Frame

Fragen zu Tkinter.
Antworten
winter-mute
User
Beiträge: 4
Registriert: Dienstag 18. Juli 2017, 14:51

Hallo,

ich spiele ein wenig mit tkinter rum und versuche ein Level Layout für ein primitiven Tower Defense zu erstellen, dass aus mehreren Elementen besteht. Da ich ein Python Anfänger bin, bin ich auf einige Fragen gestossen und hoffe, dass ich hier dazu die Antworten finden werde.

Kurz dazu, was ich erreichen möchte:
- Mehrere Canvas(e?) in einem Frame platzieren
- Erfahren wie man vielleicht Ein großen Canvas mit kleineren Canvasen füllt (wenn es überhaupt Sinn macht)

Bisher sieht das ganze so aus:
- das Layout wird als String aus einer Datei ausgelesen.

Code: Alles auswählen

def loadLevel(self):
        self.fobj = open("maps/level", "r")
        return [line.strip() for line in self.fobj]
Frage1:
Wie verhält sich eigentlich Python an dieser Stelle. Wird der Data Stream bei return geschlossen, oder muss das fobj.close() explizit aufgerufen werden?

- loadLevel erstellt eine Liste und liefert diese zurück. Hier kommt das renderBoard ins Spiel():

Code: Alles auswählen

    def renderBoard(self):
        newBoard = self.loadLevel()
        for i in range(len(newBoard)):
            for j in range(len(newBoard[0])):
                if newBoard[i][j] == "1":
                    self.canvas = self.defineTileType("black", i, j)
                else:
                    self.canvas = self.defineTileType("green", i, j)
Frage2:
Wie kann ich die generierte Elemente (evtl. schöner, wie ein Pythonista) an ein (master)Canvas ranhängen und diesen Zurückgeben, so dass ich den nachher an den SpielerBoard ranhängen kann? Macht das überhaupt Sinn? Was würde sich hier besser empfehlen?

Hier der gesamte Code noch zu besseren Übersicht.

Code: Alles auswählen

class Board(tkinter.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()
        self.renderBoard()

    def renderBoard(self):
        newBoard = self.loadLevel()
        for i in range(len(newBoard)):
            for j in range(len(newBoard[0])):
                if newBoard[i][j] == "1":
                    self.canvas = self.defineTileType("black", i, j)
                else:
                    self.canvas = self.defineTileType("green", i, j)

    def loadLevel(self):
        self.fobj = open("maps/level", "r")
        return [line.strip() for line in self.fobj]

    def defineTileType(self, color, row, column):
       return tkinter.Canvas(self, width=50, height=50, highlightbackground="black", background=color).grid(row=row, column=column)

    def renderGambeBoard(self):
        pass
Ich würde mich freuen, wenn ich keine fertigen Lösungen vor die Nase geschoben bekäme, sondern einfach hilfreiche Tipps, aus den ich mir selbst eine Lösung erarbeiten muss. ( Bin halt Faul und befürchte, dass ich zu Copy-Pasta-Lösung greifen würde :P )
Ich würde mich auch freuen über allgemeine Kritik zum Code, wenn es etwas zu bemängeln gibt.

Vielen Dank im Voraus

winter-mute
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Zur ersten Frage: eine Datei wird - neben close() natuerlich - geschlossen wenn das Objekt garbage collected wird. Das passiert in deinem Fall durch das Verlassen des Funktions-Scopes.

Allerdings ist das ein Implementierungsdetail von CPython, andere Implementierungen oder eine Veraenderung in der Zukunft koennte das aendern.

Darum hat man in Python das with-Statement geschaffen:

Code: Alles auswählen

with open("dateiname", mode) as f:
     ...
Das garantiert, dass f ausserhalb des with-statements geschlossen wird.

Zu deiner generellen Frage: ich wuerde nur ein Canvas-Objekt erstellen. Mehrere ergeben IMHO keinen Sinn, ausser du machst Dinge wie zB eine Welt, und ein weiteres GUI-Element zB mit einer Art Radar-Uebersicht.

Aber Canvas in Canvas definitiv nicht.

Stattdessen kannst/solltest du einfach den einen Canvas reingeben in deine diversen Funktionen/Methoden die zB deine Level-Elemente anlegen. Oder wenn du mit OO arbeitest eben als Argument an den LevelLoader oder so.
winter-mute
User
Beiträge: 4
Registriert: Dienstag 18. Juli 2017, 14:51

Besten Danke für eine schnelle Rückmeldung!

Danke für den Tipp mit

Code: Alles auswählen

with open() as f:
Habe es sofort umgesetzt.

Eine Frage hätte ich noch zu dem Canvas... Wenn ich anstatt viele Canvas-Objekte nur einen erzeugen würde, wie müsste dann das Abrufen von unterschiedlichen Canvas-Objekten erfolgen?

Also meine Idee, wäre erst mal:

Code: Alles auswählen

def renderBoard:
   einen Canvas rendern (und die Felder mit z.B. einem Gitter trennen, anstatt für jedes Feld ein neuer Canvas)
   return canvas
Jetzt die Frage, ob wenn ich das so umsetzen würde, wäre das möglich im späterem Stadium die Bereiche auf dem Canvas einzeln anzusprechen? Oder muss das Konzept grundsätzlich umstrukturiert werden?
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich verstehe nicht, was du da gittern willst. Ein Canvas hat eine vielzahl von Objekten, so wie ein Pixel-Surface halt viele Pixel hat. Das es bei denen aber eine logische Gruppierung gibt ist einzig und alleine deiner Anwendung bekannt, und auch nur dafuer relevant. Fuer dein Beispiel eine TD-games waeren das zb ein Hintergrund, und darueber diverse Objekt-Klassen wie Dekoration, Monster, Tuerme. Darueber dann HUD-Infos (Score, Wellen-Ankuendigung) etc.

Da muss nix in Gittern verwaltet werden.

Ein Monster stellst du aus einer Reihe von Canvas-Objekten dar, die du dann ueber die Lebenszeit der Monster veraenderst und ggf. entfernst, wenn es stirbt. Etc. Die Buendelung erfolgt in einer Monster-Klasse bzw. den Instanzen davon.
Antworten