Olá programadores e programadoras! Tudo bem com vocês? Espero que sim! Na aula de hoje veremos como trabalhar com layouts no PySide2.
Até agora criamos uma janela com sucesso e adicionamos um widget a ela. No entanto, normalmente você desejará adicionar mais de um widget a uma janela e ter algum controle sobre onde os widgets adicionados vão parar. Para organizar os widgets no Qt usamos layouts. Existem 4 layouts básicos disponíveis no Qt, listados na tabela a seguir:
Tabela 1: Comportamento dos layouts.
Existem três layouts bidimensionais disponíveis no Qt. O QVBoxLayout, QHBoxLayout e QGridLayout. Além disso, há também o QStackedLayout que permite colocar widgets um em cima do outro no mesmo espaço, mas mostrando apenas um layout por vez.
Nesta aula, examinaremos cada um desses layouts, mostrando como podemos usá-los para posicionar widgets em nossas aplicações.
Para facilitar a visualização dos layouts, primeiro criaremos um widget personalizado simples que exibe uma cor sólida de nossa escolha. Isso ajudará a distinguir os widgets que adicionamos ao layout. Adicione o seguinte código ao seu arquivo como uma nova classe de nível superior e salve o arquivo como cor_layout.py:
# --- Importar as bibliotecas --- #
from PySide2.QtWidgets import QWidget
from Pyside2.QtGui import QColor, QPallete
class Cor(QWidget):
"""Classe responsável por colocar a cor no layout do app."""
def __init__(self, cor: str):
"""
Função responsável por inicializar a classe.
:param cor: Cor escolhida para o layout.
"""
# --- Herdar a classe pai (QWidget) --- #
super().__init__()
# --- Preencher o layout com a cor desejada --- #
self.setAutoFillBackground(True)
# --- Configurar a cor --- #
paleta = self.pallete()
paleta.setColor(QPallete.Window, QColor(cor))
self.setPallete(paleta)
Neste código, herdamos QWidget para criar nosso próprio widget personalizado Cor. Aceitamos um único parâmetro ao criar o widget — cor (uma string). Primeiro, definimos .setAutoFillBackground como True para dizer ao widget para preencher automaticamente seu plano de fundo com a cor da janela. Em seguida, alteramos a cor QPalette.Window do widget para uma nova QColor descrita pelo valor cor que passamos. Finalmente, aplicamos essa paleta de volta ao widget. O resultado final é um widget preenchido com uma cor sólida, que especificamos quando o criamos.
Se você achar o acima confuso, não se preocupe muito! Cobriremos a Criação de Widgets e Paletas Personalizadas em detalhes mais tarde. Por enquanto, é suficiente que você entenda que pode criar um widget vermelho que preenche toda a janela do app com o seguinte código:
Cor('red')
Primeiro, vamos testar nosso novo widget Cor usando-o para preencher a janela inteira com uma única cor. Quando estiver concluído, podemos adicioná-lo à janela principal usando .setCentralWidget e obteremos uma janela vermelha sólida:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import QApplication, QMainWindow
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Chamar o widget customizado --- #
widget = Cor('red')
# --- Centralizar o widget --- #
self.setCentralWidget(widget)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
A janela aparecerá, totalmente preenchida com a cor vermelha. Observe como o widget se expande para preencher todo o espaço disponível:
A seguir, veremos cada um dos layouts Qt disponíveis. Observe que para adicionar nossos layouts à janela, precisaremos de um QWidget fictício para manter o layout.
Com QVBoxLayout você organiza os widgets um acima do outro de forma linear. Adicionar um widget o adiciona ao final da coluna.
Vamos adicionar nosso widget a um layout. Observe que para adicionar um layout ao QMainWindow, precisamos aplicá-lo a um QWidget fictício. Isso nos permite usar .setCentralWidget para aplicar o widget (e o layout) à janela. Nossos widgets coloridos serão organizados no layout, contido no QWidget na janela. Primeiro, apenas adicionamos o widget vermelho como antes:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Criar o layout --- #
layout = QVBoxLayout()
# --- Adicionar cor ao layout --- #
layout.addWidget(Cor('red'))
# --- Criar o widget para o layout --- #
widget = QWidget()
widget.setLayout(layout)
# --- Centralizar o widget --- #
self.setCentralWidget(widget)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
Observe que há uma borda visível ao redor do widget vermelho. Este é o espaçamento do layout – veremos como ajustá-lo mais tarde:
Em seguida, adicione mais alguns widgets coloridos ao layout:
# --- Adicionar cor ao layout --- #
layout.addWidget(Cor('red'))
layout.addWidget(Cor('green'))
layout.addWidget(Cor('blue'))
À medida que adicionamos widgets, eles se alinham verticalmente na ordem em que são adicionados:
QHBoxLayout é o mesmo, exceto pelos widgets ficarem um do lado do outro. Adicionar um widget o adiciona ao lado direito.
Para usá-lo podemos simplesmente alterar QVBoxLayout para QHBoxLayout. As caixas agora fluem da esquerda para a direita:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import QApplication, QHBoxLayout, QMainWindow, QWidget
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Criar o layout --- #
layout = QHBoxLayout()
# --- Adicionar cor ao layout --- #
layout.addWidget(Cor('red'))
layout.addWidget(Cor('green'))
layout.addWidget(Cor('blue'))
# --- Criar o widget para o layout --- #
widget = QWidget()
widget.setLayout(layout)
# --- Centralizar o widget --- #
self.setCentralWidget(widget)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
Os widgets devem se organizar horizontalmente:
Para layouts mais complexos, você pode aninhar layouts uns dentro dos outros usando .addLayout em um layout. Abaixo, adicionamos um QVBoxLayout no QHBoxLayout principal. Se adicionarmos alguns widgets ao QVBoxLayout, eles serão organizados verticalmente no primeiro slot do layout pai:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import (
QApplication,
QHBoxLayout,
QMainWindow,
QVBoxLayout,
QWidget
)
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Criar os layouts --- #
layout_pai = QHBoxLayout()
layout_1 = QVBoxLayout()
layout_2 = QVBoxLayout()
# --- Adicionar cores ao layout 1 --- #
layout_1.addWidget(Cor('red'))
layout_1.addWidget(Cor('yellow'))
layout_1.addWidget(Cor('purple'))
# --- Adicionar o layout 1 no layout pai --- #
layout_pai.addLayout(layout_1)
# --- Adicionar cor ao layout pai --- #
layout_pai.addWidget(Cor('green'))
# --- Adicionar cores ao layout 2 --- #
layout_2.addWidget(Cor('red'))
layout_2.addWidget(Cor('purple'))
# --- Adicionar o layout 2 no layout pai --- #
layout_pai.addLayout(layout_2)
# --- Criar o widget para o layout --- #
widget = QWidget()
widget.setLayout(layout_pai)
# --- Centralizar o widget --- #
self.setCentralWidget(widget)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
Os widgets devem se organizar em 3 colunas horizontalmente, com a primeira coluna também contendo 3 widgets empilhados verticalmente:
Você pode definir o espaçamento ao redor do layout usando .setContentMargins ou definir o espaçamento entre os elementos usando .setSpacing. O código a seguir mostra a combinação de widgets aninhados e margens e espaçamento de layout. Coloque esse código após a criação dos layouts:
# --- Colocar a margem e espaçamento no layout pai --- #
layout_pai.setContentsMargins(0, 0, 0, 0)
layout_pai.setSpacing(20)
Você deverá ver os efeitos do espaçamento e das margens. Experimente diferentes combinações:
Por mais úteis que sejam, se você tentar usar QVBoxLayout e QHBoxLayout para definir o layout de vários elementos, por exemplo. para um formulário, você achará muito difícil garantir que widgets de tamanhos diferentes estejam alinhados. A solução para isso é QGridLayout.
QGridLayout permite posicionar itens especificamente em uma grade. Você especifica posições de linha e coluna para cada widget. Você pode pular elementos e eles ficarão vazios:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import (
QApplication,
QGridLayout,
QMainWindow,
QWidget
)
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Criar o layout --- #
layout = QGridLayout()
# --- Colocar os widgets em determindadas células do layout --- #
layout.addWidget(Cor('red'), 0, 0)
layout.addWidget(Cor('green'), 1, 0)
layout.addWidget(Cor('blue'), 1, 1)
layout.addWidget(Cor('purple'), 2, 1)
# --- Criar o widget para o layout --- #
widget = QWidget()
widget.setLayout(layout)
# --- Centralizar o widget --- #
self.setCentralWidget(widget)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
Você deverá ver os widgets organizados em uma grade, alinhados apesar das entradas faltantes:
O layout final que abordaremos é o QStackedLayout. Conforme descrito, esse layout permite posicionar elementos diretamente um em frente ao outro. Você pode então selecionar qual widget deseja mostrar. Você pode usar isso para desenhar camadas em um aplicativo gráfico ou para imitar uma interface com guias. Observe que também existe QStackedWidget, que é um widget de contêiner que funciona exatamente da mesma maneira. Isso é útil se você deseja adicionar uma pilha diretamente a um QMainWindow com .setCentralWidget:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import (
QApplication,
QMainWindow,
QStackedLayout,
QWidget
)
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Criar o layout --- #
layout = QStackedLayout()
# --- Colocar os widgets no app --- #
layout.addWidget(Cor('red'))
layout.addWidget(Cor('green'))
layout.addWidget(Cor('blue'))
layout.addWidget(Cor('yellow'))
# --- Indicar qual widget será mostrado por cima de todos --- #
layout.setCurrentIndex(3)
# --- Criar o widget para o layout --- #
widget = QWidget()
widget.setLayout(layout)
# --- Centralizar o widget --- #
self.setCentralWidget(widget)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
Você verá apenas o último widget adicionado:
QStackedWidget é como funcionam as visualizações com guias nos aplicativos. Apenas uma visualização ('guia') fica visível por vez. Você pode controlar qual widget mostrar a qualquer momento usando .setCurrentIndex() ou .setCurrentWidget() para definir o item pelo índice (na ordem em que os widgets foram adicionados) ou pelo próprio widget.
Abaixo está uma breve demonstração usando QStackedLayout em combinação com QButton para fornecer uma interface semelhante a uma guia para um aplicativo:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import (
QApplication,
QHBoxLayout,
QMainWindow,
QPushButton,
QStackedLayout,
QVBoxLayout,
QWidget
)
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Criar os layouts --- #
layout_pagina = QVBoxLayout()
layout_botao = QHBoxLayout()
self.stacked_layout = QStackedLayout()
# --- Adicionar os layouts ao layout da página --- #
layout_pagina.addLayout(layout_botao)
layout_pagina.addLayout(self.stacked_layout)
# --- Criar o botão que muda para a cor VERMELHO --- #
botao_vermelho = QPushButton('Vermelho')
botao_vermelho.pressed.connect(self.ativar_tab_vermelho)
layout_botao.addWidget(botao_vermelho)
self.stacked_layout.addWidget(Cor('red'))
# --- Criar o botão que muda para a cor Verde --- #
botao_verde = QPushButton('Verde')
botao_verde.pressed.connect(self.ativar_tab_verde)
layout_botao.addWidget(botao_verde)
self.stacked_layout.addWidget(Cor('green'))
# --- Criar o botão que muda para a cor AZUL --- #
botao_azul = QPushButton('Azul')
botao_azul.pressed.connect(self.ativar_tab_azul)
layout_botao.addWidget(botao_azul)
self.stacked_layout.addWidget(Cor('blue'))
# --- Criar o botão que muda para a cor AMARELO --- #
botao_amarelo = QPushButton('Amarelo')
botao_amarelo.pressed.connect(self.ativar_tab_amarelo)
layout_botao.addWidget(botao_amarelo)
self.stacked_layout.addWidget(Cor('yellow'))
# --- Criar o widget para o layout --- #
widget = QWidget()
widget.setLayout(layout)
# --- Centralizar o widget --- #
self.setCentralWidget(widget_pagina)
def ativar_tab_vermelho(self):
"""Função responsável pelo índice do widget vermelho."""
self.stacked_layout.setCurrentIndex(0)
def ativar_tab_verde(self):
"""Função responsável pelo índice do widget verde."""
self.stacked_layout.setCurrentIndex(1)
def ativar_tab_azul(self):
"""Função responsável pelo índice do widget azul."""
self.stacked_layout.setCurrentIndex(2)
def ativar_tab_amarelo(self):
"""Função responsável pelo índice do widget amarelo."""
self.stacked_layout.setCurrentIndex(3)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
Agora você pode alterar o widget visível com o botão:
Felizmente, o Qt fornece um widget de aba integrado que fornece esse tipo de layout pronto para uso - embora na verdade seja um widget, não um layout. Abaixo, a demonstração da guia é recriada usando QTabWidget:
# --- Importar as bibliotecas --- #
import sys
from PySide2.QtWidgets import (
QApplication,
QMainWindow,
QTabWidget,
)
# --- Importar o widget customizado --- #
from cor_layout import Cor
class TelaPrincipal(QMainWindow):
"""Classe responsável pelo app."""
def __init__(self):
"""Função responsável por inicializar a classe."""
# --- Herdar a classe pai (QMainWindow) --- #
super().__init__()
# --- Título do app --- #
self.setWindowTitle('Meu app')
# --- Criar lo widget da tabela --- #
tabela = QTabWidget()
# --- Colocar a seleção das abas em alguma direção --- #
tabela.setTabPosition(QTabWidget.West)
# --- Permitir que as abas sejam reorganizadas --- #
tabela.setMovable(True)
# --- Colcoar as cores nas tabelas --- #
for cor in ['red', 'green', 'blue', 'yellow']:
tabela.addTab(Cor(cor), cor)
# --- Centralizar o widget --- #
self.setCentralWidget(tabela)
# --- Criar a instância do app --- #
app = QApplication(sys.argv)
# --- Criar a janela do app --- #
janela = TelaPrincipal()
janela.show()
# --- Executar o loop de eventos --- #
app.exec_()
Como você pode ver, é um pouco mais simples – e um pouco mais atraente! Você pode definir a posição das guias usando as direções cardeais e alternar se as guias podem ser movidas com .setMoveable:
Você notará que a barra de guias do macOS e Windows 11 parece bem diferente das outras – por padrão, as guias do macOS e Windows 11 assumem um estilo pílula ou bolha. No macOS e Windows 11, isso normalmente é usado para painéis de configuração com guias. Para documentos, você pode ativar o modo de documento para fornecer guias finas semelhantes às que você vê em outras plataformas. Esta opção não tem efeito em outras plataformas:
tabela = QTabWidget()
tabela.setDocumentMode(True)
Na aula de hoje vimos como trabalhar com diferentes layouts e como os fazer interagir entre si. Sabendo o tipo de layout que você quer, montar uma interface gráfica para o usuário começa a ficar mais simples; pois você saberá como os widgets se comportarão e onde estarão na tela.
No próximo capítulo veremos como criar ações nos widgets, como colocar uma barra de ferramentas e como trabalhar com os menus presentes nessa barra.