Programação

Posicionamento dinâmico no Protheus

Escritor por Vinícius Gregório

 

Este tutorial é um “truque” para o desenvolvimento de rotinas que exigem posicionamento númerico fixo dentro do código-fonte (como telas e relatórios, por exemplo).

 

Existem diversas técnicas para o desenvolvimento desse tipo de rotina e, inclusive, ferramentas disponibilizadas pelos analistas (Ex.: Gaia) ou funções (Ex.: MsAdvSize), que auxiliam nesse processo. No entanto, em todos os casos, como o AdvPL é uma linguagem compilada, sempre que o posicionamento ou tamanho dos componentes da tela precisam ser alterados, é necessário recompilar o fonte.

 

Na realidade, o processo chega a ser exaustivo: sair do sistema, alterar a posição no fonte, compilar o fonte, entrar novamente no sistema, ir até a rotina e testar. Se não ficou como você queria? Repita todo o processo até ficar como esperado … -_-‘

 

Já há algum tempo, fiquei realmente incomodado com essa rotina e, como trabalhei com outras linguagens de desenvolvimento, elaborei uma forma de facilitar esse processo através de arquivos de configurações. A idéia é baseada na classe Properties do pacote java.util da linguagem JAVA*.

 

Como funciona?

 

O princípio é simples: utilizaremos algumas funções que irão ler os valores de posicionamento de arquivos textos!  Ao invés de fixar o valor dentro do seu código e ter que recompilar a cada alteração, quando precisarmos mudar uma posição, apenas alteramos no arquivo .properties e entramos novamente na nossa rotina do TOTVS Protheus.

 

Qual a estrutura de um arquivo de propriedades?

 

O .properties é um arquivo de texto puro onde as propriedades são separadas por linhas com uma estrutura bem simples: nome da propriedade=valor da propriedade. Observe o nosso exemplo logo abaixo:

 

DLG001=0
DLG002=0
DLG003=100
DLG004=230
GRP001=05
GRP002=05
GRP003=4
GRP004=2
GRP005=5
BTN001=4
BTN002=3
BTN003=10
BTN004=100
BTN005=10

 

Mantive a extensão do arquivo como .properties para diferenciá-lo dos demais arquivos de texto (e também porque é essa a extensão dos arquivos de propriedades do JAVA). Porém, nada impede que você altere as chamadas dentro do seu código e utilize, por exemplo, um arquivo com a extensão .txt. Fica a seu critério.

 

Vamos ao código…

 

Primeiro, vamos construir a função que pesquisa e retorna o valor necessário dentro do arquivo .properties:

 

/*
ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
±±ÉÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍ»±±
±±ºPrograma  ³GetProperty  ºAutor  ³Vinícius Gregório   º Data ³  27/10/10   º±±
±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍ͹±±
±±ºDesc.     ³ Rotina para pegar informações de propriedade com a estrutura  º±±
±±º          ³ chave = valor                                                 º±±
±±º          ³                                                               º±±
±±º          ³ Exp1:= Caracter: nome da propriedade a retornar o valor       º±±
±±º          ³ Exp2:= Caracter: nome do parâmetro que contém o diretório     º±±
±±º          ³ de arquivos de propriedade                                    º±±
±±º          ³ Exp3:= Caracter: nome do arquivo .properties                  º±±
±±º          ³ Exp4:= Lógico: Se exibe ou não mensagem de erro para o        º±±
±±º          ³ usuário                                                       º±±
±±º          ³                                                               º±±
±±º          ³ Retorno: Caracter: valor da propriedade                       º±±
±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹±±
±±ºUso       ³ Genérico                                                      º±±
±±ÈÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ±±
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
*/
User Function getProperty(cPropr,cParam,cArq,lMensagem)
//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Declaração de variáveis³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Local aArea		:= GetArea()
Local cRetorno	:= ""
Local cBarra	:= If(IsSrvUnix(), "/", "")
Local cPath		:= ""
Local cBuffer	:= ""
Local nHdlArq	:= 0

DEFAULT cParam 	:= "MV_XPROPER"

//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Pega a pasta a partir do parâmetro³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
cPath := Alltrim(GetMV(cParam))

//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Verifica se o diretório contido no parâmetro existe³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
If !lIsDir(cPath)

	If lMensagem
		Aviso(	"Inconsistência","O diretório informado no parâmetro "+cParam+" não existe. Informe o seu administrador de sistemas.",{"&Continua"},,"Atenção" )
	Endif

	Return cRetorno
Endif

//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Verifica se o Path termina com a Barra³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
If SubStr(cPath,Len(cPath)-1)!=cBarra
	cPath	:= cPath+cBarra
Endif

//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Abre o arquivo texto                                                          ³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
nHdlArq := FT_FUSE(cPath+cArq)

If nHdlArq <> Nil .and. nHdlArq <= 0

	If lMensagem
		Aviso(	"Inconsistência","Não foi possivel a abertura do arquivo "+cArq+" para leitura.",{"&Continua"},,"Atenção" )
	Endif

	FT_FUSE()
	Return cRetorno
Endif

//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Realiza a leitura do arquivo CSV                                              ³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
FT_FGOTOP()
While !FT_FEOF()

	//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
	//³Realiza a leitura da linha                                                    ³
	//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
	cBuffer := Alltrim(FT_FREADLN())

	If Empty(cBuffer) .or. At("=",cBuffer) == 0
		FT_FSKIP()
	Else
		//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
		//³Verifica se é a propriedade procurada³
		//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
		If At(cPropr,SubStr(cBuffer,1,At("=",cBuffer))) > 0
			cRetorno := Alltrim(Substr(cBuffer,At("=",cBuffer)+1))
		Endif

		FT_FSKIP()
	Endif

EndDo
FT_FUSE()

RestArea(aArea)
Return cRetorno

 

Básicamente, essa função recebe o nome da propriedade que estamos procurando dentro do arquivo, qual o caminho para o arquivo, qual o nome do arquivo e se ela irá exibir ou não mensagens de erro para o usuário. A partir dessas informações, ela busca dentro de cada uma das linhas do arquivo pela propriedade informada e retorna o valor correspondente, em formato string.

Fique atualizado, É GRÁTIS!
Cadastre o seu endereço de e-mail e fique por dentro de todas as atualizações da AcademiaERP.
Não enviamos spam.

Dentro da função, vocês podem observar que eu utilizei um parâmetro (SX6) como valor padrão para o caminho dos arquivos de propriedades. O intuito desse parâmetro é facilitar a chamada da função (removendo a necessidade de um parâmetro) e centralizar (em um único diretório) todos os arquivos .properties. No meu caso, passei o caminho completo para uma pasta que criei dentro do Protheus_Data.

 

Para facilitar a utilização do getProperty dentro dos fontes, criei uma função que encapsula a sua chamada:

 

/*
ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
±±ÉÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍ»±±
±±ºPrograma  ³P         ºAutor  ³Vinícius Gregório   º Data ³  25/11/13   º±±
±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍ͹±±
±±ºDesc.     ³ Função responsável por pegar os valores de posicionamento  º±±
±±º          ³ no arquivo.                                                º±±
±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹±±
±±ºUso       ³ AcademiaERP                                                º±±
±±ÈÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ±±
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
*/
User Function P(cPos)
Return Val(U_GetProperty(cPos,,"testes.properties",.T.))

 

Ela serve apenas para tratar o retorno de getProperty (convertendo o valor string em numérico), preencher demais parâmetros necessários para a função e encurtar a sua chamada. Assim, chamar U_P(“DLG001”) é bem menor do que Val(U_GetProperty(“DLG001″,,”testes.properties”,.T.)) a cada vez que precisarmos retornar uma posição 😉

 

Testando essas funções:

 

Criei uma tela bem simples para exemplificar a utilização das funções. O código completo, arquivo .properties de teste e SX6 com o parâmetro MV_XPROPER podem ser baixados aqui.

 

#INCLUDE "PROTHEUS.CH"

/*
ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
±±ÉÍÍÍÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËÍÍÍÍÍÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍ»±±
±±ºPrograma  ³XTSTPROP    ºAutor  ³V. Gregório         º Data ³  23/11/13   º±±
±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÊÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍ͹±±
±±ºDesc.     ³ Rotina para testes de utilização da rotina GetProperty       º±±
±±º          ³ Desenha uma tela simples utilizando arquivo texto para o     º±±
±±º          ³ posicionamento dinâmico                                      º±±
±±ÌÍÍÍÍÍÍÍÍÍÍØÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹±±
±±ºUso       ³ AcademiaERP                                                  º±±
±±ÈÍÍÍÍÍÍÍÍÍÍÏÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ±±
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
*/
User Function XTSTPROP()
//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Declaração de variáveis³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Local nRetorno		:= 0
Local oDlg, oGroup, oButton
Local cTitDialog	:= "TESTE"
Local cTitGroup		:= "PROPERTIES"

//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
//³Dialog principal. Na ativação ela é centralizada.³
//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
DEFINE MSDIALOG oDlg FROM U_P("DLG001"),U_P("DLG002") TO U_P("DLG003"),U_P("DLG004") PIXEL TITLE cTitDialog

	oGroup:= TGroup():New(U_P("GRP001"),U_P("GRP002"),oDlg:nClientHeight/U_P("GRP003"),(oDlg:nClientWidth/U_P("GRP004"))-U_P("GRP005"),cTitGroup,oDlg,,,.T.)

	//ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
	//³Botão para o controle de fechamento da janela³
	//ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
	oButton:=tButton():New((oDlg:nClientHeight/U_P("BTN001"))+U_P("BTN002"),U_P("BTN003"),"OK",oDlg,{||oDlg:End()},U_P("BTN004"),U_P("BTN005"),,,,.T.)

ACTIVATE MSDIALOG oDlg CENTERED

Return .T.

 

Repare que, em todos os lugares onde existem posicionamentos numéricos, substituí por uma chamada de U_P() (nossa função que encapsula a chamada de getProperty). Lembre-se de appendar o parâmetro no seu SX6, posicionar o arquivo .properties dentro da pasta correta (indicada no parâmetro) e você pode executar o exemplo. O resultado deve ser assim:

 

advpl-properties

 

Caso você queira alterar os posicionamentos, pode mudar direto no arquivo a propriedade correspondente, sair da rotina e chamá-la novamente (sem a necessidade de sair do sistema, compilar novamente, entrar novamente na rotina). No exemplo abaixo, alterei a propriedade GRP005 para 10 e executei novamente a rotina:

 

advpl-properties

 

Conclusão e bônus

 

Quem já conhecia esse tipo de código, com a utilização de getProperty (ou similares), deve estar sentindo falta do código para definir valores de propriedades (o setProperty). Essa função é utilizada quando precisamos definir valores dentro de um arquivo de propriedades (imagine algo como salvar a forma como usuário gosta da disposição dos componentes na tela). Inseri dentro do código de exemplo essa função também 🙂

 

É isso! Quando precisar desenhar telas complexas ou desenvolver relatórios que irão passar pela aprovação do usuário (muitas vezes, de posicionamento das informações), você pode poupar um bom tempo utilizando essas funções. Espero ter ajudado!

 

Abraços e até o próximo artigo

 

* Antes de começar um flame war nos comentários, eu sei que diversas linguagens tem esse tipo de classe/função. Porém, no meu caso, estou mais familiarizado com o JAVA…somente isso!

 

Sobre o Autor

Vinícius Gregório

Tecnólogo em análise de sistemas da informação pela Faculdade IBTA (SP), empreendedor da área de TI e consultor Protheus especializado em desenvolvimento AdvPL. Trabalhou em diversos projetos com os módulos de Compras, Estoque/Custos, Faturamento, Contabilidade Gerencial, Field Service/Gestão de Serviços, Financeiro, Gestão de Contratos e Gestão de Projetos. Atua também como desenvolvedor de sistemas web e é usuário fanático de sistemas e softwares open source... (vinicius.gregorio@academiaerp.com.br)

8 comentários

Deixe um comentário

Dúvida?