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!!!

3 comentários:

  1. Olá Neyton,
    Não consegui fazer a rotina que verifica se um dos blocos inseridos foi apagado funcionar. Sempre aparece o erro:
    ; error: bad argument type: VLA-OBJECT nil
    Inserir 9 blocos e o texto foi incrementado para 9. Porém ao apagar 4 blocos a mensagem de erro sempre aparece e a quantidade, no texto, não é reduzida.
    O que poderia ser?
    ---
    Parabéns pelo ótimo Blog!!
    []s

    ResponderExcluir
  2. fiz um teste aqui, o texto selecionado deve ser um TEXT e não um MTEXT, ok?

    no mais funcionou...

    ResponderExcluir
  3. Olá neyton

    procurava por esse lisp a tempos, porem frustrei-me com a tentativa pelo erro qpresentado a seguir:

    Command: teste
    Qual o nome do bloco que será monitorado?crt1_b1
    Selecione um texto que será incrementado; error: no function definition:
    VLR-ACDB-REACTOR
    Command:

    sei que este post é muito antigo e não quero pertubar-te ainda mais com isso, mais esse lisp facilitaria muito meu trabalho. uso o AutoCAD 2010 e esse lisp pelo que é descrito é sensacional e nunca encontrei algo parecido.

    desde já lhe agradeço

    ResponderExcluir