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.
Para completar este guia, você precisará ter:
pip
instalado.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.
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
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"]
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()
Esta etapa configura a janela raiz, particularmente seu título, geometria e capacidade de redimensionamento.
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")
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
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.
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}")
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)
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:
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.
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.
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)
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.
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.
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)
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.
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))
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.
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.
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.