How to change location buttons dynamically using Tkinter?

Asked

Viewed 510 times

1

I know that this code has a lot of repetition and I intend to "dry" it later. For now, I want to understand how to change the position of the buttons in Tkinter dynamically. The solution I initially thought was to use grid to position the buttons, using variables in the arguments row and column, and then create a function that changes the value of these variables. However, this "solution" is not working. Follow code below for replication of the problem:

import tkinter as tk
from tkinter import ttk
import random


def change_position():
    new_row = button_6.grid_info().get("row")
    new_col = button_6.grid_info().get("column")
    button_6.grid(row=button_empty.grid_info().get("row"), column=button_empty.grid_info().get("column"))
    button_empty.grid(row=new_row, column=new_col)

class Single_window(tk.Tk):
    def __init__(self):
        super().__init__()

        self.title('5-Sliding Block')
        self.resizable(False, False)

root = Single_window()

button_f1 = tk.Frame(root)
button_4 = ttk.Button(button_f1, text = '4')
button_4.grid(row=0, column=0)
button_5 = ttk.Button(button_f1, text = '5')
button_5.grid(row=0, column=1)
button_6 = ttk.Button(button_f1, text = '6', command = change_position)
button_6.grid(row= 0, column= 2)
button_f1.pack(side = 'top')

button_f2 = tk.Frame(root)
button_7 = ttk.Button(button_f2, text = '7')
button_7.grid(row=1, column=0)
button_8 = ttk.Button(button_f2, text = '8')
button_8.grid(row=1, column=1)
button_empty = ttk.Button(button_f2)
button_empty.grid(row=1, column=2)
button_f2.pack(side = 'top')

root.mainloop()

My goal is to create a game of "Sliding Blocks" of order 6. See example in this website.

1 answer

1


Dude turned out nice what you did but there are some mistakes first, when you create a frame, it kind of has its own grid, and in your code you end up creating two to separate the top line from the bottom line

# instanciação dos frames
button_f1 = tk.Frame(root)
button_f2 = tk.Frame(root)

so that when you clicked on the button that had the script to change the position ended up creating more lines within the frame. Another thing would be you use the functions from the class you created, then create methods to create the buttons and manage their position. I made an example of how it could be to make it easier to understand

class Single_window(tk.Tk):
    # Parametros para criar o objeto, como o exemplo que você passou o link
    def __init__(self, linhas, colunas):
        super().__init__()

        self.title('5-Sliding Block')
        self.resizable(False, False)
        self.Linhas = linhas
        self.Colunas = colunas
        # talvez uma lista com os botoes que forem instanciados para você fazer alguma validação futura
        # como ver se a pessoa concluiu o desafio
        self.botoes = []
        # uma variavel para guardar a linha e coluna que esta livre no momento
        self.PosicaoLivre = [linhas-1, colunas-1]
        print (self.PosicaoLivre)
        # Gerar os botões a partir da denifição do frame
        numeracaoAtual = 0
        for x in range (0, self.Linhas):
            for y in range (self.Colunas):
                # aqui é uma validação para não criar o ultimo botao
                if (y == self.Colunas - 1 and x == self.Linhas - 1):
                    continue
                self.GerarBotao(numeracaoAtual, x, y)
                numeracaoAtual += 1

    def GerarBotao(self, numeracaoBotao, linha, coluna):
        botao = ttk.Button(self, text = numeracaoBotao)
        botao.grid(row = linha, column = coluna)
        # esse lambda é uma "gambiarra" para fazer com que voce possa jogar o próprio objeto no command
        # caso voce tente por o objeto diretamente sem o lambda, a função é chamada na crialçao do objeto
        # logo o clique não funciona
        botao.configure(command = lambda botaoObj=botao: self.TrocarPosicao(botaoObj))
        self.botoes.append(botao)

    def TrocarPosicao(self, botao):
        # pega as linhas do botão que foi apertado
        linhaBotaoPressionado = botao.grid_info().get("row")
        colunaBotaoPressionado = botao.grid_info().get("column")
        # a primeira validação é ver se o botão esta na horizontal ou vertical em relação a posição livre
        if self.PosicaoLivre[0] == linhaBotaoPressionado or self.PosicaoLivre[1] == colunaBotaoPressionado:
            # a segunda validação verifica se o botão esta a pelo menos 1 espaço na grid de distancia do espaço livre
            if abs(self.PosicaoLivre[0] - linhaBotaoPressionado) == 1 or abs(self.PosicaoLivre[1] - colunaBotaoPressionado) == 1:
                # guarda o valor temporário da posição livre
                posicaoLivreTemp = [linhaBotaoPressionado, colunaBotaoPressionado]
                # coloca o botão na posição livre do frame
                botao.grid(row = self.PosicaoLivre[0], column = self.PosicaoLivre[1])
                self.PosicaoLivre = posicaoLivreTemp

root = Single_window(10,10)

root.mainloop()

something else, I think it is better to write things in Portuguese because this way you end up finding yourself much better in the code :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.