Desenvolvendo aplicações Python para Symbian OS
Este artigo objetiva-se introduzir o leitor ao desenvolvimento de aplicativos móveis em smartphones (telefones inteligentes) embarcados com o sistema operacional Symbian Série 60 (S60). A plataforma de desenvolvimento abordada neste artigo é o Python S60, por ser rápida, simples e fácil torna-se a escolha ideal para a criação de aplicativos para dispositivos na plataforma Symbian S60.
Desenvolvendo Aplicativos Python para Symbian OS
Introdução à programação Python para celulares Symbian S60 - Parte 02
Construindo aplicativos com PyS60
No topo da tela, você pode observar o título do aplicativo. Esta é parte de nossa aplicação onde setamos o título do aplicativo. Em PyS60, podemos setar um título usando o comando: appuifw.app.title = u"First App!" . Abaixo do título, se encontra a barra de navegação, útil para quando se deseja utilizar abas em seu aplicativo. A área que compõe a maior parte da aplicação corresponde ao corpo do aplicativo e também é considerado como a parte mais importante do mesmo. Nele, podem ser atribuídos diversos tipos de objeto de interface gráfica como:
- Canvas que manipula a parte de gráficos na tela.
- Formulários para construção de listas que abrigam diversos tipos de campos de texto.
- Um objeto do tipo texto que corresponde ao texto puro escrito na tela.
- Listas, caixas de diálogo, etc.
Na parte inferior, você pode observar 2 itens que são ativados por botões dedicados (softkeys da esquerda e direita) do teclado do seu aparelho móvel. Se nenhuma caixa de diálogo estiver presente, o softkey da esquerda ativa o menu da aplicação ('Options'), enquanto o softkey da direita corresponde ao comando de sair ('Exit') da aplicação em execução. Se uma caixa de diálogo estiver em exibição, os softkeys correspondem ao "Aceitar" ('Accept') e "Rejeitar" ('Cancel') respectivamente.
Eventos e callbacks
Funções Assíncronas
Além do conceito de eventos e callbacks descrito acima os módulos que acompanham o PyS60 também possuem várias funções que se comportam de maneira assíncrona, ou seja, elas retornam antes de terminar de executar a sua tarefa. Nesses casos é bastante comum que essas funções executem uma chamada à uma função callback para 'avisar' de que sua tarefa foi concluída.
Abaixo na Listagem 1, iremos falar rapidamente sobre os módulos do PyS60, mas por questão de espaço não poderemos entrar em maiores detalhes sobre cada um deles ou até mesmo sobre suas funções. Para uma visão detalhada de cada um deles eu recomendo uma visita à documentação oficial do Python para S60 que pode ser encontrada no endereço link.
A filosofia do Python diz que a linguagem tem "batteries included" (baterias inclusas) e isso significa que a linguagem sempre deve vir acompanhada de uma biblioteca padrão bastante completa e poderosa. Isso não é diferente no Python para S60 onde temos alguns módulos da biblioteca padrão do Python (apenas uma parcela dos módulos padrões) e mais algumas bibliotecas específicas para o desenvolvimento para S60.
Obviamente, por questões de espaço, não irei descrever ou usar todos os módulos neste tutorial, mas se você deseja obter informações detalhadas sobre o que está disponível para essa plataforma é recomendável dar uma leitura na documentação oficial do PyS60 que pode ser baixada no site do projeto listado na primeira parte deste artigo(disponível em formato PDF).
Para ilustrar o desenvolvimento de uma aplicação Python para S60 iremos utilizar o primeiro exemplo bastante simples de uma aplicação que tira uma foto e a envia via MMS para um número de telefone informado. Todas as aplicações PyS60 podem usar a estruttura abaixo (Listagem 4) para ser desenvolvida:
Listagem 4. Estrutura da Aplicação
1. import e32
2. import appuifw
3.
4. def sai():
5. #envia o sinal para o objeto "trava"
6. trava.signal()
7.
8. #Aqui começa a nossa aplicação
9. # ============================
10.#Cria um objeto "trava" que irá "segurar"
11.#a nossa aplicação rodando
12.trava = e32.Ao_lock()
11.
12.#Atribui uma chamada "callback" para
13.# o método "sai()" quando o usuário
14.#escolher a opção "Sair" no celular.
15.#Obs: Note que a chamada do método "sai()"
16.#não tem parêmteses pois a função não
17.#é executada imediatamente
18.appuifw.app.exit_key_handler = self.sai
19.
20.#Aguarda e segura a execução até que o
21.#objeto "trava" receba um sinal.
22.trava.wait()
23.
Um dos módulos mais interessantes que acompanha o PyS60 é o módulo “camera”. Com ele podemos facilmente acionar a câmera do aparelho móvel, tirar fotografias, manipular fotos, gravá-las no cartão de memória ou até enviá-las para outros celulares. Para tirar uma foto com a câmera basta executar os comandos (Listagem 5):
Listagem 5. Ligando a câmera
A função “.take_photo()” do módulo “camera" irá retornar um objeto do tipo “Image” contendo a imagem fotografada (Linha 2). Para gravar a imagem no cartão de memória basta chamar o método “.save()” deste objeto, passando como parâmetro o caminho do diretório onde a image capturada deve ser armazenada (Note que o caractere “\” precisa ser duplicado para ser reconhecido como “escaping”) (Linha 3).
Como se pode observar é extremamente simples tirar uma foto em Python, mas isso tem um inconveniente: a foto é capturada assim que a função camera.take_photo() é chamada e isso implica que o que está sendo fotografado não aparece na tela do celular, logo o usuário não conseguirá visualizar o que está sendo fotografado. Para que visualizar o que a câmera está fotografando, é necessário acionar o view finder (modo preview) da câmera e desligá-lo imediatamente antes de tirar a fotografia. Então, de volta ao esqueleto da aplicação (Listagem 4), adicionaremos algumas linhas de código a mais (Listagem 6):
Listagem 6. Adicionando o View Finder
1. import e32
2. import appuifw
3.import camera
4.
5. def sai():
6. #envia o sinal para o objeto "trava"
7. trava.signal()
8.
9. def desenha_tela(self,imagem):
10. #Pinta a imagem vista pela câmera na tela
11. canvas.blit(imagem)
12.
13.#Aqui começa a nossa aplicação
14.# ============================
15.#Vamos criar um objeto Canvas.
16.#Objeto Canvas permite a exibição de imagens
17.canvas = appuifw.Canvas()
18.
19.#Cria um objeto "trava" que irá "segurar"
20.#a nossa aplicação rodando
21.trava = e32.Ao_lock()
22.
23.#Define o titulo da aplicacao
24.#O "u" antes da string informa que
25.#o texto está no formato unicode.
26.appuifw.app.title = u"PyFoto"
27.
28.#Vamos definir que o corpo da aplicação
29.#será o objeto Canvas criado acima.
30.appuifw.app.body = canvas
31.
32.#Atribui uma chamada "callback" para
33.# o método "sai()" quando o usuário
34.#escolher a opção "Sair" no celular.
35.#Obs: Note que a chamada do método "sai()"
36.#não tem parêmteses pois a função não
37.#é executada imediatamente
38.appuifw.app.exit_key_handler = sai
39.
40.#Iniciamos o "view finder" que irá
41.#executar desenha_tela()
42.#constantemente onde iremos exibir
43.#a imagem capturada pelo finder no Canvas
44.camera.start_finder(desenha_tela)
45.
46.#Aguarda e segura a execução até que o
47.#objeto "trava" receba um sinal.
48.trava.wait()
Executando esse teste vamos obter a seguinte tela (Figura 3):
Figura 3. View Finder em execução
Agora vamos adicionar uma opção "Tirar foto" ao nosso menu "Opções". Para isso vamos adicionar esse pequeno trecho de código (Listagem 7):
Listagem 7. Adicionando a captura de foto
1. import e32
2. (...)
3.
4.
5. def tira_foto():
6. #Desliga o view finder
7. camera.stop_finder()
8.
9. #Tira a foto e grava em E:\\Images\\foto.jpg
11. foto = camera.take_photo()
12. foto.save("E:\\Images\\foto.jpg")
13.
14. #Religa o view finder
15. camera.start_finder(desenha_tela)
16.
17.#Aqui começa a nossa aplicação
18.# ============================
19.(...)
20.#Após a chamada camera.start_finder()
21.
22.#Cria uma opção "Tirar foto" no menu
23.#Opções do celular que invoca o método
24.#tira_foto() quando acionado.
25.appuifw.app.menu = [(u"Tira foto",tira_foto)]
26.(...)
Agora a foto já pode ser capturada, que será gravada no arquivo E:\Images\foto.jpg (para futuramente enviá-la via MMS) (Figura 4):
Figura 4. Opção “Tirar foto”
Uma observação importante é que a aplicação pode ficar com uma tela branca durante alguns segundos, pois é o tempo necessário para que o interpretador Python grave a foto recém-tirada. Agora com a foto salva, iremos enviá-la via MMS para um número de celular informado. Para isso adicione o código abaixo ao nosso aplicativo (Listagem 8):
Listagem 8. Adicionando o envio de MMS
1. (...)
2. import messaging
3.
4. (...)
5. def tira_foto():
6. #logo depois de foto.save(...)
7.
8. #Solicita o numero do telefone
9. numero_fone = appuifw.query("Numero do telefone","text")
10.
11. #Verifica se o numero foi informado e envia a mensagem
12. if numero_telefone:
13. messaging.mms_send(numero_telefone,u"Foto
14. tirada pelo PyFoto", "E:\\Images\\foto.jpg")
15.
16. (...)
Executando novamente a aplicação e após capturada uma foto, podemos agora informar o número do telefone do destinatário (Figura 05) e visualizar a mensagem depois de entregue (Figura 06) (Lembrando apenas de ter cuidado ao testar o envio do MMS, pois isso poderá implicar em custos associados de acordo com a sua operadora telefônica).
Finalizamos aqui nossa primeira aplicação funcional em Python para celulares Symbian S60. Obviamente, podemos melhorar muitos aspectos da nossa aplicação como exemplo:
- Reduzir o tamanho da imagem antes de enviá-la para economizar custos de tráfego de dados.
- Buscar o número de telefone da nossa lista de contatos.
- Girar a tela para aproveitarmos melhor o espaço para o View Finder.
- Adicionar outras opções de envio (Flickr, Bluetooth, etc.)
Mas fica como exercício para os leitores.
- Loops de controle a fim de controlar a aplicação
- Tempo dinâmico
- Double Buffering (técnica de animação de desenho/pintura de tela)
- Módulo random (Gerador de números aleatórios)
Estrutura de um aplicativo jogo
Quando vamos fazer animações, surgem alguns problemas relacionados aos vários métodos que podem ser utilizados. O método mais simples que podemos imaginar é aquele em que limpamos a tela, desenhamos os objetos, limpamos a tela novamente, desenhamos os objetos nas novas posições, e assim por diante. Este método, porém, tem um grave problema: a tela pisca a cada limpeza. Para contornar este tipo de problema, existem várias técnicas de animação. O PyS60 suporta a mais popular delas, o double buffering.
Listagem 10. Definindo as constantes do jogo
01.import appuifw, e32, random
02.import graphics, key_codes
03.
04.
05.
06.def handle_redraw(rect):
07. global buf, canvas
08. canvas.blit(buf)
09.
10.def handle_event(event):
11. print 'Tecla pressionada.'
12.
13.
14.def quit():
15. global running, trava
16. running = False
17. trava.signal()
18.
19.def newgame():
20. global canvas, buf, running, trava, xcoord, ycoord, game_state, dotx, doty
21.
22. #Obtem o objeto "lock" da aplicação (trava).
23. trava = e32.Ao_lock()
24.
25. #Seta a função quit() como callback do evento ExitKey.
26. appuifw.app.exit_key_handler=quit
27.
28. #Seta a aplicação para ocupar toda a tela.
29. appuifw.app.screen="full"
30.
31. #Cria um novo objeto canvas e seta os respectivos
32. #callbacks (pintura e eventos)
33. canvas=appuifw.Canvas(event_callback=handle_event, /
34. redraw_callback=handle_redraw)
35.
36. #Cria uma nova imagem (buffer)
37. buf=graphics.Image.new((240,320))
38.
39. #Seta corpo da aplicação para o objeto canvas.
40. appuifw.app.body=canvas
41.
42. #A fim de manter na memória o estado do jogo, utilizamos uma matriz
44. #ocupado por uma jogada do Fone, 2 se o quadrado for
45. #ocupado por uma jogada do jogador.
46. game_state=[[0,0,0],[0,0,0],[0,0,0]]
47.
48.
49. #Escrever X ou O requer as coordenadas pré-definidas.
50. xcoord=[32,112,192]
51. ycoord=[65,172,279]
52.
53. #Para mostrar onde o cursor do jogador se posiciona,
54. #Colocamos um ponto vermelho (No início do jogo, ele é
55. #inicializado no meio da matriz)
56. dotx=doty=1
57.
58. #Para manter o registro de qual jogador irá jogar na
61. #O jogador começa o jogo
62. turn=2
63.
64. #Variável para verificar se a partida foi encerrada.
65. gameover=False
66.
67. #Variavel que verifica se a aplicação está em execução
69. running = True
70.
71.
72.#Inicia um novo jogo assim que a aplicação
73.#é inicializada.
74.newgame()
Listagem 11. Adicionando o tratamento de eventos
01.import appuifw, e32, random
02.(...)
03.
04. def is_down(scancode):
05. global keyboard_state
06. return keyboard_state.get(scancode,0)
07.
08. def pressed(scancode):
09. global downs
10. if downs.get(scancode,0):
11. downs[scancode]-=1
12. return True
13. return False
14.
15. def handle_event(event):
16. global downs, keyboard_state
17. if event['type'] == appuifw.EEventKeyDown:
18. code = event['scancode']
19. if not is_down(code):
20. downs 21. keyboard_state 22. elif event['type']==appuifw.EEventKeyUp: 23. keyboard_state[event['scancode']]=0 24. 25. 26.(...) 18. 19.def newgame(): 20. global canvas, buf, running, trava, xcoord, ycoord, game_state, dotx, doty, keyboard_state, downs 21. 22. #Conjunto de variaveis que armazenam o estado das 23. #teclas (clique ou continuamente pressionada). 23. downs={} 24. 25. #Conjunto de variaveis que armazenam o estado das 26. #teclas. 27. keyboard_state={} 28. 29. (...) Pares de chaves e valores são especificados em um dicionário usando a notação: 01.import appuifw, e32, random 02.(...) 03. 04. def drawgrid(): 05. global xcoord, ycoord, game_state, buf, dotx, doty 06. buf.clear() 07. buf.line((80,20,80,300), 0) 08. buf.line((160,20,160,300), 0) 09. buf.line((20,107,220,107), 0) 10. buf.line((20,214,220,214), 0) 11. for i in range(3): 12. for j in range(3): 13. if(game_state[i-1][j-1]==1): 14. buf.text((xcoord[i-1],ycoord[j-1]), u"O", font="title") 15. elif(game_state[i-1][j-1]==2): 16. buf.text((xcoord[i-1],ycoord[j-1]), u"X", font="title") 17. buf.point((xcoord[dotx]+7,ycoord[doty]-10), 0xff0000, width=15) 18. 19.(...) 20. 21.def newgame(): 22. (...) 23. #Após gameOver=false 24. 24. #Variável para verificar se a partida foi encerrada. 25. gameover=False 26. 27. #Pinta a Matriz do Jogo 28. drawgrid() 29. 30. #Variavel que verifica se a aplicação está em execução 32. running = True 33. 34. #Loop de controle que move o cursor vermelho na tela 35. #de acordo com os eventos de teclado 36. while(running==1): 37. #Inicializa o menu 38. appuifw.app.menu=[(u"Novo jogo", newgame), (u"Sair", quit)] 39. 40. if(pressed(key_codes.EScancodeRightSoftkey)): 41. quit() 42. 43. #Quando for a vez do jogador jogar 44. if(turn==2): 48. if(game_state[dotx][doty]==0): 49. buf.text((xcoord[dotx],ycoord[doty]), u"X", / 51. game_state[dotx][doty]=2 52. handle_redraw(()) 53. drawgrid() 54. turn=1 55. if((pressed(key_codes.EScancodeLeftArrow)) and (dotx>0)): 56. dotx-=1 57. handle_redraw(()) 58. drawgrid() 59. if((pressed(key_codes.EScancodeRightArrow)) and (dotx<2)): 60. dotx+=1 61. handle_redraw(()) 62. drawgrid() 63. if((pressed(key_codes.EScancodeUpArrow)) and (doty>0)): 64. doty-=1 65. handle_redraw(()) 66. drawgrid() 67. if((pressed(key_codes.EScancodeDownArrow)) and (doty<2)): 68. doty+=1 69. handle_redraw(()) 70. drawgrid() 71. #Checamos se o jogo foi finalizado. Isto acontece se um dos jogadores 73. gameover=True 74. for i in range(3): 75. for j in range(3): 76. if(game_state[i-1][j-1]==0): 77. gameover=False 78. 79. #Para mostrar que o jogador venceu, sublinha a sequência de símbolos 81 82. if(game_state[0][0]==game_state[0][1]==game_state[0][2]<>0): 83. buf.line(((40,20),(40,300)), 0x33CC00) 84. gameover=True 85. e32.ao_sleep(1) 86. if(game_state[1][0]==game_state[1][1]==game_state[1][2]<>0): 83. buf.line(((120,20),(120,300)), 0x33CC00) 84. gameover=True 85. e32.ao_sleep(1) 86. if(game_state[2][0]==game_state[2][1]==game_state[2][2]<>0): 87. buf.line(((200,20),(200,300)), 0x33CC00) 88. gameover=True 89. e32.ao_sleep(1) 90. if(game_state[0][0]==game_state[1][0]==game_state[2][0]<>0): 91. buf.line(((20,53),(220,53)), 0x33CC00) 92. gameover=True 93. e32.ao_sleep(1) 94. if(game_state[0][1]==game_state[1][1]==game_state[2][1]<>0): 95. buf.line(((20,160),(220,160)), 0x33CC00) 96. gameover=True 97. e32.ao_sleep(1) 98. if(game_state[0][2]==game_state[1][2]==game_state[2][2]<>0): 99. buf.line(((20,267),(220,267)), 0x33CC00) 100. gameover=True 101. e32.ao_sleep(1) 102. if(game_state[0][0]==game_state[1][1]==game_state[2][2]<>0): 103 buf.line(((20,20),(220,300)), 0x33CC00) 104 gameover=True 105 e32.ao_sleep(1) 106. if(game_state[2][0]==game_state[1][1]==game_state[0][2]<>0): 107. buf.line(((20,300),(220,20)), 0x33CC00) 108. gameover=True 109. e32.ao_sleep(1) 110. 111. if(gameover==True): 112. if(appuifw.query(u"Fim de jogo. Jogar de novo?", "query")): 113. newgame() 114. else: 115. quit() 116. 117. #Se for o turno do jogador 'Telefone' e 119. while((turn==1) and (gameover==False)): 120. px=random.choice([0,1,2]) 121. py=random.choice([0,1,2]) 122. if(game_state[px][py]==0): 123. buf.text((xcoord[px],ycoord[py]), u"O", font="title") 124. game_state[px][py]=1 125. handle_redraw(()) 126. drawgrid() 127. turn=2 128. 129. e32.ao_yield() 130. 131.#Inicia um novo jogo assim que a aplicação 132.#é inicializada. 133.newgame() Mas fica como exercício para os leitores.= downs.get(code,0)+1
=1
d = {chave1:valor1, chave2:valor2}
Observe que os pares chave/valor são separados por vírgula. Dentro de cada par a chave é separada do valor por dois pontos e todo o dicionário deve estar envolvido com o sinal de chaves { }.Lembre-se que os pares chave/valor em um dicionário não são organizados por padrão. Assim, se você quiser os dados organizados em uma ordem particular, terá que organizá-los.Os dicionários são instâncias/objetos da classe dict. Mais informações sobre dicionários ver podem ser consultadas neste link.
Marcel Pinheiro Caraciolo
Marcel Pinheiro Caraciolo (caraciol@gmail.com) é mestrando do Curso de Pós Graduação em Ciência da Computação pela Universidade Federal de Pernambuco.
Interessado em Computação Móvel e Computação Inteligente. Possui blog na área de computação móvel há 3 anos: http://mobideia.blogspot.com
Clique aqui para efetuar o login
Caso não tenha um cadastro DevMedia, clique aqui para se cadastrar (gratuito)
Este é um post fechado - Você precisa ter acesso ao post para habilitar os links de download.

download



