PyQt5: Uma Visão Geral
PyQt5 é um poderoso cojnunto de bindings Python para o framework Qt. Ele permite criar interfaces gráficas de usuário (GUIs) ricas e multiplataforma usando Python. Este guia aborda os fundamentos do PyQt5, desde a instalação até a criação de janelas, widgets, layouts e o manejo de sinais e slots.
Instalação
Para começar, você precisa instalar o PyQt5. Utilize o pip para isso:
pip install PyQt5
pip install PyQt5-tools
Módulos Principais do PyQt5
- PyQt5.QtWidgets: Contém todos os elementos visuais (widgets) para a interface gráfica.
- PyQt5.QtCore: Fornece funcionalidades não relacionadas à GUI, como manipulação de arquivos, diretórios, threads e eventos.
- PyQt5.QtGui: Lida com processamento gráfico, tratamento de eventos e formatação de texto e fontes.
- PyQt5.Network: Oferece classes para programação de rede, suportando protocolos TCP/IP e UDP.
- PyQt5.QtBluetooth: Permite interagir com dispositivos Blueototh, incluindo escaneamento, conexão e troca de dados.
Tipos de Janelas
PyQt5 oferece três classes principais para a criação de janelas:
1. QWidget
QWidget é a classe base para todos os elementos da interface do usuário. É uma janela genérica com alta flexibilidade para adicionar e estilizar controles.
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
class JanelaWidget(QWidget):
"""
Uma janela baseada em QWidget, permitindo grande flexibilidade.
"""
def __init__(self):
super().__init__()
self.setWindowTitle("Exemplo QWidget")
# Adiciona um rótulo à janela
rotulo = QLabel('Esta é uma janela QWidget', self)
rotulo.setStyleSheet('color: blue;') # Estilo simples
rotulo.move(50, 50) # Posição do rótulo
if __name__ == '__main__':
app = QApplication([])
janela = JanelaWidget()
janela.setGeometry(300, 300, 300, 200) # Define posição e tamanho da janela
janela.show()
app.exec_()
2. QMainWindow
QMainWindow é uma subclasse de QWidget que fornece uma estrutura para aplicativos com barras de menu, barras de ferramentas e barras de status.
from PyQt5.QtWidgets import QMainWindow, QApplication, QLabel, QMenuBar
class JanelaPrincipal(QMainWindow):
"""
Uma janela principal com menu, ferramentas e status.
"""
def __init__(self):
super().__init__()
self.setWindowTitle("Exemplo QMainWindow")
self.setGeometry(300, 300, 400, 300)
# Central widget
rotulo_central = QLabel('Conteúdo principal da janela', self)
rotulo_central.setStyleSheet('color: red;')
self.setCentralWidget(rotulo_central)
# Menu Bar
barra_menu = QMenuBar(self)
menu_arquivo = barra_menu.addMenu('Arquivo')
menu_arquivo.addAction('Novo')
menu_arquivo.addAction('Abrir')
menu_arquivo.addAction('Salvar')
barra_menu.addMenu('Editar')
barra_menu.addMenu('Exibir')
self.setMenuBar(barra_menu)
if __name__ == '__main__':
app = QApplication([])
janela = JanelaPrincipal()
janela.show()
app.exec_()
3. QDialog
QDialog é usado para criar janelas de diálogo, geralmente modais, para interações específicas do usuário, como exibir mensagens ou solicitar informações.
import sys
from PyQt5.QtWidgets import QDialog, QLabel, QPushButton, QWidget, QApplication
class DialogoSimples(QDialog):
"""
Um diálogo simples com uma mensagem.
"""
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Diálogo")
self.setGeometry(400, 400, 200, 100)
rotulo = QLabel('Mensagem do Diálogo!', self)
rotulo.move(50, 30)
botao_fechar = QPushButton('Fechar', self)
botao_fechar.move(70, 60)
botao_fechar.clicked.connect(self.accept) # Fecha o diálogo
class JanelaPrincipalComDialogo(QWidget):
"""
Janela principal que abre um diálogo.
"""
def __init__(self):
super().__init__()
self.setWindowTitle("Janela Principal")
self.setGeometry(300, 300, 300, 150)
self.dialogo = DialogoSimples(self)
botao_abrir_dialogo = QPushButton('Abrir Diálogo', self)
botao_abrir_dialogo.move(100, 60)
botao_abrir_dialogo.clicked.connect(self.dialogo.show)
if __name__ == '__main__':
app = QApplication(sys.argv)
janela = JanelaPrincipalComDialogo()
janela.show()
app.exec_()
Widgets Comuns
QWidget é a classe base para todos os widgets. Abaixo estão exemplos de widgets frequentemente utilizados:
from PyQt5.QtWidgets import *
class ExemploWidgets(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Exemplo de Widgets")
self.setGeometry(300, 300, 400, 400)
layout_formulario = QFormLayout(self) # Layout para formulários
# Widgets
layout_formulario.addRow("Texto Puro:", QLabel("Exibe texto ou links."))
layout_formulario.addRow("Campo de Texto:", QLineEdit("Texto inicial"))
layout_formulario.addRow("Área de Texto:", QTextEdit("Texto multilinha."))
grupo_radio = QGroupBox("Opções Únicas")
layout_grupo_radio = QVBoxLayout(grupo_radio)
layout_grupo_radio.addWidget(QRadioButton("Opção A"))
layout_grupo_radio.addWidget(QRadioButton("Opção B"))
layout_formulario.addRow(grupo_radio)
grupo_checkbox = QGroupBox("Opções Múltiplas")
layout_grupo_checkbox = QVBoxLayout(grupo_checkbox)
layout_grupo_checkbox.addWidget(QCheckBox("Item 1"))
layout_grupo_checkbox.addWidget(QCheckBox("Item 2"))
layout_grupo_checkbox.addWidget(QCheckBox("Item 3"))
layout_formulario.addRow(grupo_checkbox)
layout_formulario.addRow(QPushButton("Um Botão"))
if __name__ == '__main__':
app = QApplication([])
janela = ExemploWidgets()
janela.show()
app.exec_()
Widgets Personalizados com QPainter
É possível criar widgets completamente personalizados desenhando diretamente neles usando QPainter.
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QColor, QPen, QPolygon, QPoint
from PyQt5.QtCore import QRect, Qt
class RelogioWidget(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Widget Personalizado")
self.resize(300, 300)
def paintEvent(self, event):
"""
Este método é chamado quando o widget precisa ser redesenhado.
"""
painter = QPainter(self)
# Exemplo de desenho de um arco (fatia de pizza)
# QRect(x, y, largura, altura), angulo_inicio, angulo_extensao
rect_desenho = QRect(20, 20, 260, 260)
angulo_inicio = 30 * 16 # 30 graus em 1/16 de grau
angulo_extensao = 150 * 16 # 150 graus em 1/16 de grau
painter.setBrush(QColor(255, 100, 0)) # Cor de preenchimento
painter.setPen(QPen(Qt.black, 2)) # Cor e espessura da linha
painter.drawPie(rect_desenho, angulo_inicio, angulo_extensao)
# Exemplo de desenho de texto
painter.setPen(QColor(0, 0, 255)) # Cor do texto
painter.drawText(150, 150, "Desenho Personalizado")
if __name__ == '__main__':
app = QApplication([])
widget = RelogioWidget()
widget.show()
app.exec_()
Design de UI com Designer e PyQt-tools
O Qt Designer é uma ferramenta gráfica para criar interfaces. O arquivo .ui gerado pode ser convertido para Python com pyuic5 ou carregado dinamicamente.
# Converter UI para Python
pyuic5 seu_arquivo.ui -o seu_arquivo_convertido.py
Carregamento dinâmico:
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5 import uic
class JanelaComUI(QWidget):
def __init__(self):
super().__init__()
# Carrega o arquivo UI diretamente
uic.loadUi("caminho/para/seu_arquivo.ui", self)
self.setWindowTitle("UI Carregada Dinamicamente")
if __name__ == '__main__':
app = QApplication([])
janela = JanelaComUI()
janela.show()
app.exec_()
Exibindo Imagens
Imagens podem ser carregadas diretamente ou incorporadas ao código usando o recurso de arquivos de recusros do Qt.
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from PyQt5.QtGui import QPixmap
class JanelaComImagem(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Exemplo de Imagem")
self.setGeometry(300, 300, 400, 300)
label_imagem = QLabel(self)
pixmap = QPixmap("./icone.png") # Certifique-se que o arquivo existe
if not pixmap.isNull():
label_imagem.setPixmap(pixmap)
label_imagem.resize(pixmap.width(), pixmap.height())
else:
label_imagem.setText("Erro ao carregar imagem.")
if __name__ == '__main__':
app = QApplication([])
janela = JanelaComImagem()
janela.show()
app.exec_()
Para incorporar imagens, crie um arquivo .rcc:
<RCC>
<qresource prefix="/">
<file>./icone.png</file>
</qresource>
</RCC>
Compile-o com pyrcc5:
pyrcc5 seu_recurso.rcc -o recursos_rc.py
E importe no seu código Python:
# from . import recursos_rc # Se no mesmo diretório
from PyQt5.QtWidgets import QLabel, QApplication, QWidget
from PyQt5.QtGui import QPixmap
class JanelaComRecurso(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Imagem de Recurso")
self.setGeometry(300, 300, 200, 200)
label = QLabel(self)
# Carrega a imagem do recurso compilado
pixmap = QPixmap(":/icone.png")
label.setPixmap(pixmap)
if __name__ == '__main__':
app = QApplication([])
janela = JanelaComRecurso()
janela.show()
app.exec_()
Layouts: Organizando Widgets
Layouts gerenciam o posicionamento e o redimensionamento dos widgets dentro de uma janela.
1. Box Layouts (QVBoxLayout, QHBoxLayout)
QVBoxLayout organiza widgets verticalmente e QHBoxLayout horizontalmente.
import sys
from PyQt5.QtWidgets import *
class ExemploBoxLayout(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Box Layouts")
self.setGeometry(300, 300, 350, 250)
# Layout principal vertical
layout_principal = QVBoxLayout(self)
# Grupo de Checkboxes (Vertical)
grupo_esportes = QGroupBox("Esportes Favoritos")
layout_esportes = QVBoxLayout(grupo_esportes)
layout_esportes.addWidget(QCheckBox("Futebol"))
layout_esportes.addWidget(QCheckBox("Basquete"))
layout_esportes.addWidget(QCheckBox("Vôlei"))
# Grupo de Radio Buttons (Horizontal)
grupo_genero = QGroupBox("Gênero")
layout_genero = QHBoxLayout(grupo_genero)
layout_genero.addWidget(QRadioButton("Masculino"))
layout_genero.addWidget(QRadioButton("Feminino"))
layout_genero.addStretch(1) # Espaço flexível
# Adiciona os grupos ao layout principal
layout_principal.addWidget(grupo_esportes)
layout_principal.addWidget(grupo_genero)
layout_principal.addStretch(1) # Empurra os widgets para cima
if __name__ == '__main__':
app = QApplication(sys.argv)
janela = ExemploBoxLayout()
janela.show()
app.exec_()
2. Grid Layout (QGridLayout)
QGridLayout organiza widgets em uma grade bidimensional.
import sys
from PyQt5.QtWidgets import *
class ExemploGridLayout(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Grid Layout")
self.setGeometry(300, 300, 300, 200)
layout_principal = QVBoxLayout(self)
campo_texto = QLineEdit("Campo Principal")
layout_principal.addWidget(campo_texto)
grid_layout = QGridLayout()
botoes = [
'7', '8', '9', '/', 'C',
'4', '5', '6', '*', '.',
'1', '2', '3', '-', '=',
'0', '(', ')', '+', 'CLR'
]
posicoes = [(r, c) for r in range(4) for c in range(5)] # 4 linhas, 5 colunas
for texto, pos in zip(botoes, posicoes):
botao = QPushButton(texto)
# Adiciona o botão na linha 'pos[0]' e coluna 'pos[1]'
grid_layout.addWidget(botao, pos[0], pos[1])
layout_principal.addLayout(grid_layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
janela = ExemploGridLayout()
janela.show()
app.exec_()
3. Form Layout (QFormLayout)
Ideal para formulários, alinha pares de rótulos e campos de entrada.
from PyQt5.QtWidgets import *
class ExemploFormLayout(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Form Layout")
self.setGeometry(300, 300, 300, 200)
layout_principal = QVBoxLayout(self)
form_layout = QFormLayout()
campo_usuario = QLineEdit()
campo_usuario.setPlaceholderText("Digite seu nome de usuário")
campo_senha = QLineEdit()
campo_senha.setPlaceholderText("Digite sua senha")
campo_senha.setEchoMode(QLineEdit.Password) # Esconde a senha
form_layout.addRow("Usuário:", campo_usuario)
form_layout.addRow("Senha:", campo_senha)
checkbox_lembrar = QCheckBox("Lembrar-me")
# Layout horizontal para botões
layout_botoes = QHBoxLayout()
botao_login = QPushButton("Login")
botao_reset = QPushButton("Reset")
layout_botoes.addWidget(botao_login)
layout_botoes.addWidget(botao_reset)
layout_principal.addLayout(form_layout)
layout_principal.addWidget(checkbox_lembrar)
layout_principal.addLayout(layout_botoes)
layout_principal.addStretch(1) # Espaço flexível no final
if __name__ == '__main__':
app = QApplication([])
janela = ExemploFormLayout()
janela.show()
app.exec_()
4. Stacked Layout (QStackedLayout)
QStackedLayout gerencia uma pilha de widgets, mostrando apenas um por vez.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QTimer
class ExemploStackedLayout(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Stacked Layout")
self.setGeometry(300, 300, 300, 200)
stacked_layout = QStackedLayout(self)
# Adiciona widgets à pilha
stacked_layout.addWidget(QLabel("Página 1: Bem-vindo!"))
stacked_layout.addWidget(QLabel("Página 2: Informações"))
stacked_layout.addWidget(QLabel("Página 3: Contato"))
# Timer para alternar páginas a cada segundo
self.timer = QTimer(self)
self.timer.timeout.connect(self.proxima_pagina)
self.timer.start(1000) # Intervalo de 1000ms (1 segundo)
self.pagina_atual = 0
def proxima_pagina(self):
# Avança para a próxima página, voltando ao início se chegar ao fim
self.pagina_atual = (self.pagina_atual + 1) % stacked_layout.count()
stacked_layout.setCurrentIndex(self.pagina_atual)
if __name__ == '__main__':
app = QApplication([])
janela = ExemploStackedLayout()
janela.show()
app.exec_()
Sinais e Slots
Sinais são emitidos quando um evento ocorre (ex: clique de botão), e slots são funções que respondem a esses sinais.
Conectando Sinais e Slots
import sys
from PyQt5.QtWidgets import *
class ExemploSinaisSlots(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Sinais e Slots")
self.setGeometry(300, 300, 250, 100)
botao = QPushButton("Clique Aqui", self)
botao.setGeometry(50, 30, 150, 40)
# Conecta o sinal 'clicked' do botão ao slot 'on_clique'
botao.clicked.connect(self.on_clique)
def on_clique(self):
"""
Este é o slot que será chamado quando o botão for clicado.
"""
print("Botão foi clicado!")
if __name__ == '__main__':
app = QApplication(sys.argv)
janela = ExemploSinaisSlots()
janela.show()
app.exec_()
Criando Sinais Personalizados
Você pode definir seus próprios sinais para comunicação entre objetos.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import pyqtSignal
class EmissorSinal(QWidget):
# Define um sinal que pode emitir uma string
sinal_personalizado = pyqtSignal(str)
def __init__(self):
super().__init__()
self.setWindowTitle("Sinais Personalizados")
self.setGeometry(300, 300, 300, 150)
# Conecta o sinal personalizado ao slot 'processar_dado'
self.sinal_personalizado.connect(self.processar_dado)
botao_emitir = QPushButton("Emitir Sinal", self)
botao_emitir.setGeometry(80, 40, 120, 40)
botao_emitir.clicked.connect(self.emitir)
def emitir(self):
mensagem = "Olá do Sinal!"
print(f"Emitindo sinal com: '{mensagem}'")
# Emite o sinal com a mensagem
self.sinal_personalizado.emit(mensagem)
@staticmethod
def processar_dado(dado):
"""
Slot que recebe os dados do sinal.
"""
print(f"Slot recebeu: '{dado}'")
if __name__ == '__main__':
app = QApplication(sys.argv)
emissor = EmissorSinal()
emissor.show()
app.exec_()