O Autolisp não está configurando o atributo de bloqueio corretamente para alguns usuários
Questionador da primeira vez, então espero que eu esteja descrevendo o problema bem o suficiente.
Temos um código Autolisp em nossa empresa que é utilizado por várias pessoas com a mesma versão do AutoCAD, mas para alguns usuários o lisp parou de funcionar corretamente.
A função do lisp é a seguinte:
- o usuário executa o lisp
- o programa pede as seguintes coisas:
- escala do bloco
- prefixo para texto em bloco
- o número de execução para a primeira entidade de bloco
- o incremento do número em execução
- onde colocar o primeiro bloco
Isso deve levar a um bloco com um marcador e um texto com o seguinte formato (prefixo) (possível seção intermediária se o número não consistir em três números) (o número contínuo), por exemplo, PT001 ou PX100.
Em vez de fazer isso, no entanto, alguns dos usuários experimentaram a falta de prefixo e número e começaram a ver apenas a seção intermediária possível do texto mencionada, enquanto outras vezes o mesmo usuário pode perceber que apenas o prefixo é mostrado. O marcador é exibido como deveria, mas o texto simplesmente não funciona como esperado.
Qualquer ajuda na análise do código abaixo em busca de falhas é muito apreciada.
Se o código parecer "perfeito", presumo que haja um problema com o bloco ou seus atributos.
-E
(defun c:pointnumber()
(setvar "ATTDIA" 0)
(setq sc (getreal "\nEnter scale: "))
(setq px (getstring "\nSet prefix for point number: "))
(setq nr (getint "\nThe number for the first point: "))
(setq ic (getint "\nIncrement of the number: "))
(setq point 1)
(while (/= point nil)
(setq point (getpoint "\nChoose a point: "))
(if (/= point nil)
(progn
(setq inr (itoa nr))
(if (< nr 100) (setq md "0"))
(if (< nr 10) (setq md "00"))
(if (> nr 99) (setq md ""))
(setq ph (strcat px md inr))
(command "insert" "pointnumber" point sc sc 0 ph)
(setq nr (+ nr ic))
)
)
)
(setvar "ATTDIA" 1)(princ)
)
Respostas
Há uma série de problemas com seu código atual: alguns dos quais podem ser meramente considerados uma prática ruim, alguns farão com que o programa falhe se o usuário responder com dados inválidos e outros farão com que o programa falhe ou se comporte de forma inesperada, dependendo do configurações do ambiente AutoCAD em que o programa é executado.
1. ATTREQ
O principal culpado pelo comportamento que você descreveu provavelmente é a ATTREQvariável do sistema, que determina se o usuário receberá solicitações de valores de atributo como parte do INSERTcomando. Se, ATTREQ=0
quando o programa for executado, o bloco for inserido com seus valores de atributo padrão.
Você pode garantir um comportamento consistente entre os ambientes, armazenando o valor atual desta variável do sistema e definindo-o 1
antes de chamar o INSERTcomando (para garantir que os prompts de atributo sejam emitidos) e, em seguida, restaurando o valor original após o comando ou no final do programa.
Por exemplo:
(defun c:test ( / atr )
(setq atr (getvar 'attreq))
(setvar 'attreq 1)
;; ... Do your thing
(setvar 'attreq atr)
(princ)
)
2. OSMODE
Ao fornecer dados de ponto a um comando por meio do AutoLISP, o ponto será afetado por quaisquer modos de snap a objeto ativos no momento em que o ponto for fornecido. Eu descrevo isso com mais detalhes em minha resposta aqui .
A maneira mais fácil de evitar isso é usando o modificador de snap ao objeto "_non"
ou "_none"
para instruir o AutoCAD a ignorar todos os modos de snap ao objeto para a entrada de ponto subsequente, por exemplo:
(command "insert" "pointnumber" "_non" point sc sc 0 ph)
3. Entrada do usuário
Você deve levar em conta a falta de entrada do usuário ou entrada inválida do usuário para evitar erros durante a execução do programa - isso é facilmente alcançado através do uso de if
instruções ou da initget
função, por exemplo:
(initget 7) ;; Prevents Enter, zero, or negative numbers
(setq sc (getreal "\nEnter scale: "))
Ou:
(initget 6)
(if
(and
(setq sc (getreal "\nEnter scale: "))
(setq px (getstring "\nSet prefix for point number: "))
(setq nr (getint "\nThe number for the first point: "))
(setq ic (getint "\nIncrement of the number: "))
)
;; ... Do your thing
)
Como alternativa, você pode configurar os valores padrão para cada um desses prompts, usando um dos métodos que descrevo em meu tutorial sobre Solicitação com uma opção padrão , por exemplo:
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0)))
4. Variáveis locais vs. globais
Atualmente, todas as variáveis em seu programa são variáveis globais : ou seja, elas são definidas no namespace do documento (desenho) e manterão seus valores mesmo depois que o programa tiver concluído sua execução.
Conforme descrevo em meu tutorial sobre localização de variáveis , isso pode causar problemas se tais variáveis inadvertidamente compartilharem seus nomes com variáveis globais usadas por outros programas, ou quando um programa está construindo uma lista ou outra estrutura de dados acumulativos dentro de um loop.
A menos que o uso de uma variável global seja necessário para o funcionamento correto do programa, sugiro declarar essas variáveis locais para a função, por exemplo:
(defun c:pointnumber ( / ic inr md nr ph point px sc ) ;; Local variables
;; ...
)
5. Verificando a existência do bloco
Fornecer o nome do bloco diretamente para o INSERTcomando pressupõe que uma definição desse bloco já exista no desenho ativo ou que um desenho com esse nome de arquivo exista no diretório de trabalho ou em um Caminho de pesquisa de arquivo de suporte do AutoCAD - se nenhuma das condições for atendida, o INSERTcomando apresentará um erro durante a execução do programa.
Você pode, portanto, testar essas condições de antemão, notificando o usuário se o bloco não for encontrado, caso contrário, proceder à execução do restante das operações:
(if
(or
(tblsearch "block" "pointnumber") ;; Checks for existing definition
(findfile "pointnumber.dwg") ;; Checks for drawing file
)
;; ...
)
Você também pode usar a cond
função no lugar de uma sequência de if/else
expressões.
6. Reinicialização do ambiente em caso de erro
Uma vez que você está alterando os valores das variáveis do sistema durante a execução do programa, você deve garantir que o ambiente AutoCAD do usuário seja redefinido para seu estado original no caso de um erro durante a execução do programa - observando que o usuário pressionando Escpara sair do programa também resultará em um erro.
Você pode fazer isso definindo um manipulador de erros local, conforme descrevo em meu tutorial sobre Tratamento de erros . A função de erro local é avaliada se um erro for encontrado durante a execução do programa e, portanto, você pode incluir expressões dentro da definição desta função para redefinir o ambiente AutoCAD ao seu estado original - no seu caso, isso envolveria redefinir o valor original do ATTDIA
variável do sistema.
Juntando tudo
;; Define function, declare local variables
(defun c:pointnumber ( / *error* bn ic nr ns pt px sc vl vr )
;; Define local error function to reset system variables on error
(defun *error* ( msg )
(mapcar 'setvar vr vl) ;; Reset list of system variables
(if (not (wcmatch (strcase msg t) "*break,*cancel*,*exit*"))
(princ (strcat "\nError: " msg))
) ;; end if
(princ)
) ;; end defun
;; Define block name
(setq bn "pointnumber")
(if (or (tblsearch "block" bn) ;; Definition in drawing
(findfile (strcat bn ".dwg")) ;; Drawing file in support path
) ;; end or
(progn
(initget 6) ;; Prevents 0 & negatives
(setq sc (cond ((getreal "\nSpecify scale <1.0>: ")) (1.0))
px (getstring "\nSpecify prefix <none>: ")
) ;; end setq
(initget 4) ;; Prevents negatives
(setq nr (cond ((getint "\nSpecify starting number <1>: ")) (1)))
(initget 6) ;; Prevents 0 & negatives
(setq ic (cond ((getint "\nSpecify increment <1>: ")) (1)))
(setq vr '(attreq attdia cmdecho) ;; List of system variables
vl (mapcar 'getvar vr) ;; Store current values
) ;; end setq
(mapcar 'setvar vr '(1 0 0)) ;; Set system variables appropriately
(while (setq pt (getpoint "\nSpecify point <exit>: "))
(setq ns (itoa nr)
nr (+ nr ic)
)
(repeat (- 3 (strlen ns)) (setq ns (strcat "0" ns))) ;; Pad to 3 digits
(command "_.-insert" bn "_S" sc "_R" "0" "_non" pt (strcat px ns))
) ;; end while
(mapcar 'setvar vr vl) ;; Reset list of system variables to their original values
) ;; end progn
;; Else the block was not defined/found
(princ (strcat "\nThe block \"" bn "\" is not defined in the active drawing and cannot be found."))
) ;; end if
(princ) ;; Suppress the value returned by the last evaluated expression
) ;; end defun
Existem outras melhorias possíveis que também podem ser implementadas, tais como:
Remoção da dependência de chamadas para comandos padrão do AutoCAD (neste caso, o INSERTcomando) por meio do uso do
insertblock
método ActiveX ou das funçõesentmake
/entmakex
para gravar dados DXF diretamente no banco de dados de desenho.Preencher atributos referenciando seus nomes de tag de atributo para remover a dependência da ordem em que as referências de atributo são encontradas na referência de bloco (que pode ser modificada em uma base por desenho por meio do uso do BATTMANcomando).
Usando um 'padrão dinâmico' (conforme descrito em meu tutorial ) e potencialmente armazenando o valor dos padrões entre as sessões de desenho.