Simplified board for X's and O's using Tkinter
Is it possible to have one listener for all the cells?


You still have to bind to each canvas but we can bind to one function that can handle all your conditions. The best way to do this is to put your canvas widgets into a list and then reference them by index.


  How could I avoid repeat so much code?


Its good to try and reduce your code. Try to keep it DRY (Don't Repeat Yourself) is something to strive for and in this case the bast way to do this is to build a single function that can handle all your conditions based on the event being passed and an index value.

Another helpful way to stay DRY is to know when to use a for loop to managed creations of widgets or variables needed. Here I have changed your code to use a for loop to build a list with your canvas widgets and apply the binds and pack all at once.

I have also added a check to see if something has already been written to your canvas so it cannot be overwritten.

By utilizing a list to and building our functionality around that list we can reduce the amount of code we need and at the same time make the boxes infantry scalable.

I have added a variable called np_tuple. All you have to do is change the numbers to change the size of your game.

I have combined all your code into one .py file but you can easily move your Tablero class back out.

from tkinter import *
import numpy as np


class Tablero:
    def __init__(self, np_tuple):
        self.tablero = np.zeros(np_tuple)

    def poner_ficha(self, x, y, tipo_ficha):
        if self.tablero[x][y] == 0:
            self.tablero[x][y] = tipo_ficha
            # print(self.tablero)


root = Tk()
clist = []
np_tuple = (3, 3)
juego = Tablero(np_tuple)


def create_circle(x, y, r, canvas_name):
    canvas_name.create_oval(x - r, y - r, x + r, y + r)


def click(event, xdex, ydex):
    if clist[xdex][ydex][1] == '':
        if event.num == 1:
            clist[xdex][ydex][1] = 'X'
            clist[xdex][ydex][0].create_line(0, 0, 50, 50)
            clist[xdex][ydex][0].create_line(0, 50, 50, 0)
            juego.poner_ficha(xdex, ydex, 1)

        else:
            clist[xdex][ydex][1] = 'O'
            create_circle(24, 24, 20, clist[xdex][ydex][0])
            juego.poner_ficha(xdex, ydex,  2)


box_frame = Frame(root)
box_frame.pack()

for x in range(np_tuple[0]):
    prep_list = []
    for y in range(np_tuple[1]):
        prep_list.append([Canvas(box_frame, width=50, height=50), ''])
        prep_list[y][0].create_line(50, 50, 0, 50)
        prep_list[y][0].create_line(50, 50, 50, 0)
        prep_list[y][0].bind("&lt;Button-1&gt;", lambda e, xdex=x, ydex=y: click(e, xdex, ydex))
        prep_list[y][0].bind("&lt;Button-3&gt;", lambda e, xdex=x, ydex=y: click(e, xdex, ydex))
        prep_list[y][0].grid(row=x, column=y)
    clist.append(prep_list)

root.mainloop()