GUIs com Python e PyQt5 — Criando o seu primeiro App

Olá para todos! Como estão? Espero que bem!

Nesse artigo vamos finalmente desenvolver o nosso primeiro App com Python e PyQt5! Caso você queira ver a aula anterior, basta clicar aqui. Ah, os códigos dessa aula estão no meu repositório GitHub, que pode ser acessado aqui. Sem mais delongas, vamos à aula!!

Criar um App

Vamos criar nosso primeiro aplicativo! Para começar, crie um novo arquivo Python?-?você pode chamá-lo como quiser (por exemplo, meu_app.py) e salvá-lo em algum lugar acessível. Vamos escrever o nosso primeiro aplicativo nesse arquivo.

O código-fonte de seu primeiro aplicativo será mostrado no decorrer deste artigo. Digite-o literalmente e tome cuidado para não cometer erros. Se você errar, o Python avisará o que está errado. Se você não quiser digitar tudo, o meu repositório possui os códigos para essa aula, basta baixá-los e estudá-los:

from PyQt5.QtWidgets import QApplication, QWidget

# Necessário somente para acessar os argumetos da linha de comando.
import sys

# Você precisa de uma (e somente uma) instância do QApplication por aplicativo.
# Passe sys.argv para permitir argumentos de linha de comando para seu aplicativo.
# Se você sabe que não usará argumentos de linha de comando, QApplication([]) também funciona.
app = QApplication(sys.argv)

# Crie um widget Qt, que será a nossa tela
tela = QWidget()
tela.show()  # IMPORTANTE!!! As telas ficam ocultas por padrão

# Iniciar o loop de eventos
app.exec_()

# Seu aplicativo não chegará aqui até que saia e o evento tenha parado

Primeiro, inicie seu aplicativo. Você pode executá-lo a partir da linha de comando como qualquer outro script Python, promtp de comando ou da IDLE que estiver usando, por exemplo:

python meu_app.py

Caso o comando acima não funcione, tente:

python3 meu_app.py

Agora você verá sua tela. O Qt cria automaticamente uma tela com as decorações normais de tela e você pode arrastá-la e redimensioná-la como qualquer outra tela:

Figura 1: Tela do app

O que você verá dependerá da plataforma em que estiver executando este exemplo. Nesse caso, a plataforma é Windows 10.

Percorrendo o código

Vamos examinar o código linha por linha para entendermos exatamente o que está acontecendo.

Primeiro, importamos as classes do PyQt5 de que precisamos para o aplicativo. Aqui estamos importando QApplication, o manipulador do aplicativo, e QWidget, um widget básico de GUI vazio, ambos do módulo QtWidgets:

from PyQt5.QtWidgets import QApplication, QWidget

Os principais módulos do Qt são:

  • QtWidgets;
  • QtGui e;
  • QtCore.

Em seguida, criamos uma instância do QApplication, passando sys.arg, que é uma lista Python que contém os argumentos da linha de comando passados para o aplicativo:

app = QApplication(sys.argv)

Se você sabe que não usará argumentos de linha de comando para controlar o Qt, pode passar uma lista vazia, por exemplo:

app = QApplication([])

Em seguida, criamos uma instância de um QWidget passando o nome da variável de tela:

tela = QWidget()
tela.show()

No Qt, todos os widgets de nível superior são telas, ou seja, eles não têm um pai e não estão aninhados em outro widget ou layout. Isso significa que você pode tecnicamente criar uma tela usando qualquer widget que desejar.

Os widgets sem um pai são invisíveis por padrão. Portanto, após criar o objeto tela, devemos sempre chamar .show() para torná-lo visível. Você pode remover o .show() e executar o aplicativo, mas não terá como sair dele!

Por fim, chamamos app.exec_() para iniciar o loop de eventos.

O que é o loop de eventos?

Antes de colocar o aplicativo para rodar, há alguns conceitos-chave a serem introduzir sobre como os aplicativos são organizados no mundo Qt. Se você já está familiarizado com loops de eventos, pode pular com segurança para a próxima seção.

O núcleo de todos os aplicativos Qt é a classe QApplication. Todo aplicativo precisa de um?-?e somente um?-?objeto QApplication para funcionar. Esse objeto contém o loop de eventos do seu aplicativo?-?o loop central que governa toda a interação do usuário com a GUI.

Figura 2: Esquema do loop de evento de Qt

Cada interação com o aplicativo?-?seja o pressionamento de uma tecla, o clique de um mouse ou movimento do mouse?-?gera um evento que é colocado na fila de eventos. No loop de eventos, a fila é verificada em cada iteração e, se um evento em espera for encontrado, o evento e o controle serão passados para o manipulador de eventos específico do evento. O manipulador de eventos lida com o evento e, em seguida, passa o o controle de volta ao loop de eventos para aguardar mais eventos. Há apenas um loop de eventos em execução por aplicativo.

A classe QApplication:

  • O QApplication mantém o loop de eventos do Qt;
  • É necessária apenas uma instância do QApplication;
  • Seu aplicativo fica aguardando no loop de eventos até que uma ação seja executada e;
  • Há apenas um loop de eventos em qualquer momento.

O sublinhado está lá porque exec era uma palavra reservada no Python 2.7. O PyQt5 lida com isso acrescentando um sublinhado ao nome usado na biblioteca C++ . Você também verá métodos .print_() em widgets, por exemplo.

QMainWindow

Como vimos na última parte, no Qt todos os widgets podem ser telas. Por exemplo, se você substituir QtWidget por QPushButton. No exemplo abaixo, você obteria uma janela com um único botão que pode ser pressionado:

from PyQt5.QtWidgets import QApplication, QPushButton
import sys

app = QApplication(sys.argv)

tela = QPushButton('Me aperte')
tela.show()

app.exec_()

Figura 3: Tela gerada pelo código acima

Isso é legal, mas não é muito útil?-?é raro você precisar de uma interface de usuário que consista em apenas um único controle! Mas, como descobriremos mais tarde, a capacidade de aninhar widgets dentro de outros widgets usando layouts significa que você pode construir UIs complexas dentro de um QWidget vazio.

Porém, o Qt já tem uma solução para você: o QMainWindow. Esse é um widget pré-fabricado que fornece muitos recursos de janela padrão que você usará em seus aplicativos, incluindo barras de ferramentas, menus, uma barra de status, widgets acopláveis e muito mais. Veremos esses recursos avançados mais tarde, mas, por enquanto, adicionaremos um simples QMainWindow vazio ao nosso aplicativo:

from PyQt5.QtWidgets import QApplication, QMainWindow
import sys

app = QApplication(sys.argv)

tela = QMainWindow()
tela.show()

app.exec_()

Ao executar o código, você verá a janela principal. Ela tem exatamente a mesma aparência de antes!

Portanto, nossa QMainWindow não é muito interessante no momento. Podemos corrigir isso adicionando algum conteúdo. Se você quiser criar uma tela personalizada, a melhor abordagem é subclasse QMainWindow e, em seguida, incluir a configuração para a janela no bloco init. Isso permite que o comportamento da janela seja autônomo. Podemos adicionar nossa própria subclasse de QMainWindow, chamando-a de TelaPrincipal para manter as coisas simples:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton  # 1


# Subclasse QMainWindow para personalizar a tela principal do seu aplicativo
class TelaPrincipal(QMainWindow):
    def __init__(self):
        super().__init__()  # 2

        self.setWindowTitle('Meu App')

        botao = QPushButton('Me aperte!')

        # Definir o widget central da tela
        self.setCentralWidget(botao)  # 3


app = QApplication(sys.argv)

tela = TelaPrincipal()
tela.show()

app.exec_()

#1?-?Os widgets comuns do Qt são sempre importados do namespace QtWidgets;

#2?-?Devemos sempre chamar o método init da classe super() e;

#3?-?Use .setCentralWidget para colocar um widget no QMainWindow.

Em nosso bloco init, primeiro usamos .setWindowTitle() para alterar o título da nossa janela principal. Em seguida, adicionamos nosso primeiro widget?-?um QPushButton?-?no meio da janela. Esse é um dos widgets básicos disponíveis no Qt. Ao criar o botão, você pode passar o texto que deseja que o botão exiba.

Por fim, chamamos .setCentralWidget() na janela. Essa é uma função específica do QMainWindow que permite que você defina o widget que fica no meio da janela.

Execute o código. Agora você verá sua janela novamente, mas desta vez com o widget QPushButton no meio. Pressionar o botão não fará nada, mas resolveremos isso nas próximas aulas:

Figura 4: Tela gerada pelo código acima

Dimensionamento de janelas e widgets

No momento, a janela pode ser redimensionada livremente?-?se você pegar qualquer canto com o mouse, poderá arrastá-lo e redimensioná-lo para o tamanho que desejar. Embora seja bom permitir que os usuários redimensionem os aplicativos, às vezes você pode querer impor restrições a tamanhos mínimos ou máximos, ou bloquear uma janela em um tamanho fixo.

No Qt, os tamanhos são definidos por meio de um objeto QSize. Ele aceita parâmetros de largura e altura, nessa ordem. Por exemplo, o seguinte criará uma janela de tamanho fixo de 400x300 pixels:

import sys
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton


class TelaPrincipal(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('Meu App')

        botao = QPushButton('Me aperte!')

        self.setFixedSize(QSize(400, 300))  # 1

        self.setCentralWidget(botao)


app = QApplication(sys.argv)

tela = TelaPrincipal()
tela.show()

app.exec_()

#1?-?Definir o tamanho da janela.

Execute o código. Você verá uma janela de tamanho fixo. Tente redimensioná-la, mas isso não funcionará.

Além de .setFixedSize(), você também pode chamar .setMinimumSize().setMaximumSize() para definir os tamanhos mínimo e máximo, respectivamente. Experimente você mesmo!

Conclusão

Nesta seção, abordamos a classe QApplication, a classe QMainWindow, o loop de eventos e fizemos experiências com a adição de um widget simples a uma janela. Na próxima aula, daremos uma olhada nos mecanismos que o Qt fornece para que os widgets e janelas se comuniquem entre si e com seu próprio código.

Por hoje é só! Te vejo no próximo artigo!

Consegue traçar comparativos entre o PySimpleGui, Tkinter (ou Ttk) e este novo que está apresentando?