Construindo um Layout Usando Python Tkinter - Programadores Brasil
Linguagens de programaçãoPythonTutoriais de PythonTutoriais para Python

[Python] Construindo um Layout para seu App usando Tkinter

11 Mins read
A nova versão da linguagem terá remoção da maioria das camadas de compatibilidade com versões anteriores ao Python 2.7. Entenda.

No tutorial de hoje, veremos como construir e desenhar um layout Tkinter. Esta é a segunda parte da nossa série de artigos sobre Python Tkinter. No artigo anterior, abordamos como criar uma janela e adicionar e posicionar texto nesta tela.

Controlando o layout Tkinter com gerenciadores geométricos

Até agora, foram adicionado widgets a janelas e widgets de Frame usando .pack( ), mas não foi aprofundado o que exatamente esse método faz. Vamos esclarecer as coisas! O layout Tkinter é controlado com gerenciadores de geometria. Embora .pack () seja um exemplo de um gerenciador de geometria, não é o único. O Tkinter tem dois outros: .place( ) e .grid( ).

Cada janela e Frame em um aplicativo com layout Tkinter pode usar apenas um gerenciador de geometria. No entanto, diferentes frames podem usar diferentes gerenciadores de geometria, mesmo se eles forem atribuídos a um frame ou janela usando outro gerenciador de geometria. Vamos começar pelo .pack( ).

O método .pack( ) usa um algoritmo de empacotamento para colocar widgets em um Frame ou janela em uma ordem especificada. Para um determinado widget, o algoritmo de empacotamento tem duas etapas principais:

  1. Calcule uma área retangular chamada parcela que é alta (ou larga) o suficiente para conter o widget e preenche a largura (ou altura) restante da janela com espaço em branco.
  2. Centralize o widget no lote, a menos que um local diferente seja especificado.

.pack( ) é poderoso, mas pode ser difícil de visualizar. A melhor maneira de ter uma ideia de como funciona é ver alguns exemplos. Veja o que acontece quando usamos o método em três widgets Label em um Frame:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=100, height=100, bg="red")
frame1.pack()

frame2 = tk.Frame(master=window, width=50, height=50, bg="yellow")
frame2.pack()

frame3 = tk.Frame(master=window, width=25, height=25, bg="blue")
frame3.pack()

window.mainloop()

Resultado no seu layout Tkinter:

Layout Tkinter - frames de tamanhos diferentes

Cada quadro é colocado na posição mais superior disponível. A moldura vermelha é colocada na parte superior da janela. Em seguida, a moldura amarela é colocada logo abaixo da vermelha e a moldura azul logo abaixo da amarela.

Existem três pacotes invisíveis contendo cada um dos três widgets de frame. Cada pacote tem a largura (width) da janela e a altura (height) da moldura que ela contém. Como nenhum ponto de ancoragem foi especificado quando .pack( ) foi chamado para cada Frame, eles estão todos centralizados dentro de seus pacotes. É por isso que cada Frame é centralizado na janela.

Este método aceita alguns argumentos de palavra-chave para configurar com mais precisão o posicionamento do widget. Por exemplo, você pode definir o argumento de palavra-chave fill para especificar em qual direção os quadros devem ser preenchidos. As opções são tk.X para preencher na direção horizontal, tk.Y para preencher verticalmente e tk.BOTH para preencher em ambas as direções. Veja como você empilharia os três quadros de modo que cada um ocupasse toda a janela horizontalmente:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, height=100, bg="red")
frame1.pack(fill=tk.X)

frame2 = tk.Frame(master=window, height=50, bg="yellow")
frame2.pack(fill=tk.X)

frame3 = tk.Frame(master=window, height=25, bg="blue")
frame3.pack(fill=tk.X)

window.mainloop()

Observe que a largura não é definida em nenhum dos widgets Frame. largura não é mais necessária porque cada frame define .pack( ) para preencher horizontalmente, substituindo qualquer largura que você definir. O preenchimento da janela com .pack( ) responde ao redimensionamento da janela. Resultado:

Layout Tkinter - frames de tamanhos iguais

O argumento de palavra-chave side de .pack( ) especifica em qual lado da janela o widget deve ser colocado. Estas são as opções disponíveis:

  • tk.TOP
  • tk.BOTTOM
  • tk.LEFT
  • tk.RIGHT

Se não for definido tk.TOP é usado automaticamente e colocará novos widgets no topo da janela ou na parte superior da janela que ainda não esteja ocupada por um widget. Por exemplo, o script a seguir coloca três frames lado a lado da esquerda para a direita e expande cada frame para preencher a janela verticalmente:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg="red")
frame1.pack(fill=tk.Y, side=tk.LEFT)

frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.Y, side=tk.LEFT)

frame3 = tk.Frame(master=window, width=50, bg="blue")
frame3.pack(fill=tk.Y, side=tk.LEFT)

window.mainloop()

Desta vez, você deve especificar o argumento da palavra-chave height em pelo menos um dos frames para forçar a janela a ter alguma altura. Da mesma forma que o caso anterior, os frames do seu layout tkinter são responsivos verticalmente, porém não horizontalmente.

A janela resultante tem a seguinte aparência:

Layout Tkinter - frames de mesma altura

Para tornar o layout realmente responsivo, você pode definir um tamanho inicial para seus quadros usando os atributos de largura e altura. Em seguida, defina o argumento de palavra-chave fill de .pack( ) como tk.BOTH e defina o argumento de palavra-chave de expansão como True:

import tkinter as tk

window = tk.Tk()

frame1 = tk.Frame(master=window, width=200, height=100, bg="red")
frame1.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame2 = tk.Frame(master=window, width=100, bg="yellow")
frame2.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

frame3 = tk.Frame(master=window, width=50, bg="blue")
frame3.pack(fill=tk.BOTH, side=tk.LEFT, expand=True)

window.mainloop()

Ao executar o script acima, você verá uma janela que inicialmente parece igual à que você gerou no exemplo anterior. A diferença é que agora você pode redimensionar a janela como quiser e os frames irão expandir e preencher a janela responsivamente.

Controlando precisamente a posição dos widgets

Para controlar a localização precisa que um widget deve ocupar em uma janela ou frame, .place( ). É preciso fornecer dois argumentos de palavra-chave, x e y, que especificam as coordenadas x e y para o canto superior esquerdo do widget. Tanto x como y são medidos em pixels, não em unidades de texto.

Lembre-se de que a origem (onde x e y são ambos 0) é o canto superior esquerdo do frame ou janela. Portanto, o argumento y de .place( ) seria como o número de pixels da parte superior da janela e no argumento x como o número de pixels à esquerda da janela.

Exemplo:

import tkinter as tk

window = tk.Tk()

frame = tk.Frame(master=window, width=150, height=150)

frame.pack()

label1 = tk.Label(master=frame, text="I'm at (0, 0)", bg="red")

label1.place(x=0, y=0)

label2 = tk.Label(master=frame, text="I'm at (75, 75)", bg="yellow")

label2.place(x=75, y=75)

window.mainloop()

As linhas 5 e 7 criam um novo widget Frame chamado frame1, com 150 pixels de largura e 150 pixels de altura, e o adicionam na janela com .pack( ). As linhas 9 e 11 criam um novo label chamado label1 com um fundo amarelo e coloque-o no frame1 na posição (0, 0). As linhas 13 e 15 criam um segundo label chamado label2 com um fundo vermelho e coloque-o no frame1 na posição (75, 75).

Layout Tkinter - posicionando labels em posições específicas da janela

Apesar disso, .place( ) não é amplamente utilizado. Este método tem dois pontos negativos:

  • O layout pode ser difícil de gerenciar com .place( ). Isso é especialmente verdadeiro se seu aplicativo tiver muitos widgets.
  • Layouts criados com .place( ) não são responsivos. Eles não mudam quando a janela é redimensionada.

Um dos principais desafios do desenvolvimento de interface de usuário de multiplataformas é fazer layouts que tenham uma boa aparência, independentemente da plataforma em que são visualizados, e .place( ) é uma escolha ruim para criar layouts multiplataformas.

Isso não quer dizer que .place( ) nunca deve ser usado! Em alguns casos, pode ser exatamente o que você precisa. Por exemplo, se você estiver criando uma interface GUI para um mapa, .place( ) pode ser a escolha perfeita para garantir que os widgets sejam colocados na distância correta uns dos outros no mapa.

.pack( ) é geralmente uma escolha melhor do que .place( ), mas mesmo .pack( ) tem algumas desvantagens. O posicionamento dos widgets depende da ordem em que .pack( ) é chamado, portanto, pode ser difícil modificar os aplicativos existentes sem compreender totalmente o código que controla o layout. O gerenciador de geometria .grid( ) resolve muitos desses problemas.

Organizando a tela em um grid

O gerenciador de geometria usado com mais frequência é .grid( ), que fornece todo o poder de .pack( ) em um formato mais fácil de entender e manter.

.grid( ) funciona dividindo uma janela ou quadro em linhas e colunas. Você especifica a localização de um widget chamando .grid( ) e passando os índices de linha e coluna para os argumentos de palavra-chave de linha e coluna, respectivamente. Os índices de linha e coluna começam em 0, portanto, um índice de linha de 1 e um índice de coluna de 2 informa a .grid( ) para colocar um widget na terceira coluna da segunda linha.

O script a seguir cria uma grade 3 × 3 de frames com widgets Label empacotados neles:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack()

window.mainloop()

Resultado:

Layout Tkinter - grid

Dois gerenciadores de geometria estão sendo usados neste exemplo. Cada Frame é anexado à janela com o gerenciador de geometria .grid( ). Cada label é anexada ao seu quadro mestre com .pack( ).

O importante a perceber é que, embora .grid( ) seja chamado em cada objeto Frame, o gerenciador de geometria se aplica ao objeto window. Da mesma forma, o layout de cada frame é controlado com o gerenciador de geometria .pack( ).

Os frames no exemplo anterior são colocados próximos uns dos outros. Para adicionar algum espaço ao redor de cada quadro, você pode definir o padding (preenchimento) de cada célula na grade. Preenchimento é apenas um espaço em branco que envolve um widget e o separa visualmente de seu conteúdo.

Os dois tipos de preenchimento são preenchimento externo e interno. O preenchimento externo adiciona algum espaço ao redor da parte externa de uma célula da grade. É controlado com dois argumentos de palavra-chave para .grid( ):

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)
        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack()

window.mainloop()

Resultado:

Adicionando padding no layout Tkinter

O método .pack( ) também possui parâmetros padx e pady. O código a seguir é quase idêntico ao código anterior, exceto por adicionar 5 pixels de preenchimento adicional ao redor de cada rótulo nas direções x e y:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    for j in range(3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)

        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack(padx=5, pady=5)

window.mainloop()

Resultado:

Adicionando padding interno no layout Tkinter

É importante ressaltar que esse código não é responsivo, portanto os labels não esticam quando o tamanho da janela é alterado. A grid inteira permanece no canto superior esquerdo à medida que a janela se expande.

Você pode ajustar como as linhas e colunas da grid crescem conforme a janela é redimensionada usando .columnconfigure( ) e .rowconfigure( ) no objeto janela. Lembre-se de que a grade é anexada à janela, mesmo que você esteja chamando .grid( ) em cada widget Frame. TAmbos usam três argumentos essenciais:

  • O índice da coluna ou linha da grade que você deseja configurar (ou uma lista de índices para configurar várias linhas ou colunas ao mesmo tempo);
  • Um argumento de palavra-chave chamado weight que determina como a coluna ou linha deve responder ao redimensionamento da janela, em relação às outras colunas e linhas;
  • Um argumento de palavra-chave denominado minsize que define o tamanho mínimo da altura da linha ou largura da coluna em pixels.

Weight é definido como 0 por padrão, o que significa que a coluna ou linha não se expande conforme a janela é redimensionada. Se cada coluna e linha tiver o peso 1, então todas elas crescerão na mesma taxa. Se uma coluna tiver peso 1 e outra coluna 2, a segunda coluna se expandirá duas vezes a taxa da primeira. Ajuste o script anterior para lidar melhor com o redimensionamento da janela:

import tkinter as tk

window = tk.Tk()

for i in range(3):
    window.columnconfigure(i, weight=1, minsize=75)
    window.rowconfigure(i, weight=1, minsize=50)

    for j in range(0, 3):
        frame = tk.Frame(
            master=window,
            relief=tk.RAISED,
            borderwidth=1
        )
        frame.grid(row=i, column=j, padx=5, pady=5)

        label = tk.Label(master=frame, text=f"Row {i}\nColumn {j}")
        label.pack(padx=5, pady=5)

window.mainloop()

.columnconfigure( ) e .rowconfigure( ) são colocados no corpo do loop for externo. (Você poderia configurar explicitamente cada coluna e linha fora do loop for, mas isso exigiria a gravação de seis linhas de código adicionais.)

Em cada iteração do loop, a i-ésima coluna e linha são configuradas para ter um weight de 1. Isso garante que cada linha e coluna se expanda na mesma taxa sempre que a janela for redimensionada. O argumento minsize é definido como 75 para cada coluna e 50 para cada linha. Isso garante que o widget Label sempre exiba seu texto sem cortar nenhum caractere, mesmo se o tamanho da janela for extremamente pequeno.

Por padrão, os widgets são centralizados em suas células de grid. Por exemplo, o código a seguir cria dois widgets Label e os coloca em uma grid com uma coluna e duas linhas:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0)

label2 = tk.Label(text="B")
label2.grid(row=1, column=0)

window.mainloop()

Cada célula da grid tem 250 pixels de largura e 100 pixels de altura. Os labels são colocados no centro de cada célula, como você pode ver na figura a seguir:

Centralização default

Você pode alterar a localização de cada rótulo dentro da célula da grade usando o parâmetro sticky. Sticky aceita uma string contendo uma ou mais das seguintes letras: n ou N, representando o topo centro da célula; e ou E, representando o lado direito central; s ou S, representando a parte de baixo e central da célula; e w ou W, representando o lado esquerdo central. Esses parâmetros também podem ser combinados. Exemplo:

import tkinter as tk

window = tk.Tk()
window.columnconfigure(0, minsize=250)
window.rowconfigure([0, 1], minsize=100)

label1 = tk.Label(text="A")
label1.grid(row=0, column=0, sticky="ne")

label2 = tk.Label(text="B")
label2.grid(row=1, column=0, sticky="sw")

window.mainloop()

Resultado:

Ajustando elementos em partes diferentes da grid

Quando um widget é posicionado com sticky, o tamanho do widget em si é grande o suficiente para conter qualquer texto e outros conteúdos dentro dele. Não vai preencher toda a célula da grade. Para preencher a grade, você pode especificar ns para forçar o widget a preencher a célula na direção vertical ou ew para preencher a célula na direção horizontal. Para preencher a célula inteira, defina sticky como nsew. O exemplo a seguir ilustra cada uma dessas opções:

import tkinter as tk

window = tk.Tk()

window.rowconfigure(0, minsize=50)
window.columnconfigure([0, 1, 2, 3], minsize=50)

label1 = tk.Label(text="1", bg="black", fg="white")
label2 = tk.Label(text="2", bg="black", fg="white")
label3 = tk.Label(text="3", bg="black", fg="white")
label4 = tk.Label(text="4", bg="black", fg="white")

label1.grid(row=0, column=0)
label2.grid(row=0, column=1, sticky="ew")
label3.grid(row=0, column=2, sticky="ns")
label4.grid(row=0, column=3, sticky="nsew")

window.mainloop()

Resultado:

Exemplos de diferentes usos de posicionamento no grid

O que o exemplo acima ilustra é que o parâmetro aderente do gerenciador de geometria .grid( ) pode ser usado para obter os mesmos efeitos que o parâmetro de preenchimento do gerenciador de geometria .pack( ). A correspondência entre os parâmetros sticky e fill é resumida na seguinte tabela:

.grid( ) .pack( )
sticky = “ns”fill=tk.Y
sticky = “ew”fill=tk.X
sticky = “nsew”fill=tk.BOTH

.grid( ) é um gerenciador de geometria poderoso. Muitas vezes é mais fácil de entender do que .pack( ) e é muito mais flexível do que .place( ). Ao criar novos aplicativos com layout Tkinter, você deve considerar o uso de .grid( ) como seu gerenciador de geometria principal.

Agora que vimos alguns labels básicos do layout Tkinter e como posicionar eles geometricamente, no próximo tutorial, veremos como tornar as interfaces interativas.

Cursos de Programação -> Veja também: [+] 4 Dicas de Como Escolher Os Melhores Cursos de Programação.

Curso de Programação -> Veja também: [+] Pacote Full Stack para Iniciantes em Programação.

Curso para Desenvolvimento de Games -> Veja também: [+] Curso completo de Desenvolvimento de Games

Curso de Python para iniciantes ->  Veja também: [+] Pacote Python Faixa preta para iniciantes.


Deixe o seu comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Continue Lendo

[Iniciantes] Aprenda a Como Fazer ForEach em Javascript

Next.js: Conheça o Framework para ReactJS

[Python] Criando um Layout Interativo com Tkinter

[Python] Tkinter - Aprenda a Programar em Python Com Interfaces Gráficas

[Iniciantes] Python List: Aprenda como Manipular as Listas

[Iniciantes] Veja Como Instalar o Python 3 no Windows

WooCommerce: Veja Como Criar Sua Loja Virtual Wordpress

Sistema SAP: Entenda o que é e Como Funciona

[Python] PyCharm ou VSCode ? Qual o Melhor? Veja os Prós e Contras de cada um

[Iniciantes] Java Server Faces (JSF) - Começando do Zero