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

movetotop

Faz tempo que não posto nada, por isso aí vai uma rotinazinha simples que usa o método MoveToTop do activex, ou seja, vla-movetotop. Na verdade é o mesmo que um draworder trazendo a entidade pra frente... mas vai servir de exemplo para quem ainda não sabe como usar safearray
vera rotina...
(defun movetotop (ent / tmp sort)
  (if (and vla-movetotop (setq ent (ename-of ent))) ;cad2007 e a entidade existe?
    (progn 
      (setq tmp (vla-GetExtensionDictionary (get-activespace))
            ent
 (vlax-ename->vla-object ent))
      (if (vl-catch-all-error-p ;pega ou cria o dictionary do draworder
            (setq sort (vl-catch-all-apply 'vla-item (list tmp "ACAD_SORTENTS"))))
        (setq sort (vla-AddObject tmp "ACAD_SORTENTS" "AcDbSortentsTable")))
      (vla-movetotop sort
        (vlax-make-variant ;|cria uma array com a entidade... se tivesse uma
selectionset, teria de trasformar ela numa lista e substituir abaixo|;

            (vlax-safearray-fill 
              (vlax-make-safearray ;cria uma safearray
                vlax-vbObject ; de vla
               '(0 . 0)) ;com a dimensao 0 igual a 0 (uma entidade)
              (list ent)))) ;lista a ser usada para preeencher a safearray
      (mapcar 'vlax-release-object (list ent sort tmp)) ;discutivel a utilidade disso
)))

Link(s) da(s) subrotina(s) usada(s):ename-of, get-activespace