paint-brush
Construindo sua primeira GUI Python com Tkinterpor@pictureinthenoise
8,720 leituras
8,720 leituras

Construindo sua primeira GUI Python com Tkinter

por Picture in the Noise13m2022/11/14
Read on Terminal Reader
Read this story w/o Javascript

Muito longo; Para ler

Tkinter é o padrão do Python para construção de GUIs e está incluído na Python Standard Library. Este guia percorre o design de um aplicativo GUI simples para explicar os conceitos básicos do Tkinter, incluindo o layout dos elementos GUI, capturando a entrada do usuário e vinculando elementos GUI a métodos de retorno de chamada.

People Mentioned

Mention Thumbnail
featured image - Construindo sua primeira GUI Python com Tkinter
Picture in the Noise HackerNoon profile picture

Introdução

Talvez você tenha acabado de começar a aprender Python e tenha criado um ou dois aplicativos simples. Parabéns! Mas, e agora? Bem, que tal construir uma interface gráfica do usuário ("GUI") para interagir com seu novo e brilhante aplicativo Python?


Existem várias opções para criar aplicativos GUI com Python, incluindo PyQt e wxPython . Este guia, no entanto, irá apresentá-lo ao Tkinter .


Tkinter , que significa "interface Tk", é o padrão Python para construção de GUIs e está incluído na Python Standard Library . É uma ligação ao kit de ferramentas Tk GUI , uma biblioteca de widgets GUI gratuita e de código aberto que pode ser usada para construir interfaces gráficas em várias linguagens de programação.


Este tutorial percorre o design de um aplicativo GUI básico que exibe a hora local em um fuso horário selecionado pelo usuário. As etapas constroem o aplicativo progressivamente e descrevem alguns dos principais conceitos ao trabalhar com o Tkinter , incluindo o layout dos elementos da GUI, a captura da entrada do usuário e a ligação dos elementos da GUI aos métodos de retorno de chamada.


Pré-requisitos

Para completar este guia, você precisará ter:


  • Python instalado .
  • pip instalado.
  • Instalou a biblioteca de fuso horário pytz . A biblioteca pode ser instalada usando pip .


 pip install pytz


Este guia usará a terminologia janela raiz e janela principal de forma intercambiável.


Etapa 1 - Criar um novo aplicativo Python e definir as importações necessárias

Crie um novo aplicativo Python chamado timezone.py e adicione as seguintes instruções de import para importar os módulos Tkinter , datetime e pytz .


 import tkinter as tk import datetime import pytz


Etapa 2 - Adicionar fusos horários

Este guia incorpora o conjunto de fusos horários dos Estados Unidos, que são uma pequena fração dos fusos horários suportados pelo pytz . A funcionalidade do aplicativo pode ser estendida adicionando nomes de fuso horário pytz adicionais. Uma lista completa de fusos horários disponíveis com pytz pode ser gerada executando o seguinte comando:


 print(pytz.all_timezones)


O conjunto de fusos horários dos Estados Unidos disponíveis via pytz é especificado como uma lista.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"]


Passo 3 - Crie uma Instância da Classe Tk

A instanciação da classe Tk cria uma janela raiz que servirá como a janela principal da GUI do aplicativo de fuso horário.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk()


Etapa 4 - Configurar a janela principal

Esta etapa configura a janela raiz, particularmente seu título, geometria e capacidade de redimensionamento.

Etapa 4a - Definindo o título da janela principal

O título da janela é definido usando o método title .


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application")

Etapa 4b - Configurando a largura e a altura da janela principal

Os valores de largura e altura da janela, expressos em pixels, podem ser atribuídos a variáveis para facilitar o acompanhamento do código.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175

Etapa 4c - Calculando a posição central da janela principal

A janela principal pode ser colocada em qualquer lugar na tela, por exemplo, no centro, no canto superior esquerdo, winfo_screenwidth() e winfo_screenheight() juntamente com alguns cálculos simples. Esses dois métodos retornam a largura e a altura da tela que são usadas para calcular as coordenadas apropriadas (x,y) para centralizar a janela principal. Assim como a largura e a altura da janela, os valores da posição central também podem ser atribuídos a variáveis para tornar o código mais legível.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2)


Observe que os valores center_x e center_y também são expressos em pixels e int é usado para garantir que ambos os valores calculados sejam inteiros.

Etapa 4d - Configurando a Geometria da Janela Principal

A geometria da janela root , que especifica o tamanho e a posição da janela principal, é definida usando o método de geometry da janela raiz. Para tornar as coisas simples, você pode usar um literal sting formatado, ou f-string , que irá interpolar as expressões de variáveis geométricas em tempo de execução. De acordo com o bloco de código a seguir, o literal f-string é passado para o método de geometry como um parâmetro.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}")


Etapa 4e - Configurando o redimensionamento da janela principal


A capacidade de redimensionamento da janela raiz ao longo de seus eixos x e y pode ser definida por meio do método resizable da janela raiz. O método resizable aceita parâmetros de height e width , nessa ordem. Um valor True ou diferente de zero para qualquer parâmetro especifica que a janela principal é redimensionável ao longo do eixo associado. Um valor False ou 0 para qualquer parâmetro especifica que a janela principal não é redimensionável ao longo do eixo especificado. O aplicativo de timer usará um valor de False para altura e largura para evitar que a janela principal seja redimensionada.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False)


Etapa 5 - Especifique uma grade para o layout do widget

Os widgets podem ser organizados na parte superior da janela principal de diferentes maneiras usando gerenciadores de geometria . Geralmente, os widgets podem ser organizados de 3 maneiras:


  1. Empacotado usando o método pack() . O gerenciador de geometria pack() organiza os widgets em blocos antes de serem adicionados a uma janela raiz ou widget pai. Uma maneira imperfeita, mas talvez útil, de pensar sobre a embalagem é pensar em adicionar itens de mercearia a uma sacola de compras. Os itens não são necessariamente adicionados a locais pré-definidos na sacola. Em vez disso, eles são embalados um após o outro, usando o espaço disponível até que todos os itens tenham sido colocados na sacola. O gerenciador de geometria pack() funciona de maneira semelhante.

  2. Colocado usando o método place() . O gerenciador de geometria place() coloca os widgets em posições específicas e predefinidas na janela raiz ou no widget pai. Este gerenciador de geometria é obviamente útil ao construir layouts de GUI precisos.

  3. Organizado como uma grade usando o método grid() . As grades são tabelas bidimensionais de linha/coluna. Os widgets são adicionados a uma grade especificando a linha e a coluna específicas onde o widget deve ser colocado. As grades são configuradas usando os métodos columnconfigure e rowconfigure da janela raiz. Cada um desses métodos tem um index e um atributo de weight . O atributo index especifica a posição da coluna ou linha usando uma base inicial de 0 . O atributo de weight especifica o tamanho de uma determinada coluna ou linha em relação a outras colunas. Por exemplo, se a coluna 0 tiver peso 1 e a coluna 1 tiver peso 3 , a coluna 1 será 3 vezes maior que a coluna 0 .


As grades são úteis ao colocar elementos próximos uns dos outros e são relativamente simples de implementar. O aplicativo de fuso horário usará uma grade da janela principal dividida em 4 colunas iguais.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1)


Etapa 6 - Adicionar widgets

Nesta etapa, os widgets são adicionados à grade da janela principal definida na Etapa 4. A aplicação de fuso horário utilizará 3 widgets TKinter :


  • Label
  • Listbox
  • Button


Os widgets Label e Button do Tkinter são exatamente como são nomeados. O widget Label permite que o programador exiba texto (ou seja, um rótulo) e o widget Button é usado para exibir botões. O widget Listbox é usado para selecionar um valor (ou valores) de uma lista de opções.

Claro, existem outros widgets além dos 3 listados acima, como os SpinBox , Entry e Message . A documentação formal dos comandos Tk é um recurso útil para aprender mais sobre os widgets disponíveis através do kit de ferramentas Tk.

Etapa 6a - Criar widgets

Uma instância de cada classe de widget é criada para um determinado widget usado com o design da GUI. O aplicativo de fuso horário usa 4 instâncias de widget: 2 widgets de Label , 1 widget de caixa de Listbox e 1 widget de Button .


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="")


Conforme visto no bloco de código acima, cada instância do widget é "anexada" à janela root . O widget Listbox também requer a instanciação de uma classe especial Tkinter Variable() que é usada para fornecer ao widget a lista de opções de fuso horário que o usuário pode selecionar por meio do atributo listvariable Listbox . O texto exibido por cada widget Label pode ser configurado usando o atributo text . O valor de texto para time_label é deixado em branco, pois será definido dinamicamente sempre que o usuário "obter" o horário de um fuso horário selecionado.

Etapa 6b - Colocar widgets na grade

As instâncias do widget criadas na Etapa 5a podem ser colocadas na grade definida na Etapa 4 usando o método grid() . O aplicativo de fuso horário usará os seguintes atributos do método grid() para definir o layout:


  • column : Especifica a coluna específica onde o widget será colocado usando uma base 0 .

  • row : Especifica a linha específica onde o widget será colocado usando uma base 0 .

  • columnspan : especifica que o widget deve abranger o número especificado de colunas. Por exemplo, um widget com um valor columnspan=3 abrangerá 3 colunas, mesmo que o próprio widget seja menor que 3 colunas.

  • sticky : O comportamento padrão do Tkinter é colocar um widget no centro horizontal e vertical da célula (ou seja, posição particular de linha/coluna) onde ele é colocado. Esse comportamento padrão pode ser substituído usando o atributo sticky que usa valores semelhantes a bússola, incluindo NW , N , NE , W , E , SW , S e SE , para alinhar o widget em um local específico dentro da célula do widget. Por exemplo, sticky=tK.W especifica que o widget deve ser alinhado ao canto oeste de sua célula de grade. Analogamente, sticky=tK.E especifica que o widget deve ser alinhado ao canto leste de sua célula de grade.

  • padx , pady : Os atributos padx e pady são usados para adicionar o preenchimento dos eixos x e y, respectivamente, com valores especificados em pixels. Naturalmente, o preenchimento pode fornecer uma GUI com aparência mais profissional, garantindo que os widgets não "se choquem" diretamente contra as bordas da janela raiz ou de outros widgets.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10)


Etapa 7 - Criar método de retorno de chamada do botão Get Time

Um método de retorno de chamada precisa ser definido para manipular eventos quando o usuário clicar no botão select_timezone_button criado na Etapa 5.

Etapa 7a - Vinculando o botão Get Time

Antes de definir a lógica de retorno de chamada, é útil vincular primeiro o botão ao nome do método que eventualmente abrangerá o código de retorno de chamada. O método bind() pode ser usado em conjunto com uma função lambda para vincular o select_timezone_button ao método de retorno de chamada especificado. Além disso, observe que a função lambda é usada para passar referências aos widgets select_timezone_listbox e time_label como parâmetros de retorno de chamada. Na verdade, esses parâmetros de retorno de chamada não são necessários, pois select_timezone_listbox e time_label estão dentro do escopo global. No entanto, é indiscutivelmente útil demonstrar como os argumentos podem ser passados para a função de retorno de chamada.


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_button.bind("<Button>", lambda e, args=[select_timezone_listbox, time_label]: get_timezone_time(e, args))

Etapa 7b - Definindo a lógica de retorno de chamada

A lógica de retorno de chamada para lidar com eventos de clique de botão é definida abaixo.


 def get_timezone_time(e, args): select_timezone_listbox = args[0] time_label = args[1] selection_index = select_timezone_listbox.curselection() selected_timezone = select_timezone_listbox.get(selection_index) now_time = datetime.datetime.now() tz_time = now_time.astimezone(pytz.timezone(selected_timezone)) tz_formatted = tz_time.strftime("%H:%M:%S") time_label.configure({"text": f"The time in {selected_timezone} is {tz_formatted}."}) time_label.update()


Os curselection() e get() são usados para recuperar o fuso horário selecionado do usuário a partir da referência ao widget select_timezone_listbox . A hora atual do usuário é então convertida no horário do fuso horário selecionado. Finalmente, o método configure é usado para alterar o atributo text da referência para o time_label com a hora local no fuso horário selecionado. Observe que o método update() é usado para "forçar" o widget time_label a se atualizar com o novo valor de texto.

Etapa 8 - Concluir e executar o aplicativo

O método mainloop() da janela raiz é aplicado como a última linha de código. O método mainloop() executará a GUI em um loop infinito até que o usuário saia.


O código completo é o seguinte:


 import tkinter as tk import datetime import pytz timezones = ["US/Alaska", "US/Aleutian", "US/Arizona", "US/Central", "US/East-Indiana", "US/Eastern", "US/Hawaii", "US/Indiana-Starke", "US/Michigan", "US/Mountain", "US/Pacific", "US/Samoa"] def get_timezone_time(e, args): select_timezone_listbox = args[0] time_label = args[1] selection_index = select_timezone_listbox.curselection() selected_timezone = select_timezone_listbox.get(selection_index) now_time = datetime.datetime.now() tz_time = now_time.astimezone(pytz.timezone(selected_timezone)) tz_formatted = tz_time.strftime("%H:%M:%S") time_label.configure({"text": f"The time in {selected_timezone} is {tz_formatted}."}) time_label.update() root = tk.Tk() root.title("Simple Timezone Application") window_width = 450 window_height = 175 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() center_x = int((screen_width - window_width)/2) center_y = int((screen_height - window_height)/2) root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}") root.resizable(False, False) root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) # Instance of Label widget class for selection prompt. select_timezone_label = tk.Label(root, text="Please select a timezone.") # Instance of Listbox class for selection of timezone from list. list_var = tk.Variable(value=timezones) select_timezone_listbox = tk.Listbox(root, listvariable=list_var, height=1) # Instance of Button class to get the local time in the selected timezone. select_timezone_button = tk.Button(root, text="Get Time") # Second instance of the Label class to display the local time in the selected timezone. time_label = tk.Label(root, text="") # Place widgets on grid. select_timezone_label.grid(column=0, row=0, columnspan=4, sticky=tk.W, padx=10, pady=10) select_timezone_listbox.grid(column=0, row=1, columnspan=3, sticky=tk.EW, padx=10, pady=10) select_timezone_button.grid(column=4, row=1, sticky=tk.E, padx=10, pady=10) time_label.grid(column=0, row=4, columnspan=4, sticky=tk.W, padx=10, pady=10) # Bind button to callback. select_timezone_button.bind("<Button>", lambda e, args=[select_timezone_listbox, time_label]: get_timezone_time(e, args)) root.mainloop()


Quando o aplicativo é iniciado, a GUI deve se parecer com:




Para usar o aplicativo, clique na caixa de listagem e use as teclas de cursor para cima/para baixo do teclado para percorrer as opções de fuso horário. Clique no botão Get Time para exibir a hora atual no fuso horário selecionado.




Próximos passos

Parabéns por criar seu primeiro aplicativo GUI Python com o Tkinter ! Conforme mencionado na Introdução, este guia foi elaborado para apresentar a você alguns conceitos básicos. A documentação da Python Standard Library e as referências de documentação de comandos Tk mencionadas anteriormente são dois recursos fantásticos para ajudá-lo a aprender sobre os recursos e funcionalidades mais avançados do Tkinter.


Os aplicativos Tkinter GUI às vezes são criticados por terem uma aparência não nativa. Isso pode ser verdade. Mas os widgets do kit de ferramentas são altamente configuráveis e os aplicativos Tkinter GUI podem ser construídos com relativa rapidez, sem a necessidade de instalar nenhum pacote Python externo.


Além da documentação formal, existem inúmeros tutoriais disponíveis na Internet para aprender a construir aplicativos GUI mais sofisticados com o Tkinter.