Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login

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.

Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Para efetuar o download você precisa estar logado. Clique aqui para efetuar o login
Confirmar voto
0
 (0)  (0)

Desenvolvendo Aplicativos Python para Symbian OS

Introdução à programação Python para celulares Symbian S60 - Parte 02

O desenvolvimento de aplicações móveis tem crescido fortemente devido ao aumento do número de aparelhos móveis, os quais tem trazido novas funcionalidades, inovações e além de oferecer uma nova variedade de serviços. Essa tendência foi mais acentuada ainda com a chegada de novos aparelhos móveis: os smartphones, que unem o poder dos PDAs (Personal Digital Assistant ou Assistente Pessoal Digital) com os celulares. Esses dispositivos são embarcados com diversos sistemas operacionais: Linux, Symbian, Windows Mobile dentre outros.  No que diz respeito a linguagens de programação, existe um universo muito maior, como exemplo as linguagens Brew, C++, JavaME, FlashLite e Python.
 
Um dos sistemas operacionais que tem predominado no mercado de smartphones é o Symbian OS. Ele foi desenvolvido para esse perfil de aparelhos celulares, que agrega mais recursos comparado aos comuns. Além de ser um sistema multitarefa com uma interface gráfica intuitiva, ele possui várias plataformas para desenvolvimento de aplicativos móveis.  Dentre elas, vale destacar a plataforma Symbian C++, a qual é uma variação da linguagem C++ usada também no desenvolvimento do sistema operacional e tem acesso a diversos recursos do celular. Outro exemplo é a plataforma Java, que é representada por uma Java Virtual machine compatível com JavaME/MIDP.  Todas essas plataformas de desenvolvimento de aplicações para um aparelho Symbian possuem várias vantagens e desvantagens relacionadas ao grau de rapidez, acesso a recursos do aparelho e complexidade da linguagem de programação.
 
A maior dificuldade encontrada foi a necessidade de um estudo prévio aprofundado dessas plataformas e suas particularidades. Isso se tornou dispendioso, já que o foco do desenvolvimento de aplicativos móveis deveria ser em aprendizado fácil e produção rápida de aplicativos.  A Nokia, então, ciente dessa necessidade trouxe uma linguagem que oferecesse aos desenvolvedores um meio que pudesse implementar aplicações úteis sem a obrigação de gastar meses aprendendo Java ou C++, além dos detalhes técnicos envolvidos no sistema operacional Symbian.  O resultado foi o Python para S60 (PyS60), uma linguagem de script portada  para celulares Symbian OS.
 

A linguagem além de ser bastante simples, através da rápida contrução de programas, oferece novas oportunidades ao desenvolvimento de aplicativos móveis. Não somente os iniciantes na plataforma irão se beneficiar dessa oportunidade, como também os desenvolvedores experientes que poderão usufruir desse meio para desenvolvimento ágil e poderoso de aplicativos móveis.
 
Neste artigo, que é o segundo de uma série que começou com a introdução à plataforma Python para Série 60 e a descrição de instalação e configuração do ambiente de  desenvolvimento, iremos explorar alguns recursos disponibilizados pela plataforma PyS60 através de simples exemplos de aplicações a fim de ilustrar os conceitos aqui apresentados.

Construindo aplicativos com PyS60

Para ilustrar o desenvolvimento de uma aplicação Python para S60 iremos utilizar alguns exemplos bastante simples de aplicações que explorem os recursos avançados da plataforma PyS60. O primeiro exemplo consiste de uma aplicação que captura fotos e os envia via MMS para um número de telefone informado.  O segundo exemplo consiste do clássico jogo da velha (tic-tac-toe) a fim de demonstrar a possibilidade de construção de aplicativos que utilizam gráficos e eventos de teclado suportados pela plataforma Python para S60. Porém, antes de explorarmos o código, é preciso introduzir alguns conceitos que serão utilizados no decorrer do desenvolvimento dos aplicativos.
 
Estrutura de um aplicativo
 
Diversos aplicativos disponíveis para o Symbian S60 compartilham do mesmo layout de interface gráfica. Basta abrir alguns aplicativos existentes no seu aparelho (inclusive o interpretador do PyS60),  para perceber que eles possuem a mesma estrutura no quesito de interface gráfica. A figura 01 ilustra a estrutura de um aplicativo utilizando o framework de interface gráfica do aparelho S60. Observando o diagrama na figura 02, pode-se comparar como essa estrutura é mapeada em um aplicativo real desenvolvido com este framework.


                                                                           
                 Figura 01: Estrutura do aplicativo                        Figura 02: Aplicativo com S60 UI
 

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.

Em PyS60, você pode acessar os elementos de interface gráfica através do objeto app que faz parte do módulo appuifw. Modificá-los é bastante simples:  como cada elemento é uma variável dentro do objeto appuifw.app, basta atribuir a um valor desejado, da mesma maneira que uma atribuição de uma varíável qualquer (Ex: appuifw.app.exit_key_handler= sai ).  Além do módulo appuifw,  há o módulo e32 que é responsável pela manipulação de objetos e funções nativas relacionadas ao sistema operacional Symbian OS (Ex: locks, threads, etc.). No decorrer deste artigo, iremos utilizar de alguns objetos deste módulo, que serão descritos conforme forem aparecendo.

Eventos e callbacks

Para facilitar a vida do programador no desenvolvimento de aplicações que consumam poucos recursos os módulos Python específicos para a plataforma S60 usam e abusam do conceito de eventos e callbacks. Esse conceito é simples: a aplicação fica "dormindo" até que um evento ocorra (tecla pressionada, sinal de relógio, etc) e dispare uma função pré-determinada (callback).
 

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.

 
 
Listagem 1. Módulos específicos do PyS60
e32  Este módulo fornece acesso às funções específicas do sistema operacional Symbian que não possuem relação direta com a interface com o usuário. Neste módulo você irá encontrar funções que retornam a versão do PyS60, se seu programa está rodando no emulador, a lista de todos os drives disponíveis e funções e objetos que lidam com locks, threads, etc. sysinfo  Este módulo fornece funções que retornam dados do aparelho tais como qual o perfil escolhido (geral, reunião, silencioso, ...), o estado da carga da bateria, tamanho da tela, espaço livre em disco, IMEI, potência do sinal da rede telefônica, tipo de toque, informações sobre a memória do aparelho, versão do firmware, etc. appuifw  Neste módulo você irá encontrar tudo o que tem relação com a interface gráfica com o usuário (GUI). É um dos módulos mais importantes do PyS60. graphics  Módulo com funções gráficas para manipulação de imagens, desenho de primitivas, impressão de textos em imagens, funções para tirar screenshots, etc. Esse módulo tem total interoperabilidade com os módulos camera e appuifw. camera  Um dos módulos mais interessantes do PyS60 por sua facilidade de uso. Este módulo disponibiliza funções para manipular a(s) câmera(s) do celular permitindo que se tire fotografias ou que se grave vídeos com elas. gles  Biblioteca que fornece uma API compatível com OpenGL/ES para desenho de gráficos 3D com aceleração (alguns dispositivos da S60 possuem um chip para aceleração gráfica 3D). sensor  Este módulo dá acesso aos sensores de aceleração, rotação e tapping (bater com o dedo na tela do celular aciona esse sensor). Vale lembrar que apenas alguns modelos de celulares S60 dispõem desses sensores. audio  Esse módulo permite a manipulação total do sistema de áudio do aparelho. Com ele é possível manipular tanto o auto-falante externo (tocando um MP3, por exemplo) quanto o áudio de uma ligação telefônica (emitir um som no meio de uma conversa ou até mesmo gravá-la). telephone  Funcionalidades de telefonia tais como fazer uma ligação ou atender à uma chamada estão neste módulo. messaging  Esse módulo tem as funções responsáveis pelo envio de SMS e MMS. inbox, contacts, calendar  Manipulam respectivamente a caixa de entrada de mensagens (SMS/MMS), os contatos da agenda e os eventos de calendário. Esses módulos são extremamente poderosos. location, positioning  Módulos de localização que utilizam respectivamente os dados da rede GSM e dados do GPS (interno ou externo) do aparelho. e32db  Mini banco de dados relacional que permite manipulação utilizando SQL (será substituído pelo SQLite em versões futuras do PyS60). socket  Módulo que já acompanha o Python e recebeu adições para suportar conexões via Bluetooth.
 
Desenvolvendo o primeiro aplicativo - FotoPy

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.

 
Esta primeira aplicação não fará nada de útil. A única coisa que foi codificada até agora foi a  implementação da função “.sai()” (Linha 4)  onde programamos a opção “Sair” ('Exit')  da aplicação utilizando um “ActiveObjectLock” do Symbian. Este objeto “trava” (“ActiveObjectLock” - Ao_lock') (Linha 12) é  necessária, pois as aplicações no universo Symbian são desenvolvidas para trabalharem no modelo assíncrono, ou seja, as funções retornam imediatamente após serem chamadas antes mesmo de terem concluído as suas tarefas. Isso acarretaria na saída súbita da aplicação sem que o usuário consiga enxergar a aplicação sendo executada. Por isso, utiliza-se a chamada à função wait() (Linha 22) que coloca a aplicação em modo de espera (a aplicação continua em execução) até que o objeto "trava" receba um sinal pela chamada da função signal() (Linha 6). Esa função deve ser chamada quando o usuário deseja finalizar a aplicação, por isso a sua chamada apenas dentro da função sai().  Muitas funcionalidades do PyS60 também são implementadas usando o modelo de callback, ou seja, o programador associa funções a eventos e quando esses eventos ocorrem, a função apropriada é invocada. Na nossa aplicação o método “.sai()” é executado sempre que o evento exit_key (Linha 18) for disparado e o mesmo ocorre quando pressionamos o softkey direito do aparelho móvel.
 

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

  1. import camera
  2. foto = camera.take_photo()
  3. foto.save(("E:\\Images\\foto.jpg")
 

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).


                                                  

             Figura 05: Solicitando o número                               Figura 06: Mensagem recebida
 

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.

 
Desenvolvendo o segundo aplicativo - TicTacToePy (Jogo da Velha)
 
O último exemplo de aplicativo deste artigo é o desenvolvimento de um jogo com o PyS60. Construiremos o clássico popular jogo da velha a fim de ilustrar algumas funcionalidades que o PyS60 suporta como desenho/pintura na tela e  eventos de teclado do aparelho móvel.
 
Antes de iniciarmos a construção do aplicativo, é interessante apresentarmos alguns conceitos úteis que são bastante usados na construção de jogos:
  • 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

Como vimos no desenvolvimento da aplicação FotoPy conforme a  Listagem 4, utilizamos um objeto e32.Ao_lock (trava) para interropermos a execução e iniciar no aplicativo a espera por entradas do usuário. Entretanto, em um aplicativo como um jogo, toda a lógica do jogo deve estar em constante execução, até mesmo quando o usuário não interage com o jogo. Então, em vez de uma "trava",  para controlar a aplicação, utilizamos  um loop de controle.
Um loop de controle é simplesmente um loop while que toma conta do tempo do jogo passo a passo. Como a trava, o loop previne que a aplicação saia de execução instantaneamente. Durante a execução do jogo, a aplicação também deve ser capaz de responder por quaisquer entradas do usuário. Isto é feito da mesma maneira que qualquer outra aplicação, usando os eventos callbacks (ler seção Eventos e callbacks ), que modificam os valores das variáveis globais que consequentemente alteram o estado jogo. Tipicamente,  o loop de controle é estruturado conforme o algoritmo a seguir (Listagem 9):
 
Listagem 9. Loop de controle
  1.Inicializar todos os eventos e callbacks
  2. while  :
  3.     Atualiza o estado do jogo
  4.     Redesenha/Repinta a tela do jogo
  5.     Pausa por algum tempo.
 
O último comando no loop de controle (Linha 5) força a execução pausar por um tempo. Isso é necessário pois queremos que o usuário possa perceber o que está acontecendo dentro do jogo, já que a percepção humana é bem mais lenta em comparação a um processador de um telefone móvel (muito mais rápido). E ainda precisamos dar a aplicação um certo tempo para que ela possa reagir aos eventos do usuário. Por estas razões, pausamos o jogo por um pequeno tempo a cada iteração do loop. O método responsável por esta pausa é o e32.ao_sleep()  que interrompe a execução por um parâmetro de tempo pré-determinado. No nosso jogo, pausamos por 1 segundo, e32.ao_sleep(1), a cada iteração. Isto permite que o usuário possa visualizar se venceu ou perdeu o jogo, antes de iniciar um novo jogo. 
 
Em alguns casos, nós queremos pausar o jogo em uma menor quantidade de tempo possível, 0 segundos,  mas ao mesmo tempo permitir que a aplicação possa tratar quaisquer entradas/interações do usuário no jogo. Para este caso específico, há uma função especial e32.ao_yield() , que ao chamada, garante que a interface gráfica fique em estado de  "escuta" para quaisquer eventos, mesmo que a aplicação esteja desempenhando outra tarefa.
 

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.

 
Podemos concluir, a partir do nome, como funciona esta técnica. Em vez de pintarmos na tela diretamente com o objeto Canvas, dispomos de um objeto bitmap auxilixar (chamado de buf) que, normalmente, possui o tamanho da tela (ou o tamanho da região onde ocorre a animação). Desenhamos, neste buf, os objetos que devem ser apresentados na tela. Após isso, desenhamos o conteúdo do buf na tela, fazendo com que os objetos apareçam através da chamada da função canvas.blit(buf). Limpamos, então, o buf, desenhamos os objetos novamente em suas novas posições, passamos o conteúdo do buf para a tela, e assim por diante. Desta maneira, o usuário não precisará visualizar a operação de pintura de cada elemento individual na tela, e sim apenas o resultado final, reduzindo assim o problema de  "piscadela" na tela.
 
Por fim, diversos jogos necessitam de um ingrediente "aleatório" a fim de deixar o jogo interessante. Python provê um módulo básico denominado random que contém métodos para geração de números aleatórios. Neste jogo, a função random.choice() é usada para escolha aleatória de um dos valores de uma lista passada como parâmetro.
 
O Código da Aplicação Jogo TicTacToePy
 
O código da aplicação do jogo está dividido em 3 partes que após combinados gerarão o jogo completo e funcional. Nas próximas seções iremos explorar estas partes uma por uma.
 
A primeira parte do código do jogo TicTacToe (Listagem 10) define as contantes necessárias para o jogo e alguns métodos utilitários. Nas linhas 01 a 02, importamos os módulos appuifw, e32, random , graphics, e key_codes que serão necessários para manipulação da interface, controle, lógica, pintura de tela e eventos do nosso aplicativos.  As funções handle_redraw e handle_event são responsáveis pela pintura do jogo na tela e tratamento de eventos do teclado respectivamente. As linhas 07, 15 e 20 definem as variáveis utilizadas no jogo. O modificador global avisa que as variáveis são compartilhadas por todo o código, a fim de que não haja uma sobreposição no uso das mesmas. (Ex: O estado da variável deve ser o mesmo por quaisquer partes do código.)  A linha 29 appuifw.app.screen="full" define que a tela do aplicativo deve ocupar toda a área disponível pelo o aparelho para pintura de tela.
 
A linha 33, nós instanciamos a classe Canvas existente no módulo appuifw. Ele é responsável pelo controle de toda pintura e desenho de objetos na tela do aparelho. Observe que ele recebe como parâmetros os callbacks responsáveis pelo tratamento de eventos (event_callback) e pela pintura de tela (redraw_callback). Estas funções devem ser providas pelo desenvolvedor, se ele deseja que sua aplicação suporte essas funcionalidades. Na linha 36 criamos um objeto buffer (buf), onde será responsável por receber os elementos a serem pintados na tela (posição, formato, etc) com um tamanho pré-definido pelo desenvolvedor (Nesse exemplo: uma tupla com 240 pixels de largura e 320 de altura). Por fim, criamos algumas variáveis de controle e estado do jogo a fim de poder controlar a ação e execução do jogo (Linhas 46 a 69).
  
 

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 

43.  #preenchida de: 0 se o quadrado estiver vazio, 1 se o quadrado for

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

59.  #rodada,utilizamos a variavel turn (1 para o telefone,
60.  #2 para o jogador)

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

68.  #(True para Em Execução , False para Finalizado)

69.  running = True

70.

71.

72.#Inicia um novo jogo assim que a aplicação  

73.#é inicializada.

74.newgame()

 
 
Nesta segunda parte do código (Listagem 11), adicionamos ao aplicativo funções relacionadas ao tratamento de eventos que registram ações como pressionamento de botões (direcionais) e softkeys. Isso corresponde às ações do jogo em  relação a qual quadrado o jogador irá executar sua jogada através do movimento do cursor (ponto vermelho)  pela tela através do teclado do celular. Primeiro definimos a função is_down  (Linha 04) que dado um codigo identificador de tecla (scancode), retorna o estado da tecla  atual da tecla. A segunda função pressed (Linha 08)  será utilizada dentro do loop de controle e checa se o estado da tecla passado como parâmetro está pressionada ou não. Para isso ele checa a variável downs e retorna se o estado dela é pressionado (True) ou não-pressionado (False).
E por fim temos a função handle_event que funciona da seguinte maneira: Se a tecla estiver pressionada (evento do  tipo appuifw.EEventKeyDown) então devemos verificar se a tecla já estava pressionada (is_down() retornando 'True' ) ou se foi apenas um clique (is_down() retornando 'False') . Caso a tecla seja solta (quando o usuário libera a tecla pressionada - appuifw.EEventKeyUp) , é setado o estado da tecla para não pressionado (Linha 23). Declaramos também algumas variáveis de controle adicionais que armazenam o estado das teclas pressed e down ( Linhas 23 e 27) e são utilizadas nas funções de tratamento de eventos descritos acima. Essas variáveis são do tipo especial denominado dicionário (representado pelas {}) em Python (Ver Nota 01).
 

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

= downs.get(code,0)+1

21.       keyboard_state

=1

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. (...)

 
 
Nota 01. Manipulando dicionários em Python 
 
Um dicionário é como um livro de endereços onde você pode encontrar o endereço de uma pessoa apenas sabendo seu nome, ou seja, nós associamos chaves (nome) com valores (endereço). Observe que a chave deve ser única, pois caso existam duas chaves iguais (duas pessoas com o mesmo nome) você pode encontrar informações incorretas.Você só pode usar objetos imutáveis (como strings) como chaves de um dicionário, mas para os valores você pode usar objetos mutáveis ou imutáveis. Isto basicamente significa dizer que você só deve usar objetos simples como chaves.

Pares de chaves e valores são especificados em um dicionário usando a notação:

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.

Chegamos na última parte do código da aplicação do jogo da velha. Nesta parte adicionamos toda a estrutura relacionada à pintura de tela (desenho do cursor, matriz, etc) e a lógica do jogo. Não entrarei muito em detalhes na lógica do jogo, pois o código está comentado e auto-explicativo. Adicionamos uma nova função drawgrid() (Linha 04), que é responsável  pela limpeza de tela, pintura dos elemento na tela (linhas da matriz , jogadas dos jogadores  e cursor).  Adicionamos o loop de controle a partir da linha 36. O loop verifica se o aplicativo está ainda em execução (verifica a condição de parada - variável running). Caso ele esteja em execução, é verificado se é a turno do jogador 2 jogar (Linha 44). Se sim, verifica o estado das teclas (se o cursor foi movimentado ou selecionado) para efetuar a jogada do jogador (Linhas 45 a 70). Após verificamos se o jogo foi finalizado, isto acontece se um dos jogadores completou a sequência contínua de 3 elementou ou se não há mais quadrados em branco para jogar (Linhas 71 a 77). Para exibirmos na tela que algum jogador venceu, nós adicionamos um pequeno trecho de código que faz sublinhar a sequência de símbolos com uma linha verde (Linhas 79 a 109). Por fim, após o jogador 02 ter efetuado a sua jogada, chegou o turno do jogador 1 (controlado pelo telefone). O trecho de código (Linhas 117 a 127) é responsável pela escolha e efetuação da jogada do jogador controlado pelo telefone.  Na linha 129, adicionamos uma chamada à função especial e32.ao_yield() , que ao chamada, garante que a interface gráfica fique em estado de  "escuta" para quaisquer eventos, mesmo que a aplicação esteja desempenhando outra tarefa. Finalizado este loop, uma nova iteração do loop de controle é adicionada onde o fluxo acima descrito inicia-se novamente.
 
Listagem 12. Adicionando a Pintura de tela e lógica do jogo

 

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

31.  #(True para Em 31,  #Execução , False para Finalizado)

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):

45.             #Se o botão "Select" for pressionado
46.             if(pressed(key_codes.EScancodeSelect)): 
47.                  #Se o quadrado for disponível para ser jogado

48.                  if(game_state[dotx][doty]==0):                      

49.                   buf.text((xcoord[dotx],ycoord[doty]), u"X", /

50.                      font="title")

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

72.  #completou a sequência contínua de 3 símbolos ou se a matriz está cheia.

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

80.       #com uma reta de cor verde (0x33C00).

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

118. #se o jogo ainda não estiver ainda finalizado...

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()

 
 
 
Vejam os screenshots do aplicativo final acima em execução (Figuras 07 e 08):

             http://wiki.forum.nokia.com/images/d/d1/TicTacToe2.jpg                         Image:TicTacToe3.jpg

                  Figura 07: Jogo em execução.                              Figura 08: Iniciando o jogo.
 
 
Finalizamos aqui nossa última aplicação funcional em Python para celulares Symbian S60. Obviamente, podemos melhorar muitos aspectos da nossa aplicação como exemplo:
  • Adicionar sons de áudio e  música no aplicativo 
  • Criar uma tabela de recordes (vitórias e derrotas do Jogador 01 x Jogador 02).
  • Melhorar a inteligência artificial do jogador 02 (Deixá-lo mais desafiante!)

Mas fica como exercício para os leitores.

 
Podemos inferir a partir desta série de artigos que o Python para S60 é uma escolha ideal para começar a criar aplicações para dispositivos baseados na plataforma Symbian S60, porque o desenvolvimento é relativamente simples, fácil e rápido. Com algumas linhas de código em Python, conseguimos produzir aplicativos simples e funcionais sem se preocupar com estruturas e particularidades da linguagem de programação em si. É bem adequado para o desenvolvimento de protótipos ou para a construção de aplicações para fazer prova de conceito com uma linguagem simples e consistente.
 
Todos os códigos - fonte coberto por este artigo  estão disponíveis neste link.

 
Você precisa estar logado para dar um feedback. Clique aqui para efetuar o login
Ajude-nos a evoluir: você gostou do post?  (0)  (0)
Confirmar voto
Compartilhe:
Ficou com alguma dúvida?