Interface com instrumentação em Python


Interface Tkinter com o tempNTC

O TKinter é uma biblioteca muito adequada para a criação rápida de interfaces gráficas aplicadas à interação com dispositivos microcontrolados remotos. Neste exemplo vamos implementar a IHM (interface homem máquina) com o sistema de leitura de temperatura com sensor NTC e processador Arduino UNO. Utilizarmos aqui a versão 3.5 do python e o sistema operacional Linux Mint 18.1 . O desenvolvimento foi realizado no ambiente virtual testeP3. A interface com o microcontrolador é realizada através da comunicação USB emulando o padrão serial. Apresentamos a seguir um resumo das características que impactam na comunicação entre os dois sistemas:

Características principais da comunicação entre os dois sistemas

Configuração serial: 9600,8,N,1

Os comandos e respostas são strings ascii, sempre terminadas com um caractere 0x0A, ou LF. A lista de comandos é a seguinte:

.Comandos e respostas através da serial

Comando externo significado resposta
0 \n Quem é você? $0,tempNTC v0.1
1 \n Qual é a temperatura? $1,xy.z, ex: $1,32.3
2,nnn Configure intervalo de operação (ms) $2,nnn

O sistema tempNTC pode ser operado de dois modos. O modo autônomo provoca a leitura e envio de mensagens indicadoras da temperatura, sem que a interface tenha que enviar nenhum comando, e o modo escravo, onde a leitura da temperatura só é realizada sob comando da interface. Em ambos os casos a mensagem de temperatura possui a forma $1,xx.y, onde xx.y é o valor da temperatura em graus centígrados. A seleção entre os modos é realizada através de hardware, com um interruptor que leva a terra o pino D12 do microcontrolador.

Configurando o editor Geany para a edição de programas Tkinter

Para facilitar o desenvolvimento de programas baseados no Tkinter é interessante criar uma opção de geração de um programa básico que inclua a biblioteca tkinter. Para isto devemos criar este programa “modelo” e salvá-lo, ou no diretório /usr/share/geany/templates/files, que o fará accessível a todo o sistema, ou no diretório ~/.config/geany/templates/files/, que o disponibilizará para o seu usuário. Para este último você não necessitará de acesso “root” ao sistema. Em qualquer dos casos denominaremos a arquivo modelo de mainTk.py.

O modelo que utilizamos é o seguinte:

.mainTk.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
{fileheader}


import tkinter as tk
import sys


class myApp(object):
    
    def __init__(self, **kw):
        #insira toda a inicialização aqui 
                           
        self.root = tk.Tk()
        self.root.title("Titulo da janela da aplicação")
        self.root.geometry('800x600')
        self.create_menu_bar()
        self.create_canvas_area()
        self.create_status_bar()
        
        
    def create_status_bar(self):
        self.status = tk.Label(self.root, 
                               text="Welcome to myApp", 
                               bd=1, relief=tk.SUNKEN, anchor=tk.W)
        self.status.pack(side=tk.BOTTOM, fill=tk.X)

    def clear_status_bar(self):
        self.status.config(text="")
        self.status.update_idletasks()  
        
    def set_status_bar(self, texto):
        self.status.config(text=texto)
        self.status.update_idletasks()        

    def create_menu_bar(self):            
        menubar = tk.Menu(self.root)
        
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="Exit", command=self.finaliza_software)
       
       
        menubar.add_cascade(label="File", menu=filemenu)
        
        helpmenu = tk.Menu(menubar, tearoff=0)
        helpmenu.add_command(label="About", command=self.mnu_about)
        menubar.add_cascade(label="Help", menu=helpmenu)
        
        self.root.config(menu=menubar)

    def create_canvas_area(self):
        pass
 
    def finaliza_software(self):
        self.root.quit()        
    
        
    def mnu_about(self):
        pass
 
    
    def execute(self):
        self.root.mainloop()

def main(args):
    app_proc = myApp()
    app_proc.execute()
    return 0


if __name__ == '__main__':
    sys.exit(main(sys.argv))

Assim, sempre que você for desenvolver um novo programa utilizando o editor Geany, e que seja baseado na biblioteca Tkinter, utilize o menu Novo(com template)/mainTk.py. Isto fará com que todo este código padrão seja automaticamente gerado.

Desenvolvimento da interface

Após abrirmos o programa modelo executamos algumas configurações básicas e salvamos com o nome tkTempInterface.py. O código fonte fica da seguinte forma:

.tkTempInterface.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  tkTempInterface.py
#  Copyright 2017-08-02 tavares <tavares arroba cadernodelaboratorio.com.br>
#  0.1
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#  
#  



import tkinter as tk
import sys


class myApp(object):
    
    def __init__(self, **kw):
        #insira toda a inicialização aqui 
                           
        self.root = tk.Tk()
        self.root.title("Interface Tk tempNTC")
        self.root.geometry('800x600')
        self.create_menu_bar()
        self.create_canvas_area()
        self.create_status_bar()
        
        
    def create_status_bar(self):
        self.status = tk.Label(self.root, 
                               text="Bemvindo a Interface tk tempNTC", 
                               bd=1, relief=tk.SUNKEN, anchor=tk.W)
        self.status.pack(side=tk.BOTTOM, fill=tk.X)



    def clear_status_bar(self):
        self.status.config(text="")
        self.status.update_idletasks()  
        
    def set_status_bar(self, texto):
        self.status.config(text=texto)
        self.status.update_idletasks()        

    def create_menu_bar(self):            
        menubar = tk.Menu(self.root)
        
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="Exit", command=self.finaliza_software)
       
       
        menubar.add_cascade(label="File", menu=filemenu)
        
        helpmenu = tk.Menu(menubar, tearoff=0)
        helpmenu.add_command(label="About", command=self.mnu_about)
        menubar.add_cascade(label="Help", menu=helpmenu)
        
        self.root.config(menu=menubar)

    def create_canvas_area(self):
        pass
 
    def finaliza_software(self):
        self.root.quit()        
    
        
    def mnu_about(self):
        pass
 
    
    def execute(self):
        self.root.mainloop()




def main(args):
    app_proc = myApp()
    app_proc.execute()
    return 0


if __name__ == '__main__':
    sys.exit(main(sys.argv))

Executando o programa a partir do menu construir/execute ou pressionando F5 temos a seguinte tela:

Programa modelo Tkinter

Nada mau para 10 segundos de digitação 🙂 . Agora vamos idealizar a nossa interface.

Prototipando a interface

Em um pedaço de papel podemos rascunhar a primeira visão da interface. Que tal algo da seguinte forma?:

Rascunho da interface

Temos os seguintes elementos nesta interface:

  • 1- Um texto com a identificação do programa
  • 2- Um campo onde as mensagens enviadas do dispositivo remoto são apresentadas.
  • 3- Um botão que envia uma mensagem solicitando a versão do dispositivo remoto
  • 4- Um botão que solicita o envio da temperatura
  • 5- Um botão que configura o intervalo de amostragem de temperatura no caso do modo autônomo
  • 6- Um campo texto para entrada do novo intervalo de amostragem

Criando o item “1”. Texto com a identificação do programa

Trata-se de um label, que pode ser inserido da seguinte maneira (inserir no método create-canvas-area.)

 lbl1 = tk.Label(self.root, text="Interface tempNTC",fg= "blue", font= ("Arial" ,"28", "bold"))

Criando o campo “2”. Widget para apresentação dos dados advindos da serial

Trata-se de um widget “Text”, que pode ser implementado da seguinte forma:

 text1 = tk.Text(self.root, height=10, width=60)
 text1.insert(tk.END, "\t\tPronto para entrar em operação!\n")
 text1.insert(tk.END, "\tNão esqueça a configuração da porta serial")

Criando os campos 3, 4, e 5 , botões que enviam comandos ao dispositivo remoto

Como estes botões estão alinhados um ao lado do outro, vamos criar um frame em que eles serão inseridos nesta geometria e depois inserimos o frame na tela.

  frame1 = tk.Frame(self.root)

  btnVersao= tk.Button(frame1, text = "Versão?")
  btnTemp= tk.Button(frame1, text = "Temperatura?")
  btnIntervalo= tk.Button(frame1, text ="Intervalo:")

Criando o campo de entrada de texto referente ao intervalo

O campo de entrada de intervalo de amostragem de temperatura é inicializado com o valor 10, que corresponde a 1s de intervalo.

 entry1= tk.Entry(frame1,width=5 )
 entry1.insert( 0,"10")

Posicionando os widgets criados na tela

Como esta interface é muito simples, podemos usar o gerenciador de posição packer. O que temos que fazer é posicionar os widgets na tela de forma a replicar o nosso rascunho que geramos anteriormente. Vejamos passo a passo como esto é realizado:

1-Posicionamos os botões e o campo de entrada do intervalo dentro do frame1, um ao lado do outro:

 btnVersao.pack(side = tk.LEFT, padx= 10, pady= 15)
 btnTemp.pack(side = tk.LEFT,  padx= 10, pady= 15)
 btnIntervalo.pack(side = tk.LEFT, padx= 10, pady= 15)
 entry1.pack(side = tk.LEFT,pady= 15)

2-Inserimos agora na tela o label com a identificação do programa:

 lbl1.pack()

3-Inserimos a campo de texto onde serão apresentadas as mensagens advindas do dispositivo serial:

 text1.pack()

4-E finalmente inserimos na tela o frame1, que contém todos os botões e o campo de entrada de dados.

 frame1.pack()

O resultado que se ve na tela após executado o programa é:

Painel do programa tempNTC

O script python que implementa este visual é o seguinte:

.tkTempInterface1.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  tkTempInterface.py
#  Copyright 2017-08-02 tavares <tavares arroba cadernodelaboratorio.com.br>
#  0.1
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#  
#  



import tkinter as tk
from tkinter import font

import sys


class myApp(object):
    
    def __init__(self, **kw):
        #insira toda a inicialização aqui 
                           
        self.root = tk.Tk()
        self.root.title("Interface Tk tempNTC")
        self.root.geometry('550x300')
        self.create_menu_bar()
        self.create_canvas_area()
        self.create_status_bar()
        
        
        
    def create_status_bar(self):
        self.status = tk.Label(self.root, 
                               text="Bemvindo a Interface tk tempNTC", 
                               bd=1, relief=tk.SUNKEN)
        self.status.pack(side= tk.BOTTOM, fill = tk.X)



    def clear_status_bar(self):
        self.status.config(text="")
        self.status.update_idletasks()  
        
    def set_status_bar(self, texto):
        self.status.config(text=texto)
        self.status.update_idletasks()        

    def create_menu_bar(self):            
        menubar = tk.Menu(self.root)
        
        filemenu = tk.Menu(menubar, tearoff=0)
        filemenu.add_command(label="Exit", command=self.finaliza_software)
       
       
        menubar.add_cascade(label="File", menu=filemenu)
        
        helpmenu = tk.Menu(menubar, tearoff=0)
        helpmenu.add_command(label="About", command=self.mnu_about)
        menubar.add_cascade(label="Help", menu=helpmenu)
        
        self.root.config(menu=menubar)

    def create_canvas_area(self):
        lbl1 = tk.Label(self.root, text="Interface tempNTC",fg= "blue", font= ("Arial" ,"28", "bold"))

        
        text1 = tk.Text(self.root, height=10, width=60)
        text1.insert(tk.END, "\t\tPronto para entrar em operação!\n")
        text1.insert(tk.END, "\tNão esqueça configuração da porta serial")

        
        
        frame1 = tk.Frame(self.root)
        
        btnVersao= tk.Button(frame1, text = "Versão?")
        btnTemp= tk.Button(frame1, text = "Temperatura?")
        btnIntervalo= tk.Button(frame1, text ="Intervalo:")
        entry1= tk.Entry(frame1,width=5 )
        entry1.insert( 0,"10") 
        
        btnVersao.pack(side = tk.LEFT, padx= 10, pady= 15)
        btnTemp.pack(side = tk.LEFT,  padx= 10, pady= 15)
        btnIntervalo.pack(side = tk.LEFT, padx= 10, pady= 15)
        entry1.pack(side = tk.LEFT,pady= 15)
                
        lbl1.pack()
        text1.pack()
        frame1.pack()
        
        
        
        
        
    def finaliza_software(self):
        self.root.quit()        
    
        
    def mnu_about(self):
        pass
 
    
    def execute(self):
        self.root.mainloop()




def main(args):
    app_proc = myApp()
    app_proc.execute()
    return 0


if __name__ == '__main__':
    sys.exit(main(sys.argv))

Uma observação sobre instrumentação RS232-C

Embora a interface serial RS232C não seja mais fisicamente tão presente em nossos computadores como era comum até uns 10 anos atrás, emulações sobre cabeamento físico USB ou perfis seriais bluetooth continuam a existir. Devido a grande simplicidade do lado do microcontrolador, é previsível que esta interface ainda vá resistir durante muitos anos, daí saber como utilizá-la com o python, “c”, ou mesmo saber exatamente o que significam os seus parâmetros é muito importante para o desenvolvedor.

 

Se você preferir, pode obter os programas  diretamente na página de downloads do caderno, http://wp.me/P6gfIl-BF , no arquivo de nome pack170809.tar.gz.

Bem, a interface visual está terminada!. Precisamos agora implementar a funcionalidade de comunicação serial e troca de mensagens com o dispositivo remoto. Até a próxima!

Deixe um comentário