¡Volando! tic() … toc()

En estos días he descubierto un gran lugar donde aprender y practicar nuevas técnicas de programación y mejorar nuestra habilidades: hackerrank.com. De ahí he podido, además, sacar una comparativa de rendimiento de lenguajes de programación basada en el entorno de compilación / ejecución que se le asignan a cada lenguaje en las máquinas virtuales (ver entorno).

Se puede ver que los lenguajes a los que se les da menos tiempo de ejecución (por ende los más rápidos) son: C, C++ (de esperar), Objective-C (esperable), Pascal (curioso, pero no de extrañar)… y… ¡tarán! ¡Swift!. Además, todos ellos están en el mínimo de memoria (512MB), aunque en este caso sí es de notar que muchos otros lenguajes se suman al carro (salvo Go con 1GB, y en los problemas más exigentes, Java 7 y Python con 1GB, ¡Java 8 con 2GB!).

Y si queréis leer un poco más de Swift, podéis entrar en la página oficial del lenguaje.

Optimización 90-10

Ya dicen que el 90% del tiempo de ejecución está en el 10% del código, y en situaciones como la que menciono hoy es muy patente.

En un código para leer un determinado tipo de fichero tenía la siguiente secuencia (la función completa es unas 7 veces más larga):

for (ii = 0; ii < 3; ++ii) {
 if (tokens[ii].empty()) {
 break;
 }
 elem.clear();
 boost::split(elem, tokens[ii], boost::is_any_of("/"));

 if (elem.empty()) {
 break;
 }
 a[ii] = stoi(elem[0]) - 1;
}

Este fragmento se repite unas 80000 veces en el fichero de pruebas (los ficheros más grandes pueden requerir más de 2 millones de iteraciones). La función contenedora ya estaba optimizada en términos de uso de memoria, creación de objetos temporales, rutas más probables, etc.

Aunque en la versión de despliegue la función es suficientemente rápida (0,45 segundos de media), en modo depuración se vuelve insoportablemente lenta, en torno a los 60 segundos.

Analizando el problema se ve que lo único importante es el primer elemento del vector, es decir, lo que va desde el comienzo de tokens[ii] hasta el primer carácter “/”, por lo que la función puede re-escribirse como sigue:

for (ii = 0; ii < 3; ++ii) {
 if (tokens[ii].empty()) {
 break;
 }

 int p = tokens[ii].find('/');
 a[ii] = stoi(tokens[ii].substr(0, p)) - 1;
}

En modo depuración ahora tarda 28 segundos: ¡50% menos!. En modo final la optimización es menos notable para ficheros pequeños, ya que la función de Boost en cuestión es mucho más eficiente en este modo. De todas formas, los tiempos han mejorado y ahora tardo menos en abrir mis ficheros para corregir errores.

OpenSSL y la intuición: exportando claves RSA

Pocas cosas hay menos intuitivas en este mundo que el exportar las claves RSA privada y pública en OpenSSL: de entrada uno piensa que son PEM_write_bio_RSAPrivateKey y PEM_write_bio_RSAPublicKey. Todo compila, las salidas son coherentes y parece que todo va bien, pero pronto uno descubre no hay manera de hacer que el programa no falle al usar la clave pública. El problema radica en que la función a utilizar es ¡PEM_write_bio_RSA_PUBKEY!

Una explicación a fondo de la diferencia entre ambas en http://dmiyakawa.blogspot.com.es/2013/03/pemwritebiorsapubkey-vs.html.

iPausa

Después de 4 años en la App Store tengo que hacer una pausa en mi suscripción, con la triste consecuencia de retirar mis apps hasta nuevo aviso.

Dejo acá una captura de las estadísticas de descargas de los últimos 6 meses, nada mal, ¿no?

IMG_9315.PNG

Espero a mi vuelta poder traer algunos de los geniales proyectos que tengo ya en mente y actualizar los actuales. ¡Muchas gracias a todos!

Traducciones en Qt (y algo más)

El sistema de traducciones de Qt es muy simple: usar el método QObject::tr() para definir los textos a traducir, crear un fichero .ts para cada idioma, ejecutar el comando lupdate para actualizar esos ficheros con las nuevas frases del proyecto, traducirlas y ejecutar lrelease para generar el fichero que usa Qt con las traducciones. El sistema es muy potente, por ejemplo, no hace falta nada especial para cambiar a idiomas orientales, simplemente funciona, además de proveer ayudas a la traducción como libros de frases para tener traducciones “pre-hechas”, o detectar incoherencias en parámetros o signos de puntuación distintos entre traducciones los muestra.

Como siempre, no quiero dar un curso, sólo recopilar algunos tips nacidos del día a día.

Frases dinámicas

Igual es de los consejos más importantes y “no opcionales”. Veamos.

“El carro de Pepe”, “Pepe’s car”. Un ejemplo rápido de que cada idioma tiene construcciones diferentes. ¿Y qué? Imaginemos que queremos que nuestra aplicación pregunte el nombre al usuario y muestre el mensaje anterior. Nuestro primer código podría ser algo como:

QString msg = tr("El carro de ") + strNombre;

El problema viene al traducirlo: la cadena a traducir es únicamente “El carro de “, mientras que en nuestro código concatenaremos el nombre a continuación. No es posible traducir la frase al inglés usando este método.

Qt ofrece una solución basada en parámetros: construimos una única frase indicando el lugar de los elementos. En tiempo de ejecución sustituimos esos parámetros con el método QString::arg. Así:

QString msg = tr("El carro de %1").arg(strNombre);

La cadena a traducir en inglés tendrá la forma “%1’s car”, y funcionará perfectamente.

Cambios en los textos originales

Un proyecto es un ser vivo, y es normal que los textos mostrados cambien. Cada vez que eso ocurre hay que ejecutar lupdate para actualizar el fichero .ts. Ahora bien, al cambiar textos o quitar frases, el fichero .ts guarda las viejas entradas (muy bueno por una parte para re-traducir, especialmente si son cambios menores como un signo de puntuación o una falta de ortografía en el texto original). Conforme crece el proyecto, las viejas entradas pueden molestar más que ayudar. Para eliminarlas basta con ejecutar lupdate -noobsolete (si se usa el plugin de Visual Studio se puede especificar este parámetro en las opciones del proyecto de Qt).

Propiedades personalizadas

Es posible definir en el Qt Designer propiedades personalizadas a los QWidgets (por ejemplo, para distinguir grupos de objetos de forma sencilla). No entraré en detalle ahora de esto, prometo un post otro día. ¿Qué tiene que ver con las traducciones? Que si el valor asignado es una cadena de texto, por defecto Qt Designer lo marca como “traducible”, por lo que nuestro fichero .ts podría verse desbordado de propiedades que no queremos traducir. Para evitarlo basta con desmarcar la casilla “Traducible” de la propiedad.

One more thing…

El útlimo consejo no es directamente propio de las traducciones sino de la clase QString y del Visual C++. El editor de código usa una codificación diferente a la que usa Qt por defecto, lo que puede llevar a que algunos caracteres se muestren mal al ejecutar la aplicación. Un caso típico es el símbolo de grado (º). El problema se magnifica si ese símbolo está en una traducción, ya que entonces Qt no asociará los textos ‘origen’ y ‘traducción’ correctamente. La solución más cómoda que he encontrado es usar un parámetro para ello e insertar el carácter Unicode correspondiente:

QString degreeMsg = tr("Ahora mismo hacen 25%1").arg(QChar(0260);

Más bocaditos de Qt

Sigo de lleno con Qt, eso es parte del motivo por el que lleve algo de tiempo sin escribir. Irónicamente, no tengo mucho para contar en esta ocasión, pero aprovecho para escribir algunos “bocaditos”.

Tipografías incrustadas

Dar un toque de distinción a nuestras aplicaciones puede incluir el utilizar tipografías que no son estándares, o que sólo vienen incluidas con instalaciones de determinados programas que no podemos asumir estén siempre presentes (ejemplo típico, MS Office).

La opción más conveniente es la de incluirlas con nuestra aplicación (no hablaré de licencias ni nada parecido, asumo que todo está en orden). Para esto se pueden bien copiar junto al ejecutable, o incluir en un fichero de recursos (QRC). En cualquier de ambos casos habrá que registrar las fuentes antes de poder utilizarlas, especialmente si deseamos usarlas desde hojas de estilos.

QFontDatabase::addApplicationFont(":/fonts/arialn.ttf");
QFontDatabase::addApplicationFont(":/fonts/arialnb.ttf");
QFontDatabase::addApplicationFont(":/fonts/arialnbi.ttf");
QFontDatabase::addApplicationFont(":/fonts/arialni.ttf");

Esto podemos hacerlo en nuestra función main(), justo después de instanciar la aplicación por ejemplo. A partir de ese momento podremos utilizar las fuentes por su nombre como si se tratase de otra fuente del sistema.

Qt5 y la “plaftorms/qwindows.dll”

Desde Qt5 existe una DLL nueva de la que no está muy bien documentada su importancia en el proceso de distribución de la aplicación. Se trata de la platforms/qwindows.dll. Junto a las DLL base, como Qt5Core.dll, Qt5Widgets.dll, etc. El caso es que si Qt no está instalado en el ordenador destino, debemos especificar la ruta hasta esta DLL. La forma más sencilla que he encontrado hasta ahora es especificar la ruta de la carpeta de plugins (donde también deberán estar las DLL para cargas imágenes, por cierto).

En Qt4 podíamos especificar la carpeta de plugins de la siguiente forma:

int main(int argc, char *argv[])
{
 QApplication a(argc, argv);

 QApplication::addLibraryPath(qApp->applicationDirPath() + "/qtplugins/");

 // ...
}

De esta forma tendríamos una carpeta “qtplugins” junto a nuestro ejecutable, y siempre podríamos obtener la ruta hasta ella. La diferencia ahora está en que en Qt5 debe hacerse antes de instanciar la aplicación, ya que es ahí cuando se cargará dicha DLL. El problema es que “qApp” es una macro de conveniencia a “QCoreApplication::instance()” (patrón singleton), por lo que es necesario que hayamos instanciado la clase antes de utilizarla. Esto nos lleva a que tenemos que asumir que el directorio actual sigue siendo el del ejecutable:

int main(int argc, char *argv[])
{
 QApplication::addLibraryPath("./qtplugins/");

 QApplication a(argc, argv);

 // ...
}

Esto sirve siempre y cuando no lancemos la aplicación desde un acceso directo que tenga cambiado el directorio de trabajo.

Extendiendo clases

Esto no es exclusivo de Qt, pero el ejemplo lo tomo de ahí. En Qt4, el método “tabBar()” de la clase QTabWidget era protegido (a partir de Qt5 es público). Si queríamos acceder a él teníamos que recurrir a la herencia para crear un método público tal que

class MyTabWidget : public QTabWidget {
public:
 QTabBar *tabBar()
 {
 return QTabWidget::tabBar();
 }
};

Ahora bien, una cosa que no todos los programadores de C++ sabrán, pero que verán claro como el agua después de leerlo: no es necesario crear el nuestro TabWidget utilizando el objeto MyTabWidget, podemos hacerlo con QTabWidget (por ejemplo, desde Qt Designer), y luego hacer un simple cast para acceder al método público.

((MyTabWidget*)ui.tabWidget)->tabBar()->setTabText(0, tr("Customers"));

Este “truco” podemos hacerlo con cualquier clase y con cualquier número de métodos, accediendo a atributos y métodos protegidos (nunca privados), siempre y cuando no declaremos nuevos atributos. Esto se debe a que la definición de nuevos métodos no modifica el contenido del objeto, por lo que el casting sigue siendo válido (el objeto sigue teniendo la estructura del tipo base), pero con una tabla de métodos extendida.

Objective-C tiene un mecanismo similar, aunque algo más elegante (y con una implementación diferente debido a que usa paso de mensajes en lugar de llamadas a funciones, pero eso es harina de otro costal). Este mecanismo se llama “categorías“, y consiste en agregar nuevos métodos a una clase existente. Este método puede acceder a atributos privados (en Objective-C no existen atributos protegidos, y cualquier método puede ser invocado siempre que se conozca su cabecera). Lo más destacable de este mecanismo es que no hace falta realizar un casting al objeto, sino que automáticamente la clase adquiere los métodos de las categorías a las que tiene acceso (incluidas en un fichero de cabecera previo). Pueden imaginar la de plugins que pueden hacerse con esto, y lo limpio que queda el código. El único cuidado que hay que tener es el de documentar bien el código a fin de no confundir métodos estándar de categorías propias.

Traducción de recursos

La traducción de cadenas de textos en Qt está muy documentada, pero no me pareció lo mismo al tratar de incorporar soporte multi-idioma en los recursos de las aplicaciones. Así que acá va. Imaginemos que tenemos una imagen “:/hola.png” que queremos traducir. Nuestro QRC originalmente contendrá algo tal que:

<qresource>
 <file>hola.png</file>
</qresource>

Para agregar las traducciones, debemos crear alias a dicha imagen desde varios ficheros (en el ejemplo, el castellano es el idioma por defecto):

<qresource>
 <file alias="hola.png">hola_es.png</file>
</qresource>
<qresource lang="en">
 <file alias="hola.png">hola_en.png</file>
</qresource>

A modo resumen, para traducir un texto debemos borrar el traductor anterior de la aplicación (si lo hubiera), crear un nuevo traductor desde nuestro fichero QM (fichero compilado de traducciones), instalar el nuevo traductor, re-traducir todas nuestras interfaces abiertas mediante “::retranslateUi()” (las ventanas sin abrir usarán la nueva traducción automáticamente al crearlas), y volver a generar los textos que programáticamente hayamos generado.

Con los ficheros de recursos debemos cambiar el parámetro global de localización de la aplicación antes de re-cargar los recursos. Para ello:

QLocale::setDefault(QLocale("en"));

Donde “en” es el código de localización deseado (“es”, “en”, “de”, “it”, etc).

Una sugerencia personal si queremos que el idioma cambie dinámicamente (sin tener que cerrar y volver a abrir nuestra aplicación) es crear una señal que se emita desde la configuración del idioma después de haber configurado el nuevo traductor y la localización, y que reciban todos los objetos que deban traducir sus textos o recursos.

Hasta la próxima, ¡espero disfruten de los bocaditos!

Qt + resources + fonts + stylesheets

A partir de esta entrega cambiaré el título de las anec-notas para reflejar mejor la temática y ahorrar espacio en la cabecera de la entrada: muchas veces estará en inglés (por eso de ser el idioma universal de la programación), quitaré el “anec-notas” del mismo y dejaré la indicación únicamente en la categoría del post.

Terminado este preámbulo, esta entrega mostrará una forma de incluir fuentes propias en un proyecto Qt, al menos la que más me gusta. Lo primero será ubicar el fichero de fuentes. Personalmente recomiendo utilizar un fichero de recursos (QRC) en el cual embeber la fuente:

<RCC>
 <qresource>
 <file>fuentes/mipropiafuente.ttf</file>
 </qresource>
</RCC>

Esta fuente deberá estar ubicada en “./fuentes/” respecto al fichero de recursos.

Antes de poder utilizar una fuente, hay que añadirla a la base de datos de fuentes de la aplicación. Para ello se usa la siguiente llamada:

QFontDatabase::addApplicationFont(":/fuentes/mipropiafuente.ttf");

A partir de este momento la fuente estará disponible en toda la aplicación. Bastará con especificarla en una hoja de estilos:

QWidget
{
 font-family: "Nombre real de nuestra fuente";
 font-size: 14px;
 color: #999999;
}

O directamente desde código:

// "pt" es un "QPainter *", seguramente un argumento del evento paint(...)

QFont fold(pt->font());
QFont fnew(fold);

fnew.setFamily("Nombre real de nuestra fuente");
fnew.setWeight(QFont::Bold);
pt->setFont(fnew);

// Escribir texto

pt->setFont(fold); // restablecer la fuente

Sugerencias finales sobre los stylesheets

Para agilizar la personalización de la interfaz sugiero cargar el fichero de estilos por fuera del fichero de recursos durante la etapa de desarrollo, y únicamente cargarlo desde el QRC cuando se pase a producción.

Además, si se están cambiando ficheros ya incluidos en el QRC, tales como imágenes, habrá que saber que los recursos de Qt sólo se re-compilan cuando el fichero .qrc es modificado, no cuando los ficheros incluidos lo hacen. Una buena técnica para automatizar este proceso es “tocar” el fichero QRC antes de cada compilación, al menos durante las pruebas de diseño de interfaz (la compilación de un QRC puede tomar su tiempo si los recursos incluidos son grandes). Para ello sugiero ver la entrada anterior sobre el comando touch en VisualStudio.

Anec-notas (V)

En estas últimas semanas he estado desarrollando una aplicación para captura de imágenes usando cámaras PointGrey. Lo primero que he de comentar es que su SDK está verdaderamente mal documentado, y salirse de los ejemplos que se instalan junto a él supone un sin fin de dolores de cabeza, así que no os extrañéis si de vez en cuando aparecen anec-notas sobre estas cámaras. En esta ocasión comentaré sólo un par.

Bayer

Para los que no sepan lo que es, os dejo la Wikipedia. Como resumen, muchas cámaras RGB no son tales, sino cámaras en escala de grises donde cada píxel captura una componente de color. Es decir, hay píxeles rojos, píxeles verdes y píxeles azules, dependiendo de la configuración del filtro. Como consecuencia, la imagen obtenida sigue siendo de un único canal, y hay que aplicarle un filtro para convertirlo a tres canales (RGB) y recuperar la información faltante (el píxel verde no captura información para el canal rojo, por ejemplo).

El SDK de las cámaras PointGrey tiene su propio conversor (obviaré los tipos de datos por simplicidad).

m_Camera-&gt;RetrieveBuffer(&amp;m_Frame);
m_Frame.Convert(PIXEL_FORMAT_RGB8, &amp;m_FrameAux);

Este filtro actúa usando una interpolación al más cercano, por lo que, como el lector podrá deducir, una cámara de 640×480 realmente capturaría como una cámara de 240×320, con un área de píxel (en el sensor) cuatro veces mayor. El resultado final es una imagen de muy poca calidad.

En cambio, es posible utilizar la función cvtColor de OpenCV para realizar una conversión de mejor calidad:

cv::Mat cvframe(m_Frame.GetRows(), m_Frame.GetCols(), CV_8UC1, m_Frame.GetData());
cvtColor(cvframe, cvframe, CV_BayerBG2RGB);

Además, este cambio de API conlleva una sobrecarga apenas notable tanto en consumo de memoria como en tiempo, ya que la matriz de OpenCV se crea utilizando la misma área de memoria del frame creado por el SDK de PointGrey. Obviamente si hay un overhead debido al uso de una interpolación más pesada.

Como comentario final, el tipo de conversión Bayer ha utilizar no es homogéneo entre las cámaras PointGrey, así la FireFly MV usa el indicado arriba, mientras que una BlackFly requeriría un CV_BayerBG2BGR.

Instancias

Otro detalle que  me ha dado más de un dolor de cabeza ha sido el hecho de que la aplicación que desarrollo actualmente debe mostrar en varios puntos de la ejecución la imagen en vivo de las diversas cámaras conectadas. El problema radica en que si un objeto de captura de PointGrey (la cámara) no es destruido, y luego se crea otro conectado al mismo dispositivo, errores inesperados pueden aparecer al tratar de escribir los registros, o, el que me ocurría a mí, tratar de cambiar el framerate. Ni molestarme en comentar la de tiempo que pasé buscando cualquier leak de objetos de captura por todo el código.

Anec-notas (IV)

Parece contradictorio que durante las vacaciones esté publicando más posts sobre mi trabajo que durante los meses laborales, pero qué le vamos a hacer, la vida es así.

Esta entrada la quiero dedicar al Visual Studio, así que allá vamos.

Implementar el comando “touch”

Unos de esos comando de UNIX que siempre me han gustado es el touch (para los que no lo conozcan, este comando modifica la cabecera de un fichero actualizando su fecha de modificación, creando el archivo si éste no existiere). Windows no tiene un comando equivalente a primera mano. Buscando un poco me encontré que se puede emular usando

copy /b <fichero> +,,

Donde es el nombre del fichero a “tocar” (pueden usarse comodines). Una limitación de este comando es que el fichero debe estar en el mismo directorio que el activo desde donde se ejecuta el comando. Ejecutar el comando siguiente no funcionará correctamente sino que copiará el fichero al directorio actual.

copy /b ..\config\game.ini +,,

La línea correcta en ese caso sería

copy /b ..\config\game.ini +,, ..\config\game.ini

Ahora bien, ¿a qué todo esto en Visual Studio? Imaginemos el siguiente escenario: estamos utilizando Qt y tenemos una hoja de estilos definida en un fichero QSS (Qt Stylesheet), el cual está incluido como recurso en un fichero QRC. Este fichero se recompila únicamente cuando se ha modificado, no cuando se han modificado los ficheros que incluye. Por lo que si estamos modificando continuamente el fichero de estilos tendríamos que “modificar” el fichero QRC para que se recompilase. En este caso el comando touch es un gran aliado. Para automatizar esta tarea podemos recurrir a los “Build Events”, más específicamente al “Pre-Build Event”. Está ubicado en “Project Properties > Build Events > Pre-Build Event”, donde especificamos el comando a utilizar (el ampersand “&” es un separador de comandos para poder indicar varios comandos uno a continuación de otro):

cd Resources &amp; copy /b *.qrc +,,

Los “Build Events” sólo permiten ejecutar un único comando (línea), por lo que si queremos realizar varias tareas lo mejor será definir un fichero por lotes que las ejecute. Otro detalle a tener en cuenta es que los ficheros QRC pueden tardar cierto tiempo en compilarse si contienen muchos recursos, especialmente imágenes, por lo que es conveniente que este evento sólo esté presente en una configuración de proyecto, tal como la Debug, o crearnos una especial.

Detalles de usabilidad

Acá me gustaría criticar comentar un detalle en la pantalla de creación de nuevos proyectos.

Nuevo proyecto VS

Resulta que en los campos de “Nombre” y “Nombre de la solución”, este cuadro de diálogo comprueba si el texto introducido es ““, en cuyo caso desactiva el botón “OK”. Probad vosotros y borrad una letra para que veáis cómo podéis crear el proyecto. Re-escribid esa letra y el botón “OK” se desactivará. ¿Tantos avances que se han hecho en usabilidad y no podían usar la propiedad “placeholder” en este caso?