2

I've been working on a tic-tac-toe projejct, and I am planning on creating 9 button widgets with a picture on each one of them. I want to move them so they'll be 3 on a line and 3 in a row. Here's my code:

#imports:
from Tkinter import *
from PIL import ImageTk, Image

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#constants
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#classes:
class App(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
myapp = App()

class GUI:
    def __init__(self):
        self.myapp = myapp
        self.myapp.master.title("tkname")
        self.myapp.master.maxsize(2000, 1200)

    def create_and_pack_canvas(self, game_board_height, game_board_width):
        canvas = Canvas(height = game_board_height, width = game_board_width)
        canvas.pack(expand=1, fill=BOTH)
        return canvas

    def form_game_board(self):
        x = 404
        y = 150
        canvas = self.create_and_pack_canvas(1000, 1000)

        for i in range(3):
            for j in range(3):
                btn = gameButton("")
                btn.create_and_pack(canvas, x, y)
                x += 200
            x = 404
            y += 208

        self.myapp.mainloop()
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class gameButton:
    def __init__(self, picture_param):
        self.picture = ImageTk.PhotoImage(Image.open("somepic"))
        self.picture_height = self.picture.height()
        self.picture_width = self.picture.width()

    def callback(self):
        print 10

    def create_and_pack(self, canvas, x_pixels, y_pixels):
        self.b = Button(myapp, text="Print 10~", command = self.callback, image = self.picture)
        self.b.pack()
        #self.b.place(bordermode = OUTSIDE, x = x_pixels, y = y_pixels)

    def __str__(self):
        pass
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#main
def main():
    gui = GUI()
    gui.form_game_board()

if __name__ == "__main__":
    main()

Please note that there's this line:

#self.b.place(bordermode = OUTSIDE, x = x_pixels, y = y_pixels)

In the code, which I've put as a note, which is basically the line that's supposed to move the buttons. If you run the code, it'll simply put all the buttons one under another in a row, 9 of them. After the self.b.place() line, they all simply disappear. Maybe they moved somewhere too far, maybe not, but I assume the place() isn't working, and I've had trouble figuring why.

Thanks in advance.

1
  • If you are using buttons, you should grid them 3 x 3 in a Frame. There are many examples of this in other tic-tac-toe questions on SO. Don't use Canvas with buttons. Note that the canvas parameter in gameButton.create_and_pack is not used. I strongly recommend using a explicit root = Tk(). Commented Dec 19, 2015 at 20:50

1 Answer 1

3

Your best bet would likely be to use the grid geometry manager, see: http://www.effbot.org/tkinterbook/grid.htm

Instead of creating a GameButton class, I would put something like this in my GUI class:

    button_list = []
    list_index_counter = 0
    row_counter = 0
    column_counter = 0
    for num in range(9):
        self.button = tk.Button(canvas,
                                text="Print 10~",
                                command=self.callback,
                                image=self.callback)
        button_list.append(self.button)
        button_list[list_index_counter].grid(row=row_counter, column=column_counter)
        list_index_counter += 1
        if column_counter < (2): # it will hit this twice
            column_counter += 1
        else:                    # then this once, then back to the other one
            row_counter += 1
            column_counter = 0

This will neatly grid your buttons into a 3x3 grid.

The button_list allows you to keep a reference to the button objects, making them easier to access later on, you can use button_list[some_index].grid_info()["row"/"column"] to obtain the row or column that a button is gridded in, for example.

This is the standard way to create a set of similar widgets, and evades the problem of generating and maintaining names with lots of identical repeated code, without needing to create a new class for it.

A word of warning, from effbot:

Warning: Never mix grid and pack in the same master window. Tkinter will happily spend the rest of your lifetime trying to negotiate a solution that both managers are happy with. Instead of waiting, kill the application, and take another look at your code. A common mistake is to use the wrong parent for some of the widgets.

Finally, as @Terry Jan Reedy said, you don't need a Canvas for this, likely a Frame, or just a straightforward Tk() would be much better.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.