Flet: Exemplo 2


Exemplos 2: Referências a controles

Variáveis são referências

Widgets são classes comuns do Python, com suas propriedades e métodos. Para atuar sobre os objetos criados fazemos uma referência a eles, por meio dos nomes de variáveis. Vale lembrar que no Python toda variável é uma refeerência a algum objeto. Por exemplo, no código abaixo, um objeto flet.Text foi atribuído à variável t.

1   import flet as ft
2   
3   def main(page: ft.Page):
4   
5       def mudar_texto(e):
6           t.value="Um texto em azul!"
7           t.color="blue"
8           page.update()
9   
10      t = ft.Text(value="Um texto em vermelho!", color="red", size=20)
11      bt=ft.ElevatedButton("Mudar texto", on_click=mudar_texto)
12      page.add(t,bt)
13
14   ft.app(target=main)
Figura 1

A linha 10 contém o código de inicialização do objeto Text onde atribuimos valor a duas propriedades, respectivamente value (o texto que será exibido) e color (a cor da fonte usada). Com um clique no botão bt as propriedades t.value e t.color são alteradas, usando a variável como referência ao objeto. A figura 1 mostra o estado inical do aplicativo, com o texto em vermelho, e após o clique no botão Mudar texto.

Obs.: para exibir t e bt na mesma linha podemos inserir uma linha na página contendo os dois widgets: page.add(ft.Row([t,bt])). Nesse caso o controle Row (em flet.Row(<lista-de-controles>))) serve de container para os controles na lista <lista-de-controles>.

Relembrando: t é uma referência à uma instância da classe Text.

Vimos que page recebe uma lista (de controles) no parâmetro controls. Essa lista pode ser tratada como qualquer outra lista do Python. Por exemplo, ela possui o método pop(n).

1   for i in range(6):
2       page.controls.append(ft.Text(f"Exibindo a linha {i}"))
3       page.update()
4
5   page.controls.pop()
6   page.update()
Figura 2

Esse código insere e exibe 6 linhas de texto na página. Em seguida ela exclui a última linha com page.controls.pop(). Assim com o fazemos com outras listas, page.controls.pop(n) excluiria a n-ésima linha.

A figura 2 mostra as 5 linhas restantes após a ação de page.controls.pop().

Referências para controles com a Classe Ref

Outro exemplo explora a mesma possibilidade manipulação das variáveis como referências aos objetos de controle. Os objetos TextField são criados com seus textos vazios, preenchidos pelo usuário, usados para inserir a linha 9de cumprimento com o clique do botão. O comando page.update() atualiza na tela apenas as campos que foram alterados. A propriedade autofocus=True na linha 4 garante que o cursor voltará para o objeto nome após a execução da função fala_oi.

1   import flet as ft
2
3   def main(page):
4       nome = ft.TextField(label="Nome", autofocus=True)
5       sobrenome = ft.TextField(label="Sobrenome")
6       coluna_ola = ft.Column()
7   
8       def fala_oi(e):
9           coluna_ola.controls.append(ft.Text(f"Olá, {nome.value} {sobrenome.value}!"))
10          nome.value = ""
11          sobrenome.value = ""
12          page.update()
13          nome.focus()
14    
15      page.add(
16          nome,
17          sobrenome,
18          ft.ElevatedButton("Diga oi!", on_click=fala_oi),
19          coluna_ola,
20      )
21
22  ft.app(target=main)

Em códigos mais longos e complexos um número maior de controles é adicionado, juntamente com suas propriedades e funções que manipulam seus eventos. Fica cada vez mais difícil, para o programador, se lembrar de todas as definições de nomes, por mais organizado que ele seja. A inserção de controles na página, efetuada nas linhas 15 a 20 não deixa muito clara a estrutura de controles inseridos.

Todos os controles do Flet possuem a propriedade ref. De acordo com os propgramadores do Flet esse é um conceito inspirado no React.

Para isso o Flet incluiu uma classe utilitária chamada Ref, usada para fazer uma referência a um controle que pode ser usada em gerenciadores de eventos (event handlers) e, posteriormente, ser conectada a um controle real quando se constroi a árvore do aplicativo.

Para demontrar o uso dessa classe vamos alterar o código do exemplo acima usando Refs. Listamos abaixo como definir uma nova referência, como usar essa referência para inserir valores e, finalmente, como atribuir um controle real a essa referência, aproveitando todas as suas propriedades já definidas.

1   # criar uma referência a um TextField (por exemplo)    
2   nome = ft.Ref[ft.TextField]()
3   
4   # acessar o controle referenciado
5   nome.current.value = "José Ninguém"
6   
7   # atribuindo a referência um controle real, que pode ser incluído na página
8   page.add(
9       ft.TextField(ref=nome, label="Nome", autofocus=True)
10   )

Vemos que objetos da classe Ref possuem a propriedade current que, por sua vez, contém as mesmas propriedades e métodos do pbjeto referenciado. Para ligar essa referência a um controle utilizamos a propriedade Control.ref do controle. Com essas definições a aplicativo pode ser escrito como:

1   import flet as ft
2   
3   def main(page):
4       nome = ft.Ref[ft.TextField]()
5       sobrenome = ft.Ref[ft.TextField]()
6       coluna_ola = ft.Ref[ft.Column]()
7   
8       def fala_oi(e):
9           coluna_ola.current.controls.append(
10              ft.Text(f"Olá, {nome.current.value} {sobrenome.current.value}!")
11          )
12          nome.current.value = ""
13          sobrenome.current.value = ""
14          page.update()
15          nome.current.focus()
16   
17      page.add(
18          ft.TextField(ref=nome, label="Nome", autofocus=True),
19          ft.TextField(ref=sobrenome, label="Sobrenome"),
20          ft.ElevatedButton("Diga oi!", on_click=fala_oi),
21          ft.Column(ref=coluna_ola),
22      )
23
24  ft.app(target=main)
Figura 3

Observe que, fazendo isso, a estrutura da árvore de controles se torna mais claramente visível quando os controles são adicionados à página, na linha 17 e seguintes.

A figura 3 mostra os campos preenchidos e o resultado do clique no botão Diga oi.

Interação com o usuário

Claro que a maioria dos aplicativos demandam algum tipo de interação com o usuário, seja para escolher um ítem de busca, inserir novos dados ou escolher entre opções. Os exemplos acima usam de flet.TextField para a inserção de textos e ft.ElevatedButton para receber cliques e acionar uma função. Claro que existem muitas outras formas de interação que podem ser simples como um clique em botão ou sofisticadas quanto capturar a posição na tela de um clique, dentro de algum objeto desenhado na página.

Caixas de checagens e “dropdowns”

Vimos vários exemplos de uso das caixas de texto puro flet.Text(“Texto a exibir!”), usadas para exibir texto, e caixas interativas flet.TextField onde o usuário pode digitar dados e inserí-los no aplicativo.

As caixas de checagem, flet.Checkbox são úteis quando a escolha de dados é binária, geralmente sim/não.

1   import flet as ft
2   
3   def main(page):
4       def checkbox_mudou(e):
5           texto_exibido.value = f"A caixa de checagem está  {'marcada' if check.value else 'desmarcada'}."
6           page.update()
7
8       texto_exibido = ft.Text()
9       check = ft.Checkbox(label="Clique na caixa de checagem...", value=False, on_change=checkbox_mudou)
10      page.add(check, texto_exibido)
11   
12  ft.app(target=main)
Figura 4

Esse código produz o resultado mostrado na figura, dependendo da caixa estar marcada ou não. O evento capturado nesse caso foi o flet.Checkbox.on_change que envia o evento para a função selecionada. Na função é lida a propriedade booleana check.value. A figura 4 mostra os dois estados possíveis do aplicativo.

Foi usado o operador trinário valor_se_verdadeiro if condição else valor_se_falso.

Dropdowns, flet.Dropdown são úteis quando várias escolhas de dados estão disponíveis. O conjunto permitido de escolhas pode ser inserido durante o desenvolvimento ou alterado pelo usuário.

1   import flet as ft
2
3   def main(page: ft.Page):
4       def dropdown_mudou(e):
5           escolhido.value = f"Você afirmou que prefere {dpd_linguagem.value}"
6           page.update()
7   
8       escolhido = ft.Text()
9       itens_escolher = [
10               ft.dropdown.Option("Python"),
11               ft.dropdown.Option("Java"),
12               ft.dropdown.Option("Javascript"),
13               ft.dropdown.Option("C, C+"),
14               ft.dropdown.Option("Rust"),
15               ft.dropdown.Option("Ruby"),
16               ]
17       dpd_linguagem = ft.Dropdown(
18           label="Linguagem de programação",
19           width=100,
20           options=itens_escolher,
21           on_change=dropdown_mudou,
22       )
23       page.add(dpd_linguagem, escolhido)
24   
25   ft.app(target=main)
Figura 5

Observe que flet.Dropdown.options é uma lista de objetos dropdown.Option(“nome”). O resultado está mostrado na figura 5.

Além do evento change, as caixas dropdown também podem responder a eventos on_blur, disparado quando o controle perde o foco, e on_focus, quando o controle recebe o foco.

Os ítens na caixa de seleção podem ser inseridos ou apagados dinamicamente, em tempo de execução, como mostramos no código seguinte.

1   import flet as ft
2   
3   def main(page: ft.Page):
4       def inserir(e):
5           if txt_insere.value:
6               dp_box.options.append(ft.dropdown.Option(txt_insere.value))
7               dp_box.value, txt_insere.value = txt_insere.value, ""
8               page.update()
9
10      def apagar(e):
11          for item in dp_box.options:
12              if item.key == dp_box.value:
13                  dp_box.options.remove(item)
14          page.update()
15
16      dp_box = ft.Dropdown(width=600)
17      txt_insere = ft.TextField(hint_text="Nome a inserir")
18      bt_insere = ft.ElevatedButton("Inserir", on_click=inserir)
19      bt_apaga = ft.OutlinedButton("Apagar item exibido", on_click=apagar)
20      page.add(dp_box, ft.Row(controls=[txt_insere, bt_insere, bt_apaga]))
21   
22  ft.app(target=main)
Figura 6

Na linha 6 um item é inserido na caixa dropdown. dp_box.options é uma lista que pode ser acrescida de elementos com append. Essa lista é composta de objetos flet.dropdown.Option(texto). A operação de apagar procura por um item nessa lista e o remove, caso encontrado. A propriedade item.key extrai o texto de cada componente da lista. O texte feito na linha 12 é necessário porque tentar remover um item inexistente de uma lista resulta em erro. O aplicativo executado exibe algo como o representado na figura 6.

Capturando cliques e “hovers”

Um exemplo de captura da posição do cursor do mouse no momento do clique pode ser visto na página Layout do Flet: View e Container na seção Container: Eventos onde um clique dentro de um container dispara um evento para exibir as posições do ponto clicado, relativo ao container e à página.

No exemplo abaixo as ações são iniciadas pelo evento flet.Container.on_hover, disparadas quando o cursor do mouse entra ou sai da área delimitada pelo controle. O evento e passado para a função limpar carrega a propriedade e.data == "true" se o cursor entrou na área, e e.data == "false" se saiu dela. Na saída uma cor é escolhida eleatoriamente entre as cores do dicionário cores.

1   import flet as ft
2   import random
3
4   def main(page: ft.Page):
5       cores = {0:"#000000", 1:"#1D74FF", 2:"#C51709", 3:"#FFE000", 4:"#00CB4F",
6                5:"#A866DC", 6:"#FF6600", 7:"#aaffff", 8:"#145375", 9:"#a4b3c5"}
7
8       def limpar(e):
9           page.controls.pop()
10          page.add(matriz())
11          page.update()
12
13      def on_hover(e):
14          if e.data == "true":
15              e.control.bgcolor = "#efefef"
16          else:
17              e.control.bgcolor = cores[random.randrange(10)]
18          e.control.update()
19
20      def matriz():
21          matr = ft.Column()
22          for i in range(10):
23              lin = []
24              for j in range(10):
25                  lin.append(ft.Container(width=50, height=50,
25                             border=ft.border.all(1, "#293B4A"),
27                             border_radius=ft.border_radius.all(10),
28                             bgcolor="#F1EBC5", on_hover=on_hover)
29                  )
30              linha = ft.Row(lin)
31              matr.controls.append(linha)
32          return matr
33
34      tit=ft.Text("Passe o cursor sobre os quadrados", size=18, weight=ft.FontWeight.BOLD)
35      bt_limpar=ft.ElevatedButton("Limpar tudo", icon="arrow_circle_left",
36                bgcolor="white", width=150, height=30, on_click=limpar)
37      page.add(ft.Row([tit,bt_limpar]), matriz())
38
39  ft.app(target=main)

 

Figura 7

O resultado do código está representado na figura 7, após o cursor ter sido passado sobre o objeto matriz(), que é uma coluna.

A construção da matriz de controles container é feita através da função matriz() que retorna 10 linhas, cada uma com 10 containeres, todos respondendo à container.on_hover com a mesma função, que altera a cor de fundo desse objeto.

O laço iniciado na linha 24 constroi um array com 10 objetos Containers (os quadrados). Esse array é inserido em uma linha flet.Row na linha 30. Essas 10 linhas são inseridas na coluna matr na linha 31.

O botão Limpar tudo remove o último controle inserido na página com page.controls.pop(), sendo que esse controle é a matriz de quadrados coloridos. Em seguida ele repõe novos quadrados com as cores originais.

Leave a Reply

Your email address will not be published. Required fields are marked *