Home > Blockchain >  Tkinter - pass button grid values between classes upon clicking button
Tkinter - pass button grid values between classes upon clicking button

Time:12-17

I am trying to create a basic Battleship game in Python using Tkinter.

Below is a very simplified version of my code. Essentially I am creating a 10*10 grid of buttons and positioning them using .grid. What I'd like to do is click one of those buttons and pass that buttons grid values (x, y) from the GameBoard class to the Battleship class to position the ship.

I have tried using self.row = row and self.column = column, however when I do this I immediately receive an attribute error, 'GameBoard' object has no attribute 'row'.

import tkinter as tk

class GameBoard:
    def __init__(self):
        self.mw = tk.Tk()
        self.size = 10

    def build_grid(self):
        for x in range(self.size):
            for y in range(self.size):
                self.button = tk.Button(self.mw, text = '', width = 2, height = 1,\
                command = lambda row = x, column = y: self.clicked(row, column))
                self.button.grid(row = x, column = y)
            
    def clicked(self, row, column):
        print(row, column)
        self.row = row
        self.column = column

class Battleship:
    def __init__(self, board):
        self.gboard = board

    def position_ship(self):
        x = self.gboard.row
        y = self.gboard.column
        for i in range (3):
            self.submarine = tk.Button(self.gboard.mw, background = "black", text = '',\
                                   width = 2, height = 1)
            self.submarine.grid(row = x, column = y)
              
def main():
    gboard = GameBoard()
    gboard.build_grid()
    bt = Battleship(gboard)
    bt.position_ship()    
main()

CodePudding user response:

As @acw1668 pointed out in a comment, the problem is the gboard attributes row and column haven't been created yet when you call bt.position_ship() in the main() function.

I don't know your overall game design, but a very simple way to fix that would be to assign a random board position to them in the GameBoard.__init__() method.

I've also modified the code to show how to call bt.position_ship() when a button is clicked. This is done by passing the BattleShip instance bt to the build_grid() function so it can be included in calls to the clicked() method, which can now call it when its called.

from random import randrange
import tkinter as tk

class GameBoard:
    def __init__(self):
        self.mw = tk.Tk()
        self.size = 10
        self.row = randrange(self.size)
        self.column = randrange(self.size)

    def build_grid(self, bt):
        for x in range(self.size):
            for y in range(self.size):
                self.button = tk.Button(self.mw, text='', width=2, height=1,
                                        command=lambda row=x, column=y:
                                            self.clicked(bt, row, column))
                self.button.grid(row=x, column=y)

    def clicked(self, bt, row, column):
        print(row, column)
        self.row = row
        self.column = column
        bt.position_ship()


class Battleship:
    def __init__(self, board):
        self.gboard = board

    def position_ship(self):
        x = self.gboard.row
        y = self.gboard.column
        for i in range (3):
            self.submarine = tk.Button(self.gboard.mw, background="black", text='',
                                       width=2, height=1)
            self.submarine.grid(row=x, column=y)


def main():
    gboard = GameBoard()
    bt = Battleship(gboard)
    gboard.build_grid(bt)
    bt.position_ship()
    tk.mainloop()

main()
  • Related