jueves, 20 de enero de 2011

Un tutorial rápido de QT: evaluador de expresiones regulares

Estaba dándome cabezazos contra la mesa el otro día porque no conseguía hacer funcionar una expresión regular en el código de Acid Rain sin encontrar la explicación. La situación me llevaba casi a la decepción por haber olvidado toda esa teoría cuando se me ocurrió buscar alguna web donde probar lo que hacía. Al encontrarla me di cuenta de que no era tan mentecato, algo fallaba ya que la expresión regular funcionaba. Tan loco me estaba volviendo que decidí hacer una aplicación en QT en donde testar de forma rápida las expresiones, ya que con la que estaba trabajando realmente me costaba un poco llegar al punto donde se evaluaba la expresión.


Y así fue que me puse a hacer una aplicación muy parecida a la web que he comentado antes. En poco más de cinco minutos la tenía hecha y se me ocurrió que sería un buen ejemplo para iniciarse en QT. Es tan sólo un poco más complicado que un "Hola mundo" y de paso se ve como utilizar aspectos esenciales de QT Creator, signals/slots y cómo trabajar con la disposición de los elementos en un formulario. ¡Vamos al tema!

Creando el proyecto

 Lo primero será crear el proyecto eligiendo "QT C++ Project" y como subopción  "QT GUI Application"


 Ahora le damos un nombre apropiado a la aplicación y apretamos sobre "Next"


 Como se nos crea un formulario, debemos definir su nombre y el de la clase asociada al mismo


En cuanto a la creación del proyecto sólo nos queda aceptar el resumen.


Lo que queremos hacer

Lo que nos interesa es tener un texto sobre el que queremos hacer alguna prueba, usando una expresión regular para reemplazar parte del él por otro texto determinado. Así pues necesitaremos unos cuantos campos etiqueta, tres campos de entrada y uno de salida.
Los campos de de entrada serán: el de la expresión regular, el de la cadena que queremos introducir y el del texto inicial sobre el que trabaja. Para los dos primeros campos nos bastará con un "line edit" mientras que para el texto usaremos un "text edit". El resultado lo mandaremos a otra caja de texto ("text edit") de salida que tendrá marcada la propiedad "read only".
En cuanto le demos al botón "Test"  se evaluará la expresión regular y, para hacerlo mas emocionante y poder probar más rápido nuestras expresiones, también se evaluará con cada cambio que hagamos en el campo de entrada de la expresión, con lo que veremos los resultados al vuelo mientras la escribimos.
En cuanto a la disposición de los elementos, usaremos dos frames para agrupar por un lado los campos del formulario y por otro el botón "Test".

Insertando los elementos de la ventana

Lo primero que haremos será insertar los dos frames, arrastrándolos a la ventana.


Lo segundo debe ser elegir la disposición de la ventana (hay que asegurarse de que está seleccionada), en este caso le daremos una disposición vertical "Layout vertically"


Es el momento de darle vida a todo esto. En el marco superior dejamos caer cuatro campos "label", los dos "line edit" y los dos "text edit"


Ahora hay que seleccionar el marco superior y después pinchar sobre "Lay out in a form layout". Todavía no quedará muy bonito asi que habrá que corregir  la posición de los elementos. También podemos redimensionar un poco el formulario haciendo uso del ratón para que no quede todo tan ahogado en cuanto espacio.
Vamos con el marco inferior: hay que añadir un "Push Button",  después seleccionar "layout horizontally" para el marco y finalmente añadirle un espaciador horizontal.


El siguiente paso es cambiar el texto que muestran las etiquetas y el botón y darle un nombre correcto a cada uno de ellos para que sea más intuitivo utilizarlos desde el código. Los nombres podrían ser estos (según el orden del formualrio):
lblRegEx | leRegEx 
lblReplace | leReplace 
lblText | teText
lblResult | teResult
         pbTest
Aprovecharemos para seleccionar teResult y marcar la propiedad "read only"


Jugando con los slots

No voy a entrar mucho en esto, tan solo una breve explicación. En QT la comunicación entre objetos se hace mediante signals  y slots. Por un lado hay determinados eventos de los objetos que lanzan señales (al hacer clic sobre un botón, al cambiar el texto en un campo de texto, etc) Por otro lado los objetos también tienen asociadas una serie de funciones llamadas slots cuya finalidad es ser receptoras de señales de otros objetos ( por ejemplo setText(QString) en un campo text edit). Uniendo ambas cosas podemos hacer que al hacer clic sobre un boton "clear" se borre todo el contenido de una caja de texto.
Volvamos a lo nuestro. Seleccionando el botón "Test", apretamos el botón derecho de nuestro ratón para obtener el menú contextual y buscamos la opción "Go to slot..."


Se nos abrirá una ventana en la que elegiremos la señal clciked() y se nos creará automáticamente la cabecera y el cuerpo vacío de un slot llamado "on_pbTest_clicked". Internamente ya se ha relacionado la señal "clicked" con el nuevo slot, así que sólo tendremos que añadir el código que queramos al mismo (cosa que haremos luego) y este se ejecutará al pinchar el botón en nuestra aplicación.


Lo anterior sirve cuando es un objeto  llamándose a si mismo, pero ¿cómo hacemos algo similar entre dos objetos distintos? Pues ahora lo vemos, recordemos antes que queríamos evaluar la expresión al vuelo mientras la escribíamos así que vamos a relacionar la señal "textChanged" del campo leRegExp con la ranura "clicked" del botón pbTest. Para ello nos vamos al modo de edición "edit signals/slots" mediante f4 o haciendo uso del botón de la parte superior de QT Creator. Una vez cambiado el modo, arrastramos el ratón desde el campo de entrada hasta el botón y se nos abrirá una nueva ventana de selección. En la izquierda escogemos la señal "textChanged" y en la derecha el slot "click", aceptamos y los tendremos ya relacionados. Esto quiere decir que cuando haya cualquier cambio en la expresión regular será a todos los efectos, como si pulsásemos el botón "Test" con lo que se irá evaluando al vuelo.


Un poquito de código

Bueno, ya tenemos todo relacionado pero esto todavía no hace nade de nada. Vamos a insertar el código, para ello abrimos el archivo "regextextwindow.cpp" y buscamos la función que QT Creator nos ha insertado. Bastará con dejarla así:

void RegExTestWindow::on_pbTest_clicked()
{
 ui->teResult->setText(ui->teText->toPlainText().replace(QRegExp(ui->leRegExp->text()),ui->leReplace->text()));
}

Lo que significa que asigne como texto del campo "teResult" lo obtenido al reemplazar en el texto de prueba que hemos dejado en "teText" todo lo que encaje con la expresión regular contenida en "leRegExp". Todo lo que encaje con la expresión se sustituye por el valor de "leReplace"
¡Ya está!
Pues si todo ha ido bien  le damos al botón de ejecución y se nos lanzará la apliación. Sólo nos queda desengrasar nuestros conocimientos en expresiones regulares :) Cuando tenga un rato colgaré el código fuente y la aplicación en esta web para los más perezosos.

Sobre mi estupidez

Tal vez os hayáis quedado pensando en lo que contaba al principio del post. Resulta que cuando probé la expresión en la aplicación test la cosa funcionaba. Así que tenía lo siguiente: funcionaba en la web, funcionaba en el testador y fallaba en mi código. Cuando pasan cosas así siempre piensas lo mismo "tiene que ser una estupidez como un piano" Efectivamente así era, no se me había pasado por la cabeza el escapar el carácter de escape, que perogrullada más infernal.
Una expresión valida como \s\d+/\d+\s escrita en código es \\s\\d+/\\d+\\s
Perder tanto tiempo con una tontería semejante suele llevarme, cuando encuentro la solución, al segundo pensamiento típico: "que empanado que estoy" 
Publicar un comentario