Pular para o conteúdo principal

Postagem em destaque

🚀 Oferecendo Serviços Remotos de Desenvolvedor AdvPL e Mais 🖥️

🚀 Oferecendo Serviços Remotos de Desenvolvedor AdvPL e Mais 🖥️ Olá pessoal, Espero que este post encontre todos vocês bem! É com grande entusiasmo que compartilho que estou expandindo meus serviços como Desenvolvedor AdvPL para novos desafios e colaborações. Com mais de duas décadas de experiência sólida, minha jornada profissional tem sido enriquecedora, com a oportunidade de participar de projetos empolgantes ao longo dos anos. Agora, estou ansioso para trazer minha experiência e habilidades para novas equipes e projetos, trabalhando de forma remota. Minha expertise abrange não apenas AdvPL, mas também outras tecnologias-chave, incluindo JS, SQL, Infraestrutura e Otimização de Processos. Acredito que essa combinação de conhecimentos me permite oferecer soluções abrangentes e eficazes para uma variedade de necessidades de desenvolvimento. Acredito que a tecnologia tem o poder de transformar negócios e impulsionar o sucesso, e estou comprometido em ajudar meus clientes a alcançar seu

BlackTDN :: Tips & Tricks ~ ADVPL Class dbTree

image Segundo o TDN

Classe: DBTree Cria um objeto do tipo árvore de itens.


Vamos utilizar o exemplo, que gerou a imagem ao lado, obtido em: TDN :: Exemplo de DBTREE com algumas modificações para uso no IDE.

image

Usando o código acima, o protheus irá gerar, em seu client, uma tela com as seguintes características:

image


A imagem acima é a representação visual criada pela classe dbTree. Mas, por traz dessa imagem tem uma tabela de dados. Vamos Observar, através do IDE, as suas características:

image

O protheus, para montagem do Tree criará um arquivo temporário com a estrutura acima. Onde:

T_IDLIST É um numero sequencial e único para cada registro
T_IDTREE Representa o Node Pai
T_IDCODE Representa o Item do Tree
T_ISTREE “S” se for um NODE
T_PROMPT Descrição a ser apresentada 
T_CARGO Campo Auxiliar para permitir Localizar e Alterar o Prompt do Tree. No Exemplo CARGO contem um número sequencial de acordo com o número de item no Tree. Nesse caso saberei que o “Item 009” sempre terá T_CARGO como #0013. Utilizado, internamente, pelo metodo TreeSeek para localizar um item do dbTree.
T_BMP001 Armazena o Número da Imagem 1 utilizada na apresentação do dbTree para o Item em questão. Por exemplo, se o Tree for de uma árvore de diretórios imagem de uma “pasta fechada” quando “recolhido”.
T_BMP002 Armazena o Número da Imagem 2 utilizada na apresentação do dbTree para o Item em questão. Por exemplo, se o Tree for de uma árvore de diretórios imagem de uma “pasta fechada” quando “expandido”.
T_CLIENT Flag que identifica se o Client já foi atualizado com a informação corrente. Utilizado internamente para otimização na montagem da imagem referente ao Tree.

Essa tabela será indexada da seguinte forma:

Ordem Chave
1 T_IDLIST
2 T_IDTREE
3 T_IDCODE
4 T_IDCARGO

O nome do índice é criado baseado no nome do arquivo temporário onde o “prefixo” “S” é substituido e o “sufixo” correspondente à ordem do índice é adicionado.

Uma outra característica importante a notar é que o nome do arquivo, no dbTree corresponde ao seu ALIAS. Informação essa armazenada na propriedade cArqTree do objeto em questão.

image

Com isso, podemos afirmar que existem duas formas de se pesquisar registros nesse arquivo. A primeira usando o método odbTree:TreeSeek(cChave) e outra,  usando o Alias como em: (odbTree:cArqTree)->(dbSeek(cChave,.F.))

Ex.:

lFound := odbTree:TreeSeek(“#00013”). A questão é que o método TreeSeek() sempre irá usar a ordem correspondente ao T_CARGO. E, teremos um grande problema ao tentar usar GetCargo logo em Seguida; pois, pressupondo que TreeSeek ira posicionar o dbTree no item localizado, imaginamos que GetCargo() ira retornar a informação de T_CARGO corrente. O que não é verdade.

Observe:

image

Então, para muitas opções de pesquisa no dbTree prefiro usar dbSeek diretamente.

image

Uma delas para localizar os Nodes Superiores ou Todos os itens que pertence a um Node. Por exemplo. Para saber todos os itens relativos a “Menu 001” e, sabendo que T_CARGO do “Menu 001” é "#0001".

image

image

Observe acima que esse item é um Node Superior, pois T_ISTREE  está com “S”. Observe também, que T_IDTREE dele está como “0000000" caracterizando que ele faz parte do primeiro nível de Nodes. Então, neste caso para achar todos os itens que correspondam ao nível mais superior do dbTree faríamos.

image E assim por Diante. No caso acima localizamos os nodes principais. Para Encontrar seus filhos é só usar T_IDCODE na chave para pesquisa a T_IDTREE.

image

 image

Se o Node for um tree basta repetir a operação até !( lFound ) ou T_ISTREE em branco. Poderá montar uma função recursiva para isso. (Consulte o exemplo postado por Luis Lacombe: DbTree recursiva)

Vale lembrar que ao usar dbSeek diretamente o Tree Visual não será atualizado. Para isso deverá usar o Método TreeSeek da classe dbTree e que o método espera, como parâmetro T_CARGO.

Neste caso, poderia trabalhar em conjunto com dbSeek e TreeSeek criando sua própria função TreeSeek.

image

e usá-la como:

image

Neste caso, o exemplo completo ficaria como:

#include "protheus.ch"
#include "dbtree.ch"

#xtranslate USER PROCEDURE <p> => PROCEDURE U_<p>

USER PROCEDURE MyTree()

PUBLIC __TTSINUSE := .T. //(Para uso no IDE RecLock precisa disso)
PUBLIC __TTSPUSH := Array(0) //(Para uso no IDE RecLock precisa disso)
PUBLIC __cLogSiga := "" //(Para uso no IDE GravaLog em RecLock precisa disso)

// Cria um diálogo
DEFINE DIALOG oDlg TITLE "Teste de DBTree" FROM 10,10 TO 400,700 COLOR CLR_BLACK,CLR_WHITE PIXEL

// Cria o DbTree no diálogo, ocupando o tamanho total do mesmo
DEFINE DBTREE odbTree FROM 00,00 TO oDlg:nHeight,oDlg:nWidth OF oDlg CARGO

DBADDTREE odbTree PROMPT "Menu 001" RESOURCE "BMPTABLE" CARGO "#0001"
DBADDITEM odbTree PROMPT "Item 001" RESOURCE "BMPSXG" CARGO "#0002"
DBENDTREE odbTree

DBADDITEM odbTree PROMPT "Item 002" RESOURCE "BMPTRG" CARGO "#0003"
DBADDITEM odbTree PROMPT "Item 003" RESOURCE "BMPCONS" CARGO "#0004"
DBADDITEM odbTree PROMPT "Item 004" RESOURCE "BMPPARAM" CARGO "#0005"
DBADDTREE odbTree PROMPT "Menu 002" OPENED RESOURCE "BMPTABLE" CARGO "#0006"
DBADDITEM odbTree PROMPT "Item 005" RESOURCE "BMPSXG" CARGO "#0007"
DBADDTREE odbTree PROMPT "Menu 003" OPENED RESOURCE "BMPTABLE" CARGO "#0008"
DBADDITEM odbTree PROMPT "Item 006" RESOURCE "BMPSXG" CARGO "#0009"
DBADDTREE odbTree PROMPT "Menu 004" OPENED RESOURCE "BMPTABLE" CARGO "#0010"
DBADDITEM odbTree PROMPT "Item 007" RESOURCE "BMPSXG" CARGO "#0011"
DBENDTREE odbTree
DBADDITEM odbTree PROMPT "Item 008" RESOURCE "BMPSXG" CARGO "#0012"
DBENDTREE odbTree
DBENDTREE odbTree
DBADDITEM odbTree PROMPT "Item 009" RESOURCE "BMPSXB" CARGO "#0013"

ACTIVATE DIALOG oDlg CENTER ON INIT ShowNodeTree(@odbTree)

Return

Static Function ShowNodeTree(odbTree)

Local cCargo

IF TreeSeek( @odbTree , "#0013" , "T_CARGO" , @cCargo )
MsgInfo( "Seek: " + cCargo + CRLF + "GetCargo: " + odbTree:GetCargo() )
EndIF

Return( TreeSeek( @odbTree , "#0013" , "T_CARGO" , @cCargo ) )

Static Function TreeSeek( odbTree , cKeySeek , cIndexKey , cCargo )

Local aIndexes := Array(0)

Local cAliasTree := odbTree:cArqTree
Local cKey := ""

Local lFound := .F.

Local nOrder

(cAliasTree)->(aEval(Array(10),{|x,y|cKey:=IndexKey(y),IF(!Empty(cKey),aAdd(aIndexes,{y,cKey}),NIL)}))

DEFAULT cIndexKey := "T_CARGO"
cIndexKey := Upper( AllTrim( cIndexKey ) )

nOrder := aScan( aIndexes , { |aBag| aBag[2] == cIndexKey } )
IF ( nOrder == 0 )
cIndexKey := "T_CARGO"
nOrder := aScan( aIndexes , { |aBag| aBag[2] == cIndexKey } )
EndIF

(cAliasTree)->( dbSetOrder( aIndexes[nOrder][1] ) )
lFound := ( cAliasTree )->( dbSeek( cKeySeek , .F. ) )

IF ( lFound )
lFound := odbTree:TreeSeek( ( cAliasTree )->T_CARGO )
IF ( lFound )
cCargo := odbTree:GetCargo(( cAliasTree )->T_IDCODE)
EndIF
EndIF

Return( lFound )



Após a execução do exemplo, o Ponteiro do dbTree estará posicionado no item cujo T_CARGO corresponda a “#0013” ´que é o “Item 009”


image

Dica de última Hora: Utilize ClassMethArr( oDbTree ) para obter todos os Métodos da Classe dbTree.

image
e __ClsArr() para os Métodos da Classe Base TTree.  Para a build abaixo seu índice é o 117

image
image 
image


[]s
иαldσ dj

Comentários

  1. Rapaz... Me indica onde você aprende essas coisas!

    Muito obrigado pela dica, você consegue transmitir a informação com muita clareza e objetividade.

    Parabéns

    ResponderExcluir
    Respostas
    1. Renato,

      uma coisa que faltou mencionar e que é fundamental para a solução definitiva do seu problema é que você pode implementar a sua própria dbTree a partir da dbTree padrão implementado os métodos necessários para o seu dia a dia. Experimente criar a sua. CLASS RB_dbTree FROM dbTREE.

      Excluir
    2. Você dizendo fico até feliz, pois foi exatamente o que tinha feito. Extendi a classe DBTree criando o atributo aIdPai, que é um vetor com o currentNodeId do nó atual e do nó pai, se você permitir eu posto o fonte aqui para compartilhar. Só está com um problema na hora que vou instanciar que não está identificando o o atributo no construtor.

      Excluir
    3. #include "totvs.ch"

      /***************************************************************
      ****************************************************************
      Classe TExTree
      Arvore Extendida para tratar o avanço entre os nós filhos e
      relacionar um nó filho com o nó pai
      Criado por: Renato de Bianchi
      Em: 25/04/2012
      ****************************************************************
      ***************************************************************/
      user function TExTree
      return

      class TExTree from DbTree
      data aIdPai //ID do nó pai

      method New(nTop,nLeft,nBottom,nRight,oWnd,bChange,bRClick,lCargo,lDisable,oFont) constructor //Construtor
      method GoToUp() //Posiciona nó pai do nó posicionado
      method GoToTop() //Posiciona nó raiz do nó selecionado
      method GetNumSon() //Obtêm o número de nós filhos de um nó selecionado

      //Metodos sobreescritos
      method AddItem(cPrompt,cCargo,cRes1,cRes2,cFile1,cFile2,nTipo)
      method AddTree(cPrompt,lOpened,cRes1,cRes2,cFile1,cFile2,cCargo)
      method AddTreeItem(cPrompt,cRes,cFile,cCargo)
      method DelItem()

      endClass

      method New(nTop,nLeft,nBottom,nRight,oWnd,bChange,bRClick,lCargo,lDisable,oFont) class TExTree
      :New(nTop,nLeft,nBottom,nRight,oWnd,bChange,bRClick,lCargo,lDisable,oFont)

      ::aIdPai := {}
      return SELF

      method GetNumSon() class TExTree
      local nSon := 0
      local nTotNode := len(aIdPai)
      local cSeekID := SELF:CurrentNodeId

      if nTotNode > 1

      for nI := 1 to nTotNode
      if ::aIdPai[nI,1] == cSeekID
      nSon++
      endIf
      next

      endIf
      return nSon

      method GoToUp() class TExTree
      nPos := aScan(::aIdPai, {|x| x[2]==SELF:CurrentNodeID} )
      if nPos > 0
      if ::aIdPai != nil .and. ::aIdPai[nPos,1] != "" .and. ::aIdPai[nPos,1] != " "
      SELF:ptGotoToNode(::aIdPai[nPos,1])
      endIf
      endIf
      return nil

      method GoToTop() class TExTree
      nPos := aScan(::aIdPai, {|x| x[2]==SELF:CurrentNodeID} )
      if nPos > 0
      while ::aIdPai != nil .and. ::aIdPai[nPos,1] != "" .and. ::aIdPai[nPos,1] != " "
      SELF:GoToUp()
      nPos := aScan(::aIdPai, {|x| x[2]==SELF:CurrentNodeID} )
      endDo
      endIf
      return nil

      method AddItem(cPrompt,cCargo,cRes1,cRes2,cFile1,cFile2,nTipo) class TExTree
      :AddItem(cPrompt,cCargo,cRes1,cRes2,cFile1,cFile2,nTipo)
      cIdPai := SELF:CurrentNodeId

      SELF:TreeSeek(cCargo)
      aAdd(::aIdPai, {cIdPai, SELF:CurrentNodeId})
      return

      method AddTree(cPrompt,lOpened,cRes1,cRes2,cFile1,cFile2,cCargo) class TExTree
      :AddTree(cPrompt,lOpened,cRes1,cRes2,cFile1,cFile2,cCargo)

      SELF:TreeSeek(cCargo)
      aAdd(::aIdPai, {" ", SELF:CurrentNodeId})
      return

      method AddTreeItem(cPrompt,cRes,cFile,cCargo) class TExTree
      :AddTreeItem(cPrompt,cRes,cFile,cCargo)
      cIdPai := SELF:CurrentNodeId

      SELF:TreeSeek(cCargo)
      aAdd(::aIdPai, {cIdPai, SELF:CurrentNodeId})
      return

      method DelItem() class TExTree
      cIdDel := SELF:CurrentNodeId

      _Super:Método()
      aDel(::aIdPai, aScan(::aIdPai, {|x| x[2]==cIdDel} ) )
      return

      Excluir
    4. Naldo, vc chegou a fazer um teste com o fonte acima?
      É necessário alterar o trecho:

      _Super:Método() para _Super:DelItem()

      Mas não sei se a classe DbTree é diferente ou está marcada como final, pois ela não identifica o atributo que eu incluo e não sobrescreve os métodos, se possível faça um teste com o código acima por favor.

      Obrigado

      Excluir
    5. Renato, tem um "gatinho" na dbTree que não permite a herança direta. Então, depois de muito tentar, resolvi pera herança indireta. Exemplo no blog.

      Excluir
  2. Naldo, boa TARDE

    Essa tela do seu editor de fontes , com um fundo preto, e letras coloridas, voce teria esse template, para me enviar.
    somente para fujir do padrão de Editor. Sei que daria para configurar, porem , se tivesse um template seria mais rapido.

    abs

    ResponderExcluir

Postar um comentário

Postagens mais visitadas deste blog

BlackTDN :: RLeg ~ Desvendando a Função ParamBox

Para quem precisar desenvolver uma interface de entrada de dados, coisa rápida, e não quer ter aquele trabalhão danado que todos já sabemos, o Protheus tem uma função que ajuda muito, é uma interface semelhante a função Pergunte, porém com muito mais opção de objeto de entrada de dados, alias até colocar o scrollbox desta interface com todos os objetos em outra MsDialog ou Wizard é simples. Vejam o exemplo abaixo, boa sorte! Rleg. //---------------------------------------------------------- // Função exemplo utilizando a função ParamBox() //---------------------------------------------------------- User Function xParamBox() Local aRet := {} Local aParamBox := {} Local aCombo := {"Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"} Local i := 0 Private cCadastro := "xParambox" // ---------------

BlackTDN :: Customizando a interface de Login no Protheus e by You

A publicação “ BlackTDN :: By You e sua nova tela de login ”  de nosso amigo OBona deu o que falar e, em função disso, esse que a muito não vos escreve resolveu criar uma versão onde será possível personalizar, “por completo”, a tela de login no Protheus/by You. Considerando que OBona já havia “mapeado, identificado e customizado” as imagens peguei-as emprestadas para o exemplo que se segue: O primeiro passo para a customização “total” da interface de login do Protheus/by You será implementar o “Ponto de Entrada” ChgPrDir (Diretório de impressão) . Usaremos esse PE juntamente como programa U_FindMsObject.prg (apresentado pela primeira vez em: Protheus :: ADVPL : The Container : Presents Pandora's box ). Diferente do exemplo proposto por OBona, que substitui, durante o processo de compilação, as imagens padrões do sistema (excluindo-as) por imagens customizadas (com o mesmo nome) este novo exemplo mantém, no RPO, as imagens padrões adicionando novas imagens customizadas que serã

Protheus :: Chamando Funções do Menu Diretamente e sem a Necessidade de Login

Ferne$ perguntou: "...é possível abrir alguma rotina do sistema sem solicitar login ao usuário, como por exemplo a rotina MATA010..." Sim Ferne$, é possível sim. Abaixo um Exemplo para a Chamada à função MATA010 sem a necessidade de Login no sistema. #INCLUDE "PROTHEUS.CH" #INCLUDE "TBICONN.CH" /*/ Funcao: MATA010Ex Data: 30/04/2011 Autor: Marinaldo de Jesus Descricao: Executar a Funcao MATA010 diretamente sem a necessidade de LOGIN no Protheus Sintaxe: 1 ) U_MATA010Ex ( Chamada diretamente na Tela de Entrada do Sistema ) ; ou 2 ) totvsclient.exe -q -p=u_MATA010Ex -a=01;01 -c=rnp_local -e=rnp -m -l ( Chamada Via Linha de Comando ) /*/ User Function MATA010Ex( cEmpFil ) Local aEmpFil Local bWindowInit := { || __Execute( "MATA010()" , "xxxxxxxxxxxxxxxxxxxx" , "MATA010" , "SIGAFAT" , "SIGAFAT", 1 , .T. ) } Local cEmp Local cFil Local cMod Local cModName := "SIGAFAT" DEFA