DOCE.9. UTILIDADES VARIAS (EL OBJETO
Utility)Como podemos comprobar al observar la tabla jerárquica de objetos y colecciones del principio del MÓDULO, existe un objeto de utilidad que es descendiente directo del documento actual activo. Este objeto llamado
Utility, posee una serie de métodos que nos harán la vida más fácil a la hora de programar, ya que podremos acceder a ellos para solicitar puntos, convertir coordenadas, utilizar marcas de inicio y fin de DESHACER, etcétera.La manera de llamar a este objeto y a sus métodos es idéntica a la ya utilizada para los demás objetos. Lo lógico será, si vamos a utilizarlo mucho, que creemos una variable para acceder a él de forma simple, como hemos venido haciendo. Por ejemplo, podemos declarar así una serie de variables de objeto:
Option Explicit Dim AcadDoc As Object Dim AcadModel As Object Dim AcadUtil As Object y luego inicializarlas así: Set AcadDoc = GetObject(, "AutoCAD.Application").ActiveDocument Set AcadModel = AcadDoc.ModelSpace Set AcadUtil = AcadDoc.Utility
Como vemos, el objeto de utilidad desciende directamente del de documento activo, por lo que la forma de inicializarlo es similar a la de la colección de objetos de Espacio Modelo por ejemplo, como se ve (están en el mismo nivel).
Pasemos directamente a ver la lista de propiedades y métodos de este objeto, para después estudiarlos detenidamente.
Propiedades del objeto de utilidad:
Application
Métodos del objeto de utilidad:
AngleFromXAxis
Como propiedad tenemos, como siempre,
Application, que es común a todos los objetos VBA. Los diversos métodos serán los que se expliquen a continuación, como venimos haciendo hasta ahora.·
AngleFromXAxis. Este método obtiene el ángulo comprendido entre la línea o vector determinado por los dos puntos que se suministran como argumentos, y el eje X actual del dibujo, siempre en sentido trigonométrico o antihorario y en radianes. La sintaxis para AngleFromXAxis es:DblÁnguloDesdeX
= ObjUtilidad.AngleFromXAxis(DblPtoInic, DblPtoFinal)Tanto
DblPtoInic como DblPtoFinal son los dos puntos de una línea o de un vector que no tiene por qué estar dibujado, por lo que serán matrices o arrays de tres elementos (coordenada X, coordenada Y y coordenada Z) de tipo de dato Double.No es indiferente el orden en que se introducen los puntos, ya que no es igual el ángulo desde el eje X a una línea que va desde un punto 1 hasta un punto 2, que el ángulo desde el eje X a una línea que va desde 2 hasta 1, evidentemente.
NOTA: La variable que recoja este valor (
DblÁnguloDesdeX) habrá sido declarada como Double.·
AngleToReal. Este método obtiene la conversión en radianes de un ángulo en formato de texto a un número real de doble precisión. Veamos la sintaxis de este método:DblÁnguloReal
= ObjUtilidad.AngleToReal(StrÁngulo, IntUnidades)Como vemos, el ángulo ha de ser en formato de cadena de texto normalmente introducido por el usuario.
IntUnidades, por su lado, es un valor Integer que determina en qué unidades aparece el ángulo del argumento anterior, procediendo a su conversión en número real según convenga. Este segundo argumento admite también las siguientes constantes:acDegrees
acDegreeMinuteSeconds
acGrads
acRadians
acSurveyorUnits
Éstas se corresponden con los diferentes tipos de ángulos que maneja AutoCAD (grados sexagesiamales decimales, grados/minutos/segundos, grados centesimales, radianes, unidades geodésicas).
Así pues, una operación como la que sigue (siguiendo con la notación especificada al principio de esta sección):
Dim Resultado As Double
Resultado = AcadUtil.AngleToReal("180", acDegrees)
MsgBox Resultado
devolverá
3.14159265358979, esto es, el resultado de pasar a radianes la cantidad obtenida de convertir el texto "180" considerado como grados en notación decimal. El resultado (PI) sería el mismo al hacer lo siguiente:Dim Resultado As Double
Resultado = AcadUtil.AngleToReal("100", acGrads)
MsgBox Resultado
·
AngleToString. AngleToString convierte el ángulo proporcionado, el cual se considera siempre en radianes, a cadena de texto de acuerdo con las unidades y los decimales de precisión indicados:StrÁnguloCadena
= ObjUtilidad.AngleToString(DblÁngulo, IntUnidades, IntPrecis)DblÁngulo
es un valor Double que, como decimos, estará en radianes, ya que VBA así siempre lo interpreta. IntUnidades es un valor Integer que admite las mismas constantes explicadas en el método anterior y que representa las unidades a las que se convertirán los radianes especificados. IntPrecis especifica la precisión del ángulo, que se convertirá a cadena, en número de decimales. Este último argumento admite un valor entre 0 y 8 (siempre entero).Veamos una rutina de ejemplo:
Dim Resultado As String
Const PI = 3.14159265358979
Resultado = AcadUtil.AngleToString(PI / 2, acDegrees, 3)
MsgBox Resultado
El resultado sería la cadena
"90". Como vemos se ha transformado en grados sexagesimales en formato decimal (acDegrees), en cadena (AngleToString) y, aunque se indicó un precisión de tres decimales (3), no se ha tenido en cuenta al resultar un valor exacto.·
DistanceToReal. Convierte un distancia en formato de texto normalmente indicada por el usuario a un valor real, de acuerdo con el tipo de unidades especificado:DblDistReal
= ObjUtilidad.DistanceToReal(StrDistancia, IntUnidades)StrDistancia
es un valor tipo String que, como decimos, será una cadena. IntUnidades es un valor entero (Integer) que también admite las siguientes constantes:acScientific
acDecimal
acEngineering
acArchitectural
acFractional
Este último argumento indica las unidades de conversión, cuyas constantes se corresponden con las unidades manejadas por AutoCAD (científicas, decimales, pies y pulgadas I, pies y pulgadas II y fraccionarias).
NOTA: El valor de la distancia obtenido es siempre en unidades de dibujo.
·
EndUndoMark. Este método coloca una señal de fin de DESHACER en el lugar del programa que se utilice. Su sintaxis es:ObjUtilidad
.EndUndoMarkSi recordamos, las marcas de inicio y fin del comando de AutoCAD 14
DESHACER las utilizábamos mucho en los programas de AutoLISP, por lo que no hay motivo para no hacerlo en VBA también.Cuando realizamos un programa o una macro que dibuja varias entidades, por ejemplo, si al término de su ejecución no estamos contentos con el resultado y utilizamos el comando
H para deshacer el dibujo, únicamente se deshará la última entidad dibujada. Si antes de comenzar el dibujo del conjunto introducimos una marca de inicio de DESHACER y, tras terminar el dibujo completo, otra de fin de DESHACER, al introducir H al final de la ejecución del programa o de la macro, el conjunto de entidades se deshará por completo como un todo, cosa que nos interesa por estética y por funcionalidad.Esta característica se corresponde con la opción
Fin (End en inglés) del comando DESHACER (UNDO en inglés) de AutoCAD.NOTA: Más adelante se explicará el método
StartUndoMark que coloca marcas de inicio de DESHACER.·
GetAngle. Acepta el valor de un ángulo indicado por el usuario. Sintaxis:DblÁngulo
= ObjUtilidad.GetAngle(DblPtoBase, StrMensaje)Este valor se puede introducir directamente desde el teclado (en el formato actual de unidades en AutoCAD o con los sufijos permitidos para radianes, centesimales, etc.), o señalando puntos en pantalla. Si se especifica un punto de base (es opcional)
Double, se muestra un cursor elástico "enganchado" a dicho punto y el ángulo es el formado por la línea desde ese punto hasta el señalado por el usuario. Si no se especifica un punto de base, el usuario puede señalar dos puntos en pantalla para indicar el ángulo. El ángulo se mide siempre en dos dimensiones, ignorándose las coordenadas Z de los puntos.En cualquiera de los supuestos, el ángulo se mide a partir del origen actualmente establecido en AutoCAD (variable de
ANGBASE), siempre en sentido antihorario. El valor devuelto es siempre en radianes. Si se introduce el valor por teclado, se considerará como radianes. A diferencia del método GetOrientation explicado más adelante, GetAngle se emplea sobre todo para medir ángulos relativos.El mensaje también es opcional (tipo
String), y especifica el texto que aparecerá como solicitud del ángulo en la línea de comandos.Comentemos la macro siguiente:
Option Explicit Dim AcadDoc As Object Dim AcadModel As Object Dim AcadUtil As Object
Sub Macro() Set AcadDoc = GetObject(, "AutoCAD.Application").ActiveDocument Set AcadModel = AcadDoc.ModelSpace Set AcadUtil = AcadDoc.Utility Dim Resultado As Double Dim PtoBase(1 To 3) As Double PtoBase(1) = 10: PtoBase(2) = 18 Resultado = AcadUtil.GetAngle(PtoBase, "Ángulo: ") MsgBox Resultado End Sub
Veamos que aquí se solicita un ángulo al usuario y luego se muestra con un
MsgBox. Percatémonos también de que la coordenada Z del punto base no es necesario que tenga valor, ya que como hemos dicho no se toma en cuenta. Sin embargo, a la hora de declarar la variable habremos de hacerlo como un array de tres valores (aunque el último luego lo dejemos vacío), ya que de otro modo no funcionará el método.·
GetCorner. Este método acepta el valor de un punto indicado por el usuario, mientras existe otro punto "enganchado", lo que forma un rectángulo. Sintaxis:VarPuntoEsquina
= ObjUtilidad.GetCorner(DblPtoBase, StrMensaje)En este caso
DblPtoBase es obligatorio (Double). StrMensaje sigue siendo opcional, al igual que con el método anterior, y es una cadena alfanumérica (String). El resultado de este método será un punto (matriz de tres elementos Double), por lo que habrá de recogerse en una variable tipo Variant para luego, y si se quieren utilizar las coordenadas de dicho punto, extraer los valores mediante índices y hacer un trasvase.·
GetDistance. Este método acepta el valor de una distancia indicada por el usuario. Este valor (Double) podrá ser introducido mediante el teclado o directamente en pantalla marcando dos puntos. En este caso da lo mismo el orden de introducción de puntos.La sintaxis para
GetDistance es la siguiente:DblDistancia
= ObjUtilidad.GetDistance(DblPtoBase, StrMensaje)Si se indica un punto base, que habrá de ser
Double, el cursor se "enganchará" a él y la distancia será medida desde ahí hasta el siguiente punto indicado por el usuario. StrMensaje (String), como viene siendo habitual, es un valor opcional.NOTA: En principio la distancia medida con
GetDistance es un distancia 3D. Aprenderemos al ver el método InitializeUserInput que esto puede modificarse.·
GetInput. Este método Se utiliza inmediatamente después de alguno de los otros métodos de solicitud (Get...), para detectar si el usuario ha dado alguna respuesta textual desde el teclado. Devuelve el texto introducido desde el teclado. Su sintaxis es:
NOTA
: Veremos más adelante algún ejemplo de este método (después de InitializeUserInput) que nos lo aclarará más.·
GetInteger. Solicita del usuario que indique un número entero. Si no es así, rechaza el dato introducido y vuelve a solicitar un número entero. Se permiten valores enteros negativos y el valor 0, a no ser que se especifique lo contrario mediante el método InitializeUserInput.La sintaxis de
GetInteger es:IntValorEntero
= ObjUtilidad.GetInteger(StrMensaje)StrMensaje
es opcional y funciona como en los métodos explicados anteriormente.NOTA: Más aclaraciones sobre este y otros métodos tras
InitializeUserInput.·
GetKeyword. Solicita del usuario que indique la palabra clave que le interesa. El mensaje de solicitud ofrecerá, lógicamente, las opciones posibles con las abreviaturas en mayúsculas para que el usuario sepa a qué atenerse. Previamente, se habrá utilizado el método InitializeUserInput para establecer las palabras clave permitidas.Su sintaxis:
StrPalabraClave
= ObjUtilidad.GetKeyword(StrMensaje)StrMensaje
es opcional y funciona como en los métodos explicados anteriormente.·
GetOrientation. Funciona de manera muy similar a GetAngle, con la única diferencia de que los ángulos se miden siempre desde el origen por defecto (posición de las 3 en el reloj o punto cardinal Este), independientemente del establecido en AutoCAD (variable de ANGBASE). Este método se emplea sobre todo para mediar ángulos absolutos y su sintaxis es:DblOrientación
= ObjUtilidad.GetOrientation(DblPtoBase, StrMensaje)Los argumentos funcionan igual que en
GetAngle.·
GetPoint. GetPoint solicita al usuario un punto que podrá ser marcado en pantalla o introducido por teclado:VarPunto
= ObjUtilidad.GetPoint(DblPtoBase, StrMensaje)Si se indica un punto
Double de base (opcional), el cursor aparece "enganchado" mediante una línea elástica a dicho punto. StrMensaje funciona igual que en métodos anteriores y también es opcional.La siguiente macro de ejemplo se utiliza para dibujar rectángulos mediante polilíneas con sólo marcar dos puntos en pantalla: el primero controlado por un
GetPoint y el segundo con un GetCorner, para poder ver el rectángulo final en tiempo real antes de ser dibujado:Option Explicit Dim AcadDoc As Object Dim AcadModel As Object Dim AcadUtil As Object
Sub Macro() Set AcadDoc = GetObject(, "AutoCAD.Application").ActiveDocument Set AcadModel = AcadDoc.ModelSpace Set AcadUtil = AcadDoc.Utility Dim Punto1, Punto2 Dim PuntoTras(2) As Double Dim PuntosPol(9) As Double Punto1 = AcadUtil.GetPoint(, "Primera esquina: ") PuntoTras(0) = Punto1(0): PuntoTras(1) = Punto1(1): PuntoTras(2) = Punto1(2) Punto2 = AcadUtil.GetCorner(PuntoTras, "Esquina opuesta: ") PuntosPol(0) = Punto1(0): PuntosPol(1) = Punto1(1) PuntosPol(2) = Punto2(0): PuntosPol(3) = Punto1(1) PuntosPol(4) = Punto2(0): PuntosPol(5) = Punto2(1) PuntosPol(6) = Punto1(0): PuntosPol(7) = Punto2(1) PuntosPol(8) = Punto1(0): PuntosPol(9) = Punto1(1) Call AcadDoc.ModelSpace.AddLightWeightPolyline(PuntosPol) End Sub
Lo primero que se hace, tras declarar las variables, es solicitar el primer punto del rectángulo. Se realiza ahora un trasvase de coordenadas (de
Variant a Double) para podérselas suministrar al método GetCorner como punto base. Se pide el segundo punto (el cursor permanecerá enganchado al primero mediante un rectángulo elástico) y se calculan los puntos para la polilínea que dibujará el rectángulo.Una nota importante que debemos reseñar de este ejemplo es la manera de dibujar la polilínea. Nótese que por primera vez en estas páginas, en lugar de utilizar el método como explicamos en su momento, lo hemos usado con
Call. Es momento ahora de decir que esto es perfectamente factible con todos los métodos de dibujo de entidades. Lo que ocurre, es que normalmente se utiliza la otra manera (guardando el objeto resultante en una variable de objeto) para después tener acceso absoluto a la entidad dibujada: cambiar su color, su tipo de línea, etcétera o utilizar cualquiera de las propiedades o métodos de ella.Ahora bien, en momentos en los que no nos interese de una entidad más que su puro dibujo, se puede utilizar esta técnica.
NOTA: Percatémonos que declarar un matriz con
(2) elementos es igual que hacerlo con (1 To 3) elementos. En este segundo caso los índices variarían de 1 a 3, y en el primero de 0 a 2; lo que da un total de tres elementos en ambos casos.·
GetReal. Solicita del usuario que indique un número real. Si no es así, rechaza el dato introducido y vuelve a solicitar un número real. Si se indica un número entero, es aceptado como real.La sintaxis de
GetReal es:DblValorReal
= ObjUtilidad.GetReal (StrMensaje)StrMensaje
es opcional y funciona como en los métodos explicados anteriormente.·
GetString. Acepta un cadena de texto introducida por el usuario. Si contiene más de 132 caracteres, sólo devuelve los primeros 132 caracteres. Sintaxis:StrCadena
= ObjUtilidad.GetString (BooModo, StrMensaje)El modo es un valor
Boolean (True o False) que indica si la cadena de texto puede contener espacios en blanco. Si es verdadero se admiten espacios y el texto introducido por teclado debe terminarse con INTRO. Si es falso, el primer espacio se considerará como un INTRO y terminará el texto. Si no se introduce ningún texto y se pulsa directamente INTRO, se devuelve una cadena vacía.·
InitializeUserInput. Este método establece limitaciones para aceptar los datos introducidos por el usuario, y también permite especificar palabras clave para ser aceptadas como nombres de opción. Veamos la sintaxis de utilización:Call ObjUtilidad.InitializeUserInput (IntModo, StrPalabrasClave)
La mayoría de los métodos
Get... que hemos visto se parecen enormemente (hasta algunos en los nombres) a las funciones GET... de AutoLISP que realizaban los mismos cometidos. Evidentemente necesitaremos pues un método como InitializeUserInput que haya opción de añadir justo antes de cualquier Get... para filtrar sus resultados; es lo que hacíamos en AutoLISP con INITGET.IntModo
es un valor entero (Integer) con código de bits que determina las limitaciones impuestas al usuario. Los modos posibles coinciden con los de la función INITGET de AutoLISP y se encuentran en la siguiente tabla:Valor de bit Modo
Al indicar un modo se pueden sumar varios de los bits. Por ejemplo, para impedir que el usuario indique un valor cero, nulo (es decir
Este método debe invocarse justo antes del método
Get... que limita. Los modos que tienen sentido para cada método Get... también coinciden con los correspondientes de AutoLISP y se encuentran en la siguiente tabla:Método Valores de bits de modo con sentido para el método
NOTA
El segundo parámetro (
String) es una cadena que define las palabras clave válidas como nombres de opciones. Estas se indican entre comillas, separadas por un espacio en blanco, y con la abreviatura en mayúsculas. La abreviatura es el mínimo número de caracteres en que debe coincidir la respuesta del usuario con una de las palabras clave válidas. El método siempre devuelve la palabra tal y como está escrita en InitializeUserInput. La solicitud de palabra clave se realiza mediante el método GetKeyword. Por ejemplo:Call AcadUtil.InitializeUserInput (7, "Alta Baja Normal")
Op = AcadUtil.GetKeyword ("Precisión Alta/Baja/Normal: ")
En el ejemplo se supone que las variables
AcadUtil y Op ya han sido definidas. El método GetKeyword solicita del usuario una opción. Si éste desea la opción Alta, puede indicar a, al, alt o alta, y en todos los casos la variable Op almacena el valor Alta.Aceptación de valores por defecto
Estudiemos ahora un mecanismo para aceptar valores por defecto, tan típicos en línea de comandos.
Cuando desde un programa en VBA se introducen valores no esperados por teclado, se produce un error de VBA. En estos casos, VBA ejecuta automáticamente una sentencia especial llamada
On Error, si se ha incluido en el programa. Si no es así, detiene automáticamente el programa informando del error. Si se indica la sentencia On Error Resume Next, el programa se reanuda en la línea siguiente a la que ha producido el error, sin detenerse.Además, existe un objeto específico denominado
Err, que tiene una propiedad Number que almacena un número de error. Cuando no hay errores, ese número o índice de error es 0. Cuando VBA recibe un tipo de dato inesperado (es lo que ocurre al pulsar INTRO en la solicitud de número entero por ejemplo, o al cancelar con ESC), el número de error es diferente de 0. Una vez que se detecta que ha habido error, para averiguar el tipo de dato inesperado causante del mismo, se puede examinar la propiedad Description del mismo objeto Err. Si el usuario ha introducido una letra o un texto por teclado ante la solicitud de un entero, real o punto, la descripción de error es "La entrada de usuario es una palabra clave". Si el usuario cancela, mediante ESC por ejemplo, la descripción de error será diferente.El método
GetInput estudiado, devuelve el texto introducido por teclado que ha producido el error. Cuando el usuario pulsa INTRO para aceptar una opción por defecto, y VBA espera un número o un punto, lo considera un texto vacío y por lo tanto produce un error con la misma descripción expuesta más arriba. En este caso, GetInput devuelve una cadena vacía "".Para que VBA considere
INTRO como un error, podría pensarse en establecer un modo 1 en InitializeUserInput. Pero en este caso, simplemente se impediría el INTRO mostrándose un mensaje y solicitando de nuevo el dato. Si no se establece un modo 1, el INTRO es aceptado, pero entonces no se produce error. El resultado es que cada tipo de solicitud acepta un valor diferente. Así, GetInteger podría considerar INTRO como 0 (depende del diseño del programa y de la definición de variable asignada), GetReal como un valor muy pequeño prácticamente 0 y GetPoint tomaría la posición del cursor en pantalla en el momento de pulsar INTRO o el punto de base si se ha especificado.La solución a este problema es utilizar el modo
128 en InitializeUserInput. Este modo acepta datos arbitrarios por teclado y tiene prioridad sobre el modo 1. Por lo tanto, si se indica el modo 129 (1 + 128), se está impidiendo el INTRO a causa del modo 1 pero el modo 128 fuerza a aceptarlo. VBA lo considera entonces un error, y lo acepta como palabra clave no esperada, con valor de cadena vacía.En resumen, un mecanismo general para detectar el
INTRO pulsado por el usuario, comprende los siguientes pasos: Establecer la sentencia
On Error Resume Next para que el programa no se detenga al producirse un error. Establecer un modo en
InitializeUserInput con 129 como sumando. Detectar si ha habido error, examinando si
Err.Number es diferente de 0. Detectar si el error se debe a texto del teclado, examinando si
Err.Description = "La entrada de usuario es una palabra clave" Recuperar el texto introducido por teclado, mediante
GetInput. Examinar si ese texto es una cadena vacía
"".El siguiente ejemplo muestra cómo aceptar una opción por defecto desde
GetInteger.Option Explicit Dim AcadDoc As Object Dim AcadModel As Object Dim AcadUtil As Object
Sub Macro() Set AcadDoc = GetObject(, "AutoCAD.Application").ActiveDocument Set AcadModel = AcadDoc.ModelSpace Set AcadUtil = AcadDoc.Utility Dim Prec As Integer Dim HayClave As Boolean Dim ValorClave As String On Error Resume Next Call AcadUtil.InitializeUserInput(135) Prec = AcadUtil.GetInteger("Valor de precisión <50>: ") If Err.Description = "La entrada de usuario es una palabra clave" Then HayClave = True Else HayClave = False End If ValorClave = AcadUtil.GetInput If Err.Number <> 0 Then If HayClave And ValorClave = "" Then Prec = 50 Else GoTo Errores End If Err.Clear ... resto del código ... End Errores: MsgBox "*NO VALE*" End Sub
El modo empleado en
InitializeUserInput es 135 (1 + 2 + 4 + 128). Esto impide valores negativos ó 0, pero no INTRO, debido a que el modo 128 prevalece sobre el 1.Mediante
GetInteger se solicita un número entero. La variable Prec se define como Integer y acepta la respuesta del usuario. Si éste indica un valor negativo o cero, se rechaza y se vuelve a solicitar. Si indica un valor entero positivo éste se almacena en Prec, el valor de Err.Number es 0 por lo que no se ejecuta la sentencia dentro del If, y Prec mantiene su valor para el resto del programa.Si el usuario cancela mediante
ESC, se produce un error. La variable HayClave, definida como Boolean, almacena el resultado verdadero o falso de comparar Err.Description con el texto de descripción, en este caso False. Por eso dentro de la sentencia de If no se cumplirá la condición y el programa saltará a la subrutina Errores, que por simplificar consiste simplemente en mostrar un cuadro de diálogo de aviso con el mensaje *NO VALE*.Si el usuario pulsa
INTRO, también se produce un error al haberse sumado 1 al modo de InitializeUserInput. La descripción del error sí coincide con el texto indicado en el código del programa. La variable ValorClave, definida como String, almacena el valor devuelto por GetInput que en este caso es una cadena vacía. Por lo tanto, se cumple la sentencia dentro del If y la variable Prec se iguala al valor por defecto 50.Si el usuario pulsa cualquier otra combinación (un valor numérico con decimales o un texto), se produce un error con la descripción de palabra clave. Pero
GetInput devuelve el texto introducido en vez de cadena vacía, por lo que la sentencia dentro del If no cumplirá su condición, y se producirá un salto a la subrutina de Errores.En todos los casos, la sentencia
Err.Clear elimina la descripción de error para que no se quede almacenada y pueda originar mal funcionamiento en la siguiente ejecución del programa.Con métodos de aceptación de cadenas de texto (
GetKeyword y GetString)Los métodos de solicitud de cadenas de texto como
GetKeyword y GetString no producen error al pulsar el usuario INTRO y lo entienden como cadena vacía "". El mecanismo para aceptar opciones por defecto difiere del explicado anteriormente. Si se utiliza el modo 128 de InitializeUserInput todos los textos introducidos por teclado se aceptan como palabras clave, sin producir error. Esto obliga a examinar esos textos para ver si coinciden con las palabras claves permitidas y se desvirtúa la finalidad de GetKeyword. Por lo tanto, un mecanismo sencillo para aceptar opciones por defecto podría ser:Dim Op As String On Error Resume Next Call AcadUtil.InitializeUserInput(0, "Alta Baja Normal") Op = AcadUtil.GetKeyword("Precisión Alta/Baja/<Normal>: ") If Err.Number = 0 Then If Op = "" Then Op = "Normal" Else GoTo Errores End If
El modo indicado en
InitializeUserInput es 0, y se incluyen tres palabras clave como nombres de opciones permitidas. El propio método GetKeyword impide indicar valores numéricos o textos que no correspondan con las tres palabras clave. Si se produce cualquier error inesperado, la sentencia Else dentro del If hace que el programa salte a la subrutina de control de errores. Si el usuario introduce una de las opciones por teclado, no se produce error y la variable Op almacena la palabra clave de la opción. Por lo tanto, la sentencia dentro del If no se cumple y la variable Op sigue con su valor. Si el usuario pulsa INTRO, no se produce tampoco error, pero la variable Op almacena una cadena vacía y eso hace que la sentencia dentro del If se cumpla. El resultado es asignar a la variable Op el valor correspondiente a la opción por defecto, en este caso Normal.Con el método de aceptación de números enteros (
GetInteger)Para aceptar un valor por defecto se puede utilizar el mecanismo ya explicado. Para aceptar palabras clave además de valores numéricos, el mecanismo se explica un poco más adelante. Estos mecanismos tienen la ventaja de que se pueden aplicar con mínimas modificaciones a todos los métodos que solicitan valores numéricos y puntos. No obstante, es posible utilizar otros mecanismos específicos dependiendo del diseño del programa y conociendo los tipos de errores producidos.
Por ejemplo, cuando se define una variable como
Integer y se le asigna el valor devuelto por GetInteger, si el usuario pulsa INTRO, VBA lo considera un tipo de dato inesperado y origina un error de desbordamiento con un valor de Err.Number igual a 6. Cualquier otro error, por ejemplo al cancelar mediante ESC, produce otro número diferente. Por lo tanto, un mecanismo sencillo para aceptar opciones por defecto es:Dim N As Integer Call AcadUtil.InitializeUserInput(6) On Error Resume Next N = AcadUtil.GetInteger("Precisión <3>: ") If Err.Number <> 0 Then If Err.Number = 6 Then N = 3 Else GoTo Errores End If
Mediante el modo
6, el método InitializeUserInput impide valores negativos y 0. La sentencia On Error Resume Next, hace que el programa no se detenga al producirse un error y continúe normalmente. El método GetInteger solicita un número entero. Si el usuario indica uno que no sea negativo ni 0, se acepta, el valor de Err.Number es 0, la condicional If no se cumple y el programa continúa sin problemas. Si el usuario indica INTRO, se produce un error aunque el programa continúa sin detenerse, el valor de Err.Number es 6, y entonces en la variable N se almacena el valor por defecto 3.Si se origina cualquier otro error (por ejemplo el usuario cancela mediante
ESC), el valor de Err.Number es diferente de 0 y 6, y entonces la sentencia GoTo salta a una subrutina Errores, donde habrá especificadas una serie de actuaciones y después se abortará el programa.Con el método de aceptación de números reales (
GetReal)Para la aceptación de valores por defecto se puede utilizar el mismo mecanismo, sustituyendo simplemente la declaración de variable como
Double en vez de Integer, y empleando lógicamente el método GetReal en lugar de GetInteger. Para aceptar palabras clave además de valores numéricos, el mecanismo se explica un poco más adelante en esta misma página.Si se desea un mecanismo específico para números reales, cuando se define una variable como
Double y se le asigna el valor devuelto por GetReal, si el usuario pulsa INTRO, VBA lo considera un valor residual muy pequeño próximo a 0. Como ese valor es un número real, GetReal no produce error. Por lo tanto, el mecanismo de aceptación no va a ser detectar un número de error, sino un valor devuelto muy pequeño.Dim Prec As Double On Error GoTo Errores Call AcadUtil.InitializeUserInput(6) Prec = AcadUtil.GetReal("Precisión <2.5>: ") If Prec < 0.00000001 Then Prec = 2.5
En este caso, la sentencia
On Error envía el programa directamente a la subrutina Errores, porque el INTRO como respuesta no va a ser considerado un error sino como un valor muy pequeño próximo a 0. Mediante el modo 6, el método InitializeUserInput impide valores negativos y 0. El propio método GetReal impide valores no numéricos. Mediante If se analiza el valor introducido por el usuario. Si es más pequeño que cualquiera que hubiera podido indicar por teclado, entonces es que ha pulsado INTRO y se asigna a la variable el valor por defecto. En caso contrario, en If no se realiza ninguna acción y el programa continúa normalmente.Otros métodos
Crear una rutina de aceptación de valores por defecto para los demás métodos resulta sencillo, ya que sólo hay que reflejarse en los ejemplos vistos hasta aquí y adecuar el más preciso.
Combinación de solicitudes numéricas con opciones textuales y
GetInputSi se ha indicado un modo
128 en InitializeUserInput, el método de solicitud empleado a continuación aceptará la entrada de teclado como palabra clave y GetInput devolverá dicha entrada como un texto. Si la entrada ha sido INTRO, lo devolverá como cadena vacía. Esto permite establecer un mecanismo de aceptación de valores por defecto cuando se emplean funciones de solicitud de datos numéricos o puntos, tal como se ha explicado anteriormente.Pero si la entrada de teclado no es
INTRO se puede emplear GetInput para combinar solicitudes numéricas con opciones de texto. Por ejemplo:Dim Prec As Integer Dim HayClave As Boolean Dim ValorClave As String On Error Resume Next Call AcadUtil.InitializeUserInput(135, "Alta Baja Normal") Prec = AcadUtil.GetInteger("Valor de precisión o Alta/Baja/Normal: ") If Err.Description = "La entrada de usuario es una palabra clave" Then HayClave = True Else HayClave = False End If ValorClave = AcadUtil.GetInput If Err.Number <> 0 Then If HayClave And ValorClave <> "" Then GoSub Precision Else GoTo Errores End If Err.Clear ... resto del código ... End Precision: If ValorClave = "Normal" Then Prec = 10: Return If ValorClave = "Alta" Then Prec = 100: Return If ValorClave = "Baja" Then Prec = 1: Return GoTo Errores
El mecanismo es similar al explicado ya. Si se indica un valor negativo o cero se rechaza. Si se indica un número entero positivo se acepta porque no produce error. Si se indica un valor no esperado por teclado, se produce un error y el modo
128 como sumando en InitializeUserInput acepta la entrada como palabra clave. GetInput devuelve esa entrada como texto y lo almacena en ValorClave. La sentencia dentro del If examina si HayClave es verdadera y si ValorClave no es una cadena vacía. En caso de ser así, llama a la subrutina Precision, donde se analiza el texto aceptado como palabra clave y se asigna a la variable Prec el valor entero correspondiente a cada precisión, continuando la ejecución del programa mediante Return. Si la palabra clave no es ninguna de las tres admitidas, se salta a la subrutina de errores.Si se produce un error inesperado, su descripción no corresponderá a la de palabra clave, y la sentencia dentro del
If no se cumplirá por lo que Else saltará a la subrutina Errores. Si se pulsa INTRO (en el ejemplo no se admite una opción por defecto), ValorClave será una cadena vacía, la sentencia dentro del If tampoco se cumplirá y se saltará a la subrutina de Errores.Sigamos pues ahora con la explicación de los métodos que faltan del objeto
Utility de utilidad.·
PolarPoint. Este método obtiene el punto (matriz de tres elementos Double) a partir de otro punto dado (matriz de tres elementos Double), según un ángulo en radianes (Double) y una distancia en las unidades actuales (Double). Es decir, obtiene un punto por coordenadas polares a partir de otro dado.La sintaxis de este método es:
DblPunto2
= ObjUtilidad.PolarPoint (DblPunto1, DblÁngulo, DblDistancia)En el siguiente ejemplo, se dibuja una línea perpendicular desde el punto medio entre otros dos puntos, con una longitud especificada:
Option Explicit Dim AcadDoc As Object Dim AcadModel As Object Dim AcadUtil As Object
Sub Macro() Set AcadDoc = GetObject(, "AutoCAD.Application").ActiveDocument Set AcadModel = AcadDoc.ModelSpace Set AcadUtil = AcadDoc.Utility Dim VPunto1, VPunto2, VPuntoFinal Dim Punto1(2) As Double, Punto2(2) As Double Dim PuntoMedio(2) As Double, PuntoFinal(2) As Double Dim Ángulo As Double, Distancia As Double Const PI = 3.1415926 Call AcadUtil.InitializeUserInput(1) VPunto1 = AcadUtil.GetPoint(, "Primer punto: ") Punto1(0) = VPunto1(0): Punto1(1) = VPunto1(1): Punto1(2) = VPunto1(2) Call AcadUtil.InitializeUserInput(1) VPunto2 = AcadUtil.GetPoint(Punto1, "Segundo punto: ") Punto2(0) = VPunto2(0): Punto2(1) = VPunto2(1): Punto2(2) = VPunto2(2) PuntoMedio(0) = ((Punto1(0) + Punto2(0)) / 2) PuntoMedio(1) = ((Punto1(1) + Punto2(1)) / 2): PuntoMedio(2) = Punto1(2) Ángulo = AcadUtil.AngleFromXAxis(Punto1, Punto2) Distancia = AcadUtil.GetDistance(PuntoMedio, "Distancia en perpendicular: ") VPuntoFinal = AcadUtil.PolarPoint(PuntoMedio, Ángulo + PI / 2, Distancia) PuntoFinal(0) = VPuntoFinal(0) PuntoFinal(1) = VPuntoFinal(1) PuntoFinal(2) = VPuntoFinal(2) Call AcadDoc.ModelSpace.AddLine(PuntoMedio, PuntoFinal) End Sub
Los puntos
Punto1 y Punto2 (VPunto1 y VPunto2 en su definición Variant) son solicitados por el programa, sin permitir INTRO como respuesta nula. Pueden ser los extremos de una línea ya dibujada o dos puntos cualesquiera. A continuación, el programa calcula el punto medio PuntoMedio haciendo medias aritméticas con las coordenadas X e Y. Mediante el método AngleFromXAxis calcula el ángulo absoluto entre los puntos 1 y 2. Después solicita la distancia en perpendicular y calcula el punto PuntoFinal a partir del punto medio, llevando la distancia a un ángulo que resulta de sumar PI / 2 al ángulo absoluto entre 1 y 2. La última operación es dibujar una línea entre los dos últimos puntos.·
RealToString. RealToString convierte el valor proporcionado, el cual será real (Double), a cadena de texto de acuerdo con las unidades y los decimales de precisión indicados:StrRealCadena
= ObjUtilidad.RealToString(DblValorReal, IntUnidades, IntPrecis)DblValorReal
es, como hemos dicho, un valor Double. IntUnidades es un valor Integer que admite las mismas constantes explicadas en el método DistanceToReal y que representa las unidades a las que se convertirá el valor real. IntPrecis especifica la precisión en decimales del número real, el cual se convertirá a cadena. Este último argumento admite un valor entre 0 y 8 (siempre entero).En la siguiente rutina:
Dim TxValor As String
TxValor = AcadUtil.RealToString(326.7539, acFractional, 2)
MsgBox TxValor
el valor devuelto será la cadena
"326 3/4".·
StartUndoMark. Este método coloca una señal de inicio del comando DESHACER en el lugar del programa que se utilice. Su sintaxis es:ObjUtilidad
.StartUndoMarkEsta característica se corresponde con la opción
Inicio (BEgin en inglés) del comando DESHACER (UNDO en inglés) de AutoCAD.NOTA: Véase en esta misma sección el método
EndUndoMark que coloca marcas de fin de DESHACER.·
TranslateCoordinates. Convierte un punto o vector de desplazamiento de un sistema de coordenadas de origen a otro de destino:VarPtoConvertido
= ObjUtilidad.TranlateCoordinates(DblPtoOriginal, IntSisOrigen,BooDesplazamiento
es un valor Boolean que indica si la matriz de tres valores Double que es DblPtoOriginal se considera un vector de desplazamiento. Si es verdadero, se considera un vector de desplazamiento. Si es falso, se considera un punto.Los sistemas de coordenadas de origen y destino se indican mediante un código de número entero (
Integer). Para mayor facilidad y comodidad existen cuatro constantes que también se pueden especificar:acWorld
acUCS
acDisplayDCS
acPaperSpaceDCS
Estas constantes se corresponden con los distintos sistemas de coordenadas que se utilizan en la interfaz gráfica de AutoCAD 14 (Sistema de Coordenadas Universal o SCU, Sistema de Coordenadas Personal o SCP, Sistema de Coordenadas de Visualización o SCV y Sistema de Coordenadas de Espacio Papel o SCEP).
En esta rutina (utilizando las convenciones que arrastramos desde el inicio de esta sección):
Dim VarPtoOr, VarPtoDest VarPtoOr = AcadUtil.GetPoint(, "Punto que convertir: ") VarPtoDest = AcadUtil.TranslateCoordinates(VarPtoOr, acUCS, acWorld, False)
el método
GetPoint solicita indicar un punto, éste se acepta en la variable (definida como Variant) VarPtoOr y después se convierte desde el SCP actual al SCU, indicando un valor False de desplazamiento para que no se considere un vector.NOTA: Recuérdese que no declarar una variable con un tipo concreto es lo mismo que declararla como
Variant.A continuación estudiaremos un programa que resulta muy jugoso como repaso de todo lo que hemos estudiado hasta ahora. Este programa maneja un cuadro de diálogo (formulario) que es el que se observa en la página siguiente.
Como vemos, resulta ser un programa para dibujar agujeros para tornillos en alzado. Se indica primero el tipo de agujero (con cajera recta, con cajera avellanada o sin cajera). Después hemos de introducir en las diferentes casillas los distintos valores necesarios para dibujar el agujero.
Al pulsar el botón Aceptar se nos solicitará el punto y el ángulo de inserción, tras lo cual se dibujará el agujero con la línea de ejes en una capa llamada
EJES, con tipo de línea TRAZO_Y_PUNTO y color rojo.El botón Cancelar termina el programa.
Veamos, tras el diseño del letrero, el código VBA de este programa ya un poco complejo.
Option Explicit Dim AcadDoc As Object, AcadUtil As Object Dim AcadObj As Object, AcadEje As Object, AcadCapa As Object Dim VPt0, VPt1, VPt2, VPt3, VPt4, VPt5 Dim VPtgj, VPt1Eje, VPt2Eje Dim DiamAgujero0, ProfAgujero0, DiamCajera0, ProfCajera0 Dim Pt0(2) As Double Dim Pt1(2) As Double, Pt2(2) As Double, Pt3(2) As Double Dim Pt4(2) As Double, Pt5(2) As Double Dim Ptgj(2) As Double, Pt1Eje(2) As Double, Pt2Eje(2) As Double Dim Refent0 As Integer, Angulo As Variant, PI As Double
Private Sub CajeraAvellanada_Click() DiamCajera.Enabled = True ProfCajera.Enabled = True Label3.Enabled = True Label4.Enabled = True Imagen.Picture = LoadPicture("cajera_avellanada.bmp") End Sub
Private Sub CajeraRecta_Click() DiamCajera.Enabled = True ProfCajera.Enabled = True Label3.Enabled = True Label4.Enabled = True Imagen.Picture = LoadPicture("cajera_recta.bmp") End Sub
Private Sub Cancelar_Click() End End Sub
Private Sub Dibujar_Click() On Error GoTo Error Chequear If Errores.Caption = "" Then Else Exit Sub formAgujeros.Hide Call AcadUtil.InitializeUserInput(1) VPt0 = AcadUtil.GetPoint(, "Punto: ") Pt0(0) = VPt0(0): Pt0(1) = VPt0(1): Pt0(2) = VPt0(2) Call AcadDoc.SetVariable("osmode", 512) Call AcadUtil.InitializeUserInput(1) Angulo = AcadDoc.Utility.GetAngle(VPt0, "Angulo (Cerca de): ") PI = 3.14159265359 If SinCajera.Value = True Then VPtgj = VPt0 DibAgujero Else DibCajera DibAgujero End If VPt1Eje = AcadDoc.Utility.PolarPoint(Pt0, Angulo + PI, 5) Pt1Eje(0) = VPt1Eje(0): Pt1Eje(1) = VPt1Eje(1): Pt1Eje(2) = VPt1Eje(2): VPt2Eje = AcadDoc.Utility.PolarPoint(Pt3, Angulo, 5) Pt2Eje(0) = VPt2Eje(0): Pt2Eje(1) = VPt2Eje(1): Pt2Eje(2) = VPt2Eje(2): Set AcadEje = AcadObj.AddLine(Pt1Eje, Pt2Eje) On Error Resume Next If IsEmpty(AcadDoc.Linetypes.Item("trazo_y_punto")) Then Call AcadDoc.Linetypes.Load("trazo_y_punto", "acadiso.lin") End If If IsEmpty(AcadDoc.Layers.Item("ejes")) Then Set AcadCapa = AcadDoc.Layers.Add("ejes") AcadCapa.Linetype = "trazo_y_punto" AcadCapa.Color = 1 End If AcadEje.Layer = "ejes" Call AcadDoc.SetVariable("osmode", Refent0) Open "agujeros.$vr" For Output As #1 Write #1, DiamAgujero, ProfAgujero, DiamCajera, ProfCajera Close #1 Error: MsgBox "¡NO VALE!", , "Mensaje de error" End Sub
Private Sub DibCajera() VPt1 = AcadUtil.PolarPoint(Pt0, Angulo - (PI / 2), Val(DiamCajera) / 2) Pt1(0) = VPt1(0): Pt1(1) = VPt1(1): Pt1(2) = VPt1(2) VPt2 = AcadUtil.PolarPoint(Pt1, Angulo, Val(ProfCajera)) Pt2(0) = VPt2(0): Pt2(1) = VPt2(1): Pt2(2) = VPt2(2) VPt3 = AcadUtil.PolarPoint(Pt2, Angulo + (PI / 2), Val(DiamCajera)) Pt3(0) = VPt3(0): Pt3(1) = VPt3(1): Pt3(2) = VPt3(2) VPt4 = AcadUtil.PolarPoint(Pt3, Angulo + PI, Val(ProfCajera)) Pt4(0) = VPt4(0): Pt4(1) = VPt4(1): Pt4(2) = VPt4(2) If CajeraAvellanada.Value = True Then VPt1 = AcadUtil.PolarPoint(Pt1, Angulo - (PI / 2), Val(ProfCajera) / 2): _ Pt1(0) = VPt1(0): Pt1(1) = VPt1(1): Pt1(2) = VPt1(2) End If If CajeraAvellanada.Value = True Then VPt4 = AcadUtil.PolarPoint(Pt4, Angulo + (PI / 2), Val(ProfCajera) / 2): _ Pt4(0) = VPt4(0): Pt4(1) = VPt4(1): Pt4(2) = VPt4(2) End If Call AcadObj.AddLine(Pt1, Pt2) Call AcadObj.AddLine(Pt2, Pt3) Call AcadObj.AddLine(Pt3, Pt4) VPtgj = AcadUtil.PolarPoint(Pt0, Angulo, Val(ProfCajera)) End Sub
Private Sub DibAgujero() Ptgj(0) = VPtgj(0): Ptgj(1) = VPtgj(1): Ptgj(2) = VPtgj(2) VPt1 = AcadUtil.PolarPoint(Ptgj, Angulo - (PI / 2), Val(DiamAgujero) / 2) Pt1(0) = VPt1(0): Pt1(1) = VPt1(1): Pt1(2) = VPt1(2) VPt2 = AcadUtil.PolarPoint(Pt1, Angulo, Val(ProfAgujero)) Pt2(0) = VPt2(0): Pt2(1) = VPt2(1): Pt2(2) = VPt2(2) VPt3 = AcadUtil.PolarPoint(Pt2, Angulo + (PI / 2), Val(DiamAgujero) / 2) Pt3(0) = VPt3(0): Pt3(1) = VPt3(1): Pt3(2) = VPt3(2) VPt3 = AcadUtil.PolarPoint(Pt3, Angulo, Val(DiamAgujero) / 4) Pt3(0) = VPt3(0): Pt3(1) = VPt3(1): Pt3(2) = VPt3(2) VPt4 = AcadUtil.PolarPoint(Pt2, Angulo + (PI / 2), Val(DiamAgujero)) Pt4(0) = VPt4(0): Pt4(1) = VPt4(1): Pt4(2) = VPt4(2) VPt5 = AcadUtil.PolarPoint(Pt4, Angulo + PI, Val(ProfAgujero)) Pt5(0) = VPt5(0): Pt5(1) = VPt5(1): Pt5(2) = VPt5(2) Call AcadObj.AddLine(Pt1, Pt2) Call AcadObj.AddLine(Pt2, Pt4) Call AcadObj.AddLine(Pt4, Pt5) Call AcadObj.AddLine(Pt2, Pt3) Call AcadObj.AddLine(Pt3, Pt4) End Sub
Private Sub Chequear() If Val(DiamAgujero) <= 0 Then Errores.Caption = "Diámetro de agujero debe ser mayor o igual que 0" DiamAgujero.SelStart = 0: DiamAgujero.SelLength = Len(DiamAgujero) DiamAgujero.SetFocus: Exit Sub End If If Val(ProfAgujero) <= 0 Then Errores.Caption = "Profundidad de agujero debe ser mayor o igual que 0" ProfAgujero.SelStart = 0: ProfAgujero.SelLength = Len(ProfAgujero) ProfAgujero.SetFocus: Exit Sub End If If Cajera.Enabled = True Then If Val(DiamCajera) <= Val(DiamAgujero) Then Errores.Caption = "Diámetro de cajera debe ser mayor que el de agujero" DiamCajera.SelStart = 0: DiamCajera.SelLength = Len(DiamCajera) DiamCajera.SetFocus: Exit Sub End If End If If Cajera.Enabled = True Then If Val(ProfCajera) <= 0 Then Errores.Caption = "Profundidad de cajera debe ser mayor o igual que 0" ProfCajera.SelStart = 0: ProfCajera.SelLength = Len(ProfCajera) ProfCajera.SetFocus: Exit Sub End If End If Errores.Caption = "" End Sub
Private Sub UserForm_Initialize() Set AcadDoc = GetObject(, "Autocad.Application").ActiveDocument Set AcadUtil = AcadDoc.Utility Set AcadObj = AcadDoc.ModelSpace On Error Resume Next Refent0 = AcadDoc.GetVariable("osmode") Open "agujeros.$vr" For Input As #1 If Err.Description = "No se encontró el archivo" Then GoSub Defecto Input #1, DiamAgujero0, ProfAgujero0, DiamCajera0, ProfCajera0 DiamAgujero = DiamAgujero0 ProfAgujero = ProfAgujero0 DiamCajera = DiamCajera0 ProfCajera = ProfCajera0 Close #1 Exit Sub Defecto: Open "agujeros.$vr" For Append As #1 Write #1, "10", "10", "20", "5" Close #1 Open "agujeros.$vr" For Input As #1 Return End Sub
Private Sub SinCajera_Click() DiamCajera.Enabled = False ProfCajera.Enabled = False Label3.Enabled = False Label4.Enabled = False Imagen.Picture = LoadPicture("sin_cajera.bmp") End Sub
Iremos comentando cada procedimiento
Sub por separado y no en el orden en que están en el listado, sino en uno quizá más lógico.
(General)_(Declaraciones)Aquí como siempre se declaran todas las variables que luego utilizaremos, tanto las de objeto (
Object), como Variant y Double. Recordar la necesidad de tener un doble juego de variables, unas Variant y otras Double, para hacer trasvase al obtener un punto y luego querer utilizarlo.
UserForm_Initialize()Este es el procedimiento que se ejecuta nada más correr el programa, al inicializarse el formulario. Lo primero es lo de siempre, esto es, asignar a cada objeto de AutoCAD que vamos a necesitar su valor correspondiente. Después se utiliza la sentencia
On Error Resume Next para controlar la apertura del archivo que vamos a explicar ahora. Luego se guarda en Refent0 el valor de la variable OSMODE de AutoCAD; ya veremos para qué.Así como AutoLISP guarda los valores de las variables globales utilizadas hasta cerrar AutoCAD, con VBA no disponemos de esa ventaja. Es por ello que, dada la característica de los programas para AutoCAD que poseen la capacidad de almacenar los últimos valores utilizados como valores por defecto, nos vamos a inventar un método para que esto suceda también en nuestros programas VBA.
El sencillo método consiste simplemente en crear un archivo de texto donde se almacenarán, en cada ejecución del programa, los últimos valores utilizados. De este modo, al correr de nuevo el programa, se leerán dichos valores y se introducirán en el cuadro para ofrecerlos por defecto.
De esta manera intentamos leer el archivo que almacenará los valores (
AGUJEROS.$VR) en el directorio actual. Si no existiera se produciría un error, por lo que la ejecución sigue en la siguiente línea (recordemos el On Error Resume Next). En esta línea se compara el texto del error con el que significa que el archivo no se ha encontrado y, si fueran iguales, la ejecución se dirige a la subrutina Defecto donde se crea y se le añaden unos valores por defecto.Tanto si existiera como si estuviera recién creado, se continúa la ejecución leyendo los valores del archivo e introduciéndolos en el cuadro.
NOTA: Es más lógico utilizar los valores numéricos de
Err en lugar de sus descripciones, ya que podría utilizarse así en cualquier plataforma idiomática VBA. Para hallar el número de un error (si no disponemos de una lista), sólo hemos de provocarlo y extraer el valor con Err.Number.
SinCajera_Click()Este procedimiento y los dos siguientes dicen referencia a la hora de hacer clic en alguno de los tres botones excluyentes para elegir el tipo de agujero. Este concretamente responde al evento de hacer clic en el botón excluyente Sin cajera. Al hacerlo, tanto la casillas de profundidad de cajera como la de diámetro de la cajera, así como sus etiquetas, deben inhabilitarse. También se refleja en el cuadro de imagen el archivo
.BMP correspondiente.
CajeraRecta_Click()Al igual que en el anterior procedimiento explicado, en
CajeraRecta_Click() se muestra la imagen correspondiente en el cuadro de imagen y se establecen como habilitadas las casillas y etiquetas de profundidad y diámetro de cajera por si al hacer clic en Cajera recta se proviniera de Sin cajera, la cual las desactiva como sabemos.
CajeraAvellanada_Click()Así también, en este procedimiento
Sub se muestra la imagen correspondiente con una cajera avellanada en el cuadro de imagen, y también se establecen como habilitadas las casillas y etiquetas de profundidad y diámetro de cajera por si al hacer clic en Cajera avellanada se proviniera de Sin cajera.
Dibujar_Click()Este es el procedimiento que arranca al ser pulsado el botón Aceptar. Lo primero que hace es definir una rutina de errores que controlará salidas no deseadas, por ejemplo (como al pulsar
ESC), u otros errores no deseados. Después se llama al procedimiento de chequeo de casillas, el cual se comentará seguido de éste.Tras ocultar el formulario (letrero de diálogo) se pregunta por el punto de inserción del agujero (sin admitir
INTRO como respuesta) y se guardan sus coordenadas en VPt0. Seguidamente se hace el trasvase de variables con Pto0, que será la que se utilice para el dibujo.Se establece el valor de
OSMODE a 512 (Cercano) y se pide el ángulo de inserción. Y tras establecer el valor de PI se comprueba si el agujero tiene cajera o no. Si no tuviera se llama únicamente a la rutina de dibujo del agujero y, si tuviera cajera (recta o avellanada), se llama primero a la rutina que dibuja la cajera y luego a la del agujero. Además, el hecho de no tener cajera hace que el punto de inserción sea igual al primer punto de dibujo del agujeroLas sentencias siguientes se corresponden con los cálculos de los puntos del eje de simetría. Además se carga el tipo de línea si no está cargado y se crea la capa si no existe que pertenecerán al eje. A esta última se le asigna el tipo de línea cargado y el color rojo.
Por último se reasigna a la variable
OSMODE su valor original (por eso lo guardamos en Refent0) y se guardan los valores utilizados en el archivo de valores por defecto.NOTA: Recuérdese que antes de finalizar este procedimiento ha habido que pasar por otros dos o tres: el de chequeo, el de dibujo de cajera y el de dibujo de agujero. Estos se estudian ahora por ese orden.
Chequear()Aquí se comprueban los valores de cada casilla y, si alguno estuviera errado, se muestra un mensaje en una línea de errores inferior (que es una etiqueta), se selecciona el texto de la casilla y se sale de procedimiento.
Al haber error el texto de la línea inferior es diferente que una cadena vacía. Si no hay error este texto es igual a la cadena vacía (
""). Repásese el código del procedimiento anterior para ver cómo se controla después esto.
DibCajera()Para dibujar la cajera se calculan todos los puntos necesarios y se dibuja. También se tiene en cuenta si es recta o avellanada.
DibAgujero()Para dibujar el agujero se calculan todos los puntos y se dibuja.
NOTA: Recuérdese que tras este procedimiento
Sub se sigue en Dibujar_Click().
Cancelar_Click()Este
Sub únicamente dice que al pulsar el botón Cancelar se acabe el programa, sin más.NOTA: Evidentemente, para que este programa funcione, habrán de estar los archivos
.BMP en el directorio actual de trabajo.
4ª fase intermedia de ejercicios
·
Escribir una programa que maneje el cuadro de diálogo que se puede ver en la página siguiente.El programa dibuja tuercas hexagonales en vista de perfil. Los datos de la distancia entre aristas y la altura de la tuerca se introducen en las casillas correspondientes. El botón Punto de inserción< sale del letrero para marcar un punto en pantalla. Al hacerlo vuelve al letrero y escribe las coordenadas en sus casillas correspondientes.
Una vez introducidos todos los datos necesarios, el botón Dibujar realiza el dibujo y acaba el programa. El botón Cancelar simplemente termina la aplicación.
Introdúzcase algún control de entrada de datos del usuario, así como unos valores por defecto con los que arranque el cuadro (se puede hacer en tiempo de diseño).
Autor:
Para: La Web del Programador.