Nothing Special   »   [go: up one dir, main page]

TutorialDaboParte 1

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 21

Creando Fabulosas Aplicaciones Cliente

Usando Dabo

Ed Leafe
Paul McNett
PyCon 2010 Atlanta
DESARROLLANDO CON DABO
PARTE 1
INTRODUCCIÓN
Introducción a Dabo
Dabo es una estructura de trabajo escrita en Python que lo capacita a usted para crear fácilmente
poderosas aplicaciones de escritorio. Estas pueden variar desde aquellas que interactúan fuertemente
con bases de datos a otras que son puras interfaces de usuario. Como cualquier herramienta poderosa,
Dabo puede ser intimidante y confuso cuando usted comienza a usarlo, así que esta sesión está
diseñada para proporcionarle un buen conocimiento funcional de lo que necesita conocer para usar
Dabo efectivamente.

Audiencia Objeto de esta Sesión


Nosotros estamos suponiendo que usted tiene experiencia en programación, y al menos tiene una
comprensión básica de Python. Obviamente, mi4entras más conozca Python, más fácil será seguir
adelante. También estamos suponiendo que tiene Python y Dabo instalados en su computadora.

Código Fuente y Docs En Línea


El código fuente para los ejemplos que presentamos en este documento pueden hallarse en nuestro
repositorio subversion en:
https://svn.paulmcnett.com/pycon2010

Descárguelo a su computadora mediante la instalación de un cliente Subversion para su sistema


operativo (http://subversion.tigris.org) y luego::
svn checkout https://svn.paulmcnett.com/pycon2010

La última versión de este documento puede ser accedido en Google Docs en:
http://dabodev.com/pycon_tutorial

¿Qué se instala con Dabo?


Cuando usted descarga Dabo, en realidad está obteniendo 3 cosas: el módulo marco de trabajo dabo, el
cual se instala como cualquier otro módulo Pyt6hon de tercero; el directorio demo, el cual contiene la
aplicación DaboDemo que muestra algo de lo que Dabo puede hacer; y el directorio ide, el cual
contiene todas las herramientas visuales. Para ser honestos, en realidad nosotros no tenemos una
verdadera IDE; el nombre proviene de nuestra idealista meta de crear una completa IDE, pero los
requerimientos del mundo real de pagar las facturas ha impedido que eso ocurra todavía. Ambos
directorios, el demo e ide pueden ser situados en cualquier parte de su disco que sea conveniente para
usted; ellos no son parte de la estructura de trabajo, si no en verdad un conjunto de aplicaciones que
¡están escritas en Dabo!

Convenciones para Nombrar y Programar de Dabo


Nosotros hemos tratado en lo posible de ser consistentes en la creación de dabo, de modo que los
desarrolladores que usen Dabo puedan aprender la estructura de trabajo y utilizarla efectivamente. Aquí
están algunas de las convenciones con que se encontrará al usar Dabo:
• El prefijo 'd' para las clases: Las clases de base en la estructura de traba<jo Dabo están todas
prefijadas con una letra minúscula 'd' (p.e., dTextBox, dBizobj, etc.). Hemos hecho esto para
hacer las clases de la estructura de trabajo distinguibles en la lectura de las clases derivadas que
usted pueda crear en su aplicación.
• Los nombres de clases para todas las otras clases comienzan con una letra mayúscula; p.e.:
Wizard, ClassDesigner, etc.
• Todos los atributos y métodos comienzan con letra minúscula; p.e.: dabo.ui.areYouSure(),
self.requery(), self.sortOrder
• Los nombres de propiedades comienzan con letra mayúscula; p.e.: self.Form, biz.KeyField,
self.Caption
• Los nombres largos se hacen legibles poner en mayúscula la inicial de cada palabra integrante
del nombre, en lugar de subrayado; p.e.: requeryAllChildren(), self.AutoPopulatePK
• Las propiedades y atributos que comiencen con un subrayado se consideran para “sólo para uso
interno” de la clase a la cual pertenecen; en otras palabras, ellas son detalles de implementación
que no deben ser accedidos desde fuera de la clase en la cual están definidos.
• No más de una linea en blanco dentro de un método; dos líneas en blanco entre métodos y tres
lineas en blanco entre clases.
• (Alerta Hereje!!!) Use tabulaciones para la indentación. Simplemente tiene sentido™.

Otra convención es el uso de propiedades Python en lugar de simples atributos para la mayor parte de
las cosas que usted estará asignando en su código. Las propiedades proveen un montón de control sobre
la implementación de varios comportamientos, y también son descubiertas fácilmente por herramientas
de documentación También hemos creado la capacidad de asignar cualquier propiedad directamente
en el constructor de cualquier objeto pasando el nombre de la propiedad como un argumento de palabra
clave. Por ejemplo, digamos que estamos creando una sencilla etiqueta, pero queremos personalizar su
aspecto. Usted podría escribir:
lbl = dabo.ui.dLabel(self)
lbl.Caption = "Esto es algo de texto"
lbl.FontSize = 14
lbl.FontFace = "Tahoma"
lbl.RegID = "laEtiquetaTexto"

O sencillamente podría pasar todas esos valores de propiedades al constructor:


lbl = dabo.ui.dLabel(self, Caption="Esto es algo de texto",
FontSize=14, FontFace="Tahoma", RegID="laEtiquetaTexto")

El resultado es equivalente, pero la segunda versión es más limpia y fácil de entender. Ambas usan
programación de instancias para manipular las propiedades. Usted puede también usar subclases para
asignar propiedades comunes por vía del método initProperties(), el cual será discutido más tarde.

El Diseño Aplicación de Tres Niveles


La estructura de trabajo está construida alrededor del modelo de diseño aplicación de tres niveles. Esto
puede causar alguna confusión, especialmente para personas con experiencia usando el patrón MVC, el
cual prevalece en el mundo de las aplicaciones web. Los dos modelos pueden tener algún traslapo,
causando que los iniciados salten a la falsa conclusión que Dabo usa el modelo MVC.
El modelo de tres niveles separa el código en tres capas: IU/presentación, lógica de negocio y acceso a
datos. La comunicación entre esas tres capas ocurre solamente en unos pocos puntos; esto es para
mantener las capas acopladas tan holgadamente como sea posible. Discutiremos la forma en que esto
trabaja más tarde en mayor detalle. Pero por ahora, usted debería saber que ellas siguen el modelo de
diseño Cadena de Responsabilidad. En este modelo, cuando un objeto recibe un mensaje, si este
“sabe” como manejar ese mensaje, lo hace. Si no, el “conoce” el siguiente objeto en la cadena, y le
pasa el mensaje a ese objeto, el cual o lo administra o lo pasa al siguiente objeto en la cadena.
Finalmente el objeto es manejado, y la respuesta recorre hacia atrás la cadena hasta el llamador
original.
Aquí está un ejemplo corriente: usted tiene un botón con el texto 'Guardar' en él; cuando el usuario lo
presiona, usted quiere guardar en la base de datos todos los datos que hayan cambiado antes. Pero los
botones son objetos de interfaz; no saben nada acerca de bases de datos. Así que como todo control de
interfaz, él pasa la solicitud a la ventana (o 'formulario', hablando en Dabo) que lo contiene. El
formulario no puede hacer nada sobre de esto, de modo que él pasa la petición al objeto de negocio
primario (o 'bizobj') del formulario. El bizobj sí sabe algo sobre guardar ; él conoce como validar los
datos para estar seguro de que estos cumplen con alguna lógica de negocio que usted haya codificado.
Si la validación falla, el bizobj levanta una excepción, que captura el formulario y muestra un mensaje
de error. Si los datos son válidos, sin embargo, el bizobj pasa la solicitud de guardado al nivel de datos,
a un objeto llamado 'cursor'. El cursor sabe como interactuar con cualquiera que sea el almacén de
datos que usted esté usando y realiza la apropiada llamada de inserción o actualización. Si hay algún
problema con la escritura a la base de datos, el cursor levanta una excepción , la cual es re-levantada
por el bizobj, la cual es capturada por el formulario e informa el problema al usuario. Sin embargo, si la
escritura en la base de datos tiene éxito, el control retorna normalmente al bizobj, regresa al formulario,
y finalmente regresa al botón.
¿Le parece esto intimidante a usted? Tal vez a un primera vista, pero piense sobre esto: cada objeto
sabe como hacer una parte del proceso de guardar, así que cuando usted necesite cambiar esa parte,
usted sabe donde hacer el cambio y puede hacerlo sin romper las otras partes. Aquí está lo que hace
cada parte:
El Botón: permite al usuario interactuar con la aplicación
El Formulario: maneja peticiones de los controles y muestra algún mensaje al usuario
El Bizobj: administra la validación de las reglas de negocio
El Cursor: administra la interacción con la base de datos

Así que si usted cambia bases de datos, la clase del cursor cambiará, pero todo lo demás trabaja como
usualmente. Probablemente, si sus necesidades de negocio cambian, usted puede actualizar el bizobj sin
que ninguna otra cosa se quiebre.
De manera que esto puede tomarse un tiempo mientras se usted acostumbra a “poner las cosas donde
pertenecen”, a largo plazo esto redundará en una aplicación mucho más flexile y robusta. Hemos
escrito aplicaciones con esta arquitectura que han estado ejecutándose por más de una década; así como
el negocio subyacente cambia, así cambia el código del objeto de negocio. Cuando se ha deseado una
nueva interfaz de usuario, solamente ese código tuvo que ser cambiado. Cuando la revolución de la dot-
com entró en su apogeo, pudimos escribir el frontal web contra nuestra capa de calidad de producción
ya lista proporcionando a nuestros clientes aplicaciones web muy rápidamente, gracias a este modelo
de diseño.
Conectividad con Base de Datos
Dabo trabaja con muchas bases de datos; actualmente damos soporte a las siguientes:
• MySQL
• PostgreSQL
• Microsoft SQL Server
• Firebird
• SQLite
SQLite y MySQL han recibido la mayor atención durante el desarrollo de Dabo, mientras Microsoft
SQL Server ha recibido la menos. ¿Por qué no nos enfocamos en Microsoft SQL Server, dado que
podría decirse que esta es la fuente del más rico potencial de conversiones de ? Vea: el Pollo y el
Huevo.
Hay dos clases usadas para las conexiones: dConnectInfo, y dConnection. Típicamente usted crea un
objeto dConnectInfo, lo llena con la información requerida, y luego llama a su método
getConnection(), el cual retorna una instancia de dConnection. Luego usted pasa ese objeto
dConnection para crear un objeto de negocios y trabajar con ese bizobj, o puede llamar a
dConnection.getDaboCursor() para dirigir la interacción con su base de datos.
Este es un ejemplo de sesión usando cursores directamente:
import dabo
ci = dabo.db.dConnectInfo(Host="dabodev.com",
DbType="MySQL", Database="webtest",
User="webuser", PlainTextPassword="foxrocks")
conn = dabo.db.dConnection(ci)
# Obtener un cursor para trabajar con él
crs = conn.getDaboCursor()
# Ejecutar una consulta
crs.execute("""select * from zipcodes
where czip like '145%' order by czip """)
print "-" * 55
print "Número de filas:", crs.RowCount
print "-" * 55
# Imprimir todas las ciudades y sus códigos zip
crs.first()
while True:
try:
rec = crs.Record
print crs.RowNumber, rec.ccity, rec.czip
crs.next()
except dabo.dException.EndOfFileException:
# terminamos
print "Hecho!"
break

Note que primero se define la información de la conexión, y luego se crea la conexión. Esto pudiera
parecer un paso innecesario, pero el objeto dConnectInfo encapsula las diferencias en la manera de
especificar los parámetros de conexión entre los varios adaptadores de backend. También, hay un
campo 'PlainTextPassword'; por predeterminación, Dabo asume que cualquier cosa que se pase en un
parámetro 'Password' está encriptado, y corre le desencriptador predeterminado sobre él. Así que
usamos el 'PlainTextPassword' dado que Dabo sabe usar la cadena como está.
Luego iteramos a través de las filas del cursor usando los métodos de navegación first() y next().
(También hay los métodos last() y prior(), si se lo está preguntando). Usted también puede asignar la
propiedad RowNumber para mover directamente el puntero del registro actual. El cursor (y el bizobj,
como verá pronto) tiene un objeto Record que refleja los datos en la fila actual. Entonces usted refiere
cada columna en el resultado como simples atributos del objeto Record.
Usted nunca los usa directamente. Incluso podría olvidarse que ellos existen; por que todos los datos
son manejados por medio del bizobj, y es en la capa bizobj que usted hace la mayor parte de su
codificación. Los estamos mostrando aquí para que usted entienda el propósito de la capa de acceso a
datos, pero también en caso de que necesite acceder a un trasfondo de base de datos directamente para
mantenimiento y otros.

Objetos de Negocio
Objetos de negocio, o 'bizobjs', como ellos son comúnmente denominados, son el pegamento que
vincula entre sí los elementos de interfaz y el trasfondo de base de datos en una aplicación Dabo de tres
niveles. Ellos sostienen la lógica de negocio de su aplicación. Pero ¿qué pasa si usted no está
escribiendo una aplicación para negocios? Bien, el término 'lógica de negocio' se usa por que hay varias
capas de lógica que van en su aplicación. “Negocio” en este contexto es más parecido a “el fin del
negocio del trapeador” y menos como “atuendo de negocio”. Del lado de la base de datos, hay
restricciones de integridad referencial que aseguran que los datos relacionales contienen referencias
válidas, y por el lado de la Interfaz de Usuario existe una presentación lógica que determina cuales
formularios y controles se muestran, están habilitados, etc. Ambas podrían ser 'correctos', pero hay otra
capa de “correctos” a considerar.
Considere este ejemplo: el usuario quiere crear una orden para un cliente. Él introduce 3 items
totalizando $20. La lógica de presentación podría controlar el despliegue del formulario orden, como se
seleccionan los items, y los tipos válidos de datos (p.e. Sólo números en la caja de texto para
'cantidad'). La base de datos se aseguraría de que todos los campos no nulos tengan valores, y que las
relaciones entre el registro del cliente y los items de la orden es el correcto. Pero ¿y si usted tiene una
regla de que la orden mínima es $50? Una orden de $20 es perfectamente válida para mostrarla en la
interfaz de usuario, y la base de datos puede sostener esos valores sin problema. Esto es lo que
significamos con 'regla de negocio', y esta lógica sería codificada en el objeto de negocio.
Hay dos cosas importantes que usted debe configurar en un bizobj: su interfaz de datos, incluyendo
SQL Builder o instrucciones SQL directas, y rutinas de validación de los datos. Para la interfaz de
datos, hay dos propiedades principales que son necesarias de establecer: DataSource, la cual es el
nombre de la tabla subyacente en la base de datos, y el KeyField, que es la clave primaria usada para
actualizar los cambios de regreso a la base de datos (Si está usando claves compuestas como su PK,
puede especificar el KeyField como una lista separada por coma de los nombres de campos clave).
Luego, usted necesita especificar el tipo de datos que quiere obtener de la base de datos. Esto lo puede
hacer escribiendo SQL directamente, lo cual es una buena opción para aquellos que se sienten
confortables con SQL, o puede utilizar los métodos de SQL Builder para especificar las parte del SQL
que quiere personalizar. De forma predeterminada el bizobj ejecutará::
select * from <self.DataSource> limit 1000
Los bizobjs tienen una propiedad UserSQL; si usted asigna esto a algún valor no vacío, esta se usará y
todos lo métodos SQL Builder se ignorarán. Esta es una buena opción si usted tiene una consulta que
necesita optimizar por desempeño, y el SQL regresado por los métodos predeterminados de SQL
Builder simplemente no lo harán. Esto es también útil si necesita tomar en cuenta especificidades del
lenguaje de la base de datos que no se apoyan generalmente.

SQL Builder
La lógica del SQL Builder le permite especificar las diversas parte de la consulta que son necesarias
para su bizobj, y el resultado será portable a través de diferentes trasfondos de base de datos. Por
ejemplo, muchas personas gustan de escribir sus pruebas unitarias contra una base de datos SQLite
local para eliminar factores de red en sus pruebas, pero luego necesitan implementar contra un RDBMS
real para producción. Cuando usted usa los métodos del SQL Builder, las diferencias de sintaxis entre
las diferentes bases de datos serán administradas automáticamente para usted, y esto sencillamente
funcionará.
Los métodos del SQL Builder son:
• addField(exp, alias=None) – Añade el campo (y alias opcionales) a los campos en la
instrucción select.
• setFieldClause(clause) – Establece la cláusula campo, borrando todo lo especificado
previamente.
• addFrom(exp) – Añade la tabla especificada a la cláusula from.
• setFromClause(clause) – Establece la cláusula from, borrando, todo lo especificado
previamente.
• addJoin(tbl, exp, joinType=None) – Añade un 'join' a la instrucción. Usted especifica la
tabla a unir y la expresión a usar para el join. 'joinType' debería ser uno entre 'inner', 'outer',
'left', 'right'. Predeterminado es 'inner'
• setJoinClause(clause) – Asigna la cláusula join, borrando todo lo establecido previamente.
• addGroupBy(exp) – Añade la expresión a la cláusula 'group by' .
• setGroupByClause(clause) – Establece la cláusula group by, borrando todo lo
especificado previamentte.
• setLimit(val) – Establece el valor límite sobre el número de registros regresados.
Predetermnado=1000. Asígnelo a None para regresar todos los registros.
• addOrderBy(exp) – Agrega la expresión a la cláusula 'order by'.
• setOrderByClause(clause) – Establece la cláusula 'order by', borrando todo lo
especificado previamente
• addWhere(exp, comp="and") – Agrega la expresión a la cláusula where´. Si hay algo ya
en la cláusula where, la nueva expresión se añade con un predeterminado 'and', pero usted
también puede especificar 'or' cuando lo necesite
• setWhereClause(clause) – Establece la cláusula where, borrando todo lo especificado
previamente
• getSQL() – Testo recogerá todas las piezs especificadas por los diverso métodos clientes SQL
y construye la instrucción SQL real a ser usada.
Observe el patrón de estos métodos: se puede construir pieza por pieza la cláusula (los métodos
añadir*), o puede establecer toda la cláusula de una vez. Por ejemplo, estos dos son equivalentes:
self.addField("nombre")
self.addField("apellido")
self.addField("teléfono")
self.addField("dirección")

y:
self.setFieldClause("nombre, apellido, teléfono, irección")

Así que vamos a continuar con nuestra sencilla aplicación añadiendo un bizobj a la mezcla.
Import dabo
ci = dabo.db.dConnectInfo(Host="dabodev.com", DbType="MySQL",
Database="webtest", User="webuser",
PlainTextPassword="foxrocks")
conn = dabo.db.dConnection(ci)
# Create the bizobj
biz = dabo.biz.dBizobj(conn)
# Estas son las dos propiedades que se requiere establecer
biz.DataSource = "zipcodes"
biz.KeyField = "iid"
# Agregar algunos campos
biz.addField("iid")
biz.addField("ccity")
# Agregar un alias para este campo
biz.addField("czip", "postalcode")
# Añadir una cláusula WHERE
biz.addWhere("czip like '1452%' ")
# Ejecutar la consulta
biz.requery()
print biz.RowCount
# mostrar el SQL ejecutado
print "SQL:", biz.LastSQL
# Iterar a través de los registros e imprimir el city/zip
for rownum in biz.bizIterator():
rec = biz.Record
print rownum, rec.ccity, rec.postalcode

Los bizobjs también tienen los métodos para navegación first(), next(), etc., pero es útil tener una
manera para procesar todas las filas en el actual conjunto de resultados. Eso es lo que hace la clase
bizIterator ; a medida que iteramos a través de los registros en el bizobj, mueve el puntero como si
hubiéramos llamado a next(), y cuando se alcanza el final del conjunto de resultados, la iteración
termina.

Validación de Datos
Hay dos clases de validación de datos: nivel de campo y nivel de fila. La validación de datos a nivel de
campo se activa cuando cualquier control de IU que esté enlazado a un bizobj trata de "descarga", o
guarda su valor al bizobj; esta 'descarga' generalmente ocurre cuando un control pierde el foco. La
validación de campo está dirigida a proveer retroalimentación inmediata para escogencias inválidas.
Para añadir validación de nivel de campo, agregue código al método validateField() del bizobj.
Cuando un control ligado pierde el foco, se llamará a este método por la estructura de trabajo y el
método recibe dos parámetros: el valor del DataField del control (el nombre del campo), y el valor
actual en el control de interfaz. Entonces usted puede usarlos para determinar si el valor del campo es
válido; si no, regresa una cadena que contiene el mensaje de error a ser mostrado al usuario. Por
defecto, se llama al método onFieldvalidationfailed del bizobj, el cual despliega el mensaje de error
en la barra de estado del formulario, y asigna el foco de nuevo al control fallido. Puede reemplazar este
método para controlar los errores de validación de una manera más apropiada a su aplicación. Si el
valor está bien retornará, ya sea None o una cadena vacía.
El requerimiento más común de validación, sin embargo, es la validación-a-nivel-de registro. En
muchos casos, usted puede evaluar la validez de los datos sin considerar todas las columnas en el
registro, y dado que el usuario puede cambiar campos en cualquier orden que le guste, no tiene sentido
evaluar sus valores hasta que el usuario intente guardar esos cambios. Como parte del proceso de
guardado, el método validateRecord() del bizobj será llamado por cada registro modificado en el
conjunto de datos. Cuando es llamado, el RowNumber del bizobj estará en el registro modificado, por
lo que usted puede examinar directamente su objeto Recor directamente. Y parecido al método
validateField(), si se regresa una cadena no vacía la validación falla, y el guardado es abortado. El
método de formulario notifyUser() es llamado entonces, lo cual, por defectoult muestra el mensaje de
error en un diálogo. Usted puede personalizar este comportamiento sustituyendo notifyUser() en sus
subclases de dabo.ui.dForm.

Relaciones de Bizobj
Una de las cosas más comunes cuando se trabaja con bases de datos relacionales son, por supuesto, las
tablas relacionales. Los bizobj de Dabo facilitan el trabajo con las tablas relacionales; usted solamente
tiene que asignar unas pocas propiedades en el bizobj hijo. Una de las que debe ser asignada es la
propiedad LinkField: esta le dice a Dabo cual columna es la clave foránea a la tabla materna. Si la
clave foránea está vinculada a la clave primaria de su tabla materna, lo cual es el caso más común,
usted no necesita hacer nada más; Dabo descifrará la relación. Pero si está ligada a otra columna
diferente a la PK materna, usted necesita asignar la propiedad ParentLinkField de la hija al nombre de
la columna relacionada en la madre. Una vez que estas están asignadas, la única otra cosa que necesita
hacer en su código es decirle a la madre que tiene una hija. Para hacerlo, pase la hija al método
addChild() de la madre y usted listo! Ahora, cuando usted cambie el registro actual en la madre, la hija
automática será requerida a extraer los registros relacionados.
Hay otras pocas propiedades disponibles para ayudar a manejar relaciones. Para bizobj hijos, existe
FillLinkFromParent, la cual, cuando es verdadera, automáticamente introduce el valor de la columna
especificada por LinkField con el valor de la columna relacionada en el registro materno actual. Hay
dos propiedades que controlan la adición automática del registro: en la madre, NewChildOnNew
determina si, cuando un nuevo registro se añade en la madre, un nuevo registro es añadido a todos los
bizobj de su hijas. Usted puede controlar eso individualmente para cada hijo asignándole su propiedad
NewRecordOnNewParent; sólo si ambas son verdaderas se añadirá automáticamente un nuevo
registro a un bizobj hijo cuando se cree un nuevo registro materno.

DataSets
Cuando se llama a un método requery() de un bizobj, él hace a uno de sus objetos cursor correr el
SQL, y luego crea un DataSet del resultado. Un DataSet es sencillamente una tupla de diccionarios:
cada registro en los datos regresados es almacenado en un diccionario, con el nombre de la columna
como clave y el contenido de la columna como valor.
Algunas veces es más eficiente recoger una gran cantidad de registros desde el servidor y luego
filtrarlos localmente para ajustarlos a las selecciones del usuario. Esto es hecho fácilmente en Dabo
llamando el método filter() del bizobj, el cual le permite aplicar un filtro a cualquier columna sin
perder el resto de los datos. Usted puede aplicar múltiples filtros en serie y luego removerlos uno a uno,
como una función 'deshacer', usando el método removeFilter(). También puede llamar al método
removeFilters() para quitar alguno y todo los filtros aplicados para regresar a sus datos originales.
Si necesita acceder a los datos actuales en un bizobj, sencillamente llame a su método getDataSet();
este retornará una copia de las datos actuales. Opcionalmente usted puede limitar los datos que se
retornan pasando alguno de varios parámetros que limitarán las columnas regresadas, la fila inicial y el
número total de filas regresadas

DataStructure
Esta propiedad contiene una descripción de la estructura de las datos subyacentes que están siendo
administrados por el bizobj. Predeterminadamente, esta es poblada luego de una consulta, tomando la
información del nivel dbapi en el subyacente cursor del bizobj. Pero debido a que no hay una relación
1:1 entre los tipos de los datos y los tipos Python, puede haber un resultado ambiguo en tanto que
ambos, la capa dbapi y la capa Dabo intenten convertir los datos desde la base de datos en el tipo
Python apropiado. Para evitar esta ambigüedad, usted puede especificar la estructura de datos
explícitamente. El formato de la DataStructure es una tupla, con un elemento por cada campo de datos.
Cada elemento de campo es una séxtupla que contiene lo siguiente:
0: alias del campo (str)
1: código del tipo de dato (str)
2: ¿es un campo pk? (bool)
3: nombre de la tabla (str)
4: nombre del campo (str)
5: escala/precisión del campo (int or None)

Vea código de bizobj generado por el AppWizard-generated bizobj code para ejemplos de
DataStructure siendo asignada explícitamente.

Campos virtuales
Frecuentemente es útil tener un campo virtual en los datos mostradas al usuario que no esté realmente
presente en la subyacente tabla de la base de datos. Probablemente el ejemplo más común que viene a
la amente es costo total de una línea de ítem: usted probablemente almacenará el costo unitario y la
cantidad ordenada, pero no el total; es fácilmente calculable multiplicando los dos. Pero como un
control de interfaz tal como una rejilla espera que todos sus datos provengan de un objeto
bizobj.Record, no hay por el contrario otra manera fácil de manejar la presentación de valores
derivados o calculados.
Defina tales cálculos en la propiedad VirtualFields del bizobj, la cual es un diccionario que aplica el
nombre del campo virtual a la función para calcular su valor. En el ejemplo de abajo, definiremos lo
siguiente:
self.VirtualFields = {"total<linea": self.calcTotalLinea}

y luego definimos el siguiente método en ese bizobj:


def calcTotalLinea(self):
return self.Record.item_precio * self.Record.cantidad
Si estuviéramos mostrando los items para una orden de cliente en una rejilla, podríamos agregar
columnas para el nombre de ese ítem, sku, precio y cantidad ordenada, y estos se obtendrán de la base
de datos. También podríamos agregar una columna cuya propiedad DataField pudiera ser establecida
como “totalLinea” y el valor calculado será mostrado para cada línea en la rejilla. Los llamamos
VirtualFields (en vez de, digamos, CalculatedFields) por que ellos siempre son calculados al vuelo,
cuando se acceden, lo que significa que ningún cálculo es almacenado nunca (bueno, a menos que la
función de retorno de su campo virtual los almacene por alguna razón ). Así que ellos añaden un muy
pequeño tiempo de carga superpuesto, y los controles virtuales de interfaz como dGrid solo requieren
los valores de campos que están presentes en la pantalla, así que aún si usted tiene 20.000 registros en
el bizobj actual, los valores del campo virtual solo necesitarán ser calculados para 10 registros
mostrados actualmente en la pantalla.
Debido a los cálculos al-acceso, usted debería mantener sus funciones de campos virtuales tan livianas
como sea posible. No espere una gran perfomance si la función necesita iterar sobre varias capas de
bizobjs hijos, por ejemplo.

Por favor, tenga en cuenta que por definición, los valores VirtualField son de solo-lectura.

La Capa IU
Dabo tiene un rico conjunto de controles de IU y un robusto modelo de eventos. A pesar de que Dabo
fue diseñado para utilizar cualquier conjunto de herramientas de IU, como Tkinter, Qt y wxPython, por
el previsible futuro la herramienta IU usada por Dabo es wxPython. Realísticamente hablando, es un
montón de trabajo envolver las clases de IU; nosotros estimamos que cerca del 80% del tiempo
empleado en el desarrollo de Dabo ha sido gastado en envolver wxPython, y mantenernos al día con
ese objetivo móvil es todo lo que hemos sido capaces de hacer.
¿Por qué no usamos tan solo la IU directamente? Aun si renunciáramos a la meta de diseño de hacer a
Dabo herramientasgráficas-agnóstico, y solo lo acoplamos a wxPython aún queremos envolver sus
clases. En primer lugar, wxPython francamente muestra sus raíces C++ con montones de
COSAS_TODAS_ EN_ MAYÚSCULAS que hacen al código feo de lectura; nosotros nos hemos
librado de eso y reemplazado con nombres más Pythónicos. Segundo, wxPython le exige realizar una
serie de tediosos pasos para lograr una sola tarea; nosotros somos capaces de manejar esas tareas
repetitivas y sin sentido por usted, haciendo su código más limpio de escribir y más fácil de leer.
Finalmente, wxPython es inconsistente en como usted hace las cosas, lo que hace necesario referirse
constantemente al archivo de Ayuda mientras codifica. Por ejemplo, es muy corriente tener un pequeño
texto asociado con un control, tal como el titulo en una ventana, una cabecera de columna en una
rejilla, la palabra OK en un botón, etc. En wxPython, todos estos tres ejemplos usan un método
diferente para asignar ese texto y ello puede ser difícil de recordar cual de ellos usa cual método.
Nosotros hemos envuelto esas diferencias de modo que en todos los tres caso usted sencillamente
asigna la propiedad Caption a ese objeto y esto hace lo que usted espera.
Esta es una lista de las propiedades IU más comunes:
• Caption: muchos controles tienen un pequeño trozo de texto desplegado en ellos. Botones,
etiquetas, ventanas, barras de título, las pestañas en páginas con pestañas, etc. Todos ellos
pueden asignados usando la propiedad Caption.
• Value: Si bien algunos controles son sólo para presentar, otros, como los controles de texto,
cajas de verificación, cajas de lista, deslizantes,etc., todos tienen un valor asociado con ellos. El
valor puede ser de cualquier tipo de datos.
• DataSource, DataField: estas son las bases de la vinculación a datos. Cuando estas son
asignadas, el control obtiene su Valor desde la DataSource/DataField especificada(o), y los
cambios en el control son propagados de regreso a la DataSource/Field. Típicamente en una
aplicación de base de datos, la DataSource es un bizobj, y el DataField es el nombre de la
columna en el conjunto de datos. Pero una Fuente de Datos puede ser cualquier objeto, y el
Campo de Datos puede ser una propiedad de ese objeto, así que los cambios en un control puede
afectar inmediatamente a otro control. ¡Las posibilidades son ilimitadas!
• Width, Height, Size: estas controlan el tamaño real del control cuando no se usan sizers
(dimensionadores). Ancho y Alto son valores enteros, en tanto que Tamaño es el par (Ancho,
Alto). Cuando se usan sizers, estos controlan el tamaño mínimo que el sizer se permitirá para
dimensionar el control durante la ejecución.
• Left, Right, Top, Bottom, Position – Estos controlan la posición del control en su contenedor
materno. Los primeros 4 son enteros, mientras que Position es un par (Izquierda, Arriba).
Cuando se usan sizers, asignar estos no tiene sentido por que el sizer controla el
posicionamiento del objeto; esencialmente, ellos son solo-lectura con sizers.
• Parent: una referencia al objeto que contiene al control.
• Form: la ventana de más alto nivel que contiene al control. Para un formulario en sí mismo, la
propiedad Form retorna None.
• BackColor, ForeColor: los colores de fondo y primer plano usados por el control. Estos puede
ser asignados con tuplas RGB como (255, 128, 0), o con nombres comunes de colores como
'blue', 'RED', 'lightGreen'. Observe que las mayúsculas son ignoradas para los colores.
• Children: retorna una lista de todos los objetos contenidos por el control.
• Enabled: determina si un control puede tener interacción con el usuario.
• Visible: determina si el control es mostrado o no.
• FontBold, FontFace, FontItalic, FontSize, FontUnderline: se usa para establecer aspectos de
la tipografia mostrada en el control.
• Name: identifica de manera única un control entre todos los controles con el mismo contenedor
materno. Todos los controles tienen un Nombre no vacío.
• RegID: opcional. Si se asigna, identifica de manera única un objeto dentro de un formulario
dado, y se puede obtener una referencia tratando la RegID como un atributo del formulario. Por
ejemplo, si un control particular tiene una RegID="estaCosa", ningún otro control en el
formulario puede referirse a él como self.Form.estaCosa, no importa cuan profundamente
enterrado está este en alguna jerarquía de contenimiento.
• ToolTipText: contiene el texto de la informacion del sugerente que se muestra cuando un
usuario posa el puntero del ratón sobre el control.
Hay muchas más propiedades que pueden usarse, como también hay unas que son específicas de ciertos
tipos de controles, pero estas cubrirán el 80% de lo que usted necesita.
Una gran porción de las propiedades de la IU puede ser también hechas 'dinámicas'. Una propiedad
dinámica es controlada por una función llamada en el momento apropiado por la interfaz, como cuando
el objeto está siendo pintado, o se han requerido los datos. Las propiedades dinámicas son
sencillamente los nombres de las propiedades regulares con el prefijo 'Dynamic', y en lugar de
asignarles un simple valor, usted le asigna un 'llamable'. Por ejemplo, si usted quiere hacer que la
visibilidad de una caja de texto dependa de alguna condición, podría usar DynamicVisible :
def miDynamicVisible(self):
return self.chkCasado.Value

self.txtNombreEsposa.DynamicVisible = self.miDynamicVisible
Ahora, la caja de texto para introducir el nombre de la esposa será visible sólo si la persona ha sido
marcada como casado'

Sugerencias Generales de Programación IU


Una aplicación con una interfaz de usuario gráfica difiere de una aplicación no IGU en que hay un
ciclo principal de eventos que espera que el usuario haga cosas, y entonces procesar esos eventos
mientras ocurren. Ocasionalmente, sin embargo, usted podría encontrar que el código que escriba no
parece hacer lo que usted estaba esperando: por ejemplo, usted podría escribir un código que se active
cuando el valor de un control cambie, pero el cual depende de que el nuevo valor sea 'visible' por otros
objetos en el sistema. Debido al orden en que los eventos se procesan, sin embargo, su código podría
activarse antes de que el valor del control haya sido actualizado, y usted obtenga valores viciados. Ese
no es un error de su parte; esto es solo una trampa de la programación dirigida a eventos.
Para resolver esto, use la función dabo.ui.callAfter(mthd, *args, **kwargs) para agregar la
llamada a su método (y algún parámetro) al final de la cola de eventos. Esto asegurará que cualquier
procesamiento previo ya ha sido completado antes de que su método se ejecute. Hay una función
análoga para demorar la asignación de propiedades: dabo.ui.setAfter(obj, propName, value).

Otro problema común en un entorno IGU es la excesiva duplicación de llamadas a la misma función,
como refresh(). Podría haber muchos objetos que llamen a refresh() cuando ellos son modificados de
alguna forma, y el código refresh() dispara cambios en otros objetos que terminan llamando a refresh()
de nuevo, y así lo que pasa es que refresh() puede ser llamada docenas de veces cuando una vez habría
sido suficiente. Para resolver ese problema use use dabo.ui.callAfterInterval(interval, mthd,
*args, **kwargs); hay una función análoga dabo.ui.setAfterInterval(interval, obj,
propName, *args, **kwargs) para asignar las propiedades. Aquí 'interval' es el número de
milisegundos para esperar por llamadas duplicadas; los otros argumentos son exactamente como
aquellos en dabo.ui.callAfter(). La manera en que esto trabaja es que cuando esta llamada es recibida,
Dabo comprueba un apuntador interno para ver si este ya tiene una llamada pendiente con la misma
firma. Si no, este crea un reloj que se activará en el intervalo especificado y añade la llamada al
apuntador. Ahora supongamos que otra llamada al mismo método con los mismos argumentos se
recibe. Dabo ve que esta es una llamada duplicada, así que restablece el reloj y descarta la petición
duplicada. Si varias docenas de de llamadas similares son recibidas, ellas igualmente son descartadas.
Una vez que se detiene la duplicación por un tiempo suficientemente largo para que el reloj se active, el
método es llamado y removido del apuntador interno.

Objetos IU Comunes
• Form: Todos los elementos IU existen en una ventana de nivel superior en la pantalla; en Dabo,
llamamos a estas ventanas 'form'. Todo control IU debe ser creado con alguna suerte de control
'contenedor' como su Padre; el primer parámetro de toda instrucción de creación de objeto IU
debe ser ese objeto Padre requerido. Los formularios son los únicos objetos que pueden tener
None como su padre, aunque usted puede crear formularios (y diálogos), como hijos de otros
formularios, así que cuando el formulario padre es destruido, el formulario hijo también.
• Panels: los Paneles son objetos contenedores convenientes para agrupar controles relacionados.
Normalmente tienen una apariencia no visible, pero usted puede asignarles su Color de Fondo, o
darles un borde, etc., si quiere visibilizarlos. Puede anidar paneles dentro de otros a cualquier
grado que desee. Para usuarios Windows: a fin de que los formularios tengan apariencia 'normal'
para un tema dado, usted debe añadir un panel al formulario, y luego agregar todos sus controles
como hijos de este panel principal. De lo contrario, el fondo aparece como un gris oscuro, y su
formulario no lucirá bien. Un gran uso de los paneles es para guardar diseños encapsulados
como clases, así que donde sea que usted necesite obtener la información estándar de contacto
del usuario, usted solamente instancia su subclase PnlContactInfo de dPanel.
• Buttons: Hay varios tipos de botones disponibles en Dabo, cada uno con un levemente diferente
uso:
• dButton: Un botón regular, con un texto como leyenda
• dBitmapButton: Utilícelo cuando quiera una imagen en el botón en vez de un texto.
• dToggleButton: Este es un botón con dos estados visualmente diferentes que alternan
entre ellos cuando se presiona el botón. A este respecto él trabaja muy parecido a una
caja de chequeo, pero luce como un botón. Puede ponerle diferentes imágenes para los
estados normal y pulsado.
• Vea también dHtmlBox, el cual no se parece a un botón a primera vista, pero puede ser
asignado para lanzar su propio código en su manejador de evento onHit.
• Text Controls: Dependiendo de sus necesidades, use uno de esos controles:
• dTextBox: el control estándar de entrada de texto de una línea
• dEditBox: Para entrada de texto multilínea.
• Text Labels:
• dLabel: es la manera estándar de desplegar texto. Las etiquetas tienen propiedades que
habilitan el texto a envolverse a través de múltiples líneas.
• dHyperLink: Crea un hipervínculo deforma que el usuario pueda pulsar para tener un
enlace abierto en su explorador web o lanzar algún código de aplicación, muy parecido a
un botón..
• dHtmlBox: Este control le permite usar marcado html sencillo para mostrar texto con
formato.
• Paged Controls: Dabo ofrece varios controles paginados; estos son contenedores que tienen
más de una página, con sólo una de las páginas visible en cualquier momento dado. La principal
diferencia escomo se activa la página:
• dPageFrame: cada página tiene una 'pestaña' con texto identificador. Presionando la
pestaña trae esa página al frente. La posición de las pestañas es especificada en la
creación con la propiedad TabPosition (predeterminado=Top).
• dPageList: presenta las leyendas de las diferentes páginas como una lista.
Seleccionando una de las leyendas en la lista selecciona la página asociada.
• dPageSelect: similar a dPageList, pero las diferentes leyendas de las páginas se
muestran como un control de lista desplegable. Seleccionando la leyenda en el
desplegable trae al frente la página asociada.
• dPageFrameNoTabs: este es un control paginado sin interfaz de usuario para cambiar la
página activa. La selección de la página debe ser hecha programáticamente,
generalmente en respuesta a algún evento generado por el usuario, como presionar un
botón 'siguiente' que usted defina.
• dPageStyled: Entre otros efectos de estilo agradables que este control ofrece, , usted
puede también elegir no mostrar la leyenda dela pagina si hay una sola página.
• List Controls: Dabo ofrece varios controles para desplegar una lista de items; ellos difieren en
como los items son mostrados y como selecciona el usuario un ítem o varios. Con los controles
de lista, usted especifica la lista de items asignando la propiedad Choices a una lista de cadenas
a mostrar. También puede asignar una propiedad opcional Keys; esta es una lista 1:1 de valores
de claves cada una de las cuales está asociada con el valor de Choices en la misma posición en
la lista. Usted puede especificar el Value del ítem seleccionado de varias maneras. Primero es el
texto que se muestra; este es llamado StringValue. Segundo es la posición del ítem
seleccionado dentro de la lista; este es el PositionValue. Finalmente, si las claves para el control
han sido asignadas, usted puede obtener la clave asociada con la selección referenciando la
propiedad KeyValue. Hay otra propiedad llamada ValueMode que puede ser una entre 'String',
'Position' o 'Key'; cómo este es establecido determina cual tipo de valor es regresado cuando
usted referencia la propiedad Value . Los controles lista que puede usar en Dabo son:
• dListBox: este control muestra varios items de una vez, dependiendo de la altura del
control. Si hay mas items que pueden ser mostrados de una vez, unas barras de
desplazamiento le permiten al usuario ver los items ocultos. Si MultipleSelect es
Verdadero, el usuario puede seleccionar más de un ítem de una vez con shift-pulsar,
control-pulsar, etc.
• dDropdownList: este control muestra un solo ítem cada vez, pero presionando en el
control causa que se despliegue toda la lista de items. Una vez que el usuario selecciona
un ítem de la lista, ésta se cierra y el ítem seleccionado es mostrado.
• dComboBox: similar al dDropdownList, excepto que el usuario puede escribir en el área
donde se muestra el único ítem. Normalmente usted tendría que escribir el código para
manejar lo que debe hacerse al valor escrito, pero si la propiedad AppendOnEnter es
Verdadero, el texto será agregado a la lista de items en el control si aun no está presente.
• DlistControl: este es un cruce entre el dListBox y dGrid. Como un listbox, usted puede
interactuar sólo con filas, pero como una rejilla puede haber múltiples columnas en cada
fila.
• Grid: Describir completamente el control dGrid podría tomar un capítulo completo – tal vez
aun varios capítulos. Pero esto servirá como una introducción básica a la clase dGrid y como
usarla efectivamente. Las rejillas son utilizadas para desplegar registros de datos (llamados un
dataset) en un formato tabular, similar a una hoja de cálculo. Si usted simplemente tiene un
dataset llamado 'ds' y lo quiere mostrar, puede llamar a dGrid.buildFromDataSet(ds),
y se le construirá una rejilla, con una columna por cada columna del dataset. Si embargo, si
quiere ejercer control sobre la apariencia de cada columna de datos, cree una instancia de
dColumn por cada columna en el dataset, y establezca las propiedades de dColumn
adecuadamente. Discutiremos esto pronto. Las rejillas tienen montones de comportamientos
maravillosos integrados, tales como columnas ordenables, edición en sitio, auto
dimensionamiento y búsqueda incremental dentro de una columna. Puede modificar o quitar
cualquiera de estas si lo desea, ya sea columna a columna o en toda la amplitud de la rejilla,
pero en general ellas hacen que la rejilla sea muy útil para sus usuarios. Generalmente usted
asigna la propiedad DataSource de la rejilla para controlar donde la rejilla obtiene sus datos
(usualmente un bizobj), y asigna la propiedad DataField a cada columna para controlar cual
columna en el dataset se muestra en esa columna de la rejilla.

Estas son las propiedades de rejilla más comúnmente usadas para controlar su apariencia:
• AlternateRowColoring, RowColorEven, RowColorOdd: cuando es verdadero la
propiedad AlternateRowColoring, esta alterna el coloreado de las filas de acuerdo a las
dos propiedades de color de fila. Esto hace que sea más fácil visualizar las filas de la
rejilla.
• Children, Columns: estas son alias que retornan una lista de instancias de dColumn que
componen la rejilla.
• CurrentRow, CurrentColumn: estas determinan la localización de la celda
seleccionada de la rejilla.
• HeaderBackColor, HeaderForeColor: estas establecen los colores de fondo y frontal
predeterminados para las cabeceras de columna. Columnas individuales pueden
sustituirlos con sus propios valores, lo cual es el modelo general para todas las
propiedades en dGrid: configure la rejilla de la manera que usted quiere que se comporte
la mayoría de las columnas, y luego sustituya las propiedades len las columnas
específicas que quiere se comporten o muestren de forma diferente.
• NoneDisplay: este es el texto presentado cuando están presentes valores nulos (None).
• ResizableColumns, ResizableRows, SameSizeRows: las primeras dos determinan si el
usuario puede cambiar el ancho de columnas o la altura de filas arrastrando las líneas
separadoras. La última controla si cambiando la altura de una fila cambia todas las filas a
esa altura.
• SelectionBackColor, SelectionForeColor: Estas controlan el color de cualquier celda
seleccionada en la rejilla.
• ShowColumnLabels, ShowRowLabels: estas determinan si se muestran las cabeceras
de columna o las etiquetas de las filas.

Estas son las propiedades más comúnmente usadas para controlar el comportamiento de la rejilla:

• ActivateEditorOnSelect: cuando la rejilla es editable, ¿debería la celda entrar en modo


edición tan pronto es seleccionada? ¿O debería esperar a ser pulsada después de ser
seleccionada a fin de mostrar su editor? Esta propiedad determina el comportamiento.
• Editable, Searchable, Sortable: estas determinan, respectivamente, si cualquier cosa en
la rejilla puede ser editada, buscada u ordenada. Si ellas son Falsas, entonces el
comportamiento es deshabilitado para toa la rejilla. Sin embargo, estableciéndola a
Verdadero no hace nada por si misma; cada columna de la rejilla para la que usted desee
ese particular comportamiento particular debe también tener su correspondiente
propiedad asignada a Verdadero antes de que la conducta pueda ocurrir.
• HorizontalScrolling, VerticalScrolling: estas controlan si la rejilla puede ser
desplazada en las direcciones respectivas.
• MovableColumns: las columnas pueden ser rea-regladas arrastrando sus cabeceras.
Poniendo esta propiedad en Falso deshabilita esta capacidad.
• MultipleSelection: cuando Verdadera, el usuario puede seleccionar más de una
celda/fila/columna al mismo tiempo.
• SearchDelay: cuando se ejecuta una búsqueda incremental, esta controla cuan larga
debe ser la pausa antes que la siguiente presión de tecla sea considerada una nueva
búsqueda en vez de agregar la la tecla a la actual cadena de búsqueda.
• SelectionMode: puede ser una de entre Celdas, Fila o Columna. Esta determina si
presionando sobre una celda selecciona solo esa celda, la fila o la columna de la celda.
Las columnas tienen una larga lista de propiedades suyas; estas son las más comúnmente usadas:
• Caption:el texto desplegado en la Cabecera.
• Order: este es un entero que controla el orden de la columna dentro de la rejilla. Usted
puede darle a sus columnas números como 1,2,34,... o 10,20,30... o aun 1, 66, 732. La
rejilla muestra las columnas dependiendo del valor relativo de su propiedad Order.
• Editable, Searchable, Sortable: Vea las propiedades de rejilla del mismo nombre
arriba. Cuando estas están habilitadas para la rejilla, habilitarlas para la columna permite
que el comportamiento ocurra.
• Visible: esta puede ser alternada para mostrar u ocultar una columna.
• Width: esta determina el Ancho de la columna.
• BackColor, ForeColor: estas controlan el color de las celdas en la columna.
• FontBold, FontFace, FontInfo, FontItalic, FontSize, FontUnderline: estas controlan
la apariencia de la tipografía para las celdas en la columna.
• HeaderBackColor, HeaderFontBold, HeaderFontFace, HeaderFontItalic,
HeaderFontSize, HeaderFontUnderline, HeaderForeColor: como la de arriba,
excepto que estas controlan la apariencia de la cabecera de la columna.
• HorizontalAlignment, VerticalAlignment, HeaderHorizontalAlignment,
HeaderVerticalAlignment: estas determinan donde aparece el texto en la celda o la
cabecera. LA primera puede ser una entre 'Izquierda', 'Centro' o 'Derecha', mientras que
la última puede ser una entre 'Arriba', 'Medio' o 'Abajo'. Predeterminado es Centro,
Medio.
• Expand: cuando es puesta en Verdadero, la columna puede expandirse/contraerse
proporcionalmente a como la rejilla cambie de tamaño.
• Precision: determina cuantos lugares decimales se muestra cuando hay valores float en
la columna.
• WordWrap: cuando Verdadera, un gran contenido de la celda será envuelto en líneas
adicionales hasta tanto haya suficiente altura en la fila para mostrar esas líneas.
• CustomEditorClass, CustomEditors, CustomRendererClass, CustomRenderers:
usted puede definir controles personales para manejar como los datos son representados
y/o editados en una rejilla. Las clases CustomEditorClass y CustomRendererClass son
usadas cuando usted necesita que los datos en una columna sean tratados diferente a
como está predeterminado. Usted puede obtener un control más preciso creando un
diccionario con el número de fila como la clave y un editor/presentador personal como el
valor, y asignando la CustomEditors/ CustomRenderers a ese diccionario. Ahora
cualquier fila con alguna entrada en ese diccionario obtendrá su propio
editor/presentador; el resto obtiene CustomEditorClass/ CustomRendererClass.

Explorando los Controls de IU


Cada una de nuestras clases base de controles IU contiene código de prueba que se ejecutará si usted
corre el archivo .py directamente. Algunos controles contienen solo un muy básico script que crea una
instancia del control, mientras que otros tienen unos casos de prueba más elaborados,pero bao ninguna
circunstancia usted podrá correr la prueba del control e interactuar con él. Si usted está corriendo OS X
o Linux, cd al directorio dabo/ui/uiwx, y teclee 'python <classname>.py' para correr esa
prueba de clase. Por ejemplo: teclee 'python dButton.py' para correr la prueba de dButton. Si usted
está corriendo Windows, usted puede ya hacer lo mismo desde cmd.exe, o puede localizar el archivo
en el explorador de Windows y pulsar doble en él para correrlo.

Usted verá que se abre una ventana, con (al menos) una instancia del control en cuestión. Usted puede
entonces presionarlo, teclearlo, lo que sea, la mayoría de las pruebas tienen manejadores ligados a los
eventos más comunes que imprimirán información cuando un evento sea recibido. Adicionalmente,
usted puede abrir una Ventana de Comandos tecleando Control-D (en Mac teclee ⌘-D); desde esa shell
interactiva usted puede coger una referencia a cualquier control usando el comando
dabo.ui.getMouseObject(), y entonces explorar sus propiedades y métodos usando la completación de
código interna de la ventana de comando.

Eventos de Interfaz de Usuario


El código de Interfaz de Usuario es lanzado en respuesta a eventos. Por ejemplo, algunos escenarios
comunes son que el formulario devenga en activo, un botón sea presionado, el ítem de menú sea
seleccionado o el reloj alcanzando su intervalo. La programación dirigida a eventos pone al usuario en
control de usar características de su aplicación cuando él escoja hacerlo. Muchos eventos son
levantados en cada minuto de la existencia de su aplicación, pero típicamente usted sólo se ocupará de
actuar sobre unos pocos de ellos. Conectar su código para responder a eventos de IU es llamado
“vincular”. Usted vincula el evento levantado a una función de devolución de llamada, llamada así por
que la función es “llamada de nuevo” desde el vínculo del evento levantado en el momento apropiado.

Dabo usa su propia capa de eventos por dos razones. Primero, First, de acuerdo con el diseño hacer
posible que utilice múltiples kit de herramientas IU gráficas y no solo wxPython, necesitamos abstraer
los eventos. Segundo, aun si fuéramos a usar solo wxPython siempre para nuestra capa IU, el diseño y
denominación de los eventos en wxPython es confuso y muy a lo C++. Hemos simplificado los
eventos, y dádosles nombres sencillos y consistentes. Todos los eventos Dabo están definidos en el
módulo dabo.dEvents.

Por ejemplo, la mayoría de los controles de IU tienen una forma común para interactuar con ellos:
usted presiona un botón, selecciona de un menú o una lista, incrementa o decrementa un numerador;
usted conmuta un botón de radio. En wxPython, cada uno de estos diferentes eventos tiene un nombre
diferente, pero en dabo lo hemos simplificado, así que todas estas acciones levantan el eventoHit.

Para vincular una acción a un evento, usted tiene varias opciones. La más directa es correr el siguiente
código:
self.bindEvent(dabo.dEvents.Hit, self.handleHit)

Asumiendo que este código esta escrito en la clase para un control, ahora él llamará al método
handleHit() de esa clase.
Hemos provisto una manera incluso más fácil para vincular a eventos; sencillamente nombre su método
manejador de evento on<EventName>', y la vinculación será hecha por usted. De modo que en el
ejemplo de código de arriba, pudimos haber sencillamente llamado al método manejador de evento
'onHit', y no necesitaremos llamar a bindEvent()! Dabo se hará cargo de la vinculación por usted.
Hay una manera más de hacer la vinculación: usted puede pasar el nombre del evento y el manejador
de al constructor del objeto como si este fuera una propiedad y Dabo se dará cuenta de ello. En este
caso, el manejador de evento está lo más probable en la clase que está creando el control, no en la clase
del control. Así que supongamos para este ejemplo que estábamos codificando un formulario, y
queríamos añadirle un botón. Queríamos manejar las presiones en el botón en el método de formulario
'buttonClick'. El código para agregar el botón se parecerá al siguiente:

self.button = dabo.ui.dButton(self, Caption ="Click Me",


> Note que en este caso, de acuerdo con el estilo de Dabo esos nombres de propiedad comienzan con
letras mayúsculas; la convención es 'On<EventName>' para el nombre de la palabra clave, y el
manejador como el argumento.
Los manejadores de evento necesitan ser escritos para aceptar un solo parámetro objeto evento. Este
objeto evento tendrá un atributo llamado 'EventData' que contendrá información relevante acerca del
evento. Por ejemplo, los eventos de ratón incluirán las coordenadas x e y del ratón, si alguna tecla
modificadora hubiera sido pulsada en ese momento, y, si es relevante, información de desplazamiento
de la rueda del ratón. Eventos de teclado contendrán el valor de la tecla que fue pulsada y también
cualquier tecla modificadora. Los eventos de menú contendrán el indicador de menú del seleccionado;
eventos de árbol contendrán información sobre el nodo seleccionado: los eventos de rejilla contendrán
valores de fila/columna.
Podría haber momentos en que usted desearía detener un evento. Por ejemplo, si el usuario está
tecleando en una caja de texto y usted tiene una tecla de atajo que realizará alguna acción, usted
probablemente no desearía el comportamiento predeterminado de tener esa tecla añadida al contenido
de la caja de texto. En caso como estos, llamando evt.stop() desde el manejador de evento bloqueará el
comportamiento predeterminado.

Eventos Comunes de IU
• Hit: Casi todos los controles tienen una forma obvia de que el usuario interactúe con él: un
botón es presionado, se hace una selección en una lista, un ítem de menú es seleccionado, un
reloj se activa, una caja de chequeo es conmutada, un deslizador es arrastrado, etc. Cada una de
estas acciones del usuario levantan un evento Hit, así que en su codificación usted no necesita
preocuparse de que cada control llame su evento principal, sabe que Hit será al que usted quiere
enlazarse.
• GotFocus, LostFocus: estos son levantados cuando un control obtiene el foco o lo pierde,
respectivamente.
• KeyDown, KeyUp, KeyChar: estos eventos le permiten responder a las presiones de tecla.
Importantes teclas de EventData son: keyChar, keyCode, unicodeChar, unicodeKey,
controlDown, shiftDown, commandDown, altDown, metaDown.
• MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick: note que hay
versiones de esos métodos para los botones derecho y central del ratón; estos son llamados de la
misma manera, pero con 'Right' y 'Middle' reemplazando a 'Left' en el nombre del evento.
Importantes teclas de EventData son: mousePosition, dragging, controlDown, shiftDown,
commandDown, altDown, metaDown.
• ContextMenu: este es levantado por un pulsado derecho o cuando el botón de menú de
contexto es presionado. Las mismas teclas de EventData para los eventos de ratón aplican aquí.
• Activate, Deactivate, Close: estos eventos son levantados cuando el formulario o la aplicación
se convierten en el más próximo, pierde ese estado, o es cerrado, respectivamente.
• Update: un evento Update es levantado por la Dabo cuando los datos necesitan ser refrescados.
• Resize: levantado cuando un objeto es redimensionado.
• Destroy:levantado cuando un objeto es liberado.
• Idle: levantado una vez que todos los otros eventos han sido procesados por el objeto. Este
comunica que el objeto está listo y esperando nuevas órdenes.

También podría gustarte