Mostrando postagens com marcador reactors. Mostrar todas as postagens
Mostrando postagens com marcador reactors. Mostrar todas as postagens

Reactor e Xdata; Coisas de doido

Por estes dias me perguntaram como usar reactors. Bem, não é complicado, mas o help não ajuda muito... enfim... Considere o exemplo abaixo:

;nome da xdata
(setq xdata:nome "TESTEXDATAREACTOR")

;carrega funções vl*
(vl-load-com)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;subrotina para adicionar xdata na entidade
;ent -> ename, por exemplo: <Entity name: 7ffff4104e0>
;xdappname -> string, nome da xdada
;xdata elista da xdata, formato:
;((ExtendDataDxfCode . Valor) ... (ExtendDataDxfCode . Valor))
(defun xdata->put  (ent xdappname xdata / elist)
  ;garanta que o nome da xdata esta no banco de dados do cad
  (regapp xdappname)
  

  ;adiciona a xdata no elist da entidade
  (setq elist (entget ent)
    elist (cons (list -3 (cons xdappname xdata)) elist))

  ;atualiza a entidade
  (entmod elist)
  (
entupd ent)
  )


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;reactor para ação da linha sendo modificada
;note que este reactor não causa modificações na LINHA, apenas no texto
;se fazemos um reactor causar modificações em algo que dispara reactors,
;temos de desabilitar eles antes de modificar
;parametros:
;linha, é o objeto que notificou o reactor, no caso a linha
;rea, é o ponteiro para o objeto do reactor
;par, são parâmetros adicionais que podem ser passados ao reactor
;na hora que o criamos, no caso, não passamos nenhum dado extra
;então não usamos o seu valor, que será nil
(defun reactor->modified (linha rea par / params texto)
  ;obter os dados do reactor. note que não seria necessario
  ;pois poderiamos encontrar o texto pelo xdata.
  ;no caso é só para mostrar o uso da função vlr-data
  ;para obter o texto pela xdata, veja a subrotina reactors->aciona
  (setq params (vlr-data rea)
    texto  (car params))

  ;atualizar o conteudo do texto:
  (vla-put-textstring
    texto
    (strcat "l=" (rtos (vla-get-length linha) 2 2)))

  (
vla-put-insertionpoint texto (vla-get-startpoint linha))
  )


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;subrotina para "ligar" os reators para as linhas já desenhadas
;em um desenho com o nosso novo comando "teste"
(defun reactors->aciona (/ ss ent linha xdata texto)
  ;seleção das linhas que tem a nossa xdata
  (setq ss (ssget "X" (list
            '(0 . "LINE")
            (
list -3 (list xdata:nome)))))

  ;se ela existir, processa as linhas
  (if ss
    (repeat (sslength ss)

      ;pega a primeira linha
      (setq ent   (ssname ss 0) ;ename
        linha (vlax-ename->vla-object ent) ;ename para vla
        xdata (assoc -3 (entget ent (list xdata:nome))) ;xdata da linha
        ;agora, pega o handle, que é string, acha o ename e obtem o vla
        texto (vlax-ename->vla-object (handent (cdr (cadadr xdata))))
        )



      ;cria o reactor para a linha
      (vlr-object-reactor
          (list linha) ;objeto modificado que notifica o reactor
          (list texto) ;parametros a serem passados
          '((:vlr-modified . reactor->modified)))

      ;tira a linha da seleção e reinicia o looping
      (ssdel ent ss)
      )
    )

  ;perfumaria:
  (princ "\nReactors criados")
  )




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;função principal
(defun c:teste (/ linha texto pa pb thisdrawing model xdata)

  ;desenha uma lina e um texto no modelspace:
  (setq pa          (getpoint "\nPa")
    pb          (getpoint pa "\nPb")
    

    ;obtem o ponteiro para o modelspace
    thisdrawing (vla-get-activedocument(vlax-get-acad-object))
    model       (vla-get-modelspace thisdrawing)

    ;cria uma linha no model
    linha       (vla-addline model
              (vlax-3d-point pa)
              (
vlax-3d-point pb))

    ;cria um texto no model
    texto       (vla-addtext model
              (strcat "l=" (rtos (vla-get-length linha) 2 2))
              (
vlax-3d-point pa)
              4)
     )


  ;vincula a linha ao texto usando um xdata
  ;1005 é o dxfcode de xdata para "handle" da entidade
  (xdata->put
    (vlax-vla-object->ename linha) ;ename da linha
    xdata:nome ;nome da xdata
    (list (cons 1005 (vla-get-handle texto))) ;a xdata propriamente dita
    )
  


  ;cria um reactor para linha, associando ao evento de modificar a linha
  (vlr-object-reactor
          (list linha) ;objeto modificado que notifica o reactor
          (list texto) ;parametros a serem passados
          '((:vlr-modified . reactor->modified) ;evento a ser chamado ao disparar o reactor
        ))

  ;perfumaria
  (princ "\nFeito")
  )



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;sempre que a lisp for carregada com APPLOAD,
;será acionada a função 'reactors->aciona' para "ligar" os reactors nas linhas criadas
;com o comando que criamos
(reactors->aciona)


O exemplo pede dois pontos, por ele faz passar uma linha e cria um texto no inicio da linha.

Na linha, cria um vinculo com o texto atravez de xdata.

Por fim, cria um reactor, que é um evento. Este evento modifica o conteúdo e a posição do texto conforme modificamos a linha. Simples, não?

O evento que você informa no reactor é monitorado pelo cad e quando acontece o evento, o reactor dispara uma função que informamos.

Igual aos eventos do VBA ou VB. Claro que o exemplo é bem simples, ainda falta monitorar o que acontece se apagamos o texto por exemplo.

O reactor não teria o que atualizar e um erro seria mostrado.

Outro exemplo: Se copiássemos a linha, o texto também poderia ser copiado.....

Outro exemplo: Se editássemos o texto, o reactor não faria nada até modificar a linha.
Neste caso, poderíamos monitorar edições no texto.

Se o fizermos, temos de ter em mente que se precisar modificar o texto, o reactor que o monitora precisa ser desligado antes de editar ele....

Para esta situação, procure por reactor no meu blog. Tem algumas rotinas bem legais!!!

vlr-acdb-reactor

Bom, já ouviram falar de reactor, certo? então vou apresentar um exemplo simples de um reactor que reaje aos eventos adicionar/apagar entidades do autocad. Funciona assim: temos um bloco qualquer que iremos monitorar no desenho, atualizando a quantidade dele num texto pré-selecionado, de forma automática, isto é, sem termos de editar este texto manualmente!!
Isto é possível com o reactor "vlr-acdb-reactor" monitorando os eventos ":vlr-objectAppended" e ":vlr-objectErased" apenas.
Claro, monitoramos apenas um tipo de bloco, mas nada impede que adaptemos o código para monitorar qualquer quantidade de blocos... clique para ver a rotina:
mais...
(setq *lista_adicionados* nil) ;inicializa a lista de blocos

(defun c:teste (/ blc_name txt)
;pede o nome do bloco e pede para selecionar um texto a ser incrementado:
  (if (setq blc_name (getstring "\nQual o nome do bloco que será monitorado?" t))
    (
if (setq txt (car (entsel "\nSelecione um texto que será incrementado")))
;tendo as duas informações, cria um reactor:
      (vlr-acdb-reactor
;dados do reactor: nome do bloco e a HANDLE do texto
    (list (strcase blc_name)  (cdr (assoc 5 (entget txt))))
;eventos & ações deste reactor:
    '((:vlr-objectAppended . objeto-adicionado)
      (
:vlr-objectErased . objeto-apagado)))))
;sai... fim do programa
  (princ)
  )


;-------------------------ações do nosso reactor:-------------------------
;ação para entidade adicionada no autocad:
(defun objeto-adicionado (rea ;o reactor em si
              par ;parametros passados pelo reactor
              /  ent name data blc_name txt qtd)
;ENAME da entidade que foi adicionada à base de entidades do cad:
  (setq ent      (cadr par)
;se for um bloco, terá o DXF 2, que é o nome do bloco:
    name     (cdr (assoc 2 (entget ent)))
;precisamos saber que tipo de entidade foi adicionada:
    tipo     (cdr (assoc 0 (entget ent)))
;dados do reactor, passamos estes valores no c:teste
;na linha (list (strcase blc_name) ...
    data     (vlr-data rea)
;nome do bloco que monitoramos
    blc_name (car data)
;handle do texto que queremos incrementar:
    txt      (cadr data))
;teste se é um bloco e se é do nome que interessa:
  (if (equal "INSERT" tipo)
    (
if (equal (strcase name) blc_name)
;o texto ainda existe?
      (if (setq txt (handent txt))
;entao incrementa ele:
    (progn
      (setq txt (vlax-ename->vla-object txt) ;conversao
        qtd (atoi (vla-get-textstring txt)) ;quantidade atual
        qtd (1+ qtd);incrementa
;aqui, temos que gravar a ENAME do novo bloco numa lista, para podermos saber
;depois se ele for apagado:
        *lista_adicionados* (cons ent *lista_adicionados*))
;altera o valor do texto e atualiza ele:
      (vla-put-textstring txt (itoa qtd))
      (
vla-update txt)
;libera o VLA da memoria:
      (vlax-release-object txt))))))

;ação para objeto APAGADO:
(defun objeto-apagado (rea par / ent data blc_name txt qtd)
;mesmas considerações do evento acima...
  (setq ent      (cadr par)
    data     (vlr-data rea)
    txt      (cadr data))
; a entidade apagada é um dos blocos que adicionamos??
  (if (member ent *lista_adicionados*)
;entao, se o texto ainda existir:
     (if (setq txt (handent txt))
       (
progn
;decrementa a quantidade e tira o ENAME da lista...
     (setq txt (vlax-ename->vla-object txt)
           *lista_adicionados* (vl-remove ent *lista_adicionados*)
           qtd (atoi (vla-get-textstring txt))
           qtd (1- qtd))
;atualiza o texto:
     (vla-put-textstring txt (itoa qtd))
     (
vla-update txt)
;libera o VLA da memoria:
     (vlax-release-object txt)
     ))))

Para os que viram a rotina, seria interessante ainda tornar o reactor persistente, assim, ao fecharmos o desenho e abrirmos ele novamente, o reactor estará ativo sem que precisemos criar ele novamente, pois da forma que apresentei o exemplo, ao fechar o desenho, o reactor é destruído, e presisaríamos recriar ele a cada "open" que déssemos no desenho... para isso veja o help para "vlr-pers"
Particularmente eu não uso o "vlr-pers" pois se nao mantivermos a rotina no start up suite (ou no acad.lsp), se esta nao estiver carregada, com o reactor no modo persistente, a cada alteração no desenho veríamos uma mensagem na linha de comando dizendo que a função XXX não está definida... isso é irritante, ainda mais se mandarmos o desenho pro cliente!!! ele seguramente não terá a rotina carregada!!
Ao invés desta solução, prefiro criar uma rotina que ao ser carregada, inicialize um reactor novinho em folha no modo nao persistente, assim, ao fechar o desenho ele é destruído... e não me encomodará o cliente!!!