Una historia de grrr, ooh y... yay?
Bueno. Me siento como si hubieran pasado meses desde la última vez que escribí aquí. Supongo que cuando uno está trabajando y no hace más que encontrarse con muros, el tiempo pasa más despacio. Esta entrada también se podría haber titulado: De cómo reescribí la vista en árbol en una semana. Ah, y perdón por adelantado, que esto va a ser largo.
En una entrada anterior comenté cómo los problemas de rendimiento al hacer búsquedas en la vista en árbol eran debidos a la propia librería Qt, y cómo sus autores me habían recomendado que la migrara al paradigma modelo/vista de Qt4. El mismo que la lista de reproducción ya usa.
Parte 1: Grrr, y más grrr
Comencé intentando utilizar algo ya existente en las librerías de KDE, y KDirModel parecía muy prometedor. Le dediqué un rato, y en un par de horas, puede que algo menos, tuve algo funcionando. Sin embargo, pese a ser fácil de utilizar, tuve que abandonarlo: era demasiado inflexible a la hora de realizar un listado recursivo del sistema de ficheros — y, de todas maneras, al no tener control de las estructuras internas, hubiera sido algo más complejo realizar búsquedas en dicha jerarquía.
A continuación, pues, comencé a implementar mi propio modelo, intentando preservar lo máximo posible del código existente para leer los directorios. Decidí representar el árbol como una lista de arrays y un mapa de índices, y me estuve peleando sin conseguir que funcionara: no conseguía encontrar el error por ninguna parte. Así las cosas, decidí desechar este modelo, y escribir uno nuevo basándome al pie de la letra en un ejemplo de la documentación de Qt.
Una vez tuve el modelo basado en el ejemplo casi funcionando, lo comparé al mío original y descubrí el error: había estado heredando de QAbstractTableModel en lugar de QAbstractItemModel! De todas maneras, me gustó más esta segunda manera (con un verdadero árbol de ítems, en lugar de una lista de arrays), y me lo quedé. Pero, terminé de copiar el ejemplo y... bum!, crash al expandir los ítems. Tras otro buen rato de depuración, encontré el fallo: pequeño error de transcripción del ejemplo (C++) a mi código (Python). Y mientras tanto el tiempo iba pasando...
Ya que estaba restructurando la vista en árbol, me dije que sería una buena oportunidad para implementar al mismo tiempo la tarea 790: leer el contenido de los directorios en un hilo aparte, para no bloquear la interfaz, particularmente si el directorio está en un sistema de ficheros en red. Pero na-nay: se seguía bloqueando la interfaz, y casi que más. Entonces pensé: con KDirModel no se bloqueaba, voy a ver si puedo investigar cómo lo hace.
Parte 2: Ooh, un KListDir
KDirModel utiliza kioslaves para realizar la lectura de los directorios. La ventaja principal de los kioslaves es que son un proceso separado, por lo que la aplicación no se bloquea en absoluto. Win!
En particular, KDirModel utiliza un objeto de la clase KDirLister, que es como una capa de abstracción sobre los kioslave. Y es una pa-sa-da: no sólo proporciona a la aplicación con la lista de contenidos de un directorio (filtrando incluso por tipo MIME, lo que le viene de perlas a Minirok), sino que se queda observando los directorios abiertos y notifica a la aplicación si se crean, borran o renombran ficheros en ellos!
El funcionamiento es sencillo, mediante señales de Qt. La aplicación solicita al objeto la lectura de un directorio con openUrl(), y el objeto devolverá los contenidos del mismo en una o varias señales newItems(), más una señal completed() al finalizar.
Así, la parte principal del modelo la componen los slots conectados a esas señales. Y la tarea 790, hecha!
Parte 3: Yay?
Tras tener una primera version del modelo con KDirLister hace unos días, no sé por qué no terminaba de quedarme un buen sabor de boca del todo. Me parecía que KDirLister iba un poco más lento que mi código anterior, y que utiliza más memoria. Sin embargo, al ir pasando los días este sentimiento se ha ido disipando, afortunadamente.
Porque ventajas las hay, por un tubo. No sólo el hecho de refrescar en tiempo real es fantástico, sino que ahora Minirok puede leer cualquier directorio al que KDE pueda acceder, por ejempo unidades compartidas de Windows. (Lamentablemente aún no podría reproducir de ellas, ya que el motor de reproducción es todavía GStreamer. Phonon no tiene Python bindings aún.)
Así que, me digo, creo que ha sido un cambio para mejor. Por cierto, el diff (gigante) es éste, y el mensaje de la revisión (la 631), aquí.
Addendum
“¿Y de la búsqueda qué?” Que, claro, fue el motivo inicial para todo esto. Pues, funciona razonablemente bien, aunque no ha sido un camino de rosas tampoco. Lo explicaré con más detalles en una entrada dentro de unos días.
Para terminar, dos cosas más:
con esta reescritura de la vista en árbol he añadido también una nueva funcionalidad que se echaba de menos: la lectura recursiva del sistema de ficheros, que sólo se necesita para que la búsqueda funcione, es ahora opcional. Esto reducirá considerablemente la cantidad de memoria utilizada con colecciones grandes de música para los usuarios que no realizan búsquedas.
Mientras se está escaneando el sistema de ficheros, aparece junto a la caja de búsqueda un botón “Stop scan”. Al pulsarlo el escaneo para, y el botón se convierte en un botón “Enable” (habilitar la caja de búsqueda). El estado (realizar escaneo o no) se preserva según se cambia de directorio, y se guarda en la configuración al cerrar Minirok.
tras pelearme un poco más con el problema con KConfigSkeleton que mencioné previamente, concluí que debía haber algún bug en los bindings de Python, y el autor así lo confirmó. Me ha escrito diciéndome que ya ha encontrado el problema y que espera poder arreglarlo la semana que viene.
Veremos si consigue arreglarlo a tiempo. Yo el código lo tengo portado y preparado, pero no quiero añadirlo al repositorio sin confirmar primero que funciona. Sin embargo, si se acerca el 7 de abril y Jim aún no lo ha podido arreglar, lo subiré a una rama a parte al menos.
En cuanto a los problemas con DBus, me ha dicho Jonathan Riddell que puede que se solucionen con Qt 4.4. Ojalá.