Interface Gráfica para Aplicações em Python com Webkit (HTML/JS/CSS)
Bom vou ensinar aqui uma coisa que aprendi esses dias e que pode ser muito útil para fazer interfaces gráficas para scripts/programas em python.
1) Instalando o Python Webkit
Há muitos jeitos de se fazer uma interface gráfica em python (QT, GTK, SFML entre outros) porém a maioria dela é de relativa complexidade para novos usuários. Mas existe uma maneira de simplificar isso, principalmente a WebDevelopers.
Python Webkit é um binding do Webkit para Python. Ele roda sobre o GTK e funciona como um Browser que roda dentro do programa em python. Para instala-lo no ubuntu basta digitar:
sudo apt-get install python-webkit
Ou instala-lo pela Central de Programas. Já no Windows também é possível rodar o Python Webkit, porém não cheguei a testa-los. Para quem quiser se aventurar, achei uns links uteis sobre isso:
GTK para Python: http://www.pygtk.org/downloads.html
Webkit para Python em Windows: http://opensourcepack.blogspot.com.br/2009/12/pywebkitgtk-windows-binary.html
Bom vamos começar então com a mão na massa!
2) Criando primeiro aplicativo com Python Webkit
Vamos criar primeiro um script em python que carregue uma janela do GTK com Webkit. E faze-la carregar o site da google:
import gtk
import webkit
import thread
import gobject
import time
view = webkit.WebView()
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
win.add(view)
win.show_all()
view.open("http://www.google.com.br/")
gtk.main()
Com isso você vai abrir uma janela do WebView com o site da google:

Porém para que seja usável essa interface, precisamos de interações entre o código python e a página carregada. Vamos começar sobre a interação Python -> Página.
3) Interação Python -> Página
Precisamos criar uma página que aceite interações, para isso crie um arquivo pagina.html. Usaremos o jQuery para facilitar nossas interações, você pode baixa-lo aqui: http://code.jquery.com/jquery-1.7.2.min.js (coloque o script na mesma pasta)
Conteúdo pagina.html:
<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript">
function AlteraConteudo(html) {
$('#conteudo').html(html);
}
</script>
<div id="conteudo">
Conteudo nao alterado!
</div>
Reparem que eu criei uma função AlteraConteudo(html) para alterar o conteúdo da div com o id conteudo , usaremos ela para alterar o conteúdo pelo python. Agora vamos ao script python. Para facilitar as coisas, iremos usar um script utilitário para formar o webview e comunicar em javascript. O script que disponibilizarei é uma versão alterada do que se pode conseguir em: http://www.aclevername.com/articles/python-webgui/
Link para script: http://xserver.energylabs.com.br/letshackit/webgui.py - salve-o e coloque na mesma pasta.
A seguir, o script em python que iremos usar:
O arquivo está comentado. Porém é importante notar que a função que faz o envio de comandos em javascript pro WebView é o web_send. Se você olhar no webgui.py, web_send é uma função criada para chamadas Assíncronas ao GTK, isso é necessário devido as threads (do GTK e do programa) não necessariamente estarem sincronizadas.
Você pode usar o web_send para executar qualquer função javascript que desejar.
Mas além da interação Python -> Página, é interessante que tenhamos uma interação Página -> Python. Como proceder então? Vamos aos callbacks!
4) Callbacks Página -> Python
Na realidade, não há maneira direta de executar um comando python pela janela em javascript. Porém algumas “gambiarras” podem ser feitas. Uma, é como o próprio site de exemplo sugere, alterando o título da página e lendo isso. Porém desta maneira há uma série de limitações quanto ao que trafegar. Então depois de umas pesquisas na internet e adaptadas cheguei a algo um pouco melhor.
Usaremos a URL do Browser Webkit. Basicamente vamos fazer um callback ligado a alteração da URL do Browser, e detectaremos um padrão. No caso http:// e file:// são usados para descrever caminhos, mas usaremos programa:// ou callback:// para fazer as chamadas.
Para isso iremos criar uma função CallBackURL no python para processar as chamadas que os scripts fizerem. A função terá o seguinte corpo:
def CallbackURL(view, frame, req, data=None):
uri = req.get_uri()
splited = uri.split(':', 2)
scheme = splited[0]
data = splited[1]
if scheme == 'callback':
if data == '//funcao1':
print "Webview executou funcao1!"
elif data == '//funcao2':
argumentos = splited[2:]
print "Funcao 2 chamadas com argumentos: "
print argumentos
return True
else:
return False
Explicando ela:
scheme => é tudo o que vem antes do ://, no caso estamos fazendo um chamado em callback://XXXX então checaremos se o scheme é callback.
uri => é tudo que vem depois dos primeiros :. Isso incluí o //. Por exemplo: callback://funcao1:argumento1 retornará //funcao1:argumento1 .
Fizemos duas funções ai. Uma é callback://funcao1 que apenas escreve no console, outra é callback://funcao2 que poderá ter argumentos separados por :. Repare que há um return True para quando nós identificamos algo. Isso é importante para que o browser não tente carregar a URL.
Após isso é necessário conectar o callback ao browser. Isso deve ser feito logo após inicializar o browser, desta maneira:
browser.connect("navigation-requested", CallbackURL)
Vamos para a página em HTML:
<html>
<head>
<title>TESTE</title>
<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript">
function ExecutaFuncao1() {
location.href='callback://funcao1';
}
function ExecutaFuncao2() {
var argumento = $('#valor').val();
location.href='callback://funcao2:'+argumento;
}
</script>
</head>
<body>
<div id="conteudo">
<input type="button" value="Funcao1" onClick="ExecutaFuncao1();">
<BR>
Argumento: <input type="text" id="valor">
<input type="button" value="Funcao2" onClick="ExecutaFuncao2();">
</div>
</body>
</html>
Criamos um botão, e um botão com campo de texto que será usado como argumento. Feito isso já estamos prontos para testar!
Código completo: http://codepad.org/ForAiYph


Viram como não é tão difícil assim? Agora você pode montar uma UI em HTML puro! Uma sugestão seria montar uma UI com jQuery Mobile, jQuery EasyUI, ExtJS ou coisa do tipo :D
Projeto completo: http://xserver.energylabs.com.br/letshackit/LHI-PyWebkit-Tuto.tar.gz
Espero ter ajudado :D
Função C para múltiplos e submúltiplos (REVISADO)
O Caio Alarcon me notificou de algumas coisas sobre minha função de múltiplos e submúltiplos, por exemplo, o tratamento de números negativos. Resolvi então revisar e otimizar a função em C. O Resultado está abaixo:
#include <stdio.h> #include <math.h> char toNotationUnit(double value,float *out) { double val; char notacoes[] = {'y','z','a','f','p','n','u','m',' ','k','M','G','T','P','E','Z','Y'}; int counter=8; char unit; val = value>0?value:-value; if(val < 1) { while( (val < 1.00) & (counter != 0)) { counter--; val=val*(double)1000; } }else{ while((val >= 1000) & (counter != 16)) { counter++; val=val/(double)1000; } } unit = notacoes[counter]; val = round(val*(double)100)/(double)100; *out = (float) value>0?val:-val; return unit; } int main() { double x = -1230000; float res; char t; t = toNotationUnit(x,&res); printf("%.2F%c",res,t); return 0; }
Quem preferir, tem um link no ideone com este código funcionando :D
Bom uso!
É isso ai, resolvi finalmente soldar o ARM em sua placa adaptadora. Fiz todas as ligações de Vcc e GND já por baixo da placa para não precisar me preocupar com isso. Coloquei todos os capacitores de decoupling de 100nF e parece que está tudo certo! Agora é só ter paciência e tentar programa-lo com JTAG :D
SOLDERING LIKE A BOSS - Modificação no Conversor USB-RS232 para usar eu poder usar eles em nível TTL (para interface direta no microprocessador). :D
Como alguns sabem, meu variac pegou fogo ( http://www.youtube.com/watch?v=BHmwMWUPKDc ), e o meu outro queimou na semana da GV. Então precisei arrumar um pelo menos para poder usado aqui. Ai estão as fotos :D
Foi testado e está funcionando corretamente agora. Foi arrumado na gambiarra, mas ta arrumado.
Bom, resolvi postar um esquema básico completo para ligar o Interruptor no modo Monofônico. Está ai. Qualquer dúvida é só falar :D
Interruptor MIDI Polifônico com PIC 16F628A (Interface)
Neste tópico colocarei apenas como é a interface do interruptor descrito anteriormente.

Para um interruptor monofônico, ligamos o Enable em Vcc e ignoramos o BUSY. Para polifônicos, colocamos o Enable do primeiro no Vcc, e nos seguintes ligamos no BUSY do anterior. Nas saídas fazemos uma operação OR, e as entradas MIDI são ligadas todas juntas.

O Gate OR pode ser substituído por dois diodos para esse uso sem problemas, como mostrado abaixo:

Boa sorte na montagem!
Código fonte completo: MIDI INT.rar ou Project Source
Créditos ao Uzzors2k pela ideia de cascatear microprocessadores para polifonia.
Interruptor MIDI Polifônico com PIC 16F628A
- Introdução
Bom, muitos sabem que eu fiz um Interruptor MIDI (ou sintetizador como preferir) com um FPGA. Porém poucos tem acesso a um FPGA principalmente para fazer algo tão *simples*, então resolvi fazer um com PIC 16F628A.
No começo queria fazer com um PIC menor, mas os menores não tem Receptor Serial via hardware, então ficaria mais complicado implementar. Outro ponto, é que o preço dos PIC’s menores são praticamente os mesmos, então era só uma questão de espaço mesmo. Usando um PIC maior, fica mais espaço livre para futuras modificações caso seja necessário.
O PIC 16F628A tem 2KB de memoria flash, 224 Bytes de RAM, 128 Bytes de EEPROM, um Comparador, 3 Timers, e um Receptor Serial Universal. Está no pacote DIP de 18 Pinos, e ele tem 2 portas de 8 bits (dependendo da configuração, a porta A pode *perder* 3 bits.
Datasheet dele: http://ww1.microchip.com/downloads/en/DeviceDoc/40044F.pdf
Ele tem um oscilador RC Interno de 4MHz calibrado de fábrica para +/- 1%, então podemos fazer uso dele tranquilamente.
- Protocolo MIDI
Vamos falar um pouco antes do protocolo MIDI. O Sinal MIDI é basicamente um sinal Serial parecido com padrão RS232. São 10 bits enviados, um bit para identificar o inicio (start bit), 8 bits de dados, e um bit para identificar o fim (stop bit). Ele só não se encaixa na RS232 por causa de sua Baud Rate (velocidade com que os bits são transmitidos), que é 31250 Hz (ou 31.25 kBits por segundo). Por apenas sua Baud Rate ser fora de padrão, podemos usar a porta serial universal do PIC, apenas usando 31250 como Baud Rate.
Outro ponto do Protocolo MIDI, é que todo dado MIDI é composto por 3 Bytes (24 bits), sendo o primeiro deles o byte de Operação, e os outros dois bytes de Dados. Para uma identificação mais fácil de qual é qual, foi padronizado que se o primeiro bit (MSB) do byte for 1, é uma Operação, se for 0 é Dado. Assim caso a transmissão de algum byte falhe (principalmente o primeiro) é possível saber.
A tabela simplificada abaixo associa os modos de operação e a função de seus dados:

Na tabela só está os mais comuns e tirando o Pitch Bend, os que vamos usar.
Iremos então fazer o PIC ler 3 bytes, e depois efetuar as operações necessárias de acordo com o primeiro byte.
- Notas musicais e Timer do PIC
Outro detalhe importante são as notas musicais. O MIDI apenas envia o numero da nota, que varia de 0 a 127. Precisamos então calcular o período das notas.
A frequência de uma dada nota musical, tem que dobrar a cada 12 notas. Para isso é dada a fórmula:
Frequência = FreqBase * 2^(n/12)
Onde Frequência é a frequência da nota que queremos, FreqBase é a frequência da nota base, n é o numero da nota relativa a nota base. A nota que usaremos como base é a 10ª nota, que tem sua frequência em 27.5Hz. Para calcularmos o período de uma nota é só pegarmos o inverso da frequência:
Período = 1 / ( FreqBase * 2^(n/12) )
Até podemos calcular isso em tempo real no PIC, porém podem haver atrasos muito grandes, então faremos uma tabela com os períodos das notas para usarmos.
Podemos usar o seguinte código em PHP para calcularmos as frequências:
<? for($i=0;$i<128;$i++) { $freq = 27.5 * pow(2,($i-9)/12); echo("Nota: $i Frequência: $freq\r\n"); } ?>
Link do Codepad: http://codepad.org/4GloHc3b
De maneira semelhante podemos fazer para o período:
<? for($i=0;$i<128;$i++) { $period = 1/ (27.5 * pow(2,($i-9)/12)); echo("Nota: $i Periodo: $period\r\n"); } ?>
Link do Codepad: http://codepad.org/yqXDTXQZ
No PIC, usaremos o TIMER1 como referência, ele é um timer de 16 bits, ou seja conta de 0 a 65535. Faremos ele incrementar de 1 em 1 micro segundo, então colocaremos o período em micro segundos. Outro detalhe é que depois que o timer está rodando, não podemos para-lo. Precisamos esperar ele chegar até o fim, então faremos ele contar apenas o necessário (o período). Sendo o máximo dele de 65535, ao iniciar uma nota, colocaremos 65535-período em seu valor inicial, assim ele contará apenas o tempo do período. Faremos então um script que já gere um array com os valores que precisamos de cada nota:
<?
echo " const unsigned int16 notas[] = {";
for($i=0;$i<128;$i++) {
$period = 65535-round(1000000 / (27.5 * pow(2,($i-9)/12)));
if($i==0) echo($period); else echo(",$period");
}
echo "};";
?>
Link do codepad: http://codepad.org/yUNkOm7V
const unsigned int16 notas[] = {4379,
7811,11051,14109,16995,19720,22291,24718,27009,29171,31212,33139,34957,
36673,38293,39822,41265,42627,43913,45127,46272,47353,48374,49337,50246,
51104,51914,52679,53400,54081,54724,55331,55904,56444,56954,57436,57890,
58320,58725,59107,59468,59808,60130,60433,60719,60990,61245,61485,61713,
61927,62130,62321,62501,62672,62832,62984,63127,63262,63390,63510,63624,
63731,63832,63928,64018,64103,64184,64259,64331,64399,64462,64523,64579,
64633,64684,64731,64777,64819,64859,64897,64933,64967,64999,65029,65057,
65084,65109,65133,65156,65177,65197,65216,65234,65251,65267,65282,65296,
65310,65322,65334,65345,65356,65366,65376,65385,65393,65401,65408,65416,
65422,65429,65435,65440,65446,65451,65455,65460,65464,65468,65472,65475,
65479,65482,65485,65488,65490,65493,65495};
Isso já nos da o array com os valores que teremos que colocar no TIMER1. Assim podemos facilmente agora começar a programar :D
- Programa do PIC
O programa do PIC não é tão complexo assim. Usaremos o CCS C como compilador C, mas o programa pode ser facilmente adaptado a qualquer outro compilador. Usaremos a Interrupção da USART para receber os bytes e armazena-los numa array de 3 bytes. Após completar os 3 bytes, ele vai marcar uma variável como 1 para sabermos que recebemos os 3 bytes.
Definiremos duas variáveis do tipo int16 para período e tOn (tempo ligado), uma array de char de tamanho 3 para os bytes, duas int1 para identificar se há alguma nota ligada e para marcar o buffer de 3 bytes como carregado, duas variáveis do tipo int para fazer a contagem dos bytes recebidos e para guardar o numero da nota carregada.
static unsigned int16 tOn = 250; //tOn static unsigned int16 period = 0; //Período static int1 noteOn = 0; //Nota Ligada static int8 loadedNote = 0; //Nota Carregada static char buffer[3]; //Buffer de Bytes static int1 buffer_loaded; //Estado do Buffer static int buffer_counter = 0; //Contador do Buffer
Assim definimos as variáveis que iremos usar no código. Agora iremos a função da interrupção da porta serial. O CCS C identifica a interrupção serial do PIC como INT_RDA, então iremos colocar uma função para ela:
#int_rda void serial_isr() { if(buffer_counter!=3) { //Se ainda não leu 3 bytes buffer[buffer_counter]=getc(); //Ler o byte e guardar no buffer buffer_counter++; if(buffer_counter==3) { //Se ler 3 bytes, buffer_counter = 0; //Resetar o contador buffer_loaded = 1; //Marcar o buffer como carregado }else{ //Se não buffer_loaded = 0; //Manter o status do buffer como 0 } }else //Caso receber algum byte com o buffer getc(); //carregado, descartar o byte. }
Com isso, estaremos recebendo os bytes e armazenando no buffer. Iremos agora então fazer a interrupção do TIMER1 que irá fazer a contagem. Nessa interrupção iremos apenas fazer ele setar o valor correto em si mesmo cada vez que sua contagem chegar ao fim. A interrupção é acionada toda vez que o TIMER1 termina de contar. Ou seja, após 65535 us no nosso caso.
#INT_TIMER1 void resetTimer1() { set_timer1(period); }
Iremos agora para a rotina principal, onde iremos fazer todo trabalho pesado.
Precisamos fazer a inicialização do PIC, faremos tudo isso dentro da void main().
Iremos definir uma variável do tipo int16 para podermos guardar temporariamente a posição do TIMER1 no código.
void main() { int16 pos; setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); setup_timer_1(T1_INTERNAL|T1_DIV_BY_1); setup_timer_2(T2_DISABLED,0,1); setup_comparator(NC_NC_NC_NC); setup_vref(FALSE); enable_interrupts(global); enable_interrupts(INT_TIMER1); enable_interrupts(INT_RDA);
Com isso iremos ter inicializado tudo que precisamos. Agora vamos aos “checks”. Usaremos o pino A0 para saída do interruptor, e A1 como Enable, A2 como saída para representar o PIC como “ocupado”.
while(true) { //Loop para sempre pos=get_timer1()-period; //Pega valor do timer, e subtrai do periodo //Iremos usar isso para o tOn if((pos<=tOn)¬eOn) //Se a posição for menor que o tOn OUTPUT_HIGH(PIN_A0); //Liga saída A0 else //Se não OUTPUT_LOW(PIN_A0); //Desliga saída A0 if(buffer_loaded) { //Aqui iremos fazer o processo do buffer //Se o valor no byte1 for 0x90, e não houver //nota ligada, e o pino A1 estiver ligado if((buffer[0] == 0x90) & !(noteOn) & INPUT(PIN_A1)) { period = notas[buffer[1]]; //Carrega o valor da nota no periodo tON = (0xFFFF-period)*0.1; //Faz o tOn ser 10% do periodo total noteOn = 1; //Fala que a nota está ligada OUTPUT_HIGH(PIN_A2); //Coloca saída A2 em alta, PIC ocupado loadedNote = buffer[1]; //Guarda o número da nota carregada }else if(buffer[0] == 0x80) {//Se for 0x80, é para desligar a nota if(buffer[1] == loadedNote) {//Verifica se a nota que esta tocando é a //mesma que está pedindo para desligar period = 0xFFFF; //Reseta periodo noteOn = 0; //Desliga nota OUTPUT_LOW(PIN_A2); //Desliga saida A2, PIC Disponível loadedNote = 0x00; //Zera nota carregada } }else if(buffer[0] == 0xB0) { period = 0xFFFF; noteOn = 0; OUTPUT_LOW(PIN_A2); loadedNote = 0x00; buffer_counter = 0; buffer_loaded = 0; } buffer_loaded = 0; //Libera o buffer para recarregamento } } }
Com isso temos nosso código completo!
Podemos usar os PIC’s em modo Pipeline para polifonia, ligando todas as recepções midis juntas, e as saídas A2 nas entradas A1 dos próximos, e fazendo operação OR entre as saídas. Colocarei mais detalhes no próximo tópico.
Créditos a ideia de serializar microcontroladores para a polifonia: Uzzors2k - http://uzzors2k.4hv.org/index.php?page=midiinterrupter
(Source: energylabs.com.br)
Função C para múltiplos e submúltiplos
[PS: ESTA É UMA VERSÃO ANTIGA VEJA A VERSÃO NOVA E REVISADA AQUI: Função C para múltiplos e submúltiplos (REVISADO) ]
Acho que tem muita gente que gostaria da mesma função que eu fiz em javascript para dar os múltiplos e submúltiplos em C. Resolvi então faze-la.
Não é muito diferente se vocês forem reparar, até por que a sintaxe básica do javascript segue o C. Mas há alguns detalhes a serem notados. De qualquer forma o código vai abaixo com um exemplo de teste:<>
#include <stdio.h>
#include <math.h>
char toNotationUnit(double value,float *out) {
double val;
char submultiplo[] = {' ','m','u','n','p','f','a','z','y'};
char multiplo[] = {' ','k','M','G','T','P','E','Z','Y'};
int counter=0;
char unit;
val = value;
if(val < 1) {
while(val < 1.00) {
counter++;
val=val*(double)1000;
if(counter==8) break;
unit = submultiplo[counter];
}
}else{
while(val >= 1000) {
counter++;
val=val/(double)1000;
if(counter==8) break;
}
unit = multiplo[counter];
}
val = round(val*(double)100)/(double)100;
*out = (float)val;
return unit;
}
int main() {
double x = 0.000000123123;
float res;
char t;
t = toNotationUnit(x,&res);
printf("%.2F%c",res,t);
return 0;
}
Se preferir, uma versão no ideone: http://ideone.com/a8oqT
O uso é bem simples, o toNotationUnit retorna o char que representa o múltiplo/submúltiplo e seta no seu segundo argumento (que será um ponteiro para float) o valor arredondado para duas casas decimais.
Simples não?





