Ir para o conteúdo

Tutorial 2 - Tornar a coisa interessante

No Tutorial 1, geramos um projeto inicial que era capaz de executar, mas não escrevemos nenhum código por conta própria. Vamos observar o que foi criado para nós.

O que foi gerado

No diretório src/helloworld, deverá ver 3 ficheiros: __init__.py, __main__.py e app.py.

__init__.py marca o diretório helloworld como um módulo Python importável. Trata-se de um ficheiro vazio; o facto de existir indica ao interpretador Python que o diretório helloworld define um módulo.

O ficheiro __main__.py marca o módulo helloworld como um tipo especial de módulo - um módulo executável. Se você tentar executar o módulo helloworld usando python -m helloworld, o ficheiro __main__.py é onde o Python vai iniciar a execução. O conteúdo do __main__.py é relativamente simples:

from helloworld.app import main

if __name__ == "__main__":
    main().main_loop()

Este ficheiro faz duas coisas:

  • Importa o método main da aplicação helloworld.
  • Depois arranca o ciclo principal da aplicação. O ciclo principal é a maneira que uma aplicação GUI espera por entradas do utilizador (tais como cliques do rato e atividades no teclado).

O ficheiro mais interessante é o app.py - este contém a lógica que cria a janela da nossa aplicação:

import toga
from toga.style.pack import COLUMN, ROW

class HelloWorld(toga.App):
    def startup(self):
        main_box = toga.Box()

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

def main():
    return HelloWorld()

Vamos ver isto linha por linha:

import toga
from toga.style.pack import COLUMN, ROW

Primeiro, importamos a kit de ferramentas de widgets toga, juntamente com constantes de utilidade relacionada com estilo. O nosso código ainda não uso estas - mas em breve vamos utilizá-las.

Em seguida, definimos uma classe:

class HelloWorld(toga.App):

Cada aplicação Toga possui uma única instância chamada toga.App, que representa a entidade em execução que é a aplicação. A aplicação pode acabar gerindo várias janelas; mas para aplicações simples, haverá uma janela principal única.

Depois, definimos um método chamado startup():

def startup(self):
    main_box = toga.Box()

A primeira coisa que o método startup() faz é definir uma caixa principal. O esquema de disposição do Toga comporta-se de maneira semelhante ao HTML. A construção de uma aplicação faz-se criando uma coleção de caixas, cada um contendo outras caixas ou as widgets propriamente dito. Depois, aplica-se estilos a essas caixas para definir como vão usar o espaço disponível na janela.

Nesta aplicação, definimos uma única caixa, mas não colocamos nada dentro dela.

Em seguida, definimos uma janela na qual podemos colocar esta caixa vazia:

self.main_window = toga.MainWindow(title=self.formal_name)

Isto cria uma instância de toga.MainWindow, que terá um título correspondente ao nome da aplicação. A Main Window (Janela Principal) é um tipo especial de janela no Toga - é uma janela que está intimamente vinculada ao ciclo de vida da aplicação. Quando a Janela Principal é fechada, a aplicação termina. A Janela Principal é também a janela que contém o menu da aplicação (se você estiver numa plataforma como o Windows, onde as barras de menu fazem parte da janela).

Onde está a minha janela?

Se tiver cometido um erro no seu código, a janela principal da aplicação poderá não ser mostrada. Se isso acontecer, pode teclar Ctrl+C no terminal onde iniciou a aplicação. Isto vai interromper a aplicação. Pode depois corrigir o erro e reiniciar a aplicação.

Então, adicionamos a nossa caixa vazia como o conteúdo da janela principal, e instruímos a aplicação a mostrar a nossa janela:

self.main_window.content = main_box
self.main_window.show()

Por último, definimos uma função main(). Isto é o que cria a instância da nossa aplicação:

def main():
    return HelloWorld()

O método main() é aquele que é importado e invocado pelo ficheiro __main__.py. Ele cria e retorna uma instância da nossa aplicação HelloWorld.

Essa é a aplicação Toga mais básica possível. Vamos agora integrar o nosso conteúdo na aplicação, e pô-la a fazer algo mais interessante.

Adicionar algum conteúdo de si próprio

Vamos fazer algo mais interessante com a nossa aplicação HelloWorld.

Nota

Quando fizer estas alterações, certifique que mantém as importações no topo do ficheiro, e o main() no fundo do ficheiro. Só precisa de atualizar a classe HelloWorld.

Modifique a sua classe HelloWorld dentro de src/helloworld/app.py para que fique como isto:

class HelloWorld(toga.App):
    def startup(self):
        main_box = toga.Box(direction=COLUMN)

        name_label = toga.Label(
            "O seu nome: ",
            margin=(0, 5),
        )
        self.name_input = toga.TextInput(flex=1)

        name_box = toga.Box(direction=ROW, margin=5)
        name_box.add(name_label)
        name_box.add(self.name_input)

        button = toga.Button(
            "Diz Olá!",
            on_press=self.say_hello,
            margin=5,
        )

        main_box.add(name_box)
        main_box.add(button)

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()

    def say_hello(self, widget):
        print(f"Olá, {self.name_input.value}")

Vamos ver em detalhe o que foi mudado.

Ainda estamos a criar a caixa principal; porém, agora estamos a aplicar um estilo:

main_box = toga.Box(direction=COLUMN)

O sistema de disposição embutido do Toga é chamado "Pack". Comporta-se muito como o CSS. Você define objetos numa hierarquia - em HTML, os objetos são <div>, <span>, e outros elementos de DOM; em Toga, são widgets e caixas. Pode atribuir estilos aos elementos individualmente. Neste caso, estamos a indicar que isto é uma caixa 'COLUMN' - isto é, é uma caixa que vai consumir toda a largura disponível, e vai expandir a sua altura à medida que conteúdo é adicionado, mas vai tentar ser o mais curto possível.

Nota

Para utilizações mais avançadas, o Toga também suporta um objeto de estilo separado, que é usado desta forma:

from toga.style import Pack
main_box = toga.Box(style=Pack(direction=COLUMN))

A seguir, definimos um par de widgets:

name_label = toga.Label(
    "O seu nome: ",
    margin=(0, 5),
)
self.name_input = toga.TextInput(flex=1)

Aqui, definimos uma Etiqueta e uma Entrada-de-texto. Ambos os widgets possuem estilos associados; a Etiqueta terá 5px de preenchimento à esquerda e à direita, e nenhuma margem no topo nem fundo. A Entrada-de-texto é marcada como flexível - isto é, vai absorver todo o espaço disponível no seu eixo de disposição.

O TextInput é atribuído como uma variável de instância da classe. Isso da-nos acesso fácil à instância do widget - algo que vamos utilizar em breve.

Após isso, definimos uma caixa para conter esses dois widgets:

name_box = toga.Box(direction=ROW, margin=5)
name_box.add(name_label)
name_box.add(self.name_input)

A name_box é uma caixa como a caixa principal; no entanto, agora é uma caixa ROW. Isto significa que o conteúdo será adicionado na horizontal e vai tentar tornar a sua largura o mais estreita possível. A caixa também tem alguma margem - 5px em todos os lados.

Agora definimos um botão:

button = toga.Button(
    "Diz Olá!",
    on_press=self.say_hello,
    margin=5,
)

O botão também tem 5px de margem em todos os lados. Também definimos um handler - um método a invocar quando o botão é pressionado.

Então, adicionamos o nome da caixa e do botão à caixa principal:

main_box.add(name_box)
main_box.add(button)

Isto completa a nossa disposição; o resto do código do método de arranque permanece como antes - definindo uma MainWindow (Janela principal), e atribuindo a caixa principal como o conteúdo da janela:

self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = main_box
self.main_window.show()

A última coisa que precisamos fazer é definir o manuseador para o botão. Um manuseador pode ser qualquer método, gerador ou co-rotina assíncrona; ele aceita o widget que gerou o evento como um argumento, e vai ser invocado sempre que o botão for pressionado:

def say_hello(self, widget):
    print(f"Olá, {self.name_input.value}")

O corpo do método é uma simples instrução de escrita - no entanto, ele vai interrogar o valor atual da entrada de nome, e vai usar esse conteúdo como o texto a ser escrito.

Agora que fizemos estas alterações, podemos visualizar o resultado iniciando outra vez a aplicação. Como antes, vamos utilizar o modo de desenvolvedor:

(beeware-venv) $ briefcase dev

[helloworld] A iniciar no modo de desenvolvimento...
===========================================================================
(beeware-venv) $ briefcase dev

[helloworld] A iniciar no modo de desenvolvimento...
===========================================================================
(beeware-venv) C:\...>briefcase dev

[helloworld] A iniciar no modo de desenvolvimento...
===========================================================================

Vai repara que desta vez, não são instaladas dependências. O Briefcase consegue detetar que a aplicação já foi aberta antes e, para poupar tempo, apenas corre a aplicação. Se adicionar novas dependências à sua aplicação, precisa garantir que elas são instaladas passando uma opção -r quando correr briefcase dev.

Isto deve abrir uma janela GUI:

Janela Hello World Tutorial 2, em macOS](../images/macOS/tutorial-2.png)

Janela Hello World Tutorial 2, em Linux](../images/linux/tutorial-2.png)

Janela Hello World Tutorial 2, em Windows](../images/windows/tutorial-2.png)

Se escrever um nome na caixa de texto e clicar no botão da GUI, vai ver uma mensagem aparecer na consola onde abriu a aplicação.

Antes de continuar, feche a aplicação. Assim como no Tutorial 1, pode fazer isso ao pressionar o botão de fechar na janela da aplicação, ao selecionar Sair/Terminar no menu da aplicação ou teclando Ctrl+C no terminal em que executou briefcase dev.

Próximos passos

Agora temos uma aplicação que faz coisas um pouco mais interessantes. Mas apenas corre no nosso computador. Vamos empacotar esta aplicação para distribuição. No Tutorial 3, vamos envolver a nossa aplicação num instalador independente, que poderemos enviar a amigos, clientes ou enviar para uma Loja de Aplicações.