OS X: crear DMG (servicio)

Como regalo de Navidad os dejo un servicio de OS X que he creado para crear un disco DMG a partir del contenido de un directorio (o si hay varios directorios seleccionados, creará un fichero por cada uno).

Para instalarlo basta con descomprimirlo y copiarlo al directorio ~/Library/Services (en Finder, abrir el menú Go (Ir) mientras se presiona la tecla de Opción (alt), y elegir Library (Biblioteca). Como veis, tengo mis sistemas en inglés, dejo el servicio con el nombre en ambos idiomas.

Descargar (español)

Descargar (inglés)

Si tenéis algún problema, dejad un comentario y procuraré resolverlo.

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!

Authoxy

Si hay una cosa que me tiene entre pensativo y molesto es la incapacidad de Apple para corregir un error que lleva tieeeeempo en MacOS X (y en el iPhone igual): el fallo de los frameworks de red para salir através de un proxy con autenticación (salvando la navegación más simple). Ejemplos: el exportador de fotos de iPhoto, cualquier aplicación (oficial) en el iPhone que acceda a red salvando MobileSafari (y de MobileMail no tengo datos), SpeedDownload, los updates de muchas aplicaciones…

Bueno, dado que Apple no es capaz todavía de corregir ese error, al menos para el sistema de escritorio he encontrado esta solución: Authoxy. Básicamente es un pequeño servidor proxy sin autenticación, y él mismo gestiona la autenticación con el proxy externo. Simplemente configuramos Authoxy (además de activarlo para que se ejecute al iniciar sesión) con los datos del servidor proxy, y luego configuramos nuestra red con un proxy en 127.0.0.1 (nuestro Mac). ¡Y listo! Llevo varios meses usándolo y va de maravilla, además que el consumo de recursos es imperceptible. Para una guía más detallada de configuración recomiendo esta página.

Locker (Dashboard widget)

Este pequeño widget tiene como única razón de su existencia bloquear nuestro Mac, una operación que no es posible hacer de forma directa en MacOS X (al menos hasta Leopard).

Locker

Usualmente, para realizar esta operación es necesario ir por la tangente, bien haciendo logout (con lo que se cierran las aplicaciones abiertas), bien activando el cambio de usuario rápido (que creo sería la opción más cuerda), o bien solicitando la contraseña para desactivar el protector de pantallas o para salir de suspensión (lo malo de esta opción es que si tenemos el protector de pantalla para que se muestre en pocos minutos, tanta petición de contraseña se vuelve insoportable.

Con este widget estos problemas quedan resueltos: simplemente hacemos doble-click sobre él y ¡woalá! la pantalla de login se muestra, nuestros programas siguen en ejecución, no tenemos por qué alterar el comportamiento de nuestro bonito protector de pantalla y, lo más importante de todo, nuestro equipo ahora está bloqueado en caso de dejarle solo (ya saben, por eso del café, de la socialización, de que tenemos que dormir…)

Spotify

La primera aplicación que comentaré es Spotify, un servicio de streaming de audio con una biblioteca de música asociada muy, muy grande donde la verdad he encontrado prácticamente de todo lo que he buscado. La velocidad el servicio es muy buena, aunque hay que considerar también que está en fase beta y que hay que solicitar una invitación para poderlo utilizar (la mía llegó a las 3 ó 4 semanas, no me recuerdo bien), o contratar el servicio premium (9,99€ al mes). El programa tiene una interfaz súper sencilla y está disponible tanto para MacOS X como para Windows XP. Una vez obtenida la invitación, debemos crearnos una cuenta e identificarnos con ella en el programa. Podemos usar la cuenta en cuantos ordenadores deseemos.

Spotify

Spotify tiene básicamente dos modos de funcionamiento: uno radio y uno de búsquedas. El modo de radio presenta una serie de canciones aleatorias en base a un par de parámetros: época (los 50s, los 90s…) y estilo (Reggae, Jazz, Rock, Disco…). Esta opción está muy bien para escuchar cosas nuevas o para tener de música de fondo.

La otra opción, bueno, permite buscar canciones (uno simplemente escribe el nombre del artista, de la canción, del álbum). Un detalle bueno es que cada búsqueda que uno hace queda guardada en una lista, así podemos crearnos recopilaciones sobre la marcha de un artista o álbum. También podemos crear listas de reproducción personalizadas las cuales se guardan en nuestra cuenta; así, podremos tener siempre nuestras listas sincronizadas en todos los ordenadores en los que tengamos Spotify instalado.

El servicio está disponible de forma gratuita con anuncios. De vez en cuando, al terminar una canción, se nos interrumpe con un pequeño anuncio de audio, el cual la verdad no resulta para nada molesto pues duran bastante poco y no son agobiantes como pueden serlo los comerciales en la TV. Si queremos suprimir dichos anuncios podemos contratar el servicio premium o, si ya tenemos una cuenta gratuita, un pase de 1 día sin anuncios (0,99€) que es ideal si se quiere tener como fondo un día en particular para una reunión con amigos).

El único contra de peso que le he podido encontrar es que no muestra sugerencias de música similar o que hayan escuchado otros usuarios con gustos parecidos a los nuestros. En contra partida, la versión MacOS X se integra perfectamente con el servicio de notificaciones Growl y funciona con los teclados con controles multimedia de Apple.

En definitiva, es una aplicación que vale realmente la pena, de gran calidad y muy sencilla de usar. No soy un reviewer de MacWorld ni mucho menos, pero yo le daría un 4.5/5.0 si me lo preguntasen.

Sustitutos de MobileMe

Durante un año fui usuario de .mac (ahora MobileMe), y la verdad me funcionó perfectamente, pero para el uso que le daba (básicamente sincronización de contactos, bookmarks, calendario, etc., de mis macs) se convertía en una opción muy costosa y decidí no renovar mi contrato (aunque el día que realmente necesite o vaya a utilizar el resto de sus opciones no dudaría en contratarlo de nuevo, quizás cuando tenga mi iFamily ;) ).

Comenzó entonces la búsqueda de sustitutos. Los más directos no servían para nada más que una línea en un blog como éste. Se trata de copiar los ficheros de contactos, calendarios, etc. de un mac a otro. El problema radica en qué pasa de los cambios han sido hechos en ambos mac, ¿cuál manda?. O, como me pasa ahora, agrego en el MacBook los datos de un investigador que me interesa contactar, y en la calle me encuentro con un amigo y me dice que ha cambiado de móvil, así que lo apunto en el iPhone. Al llegar a casa, sincronizo el iPhone con el iMac y… ¿cómo hago con los datos del MacBook cuando copie los ficheros…? No, no es solución práctica.

Urgando por la web conseguí hace tiempo SyncTogether (49,95$), una aplicación cliente-servidor para sincronizar contactos, calendario, bookmarks, preferencias, notas, widgets… Lo he usado durante un tiempo y la verdad es una opción factible pero con problemas: muchas veces se queda colgada (en mi caso, al sincronizar los bookmarks), y es bastante manual el proceso (la aplicación debe estar abierta en ambos mac). Por último, cuesta 50$ lo que, si bien es la mitad de MobileMe (y se paga una sola vez), sigue siendo un precio un poco alto para muchos usuarios.

Hace poco comencé mi búsqueda de nuevo y me topé con Fruux (gratuita). Esta pequeña aplicación que se instala como una preferencia del sistema, imita en muchos aspectos a MobileMe, incluyendo la subida de contactos, calendario y bookmarks a la Nube (por lo visto el grupo a cargo de Fruux es de confianza y nuestros datos están seguros ahí). Cada vez que modificamos un dato, son automáticamente (si así se desea) subidos a la nube y una vez cada hora el programa se sincroniza para actualizar cualquier cambio efectuado desde otro punto (aunque también podemos actualizar manualmente los datos cuando lo deseemos). Actualmente el programa está en versión beta y tienen pendientes por incorporar varias características (sincronización del KeyChain y de las preferencias, por ejemplo). Lo he estado usando por varios días y la verdad estoy muy contento por su transparencia. Echo en falta una opción para determinar qué datos han de sincronizarse (en especial con miras a la sincronización del KeyChain :S). Si bien una vez cada hora es más que suficiente en muchos casos, los usuarios que deseen una frecuencia mayor tendrán que conformarse con lo que hay (mientras más actualizaciones se hagan, mayor será el ancho de banda necesario, y recordemos que es un servicio gratuito, al menos de momento). Por último, y aunque ambos programas están basados en servicios de iSync, (servicio propio del sistema operativo), SyncTogether puede sincronizar datos dentro de una red local, mientras que Fruux necesita acceso a la nube para ello (es decir, una conexión a Internet).