<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>A cuadros &#187; Programación</title>
	<atom:link href="http://blog.rastersoft.com/index.php/category/programacion/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.rastersoft.com</link>
	<description>El primer blog con camisa de leñador</description>
	<lastBuildDate>Sat, 31 Jul 2010 10:34:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Trabajando con señales</title>
		<link>http://blog.rastersoft.com/index.php/2010/04/11/trabajando-con-senales/</link>
		<comments>http://blog.rastersoft.com/index.php/2010/04/11/trabajando-con-senales/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 20:41:49 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/?p=581</guid>
		<description><![CDATA[Estos días he seguido trasteando con Bftpd, y me puse a trabajar en resolver un problema bastante molesto: la cantidad de procesos zombie que deja tras de sí. Para entender lo que pasa, primero hay que explicar que Bftpd lanza un nuevo proceso por cada nuevo usuario que se conecta, en lugar de utilizar un [...]]]></description>
			<content:encoded><![CDATA[<pre></pre>
<p>Estos días he seguido trasteando con Bftpd, y me puse a trabajar en resolver un problema bastante molesto: la cantidad de procesos <a href="http://en.wikipedia.org/wiki/Zombie_process" target="_blank">zombie</a> que deja tras de sí.</p>
<p>Para entender lo que pasa, primero hay que explicar que Bftpd lanza un nuevo proceso por cada nuevo usuario que se conecta, en lugar de utilizar un solo proceso para gestionar todas las conexiones. Así, tenemos un proceso padre que se limita a escuchar, y cada vez que llega una petición de conexión crea un proceso hijo para que la gestione; también comprueba cuando un hijo se muere (porque se haya cerrado la conexión).</p>
<p>Y aquí es de donde surge el problema: cada vez que un hijo termina, emite una <a href="http://en.wikipedia.org/wiki/Signal_%28computing%29" target="_blank">señal</a> (en concreto <a href="http://en.wikipedia.org/wiki/SIGCHLD" target="_blank">SIGCHLD</a>) hacia el padre, el cual, al recibirla, ejecutará una pequeña función o callback y luego seguirá su ejecución normal. En el caso de Bftpd, dicho callback comprueba qué hijo es el que se ha muerto, pide su valor de retorno (para evitar que se quede zombie) y libera una serie de recursos que le reservó. En concreto, el código de dicha función comienza con un:</p>
<pre style="padding-left: 60px;">pid = wait(NULL);
[codigo para liberar los recursos del proceso PID]</pre>
<p>Hasta aquí todo parece correcto; por desgracia, después de cada sesión con Bftpd quedaban varios procesos zombie. La razón era, obviamente, que el proceso padre no había leído su valor de retorno, por lo que el sistema operativo no los podía hacer desaparecer; sin embargo, en el callback se lee siempre dicho valor, así que es obvio que algo raro estaba pasando.</p>
<p>Para depurar el código empecé por añadir unos printfs: uno en el punto en que se crean los procesos hijo, mostrando su PID; otro en el punto de finalización de dichos hijos, y otro en la función de callback de SIGCHLD. Ejecuté el servidor, hice unas cuantas operaciones, vi la salida por pantalla, y&#8230; ¡Sorpresa! ¡Había menos llamadas a la función de callback que muertes! Si todo funcionase como se esperaba, el número debería ser exactamente el mismo (cada muerte debería emitir una señal SIGCHLD, la cual haría que se ejecutase el callback). Sin embargo, por alguna misteriosa razón, algunos procesos morían sin emitir la señal.</p>
<p>Para aquellos a los que les de igual el por qué, y simplemente necesiten saber el como, daré primero la solución: se trata de leer los PIDs de todos los hijos muertos en cada llamada al callback, en lugar de leer el de uno sólo. En otras palabras, el código del callback para SIGCHLD debe ser:</p>
<pre style="padding-left: 30px;">do {
        pid = waitpid(-1,NULL,WNOHANG);
        if (pid&gt;0) {
            [codigo para liberar los recursos del proceso PID]
        }
} while (pid&gt;0);
</pre>
<p><strong>Vale, pero ¿por qué ocurre esto?</strong></p>
<p>Para entender lo que ocurre hay que irse un poco a las profundidades del núcleo, porque se debe a un problema de <a href="http://rtportal.upv.es/apps/rtl-signals/posix_signals-0.1/signals-info.pdf">cómo están implementadas las señales</a>.</p>
<p>En Linux, cada proceso tiene un conjunto de bits, y cada uno representa una señal. Cuando un proceso quiere enviar una señal a otro, lo que hace realmente es poner a uno el bit de dicha señal en el proceso receptor.</p>
<p>¿Y cuando comprueba el receptor la llegada de una señal? En cada cambio de contexto: en un sistema operativo multitarea, los procesos se van turnando en el uso de la CPU, de manera que primero la usa unos milisegundos el proceso A, luego otros milisegundos el proceso B, y así sucesivamente hasta que se acaba la lista y se vuelve al proceso A. Cada vez que se cambia de un proceso al siguiente se realiza un cambio de contexto, en el que primero se guarda el estado del proceso actual, se busca quien será el siguiente proceso, se carga su estado y se le cede el control. Pero, y aquí está la cuestión, antes de este último paso se comprueba la máscara de bits de las señales, y si alguna está activa se ejecutará primero el callback correspondiente.</p>
<p>¿Y por qué se pierden entonces señales SIGCHLD? Pues porque dos o más procesos se mueren &#8220;a la vez&#8221;; esto es, se mueren dentro del mismo intervalo entre dos ejecuciones del proceso padre. Así, lo que ocurre es que cuando se muere el primero, pone a 1 el bit de la señal SIGCHLD del proceso padre; el repartidor de tareas, al ver que ha muerto, libera todo lo que puede y pasa al siguiente proceso; éste muere también, y también pone a 1 el bit de la señal SIGCHLD del proceso padre&#8230; pero ese bit ya estaba a uno, por lo que se queda como está. Finalmente, el repartidor decidirá que es el momento de ejecutar de nuevo el proceso padre, pero antes verá que el bit de la señal SIGCHLD está activo, por lo que lo pondrá a cero y  llamará al callback. El resultado: se murieron dos procesos pero sólo se ejecutó una vez la función asociada a la señal, por culpa de que las señales se almacenan con un único bit.</p>
<p>Es por esto que la solución indicada arriba funciona: la señal nos indica que se ha muerto AL MENOS un proceso hijo, así que debemos comprobar todos, y no asumir que cada uno enviará una señal.</p>
<p>Este caso nos demuestra que las señales son un sistema de comunicación bastante frágil, por lo que no se debe abusar de él. Cosas como &#8220;contar el número de veces que llega una señal&#8221; o similares pueden dar problemas incluso si hay un único emisor, porque si emite varias veces la misma señal antes de que el padre recupere el control de la CPU, contarán como una única señal. Y como muestra para los incrédulos, un pequeño programita que ejemplifica todo esto:</p>
<pre style="padding-left: 60px;">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;signal.h&gt;

int v;

void senal(int valor) {

 printf("Recibida\n");
 v++;
}

int main(int argc, char **argv) {

 int pid1,pid2,pid3,pid4,loop;

 v=0;
 signal(SIGUSR1,senal);
 pid1=getpid();
 pid2=fork();
 if(pid2==0) {
 fork();
 fork();
 printf("hijo\n");
 kill(pid1,SIGUSR1);
 kill(pid1,SIGUSR1);
 kill(pid1,SIGUSR1);
 kill(pid1,SIGUSR1);
 kill(pid1,SIGUSR1);
 sleep(2);
 } else {
 printf("padre\n");
 for(loop=0;loop&lt;21;loop++) {
 sleep(1);
 }
 printf("Total: %d\n",v);
 }
}
</pre>
<p>Este código crea un primer hijo, el cual se divide en cuatro hijos en total, y cada uno emite, de golpe, cinco señales SIGUSR1 al padre. Sin embargo, al ejecutarlo veremos que nunca se detectan todas ellas, sino, como mucho, una por proceso hijo (y a veces ni eso).</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2010/04/11/trabajando-con-senales/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mono</title>
		<link>http://blog.rastersoft.com/index.php/2009/08/31/mono/</link>
		<comments>http://blog.rastersoft.com/index.php/2009/08/31/mono/#comments</comments>
		<pubDate>Sun, 30 Aug 2009 22:22:12 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Generales]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/?p=208</guid>
		<description><![CDATA[A principios de verano surgió una agria polémica sobre la conveniencia o no de usar Mono (la implementación libre de los estándares ECMA-334 y ECMA-335) dentro de GNOME. El problema en buena medida venía dado por las diferentes opiniones sobre la libertad de Mono. Los que estaban a favor afirmaban que Microsoft había reconocido de [...]]]></description>
			<content:encoded><![CDATA[<p>A principios de verano surgió una agria polémica sobre la conveniencia o no de usar Mono (la implementación libre de los estándares ECMA-334 y ECMA-335) dentro de GNOME. El problema en buena medida venía dado por las diferentes opiniones sobre la libertad de Mono. Los que estaban a favor afirmaban que <a href="http://www.microsoft.com/interop/cp/default.mspx" target="_blank">Microsoft había reconocido de manera explícita que renunciaba a denunciar a nadie por usar sus patentes</a> sobre ambos estándares, mientras que los que estaban en contra afirmaban que <a href="http://www.gnome.org/~seth/blog/mono" target="_blank">dicho texto legal era lo suficientemente obtuso como para que no garantizase prácticamente  nada</a>, y que habría que abandonar por completo Mono, proponiendo algunos reemplazarlo por <a href="http://en.wikipedia.org/wiki/Vala_(programming_language)" target="_blank">Vala</a>. En su momento no escribí nada, en parte porque ya había suficiente polémica, y en parte por falta de tiempo, así que ahora que los ánimos están más calmados me gustaría dar mi opinión sobre una cuestión relacionada.</p>
<p>Para empezar, todo el mundo se centra directamente en si hay riesgo de que haya demandas por patentes a la hora de usar Mono. En ese punto no voy a entrar puesto que se trata de una cuestión legal y yo no soy abogado. Donde sí me gustaría entrar es en el tema de qué aplicaciones es razonable que se escriban en Mono (y, en realidad, en cualquier otro lenguaje que trabaje sobre un runtime, como Java o Python) y cuales deberían ir siempre en Vala.</p>
<p>Para presentar el problema me iré al caso de un viejo conocido de todo el mundo: Windows Vista. Tras su lanzamiento surgieron grandes críticas por su excesivo uso de memoria, lo que obligaba a instalar al menos 2GB para conseguir una fluidez equivalente a la de un Windows XP con 512MB.</p>
<p>Y ese es el problema de utilizar Mono, Java, Perl o Python en donde no se deben usar. Recordemos que el usuario utiliza un ordenador con un fin concreto: realizar un trabajo. Para ello utiliza un programa determinado: si quiere dibujar, utilizará un programa de dibujo; si quiere escribir una carta, utilizará un procesador de texto, etc. Una vez que ha terminado la tarea, cierra el programa (liberando los recursos utilizados) y abre otro para hacer otra tarea.</p>
<p>Por otro lado, tanto los usuarios como los programas necesitan de un sistema operativo que actúe como intermediario. Es importante recalcar que el sistema operativo, hasta cierto punto, debería ser transparente: el usuario no utiliza el sistema operativo para hacer sus trabajos, sino los programas concretos. Así pues, el sistema operativo debe ser parco en el consumo de recursos, pues éstos deben estar disponibles para los programas.</p>
<p>El problema surge cuando hacemos parte del sistema operativo en un lenguaje interpretado o que necesite una máquina virtual. En ese momento nos encontraremos con un consumo mayor de memoria y de procesador, lo que significa que está consumiendo recursos que no debería. Un ejemplo de esto sería programar applets de escritorio o extensiones del gestor de archivos utilizando Mono o Python, o demonios que se queden en ejecución en segundo plano durante toda la sesión. Sin embargo, una aplicación sí puede estar escrita en estos lenguajes porque, a fin de cuentas, es lo que el usuario quiere utilizar. Podría ser deseable que utilice menos recursos, pero ya no es algo tan crítico porque aquí sí estamos utilizando lo que, a fin de cuentas, nos corresponde. Y por aplicación incluyo aquí también aplicaciones oficiales de Gnome, como por ejemplo, <a href="http://en.wikipedia.org/wiki/Banshee_%28music_player%29" target="_blank">Banshee</a> o <a href="http://en.wikipedia.org/wiki/F-Spot" target="_blank">F-Spot</a>. La clave está en si son programas que permanecen abiertos durante toda la sesión y consumiendo recursos, o son aplicaciones que el usuario lanza expresamente cuando quiere realizar una tarea y que cierra cuando ha terminado de hacerla, liberando los recursos que utilizó. No olvidemos que una aplicación se puede cerrar y volver a abrir, mientras que un applet o un demonio tienen que estar corriendo constantemente para cumplir su función.</p>
<p>¿Entonces debemos utilizar siempre C para realizar las aplicaciones de escritorio, complementos y demonios? Aparentemente sería lo deseable; sin embargo, tiene el grave problema de que programar en C no es tan cómodo, ni los programas son tan mantenibles, como programar con Python, C# o Java (por algo existen). Y es justo aquí donde Vala brilla con luz propia: al tratarse de un lenguaje de alto nivel, con gestión de memoria asistida y una biblioteca de funciones y componentes que crece día a día, simplifica la escritura y mantenimiento del código hasta el nivel de los otros lenguajes comentados; y al compilarse directamente a código nativo, sin necesidad de ningún runtime extra, el resultado es prácticamente tan bueno como si se hubiese utilizado C desde el principio. Por supuesto, Vala también se puede utilizar para escribir aplicaciones, pero considero que aquí ya es cuestión de preferencias, así que cada uno utilice lo que más le guste: Perl, Python, C, Vala, Mono, Lisp, Ook&#8230; Pero para partes del sistema operativo o del escritorio considero que sólo se debería utilizar Vala.</p>
<p>Y debo reconocer que consejos vendo pero para mí no tengo, porque una de mis aplicaciones (GtkPSproc) está escrita justo al revés: el programa principal, que sólo se lanza cuando el usuario quiere imprimir algo, y que lleva la interfaz gráfica y hace todo el trabajo, está escrito en C, mientras que un pequeño applet (que, por tanto, está ejecutándose desde que se inicia el escritorio hasta que se apaga la máquina) que se encarga de reenviar la impresión al programa principal, está escrito en Python. En mi defensa diré que la aplicación principal la escribí antes de aprender Python, y que escribir el applet en python fue una chapuza inicial que acabó quedando como definitiva (¿Cuela?).</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2009/08/31/mono/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Vala de plata</title>
		<link>http://blog.rastersoft.com/index.php/2009/08/02/vala-de-plata/</link>
		<comments>http://blog.rastersoft.com/index.php/2009/08/02/vala-de-plata/#comments</comments>
		<pubDate>Sun, 02 Aug 2009 15:12:48 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/?p=181</guid>
		<description><![CDATA[Hace un par de meses descubrí la existencia de Vala, un nuevo lenguaje de programación similar a C# pero con una característica muy interesante: en lugar de compilar directamente a código máquina o a código de una máquina virtual, compila a código C, usando GObject para implementar el sistema de clases y objetos. La gran [...]]]></description>
			<content:encoded><![CDATA[<p>Hace un par de meses descubrí la existencia de<a href="http://live.gnome.org/Vala" target="_blank"> Vala, un nuevo lenguaje de programación</a> similar a C# pero con una característica muy interesante: en lugar de compilar directamente a código máquina o a código de una máquina virtual, compila a código C, usando GObject para implementar el sistema de clases y objetos. La gran ventaja de  esto es que permite crear bibliotecas y clases GObject sin necesidad de lidiar con la complejidad de este sistema, lo que, para los fans de Gnome nos resulta especialmente atractivo. También permite trabajar en un lenguaje moderno, lo suficientemente parecido a C# como para que casi no se note la diferencia, pero sin perder ni un ápice de rendimiento (lo siento, sigo sin creerme que un <a href="http://en.wikipedia.org/wiki/Just-in-time_compilation">JIT</a> consiga siempre mejores rendimientos, fuera de casos patológicos  y pruebas de laboratorio).</p>
<p>Otro detalle que me ha parecido fascinante es que simplifica mucho la gestión de memoria. Al contrario de lo que podría parecer, no utiliza un recolector de basura como el de Java, sino <a href="http://en.wikipedia.org/wiki/Reference_counting" target="_blank">conteo de referencias</a>. Lo interesante es que todo el proceso es muy transparente, pues el propio lenguaje se encarga de utilizarlo casi siempre. Así, un trozo de código como éste:</p>
<pre>    using GLib;

    class cPrueba:Object {

    }

    int main() {

        cPrueba miObjeto,miObjeto2;

        miObjeto=new cPrueba();
        stdout.printf("Referencias: %ud\n",miObjeto.ref_count);
        miObjeto2=miObjeto;
        stdout.printf("Referencias: %ud %ud\n",miObjeto.ref_count,miObjeto2.ref_count);
        miObjeto=new cPrueba();
        stdout.printf("Referencias: %ud %ud\n",miObjeto.ref_count,miObjeto2.ref_count);
        return 0;
    }</pre>
<p>cuando se ejecuta, devuelve la siguiente salida por pantalla:</p>
<pre>Referencias: 1d
Referencias: 2d 2d
Referencias: 1d 1d</pre>
<p>Vemos que, cuando se hace la primera asignación, se crea un nuevo objeto de tipo <strong>cPrueba</strong> y se asigna a <strong>miObjeto</strong>, con lo que su contador de referencias es 1. Luego hacemos la segunda asignación. Como <strong>miObjeto</strong> y <strong>miObjeto2</strong> son, realmente, punteros al mismo objeto (y apuntan, por tanto, a la misma zona de memoria), lo único que hace Vala es incrementar en uno el contador de referencias; por eso los contadores de referencias de <strong>miObjeto</strong> y <strong>miObjeto2</strong> valen 2 en la segunda línea de la salida: porque en realidad son el mismo objeto, pero está referenciado por dos punteros.</p>
<p>Cuando, finalmente, asignamos un nuevo objeto a <strong>miObjeto</strong>, primero Vala libera el objeto al que apunta, lo que en la práctica consiste en decrementar su contador de referencias. Si en ese momento éste pasase a valer cero, entonces ese objeto quedaría huérfano (ningún puntero apunta a él), por lo que Vala procedería a destruirlo automáticamente, liberando su memoria; sin embargo, en este ejemplo eso no ocurre porque <strong>miObjeto2</strong> está apuntando al objeto viejo, así que lo único que ocurre es que su contador de referencias pasa a valer 1. Por su parte, se crea un nuevo objeto de tipo <strong>cPrueba</strong>, apuntado por <strong>miObjeto</strong>, cuyo contador de referencias también vale 1, como vemos en la tercera línea de la salida.</p>
<p>Si observamos el código fuente generado (usando <em>valac -C prueba.vala)</em> su función MAIN queda como:</p>
<pre>    gint _main (void) {
        cPrueba* miObjeto;
        cPrueba* miObjeto2;
        cPrueba* _tmp0;
        cPrueba* _tmp2;
        cPrueba* _tmp1;
        cPrueba* _tmp3;
        gint _tmp4;
        miObjeto = NULL;
        miObjeto2 = NULL;
        _tmp0 = NULL;
        miObjeto = (_tmp0 = cprueba_new (), (miObjeto == NULL) ? NULL : (miObjeto = (g_object_unref (miObjeto), NULL)), _tmp0);
        fprintf (stdout, "Referencias: %ud\n", ((GObject*) miObjeto)-&gt;ref_count);
        _tmp2 = NULL;
        _tmp1 = NULL;
        miObjeto2 = (_tmp2 = (_tmp1 = miObjeto, (_tmp1 == NULL) ? NULL : g_object_ref (_tmp1)), (miObjeto2 == NULL) ? NULL : (miObjeto2 = (g_object_unref (miObjeto2), NULL)), _tmp2);
        fprintf (stdout, "Referencias: %ud %ud\n", ((GObject*) miObjeto)-&gt;ref_count, ((GObject*) miObjeto2)-&gt;ref_count);
        _tmp3 = NULL;
        miObjeto = (_tmp3 = cprueba_new (), (miObjeto == NULL) ? NULL : (miObjeto = (g_object_unref (miObjeto), NULL)), _tmp3);
        fprintf (stdout, "Referencias: %ud %ud\n", ((GObject*) miObjeto)-&gt;ref_count, ((GObject*) miObjeto2)-&gt;ref_count);
        return (_tmp4 = 0, (miObjeto == NULL) ? NULL : (miObjeto = (g_object_unref (miObjeto), NULL)), (miObjeto2 == NULL) ? NULL : (miObjeto2 = (g_object_unref (miObjeto2), NULL)), _tmp4);
    }</pre>
<p>(He omitido toda la parte de la definición GObject de la clase ejemplo porque no aporta nada). Vemos en las distintas líneas como cada vez que se hace una asignación de un objeto a una variable, primero se procede a liberar lo que hubiese en dicha variable utilizando una llamada a <strong>g_object_unref</strong>, para asegurarse de que el contador de referencias decrece correctamente. Luego llama a <strong>g_object_ref</strong> con la variable que se copia para incrementar su contador de referencias. Como vemos, este proceso automático nos simplifica la vida lo suficiente como para que nos podamos olvidar de la gestión automática de la memoria&#8230;</p>
<p>O casi, porque existe, al menos, un caso en el que no se cumple todo ésto: las listas. Sin embargo, tras buscar y rebuscar, encontré que es un bug en Vala, así que ya está reportado (<a href="http://bugzilla.gnome.org/show_bug.cgi?id=586577" target="_blank">http://bugzilla.gnome.org/show_bug.cgi?id=586577</a>) y debería estar corregido en breve, asi que no añadais una llamada a unref a mano, sino simplemente esperad a que lo corrijan y recompilad para eliminar el memory leak.</p>
<p>Existe, de todas formas, una opción alternativa, que es utilizar <a href="http://live.gnome.org/Vala/GeeSamples" target="_blank">LibGee</a> y su ArrayList. Este tipo de listas no tiene el bug que comento, por lo que, si se utiliza, no hay riesgo de memory leaks.</p>
<p>Pese a todo, es importante señalar un par de detalles sobre las listas de Glib en Vala: si vemos la documentacion, hay varias formas de declararlas, en concreto:</p>
<ul>
<li>var milista = List&lt;contenido_de_lista&gt;();</li>
<li>List&lt;contenido_de_lista&gt; milista = List&lt;contenido_de_lista&gt;();</li>
<li>List milista = List&lt;contenido_de_lista&gt;();</li>
</ul>
<p>Siendo <strong>contenido_de_lista</strong> una clase de elementos que contendra la lista. Asi, si quiero hacer una lista de strings la definiria como <strong>List&lt;string&gt;</strong>.  La cuestion es que, en base a las pruebas que he hecho, solo se realiza gestion de memoria si la lista se define como en la primera o la segunda opcion, porque solo ahi el compilador podra estar seguro de que el contenido es una clase derivada de Object y tiene, por tanto, contador de referencias. Si definimos la lista como en el tercer caso no se realizara gestion de memoria, por lo que podriamos incluso encontrarnos con que metemos en una lista global un elemento definido localmente y que, al terminar la funcion en donde se definio dicho elemento y volver a la funcion principal, dicho elemento es liberado, consiguiendo un hermoso core dump en cuanto intentemos acceder a dicho dato en la lista. Por tanto, mi consejo es utilizar siempre listas bien definidas, salvo que se sepa perfectamente lo que se esta haciendo.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2009/08/02/vala-de-plata/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Con gettext y a lo loco</title>
		<link>http://blog.rastersoft.com/index.php/2009/07/02/165/</link>
		<comments>http://blog.rastersoft.com/index.php/2009/07/02/165/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 23:43:31 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/?p=165</guid>
		<description><![CDATA[Hoy estaba preparando la nueva versión de DeVeDe para enviarla a los traductores y, cuando ejecuté el script para actualizar los ficheros de cadenas salió un error: no podía encontrar el fichero devede.glade. Obvio, porque había cambiado a GtkBuilder, así que quité el fichero en POTFILES.IN y añadí todos los nuevos. Sin embargo, me encontré [...]]]></description>
			<content:encoded><![CDATA[<p>Hoy estaba preparando la nueva versión de DeVeDe para enviarla a los traductores y, cuando ejecuté el script para actualizar los ficheros de cadenas salió un error: no podía encontrar el fichero devede.glade. Obvio, porque había cambiado a GtkBuilder, así que quité el fichero en POTFILES.IN y añadí todos los nuevos. Sin embargo, me encontré con que ahora no encontraba ni la mitad de las frases.</p>
<p>Una rápida búsqueda en google me dio la solución: por defecto, <a href="https://bugzilla.redhat.com/show_bug.cgi?id=504483" target="_blank">intltool-update confunde los ficheros .ui de glade</a> con descripciones ui de<a href="http://es.wikipedia.org/wiki/Bonobo_(software)" target="_blank"> Bonobo</a>. Para solucionarlo no hay más que añadir la cadena [type: gettext/glade] antes de cada nombre de fichero de glade en POTFILES.IN.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2009/07/02/165/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Llega GtkBuilder</title>
		<link>http://blog.rastersoft.com/index.php/2009/04/23/llega-gtkbuilder/</link>
		<comments>http://blog.rastersoft.com/index.php/2009/04/23/llega-gtkbuilder/#comments</comments>
		<pubDate>Wed, 22 Apr 2009 23:23:56 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/?p=106</guid>
		<description><![CDATA[Actualizado. Acabo de terminar una nueva versión de DeVeDe; sin embargo no os abalanceis como locos a por ella porque todavía no está disponible. La razón es que uno de los (pocos) cambios que hice fue migrar el código a GtkBuilder; el problema es que varias de las funcionalidades que aporta se han añadido en [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Actualizado. </strong>Acabo de terminar una nueva versión de DeVeDe; sin embargo no os abalanceis como locos a por ella porque todavía no está disponible. La razón es que uno de los (pocos) cambios que hice fue migrar el código a <a href="http://library.gnome.org/devel/gtk/unstable/GtkBuilder.html" target="_blank">GtkBuilder</a>; el problema es que varias de las funcionalidades que aporta se han añadido en la nueva GTK 2.16, la cual viene en Ubuntu 9.04, la cual no sale oficialmente hasta mañana. Eso significa que si saco ahora esta nueva versión, nadie podrá usarla a menos que (como yo) instale una versión <em>Release Candidate</em> de Ubuntu. Por esa razón no la sacaré hasta el sábado (por aquello de dar tiempo a la gente a que migre de manera calmada y ordenada).</p>
<p>Sin embargo, aprovecho para escribir una breve introducción a GtkBuilder (breve porque tampoco hay mucho que explicar, es muy sencilla de usar).</p>
<p><strong>¿Qué es GtkBuilder?</strong></p>
<p>GtkBuilder es una biblioteca que llevará a cabo las mismas funciones que actualmente realiza <a href="http://en.wikipedia.org/wiki/Libglade" target="_blank">LibGlade</a>: a partir de una serie de ficheros XML creados con Glade u otro editor, generará las ventanas de una aplicación, incluyendo todos sus widgets internos.</p>
<p><strong>¿Pero si va a hacer las mismas funciones, por qué sacar una nueva biblioteca en lugar de seguir con la vieja?</strong></p>
<p>Por una razón muy simple: LibGlade no forma parte de GTK, sino que sigue un desarrollo completamente independiente. Eso la limita en algunos aspectos (por ejemplo, velocidad a la hora de soportar nuevos widgets y propiedades). Por otro lado, no tenía sentido que una biblioteca tan fundamental fuese un elemento externo. Por último, aunque LibGlade siempre funcionó muy bien y está bien implementada, había algunas cosas que se podían mejorar.</p>
<p>Por todo ésto los desarrolladores de GTK decidieron crear una biblioteca integrada en GTK y que fuese lo más parecida posible a LibGlade, con el objetivo de reemplazarla: GtkBuilder había nacido.</p>
<p><strong>Trabajando con GtkBuilder</strong></p>
<p>La forma de trabajar con GtkBuilder es muy similar a LibGlade. La principal diferencia es que en GtkBuilder no es posible generar sólo una parte del árbol (aunque se están planteando el soportar esta opción). Esto significa que es más eficiente poner cada ventana en un fichero XML independiente.</p>
<p>Si ya tienes un fichero <em>.glade</em> puedes convertirlo fácilmente al nuevo formato usando el script <strong><em>gtk-builder-convert</em></strong>. Como primer parámetro recibe el fichero <em>.glade</em> a convertir; como segundo parámetro, el fichero de salida en formato GtkBuilder; por último, opcionalmente se le puede pasar un parámetro <em>-r </em> seguido del identificador de un widget (usualmente una ventana), de manera que sólo convertirá dicho widget y todos sus hijos. Esta última opción permite separar una antigua interfaz Glade con todas las ventanas en un único fichero, en varios ficheros GtkBuilder, cada uno con una sola ventana.</p>
<p>Un ejemplo: <em>gtk-builder-convert viejo_proyecto.glade ventana_ppal.ui -r main_win</em> creará un fichero <em>ventana_ppal.ui</em> que contendrá la ventana <em>main_win</em> y todos sus elementos, del proyecto <em>viejo_proyecto.glade</em>.</p>
<p>Por cierto, la extensión de los nuevos archivos es <strong><em>.ui</em></strong>. Es un detalle que me costó encontrar.</p>
<p>La primera gran diferencia con Glade es que no hace falta importar ningún módulo, pues ya está dentro de GTK.</p>
<p>Trabajar con GtkBuilder es tan sencillo como:</p>
<pre>    class MiClase:
        __init__(self):
            builder=gtk.Builder()
            builder.set_translation_domain("mi_aplicacion")
            builder.add_from_file("path/to/file.ui")
            builder.connect_signals(self)
            ventana=builder.get_object("nombre_ventana")</pre>
<p>En la primera línea creamos un objeto de tipo GtkBuilder; en la segunda especificamos cual es el nombre de nuestra aplicación de cara a usar <em>GETTEXT</em> para las locales; a continuación le indicamos qué fichero <em>.ui</em> contiene la definición de la interfaz que queremos generar. Estas tres líneas reemplazan a la única línea que utilizábamos con LibGlade.</p>
<p>En la siguiente línea le indicamos que conecte las señales con los métodos de nuestra clase; es igual que el método <em>signal_autoconnect</em> de LibGlade.</p>
<p>Por último, pedimos una referencia a un objeto concreto; es igual que el viejo método <em>get_widget</em> de LibGlade.</p>
<p><strong>Trabajando con Glade</strong></p>
<p>Las nuevas versiones de Glade-3 no sólo soportan el nuevo formato, sino que incluso trabajan por defecto en él. En general se sigue trabajando exactamente igual que antes; la única diferencia es que es recomendable que cada ventana vaya en un fichero (y, por tanto, en un proyecto) diferente.</p>
<p>Un detalle muy interesante es la comprobación del soporte de versiones de GTK en el nuevo Glade: si tenemos abierta una interfaz y escogemos <em>Editar-&gt;Preferencias</em>, nos saldrá el siguiente cuadro de diálogo:</p>
<p><img class="alignnone size-full wp-image-111" title="glade" src="http://blog.rastersoft.com/wp-content/uploads/2009/04/glade.png" alt="glade" width="492" height="494" /></p>
<p>En la parte inferior vemos la opción <strong>Versiones de los toolkit necesarias</strong>. Dicha opción nos permite comprobar si en esta interfaz estamos usando algun widget que no esté soportado por alguna versión antigua de GTK o de GtkBuilder. Esto es precisamente lo que me ocurrió con esta versión de DeVeDe: el nuevo constructor de menús sólo está soportado a partir de GTK 2.16, que viene a partir de Ubuntu 9.04.</p>
<p>En otra entrada explicaré como se trabaja con las <strong>GtkComboBox</strong> y sus listas asociadas, pues GtkBuilder permite generar todo desde Glade, sin necesidad de picar apenas código.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2009/04/23/llega-gtkbuilder/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>!Libre!</title>
		<link>http://blog.rastersoft.com/index.php/2008/11/30/libre/</link>
		<comments>http://blog.rastersoft.com/index.php/2008/11/30/libre/#comments</comments>
		<pubDate>Sun, 30 Nov 2008 03:17:57 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Nueva versión]]></category>
		<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/index.php/2008/11/30/libre/</guid>
		<description><![CDATA[Después de cinco años de vida, FBZX por fin es totalmente libre. Efectivamente, cuando escribí mi emulador de Spectrum, había intentado sin éxito escribir también el emulador de Z80. Por desgracia la tarea era muy pesada (sobre todo por repetitiva y propensa a errores), así que al final decidí usar uno hecho por un programador [...]]]></description>
			<content:encoded><![CDATA[<p>Después de cinco años de vida, <a href="http://www.rastersoft.com/programas/fbzxesp.html" target="_blank">FBZX</a> por fin es totalmente libre. Efectivamente, cuando escribí mi emulador de Spectrum, había intentado sin éxito escribir también el emulador de Z80. Por desgracia la tarea era muy pesada (sobre todo por repetitiva y propensa a errores), así que al final decidí usar uno hecho por un programador llamado Marat Fayzullin.</p>
<p>Por desgracia había un serio inconveniente: las condiciones de uso de este código eran incompatibles con la GPL (y mucho). Esta situación nunca me agradó demasiado, pero dado que escribir desde cero y depurar un emulador de un procesador era una tarea demasiado árdua, al igual que intentar adaptar el código de FBZX a otro emulador de Z80 diferente, lo fuí dejando de lado durante años.</p>
<p>Así estuvo la cosa hasta que hace un mes, más o menos, recibí un mensaje de un empaquetador de ArchLinux que se encontró con el problema: en efecto, no podía seguir manteniendo el emulador en el repositorio por culpa de la incompatibilidad de licencias.</p>
<p>Ante ésto decidí que ya era hora de tomar cartas en el asunto, así que me puse a escribir un emulador desde cero. Sin embargo, tenía claro que no podía limitarme a teclear largas listas de sentencias CASE XX:, sino que tenía que emplear una aproximación más inteligente si quería hacerlo en un tiempo razonable y con unas mínimas garantías de éxito. Así, se me ocurrió que podía partir de una lista de opcodes de Z80, la cual podría parsearse con un pequeño programa que generaría por mí el código en C. En efecto, partiendo de una lista como:</p>
<pre>
    LD A,B</pre>
<pre>    LD A,C</pre>
<pre>    LD A,(HL)</pre>
<pre>    CP (HL)</pre>
<p>es posible separar el comando (LD, CP&#8230;) de los parámetros (A, B, C, (HL)), y generar automáticamente código que realice las operaciones. De esta forma sólo tuve que escribir una vez el código que implementase la lectura y escritura de cada uno de los 51 posibles parámetros, la comprobación de las 8 posibles condiciones, y el funcionamiento de cada uno de los 81 comandos diferentes, y dejar que el parser se encargase de generar los 1792 opcodes posibles que puede ejecutar el Z80. Además, una ventaja extra es que cualquier error que cometiese sería más fácil de corregir, porque sólo tendría que hacerlo en el parser, y automáticamente se arreglarían todas las instrucciones afectadas.</p>
<p>El resultado es que he necesitado tan sólo unas cuarenta horas netas, repartidas en tres semanas, para escribir desde cero un emulador de Z80 plenamente funcional, y que, además, emula el juego completo de instrucciones del Z80, tanto las oficiales como las no oficiales. Y gracias a él, FBZX ya es completamente libre.</p>
<p>Dado que la idea del parser puede ser útil para hacer otros emuladores diferentes, he tenido especial cuidado en escribirlo de manera que se pueda cambiar fácilmente la lista de comandos, parámetros, etc, y así poder adaptarlo a otros procesadores como el 6502, el 6800, ARM&#8230;</p>
<p><strong>Otros cambios</strong></p>
<p>Aprovechando la nueva versión, también añadí soporte para sonido mediante los controladores ALSA, y la emulación del Spectrum 128K español (el original de Investrónica y Sinclair Research). Además, también almacena el nivel de volumen que se está usando, de manera que no haya que ajustarlo cada vez que se ejecute el programa de nuevo. Por último, he cambiado la licencia a la GPLv3.</p>
<p><strong>SuperShow</strong></p>
<p>También aproveché para sacar una pequeña revisión de SuperShow. El único cambio que hice fue traducir las variables de la plantilla que se usa para generar los scripts de Flash, de manera que si alguien que no hable castellano quiere mejorarla, lo tendrá más fácil.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2008/11/30/libre/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Núcleo duro</title>
		<link>http://blog.rastersoft.com/index.php/2008/06/01/nucleo-duro/</link>
		<comments>http://blog.rastersoft.com/index.php/2008/06/01/nucleo-duro/#comments</comments>
		<pubDate>Sun, 01 Jun 2008 11:46:30 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/index.php/2008/06/01/nucleo-duro/</guid>
		<description><![CDATA[En la última versión de DeVeDe añadí por fin soporte para multithreading en Mencoder. Por desgracia no es algo tan simple como decirle &#8220;Usa threads&#8221;, sino que hay que decirle cuantos queremos usar. Alguno dirá: &#8220;¡Pues tantos como núcleos tengamos en nuestro ordenador, por supuesto!&#8221; Y efectivamente, esa es la respuesta. El problema viene cuando [...]]]></description>
			<content:encoded><![CDATA[<p>En la última versión de DeVeDe añadí por fin soporte para multithreading en Mencoder. Por desgracia no es algo tan simple como decirle &#8220;Usa threads&#8221;, sino que hay que decirle cuantos queremos usar.</p>
<p>Alguno dirá: &#8220;¡Pues tantos como núcleos tengamos en nuestro ordenador, por supuesto!&#8221;</p>
<p>Y efectivamente, esa es la respuesta. El problema viene cuando queremos saber cuantos núcleos tenemos en nuestro ordenador. La primera solución que consideramos es leer <em>/proc/cpuinfo</em> y contar el número de veces que aparece la palabra <em>processor</em>. Por desgracia la cosa no es tan sencilla por culpa de un infame invento de Intel: <a target="_blank" href="http://en.wikipedia.org/wiki/Hyper-threading">el HyperThreading</a>. Esta tecnología lo que hace es simular dos núcleos en procesadores de un único núcleo.</p>
<p>Los procesadores actuales no siempre ejecutan las instrucciones en el mismo orden en que están escritas en la memoria, sino que pueden enviar antes algunas que están después, siempre y cuando no dependan de los resultados de ninguna instrucción anterior. Esto permite aprovechar mejor tiempos muertos en la ejecución de instrucciones (por ejemplo, el tiempo que tiene que esperar una instrucción de carga a que llegue un dato desde la memoria). Pues bien, la mayor independencia de instrucciones se da entre procesos diferentes: es posible entremezclar las instrucciones de dos procesos cualesquiera sin que haya riesgo de dependencia, precisamente porque son completamente independientes (valga la redundancia). Los ingenieros de Intel lo entendieron rápidamente y se les ocurrió que podrían llenar huecos en la ejecución de un proceso con instrucciones de otro, aprovechando así al máximo las distintas unidades funcionales del procesador. Desde fuera el procesador parecería tener dos núcleos, y así lo verían los sistemas operativos, cuando en realidad sólo habría uno. Esto permitía incluso no tener que modificar nada en el software.</p>
<p>Sobre el papel la idea parecía muy prometedora, y de hecho Intel afirmaba que se conseguían aumentos de rendimiento de hasta el 30%. Pero en la práctica no sólo era raro acercarse a dicha cifra, sino que había veces que las aplicaciones iban incluso más lentas. La razón no residía en el hyperthreading en sí, sino en su interacción con el <a target="_blank" href="http://en.wikipedia.org/wiki/Replay_system">Replay System</a> de los procesadores Pentium 4 (que son los únicos que, hoy por hoy, incorporan HT). En <a target="_blank" href="http://www.xbitlabs.com/articles/cpu/display/replay.html">X-bit labs explican muy bien en qué consiste</a>, así que no lo repetiré aquí porque quedaría muy largo y peor explicado.</p>
<p>¿Y qué ocurre en el caso particular de Mencoder? En general la gente no se pone de acuerdo. Algunos aseguran que no hay ninguna mejora, pero que tampoco empeora, mientras que otros afirman que va peor si se utilizan multiples hilos. Ante la duda decidí tomar la opción conservadora, lo que implica calcular con precisión el número real de núcleos. Por desgracia no he encontrado una manera &#8220;oficial&#8221; de saber esa cantidad, sino que hay que deducirla de los datos que ofrece <em>/proc/cpuinfo</em>. Para entender bien la nomenclatura me referiré como <em>procesador</em> a cada uno de los chips que hay en un ordenador, como <em>núcleo real</em> a cada uno de los núcleos físicos de un procesador, y como <em>núcleo virtual</em> al número de núcleos que cree ver un sistema operativo. Así, en un sistema con dos procesadores, en el que cada uno hay dos núcleos, tendremos un total de cuatro núcleos reales. Si encima todos tienen HyperThreading, tendremos un sistema con ocho núcleos virtuales.</p>
<p>Estos son los cuatro parámetros importantes en un sistema con múltiples núcleos y/o procesadores:</p>
<ul>
<li><strong>Physical id</strong>: identificador único de cada procesador (chip).</li>
<li><strong>Core id</strong>: identificador único de cada núcleo real dentro de un mismo procesador.</li>
<li><strong>Cpu cores</strong>: número de núcleos reales en este procesador.</li>
<li><strong>Siblings</strong>: número de núcleos virtuales en este procesador.</li>
</ul>
<p>A la vista de estas definiciones podría parecer que basta con contar el número de combinaciones diferentes de <strong>Physical id</strong> y <strong>Core id</strong> para saber cuantos núcleos tenemos. Eso hice en la versión 3.8 de DeVeDe y el batacazo no tardó en llegar: un usuario con un procesador AMD Phenom (cuatro núcleos) se quejaba de que sólo usaba un núcleo.</p>
<p>Tras recibir una copia de su <em>/proc/cpuinfo</em> la sorpresa fue mayúscula: ¡las cuatro entradas <em>processor</em> tenían exactamente el mismo Physical id y Core id, y el número de cores en cada una era uno en lugar de cuatro!</p>
<p>Era necesaria una nueva aproximación al problema, así que recopilé las descripciones de todas las máquinas que pude, lo que complicó aún más la cosa porque los valores bailaban y diferían mucho de lo que sería lógico: en sistemas con dos núcleos <em>Cpu cores</em> vale 2, pero en el sistema con cuatro núcleos sólo vale 1. Lo mismo ocurría con el valor de <em>Siblings</em>. Probé varias aproximaciones, cada cual más complicada, hasta que encontré la solución: cada entrada en <em>cpuinfo</em> se corresponde con un núcleo virtual, así que basta con calcular a qué porcentaje de un núcleo real se corresponde. Ese valor lo obtenemos con el cociente entre <em>Cpu cores </em>y <em>Siblings</em>.</p>
<p>En efecto, en mi procesador Athlon X2 y en un Intel Core 2 Duo, <em>Cpu cores</em> y <em>Siblings</em> valen ambos 2 en cada entrada <em>processor</em>, con lo que 2/2=1. En el procesador Phenom de cuatro núcleos ambos valen 1 en cada entrada, con lo que 1/1=1. Pero en un procesador con HyperThreading <em>Siblings</em> vale 2, mientras que <em>Cpu cores</em> vale 1, con lo que 1/2=0,5. Cada entrada <em>processor</em> cuenta como medio núcleo en la máquina con HyperThreading, mientras que en el resto de sistemas cuenta como un núcleo.<br />
¿Y qué pasa en una máquina con un sólo procesador de único núcleo? Pues que no aparece ninguna de esas dos entradas, por lo que hay que asumir que <em>Cpu cores</em> y <em>Siblings</em> valen 1 salvo que aparezcan en la descripción del procesador.</p>
<p>Así pues, basta con ir sumando el cociente entre <em>Siblings</em> y <em>Cpu cores</em> para cada una de las entradas para, al final, obtener el número de núcleos reales del sistema. Este es el sistema que usaré en DeVeDe 3.9, que saldrá dentro de unas semanas.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2008/06/01/nucleo-duro/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Selección multiple</title>
		<link>http://blog.rastersoft.com/index.php/2007/09/10/seleccion-multiple/</link>
		<comments>http://blog.rastersoft.com/index.php/2007/09/10/seleccion-multiple/#comments</comments>
		<pubDate>Mon, 10 Sep 2007 21:57:50 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/index.php/2007/09/10/seleccion-multiple/</guid>
		<description><![CDATA[En Trabajando con GtkTreeView en Python explicaba como utilizar el complejo pero versatil widget GtkTreeView. Sin embargo, faltaba un detalle importante: ¿como usar selección múltiple? Lo primero es poner el selector del GtkTreeView en modo de selección múltiple:     treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) Ahora ya podremos marcar más de una fila simultáneamente usando las teclas Mayúsculas y Control. [...]]]></description>
			<content:encoded><![CDATA[<p>En <a href="http://blog.rastersoft.com/index.php/2007/01/27/trabajando-con-gtktreeview-en-python/" target="_blank">Trabajando con GtkTreeView en Python</a> explicaba como utilizar el complejo pero versatil widget <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeview.html" target="_blank">GtkTreeView</a>. Sin embargo, faltaba un detalle importante: ¿como usar selección múltiple?</p>
<p>Lo primero es poner el selector del GtkTreeView en modo de selección múltiple:</p>
<pre>    treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)</pre>
<p>Ahora ya podremos marcar más de una fila simultáneamente usando las teclas Mayúsculas y Control.</p>
<p>Leer los elementos marcados es tan sencillo como obtener primero el iterador y el arbol:</p>
<pre>    tree,iter = self.z88tree.get_selection().get_selected_rows()</pre>
<p>Si el iterador es None, no hay ninguna fila seleccionada, pero si lo está, no tenemos más que recorrer los elementos del iterador para acceder a las filas. Con ellas, podemos usar el método get_value del arbol para obtener el valor de cada una.</p>
<p>En este ejemplo almacenamos en la lista ret el valor de la columna cero de cada fila seleccionada:</p>
<pre>    ret = []
    for item in iter:
        ret.append(tree.get_value(tree.get_iter(item),0))</pre>
<p>Fácil, fácil, para toda la familia.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2007/09/10/seleccion-multiple/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Trabajando con PyCairo y GTK</title>
		<link>http://blog.rastersoft.com/index.php/2007/06/20/trabajando-con-pycairo-y-gtk/</link>
		<comments>http://blog.rastersoft.com/index.php/2007/06/20/trabajando-con-pycairo-y-gtk/#comments</comments>
		<pubDate>Wed, 20 Jun 2007 21:08:41 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/index.php/2007/06/20/trabajando-con-pycairo-y-gtk/</guid>
		<description><![CDATA[En la nueva versión de DeVeDe (que espero sacar en un par de días) he tenido que trabajar con Cairo para poder generar menús para el disco. La información la saqué de este tutorial sobre PyCairo (en inglés), en donde viene mucha información interesante. Sin embargo, faltaba un detalle importante: como mostrar el dibujo hecho [...]]]></description>
			<content:encoded><![CDATA[<p>En la nueva versión de DeVeDe (que espero sacar en un par de días) he tenido que trabajar con <a title="http://cairographics.org/" href="http://cairographics.org/" target="_blank">Cairo</a> para poder generar menús para el disco. La información la saqué de <a href="http://www.tortall.net/mu/wiki/CairoTutorial">este tutorial sobre PyCairo</a> (en inglés), en donde viene mucha información interesante. Sin embargo, faltaba un detalle importante: como mostrar el dibujo hecho en cairo en una ventana GTK.</p>
<p>En efecto, esto era fundamental porque el usuario tiene que poder ver el resultado final del menú antes de generar el disco (para, por ejemplo, asegurarse de que los títulos no son demasiado largos), y para eso necesito pintarlo en una ventana y no sólo volcarlo a un PNG en disco. Después de mucho buscar y de hacer varias pruebas encontré por fin como hacerlo.</p>
<p>Lo primero es crear un widget GtkDrawingArea, en el que mostraremos el dibujo generado con Cairo, y crear un callback para el evento expose-event. Este evento se produce cada vez que hay que redibujar la ventana (bien porque estaba tapada por otra y la hemos destapado, bien porque estaba minimizada y la hemos maximizado&#8230;). En dicho callback será donde hagamos el pintado. Para ello, lo primero que tenemos que hacer es conseguir un contexto de Cairo para dicho widget. Así, si el widget se llama MiDibujo, y arbol es el objeto Glade con nuestras ventanas, haríamos:</p>
<pre>    # asignamos el callback a nuestra funcion de repintado
    w=arbol.get_widget("MiDibujo")
    w.connect("expose-event",repintado)

    # y definimos el callback. Tiene dos argumentos
    def repintado(dwidget,evento):
        cr=dwidget.window.cairo_create()
        [funciones y métodos de Cairo para pintar]</pre>
<p>Y ya podremos usar ese contexto para pintar y trazar todo lo que queramos.</p>
<p>Sin embargo, si el dibujo es estático no tiene sentido que lo tracemos una y otra vez cada vez que se produzca un evento, porque desperdiciamos tiempo de proceso. Lo mejor es tenerlo pintado desde el principio y limitarnos a copiar el bitmapa final en el callback de repintado. Para ello sólo tenemos que crear primero una superficie de Cairo y obtener un contexto para ella:</p>
<pre>    sf=cairo.ImageSurface(cairo.FORMAT_ARGB32,ResX,Resy)
    cr1=cairo.Context(sf)</pre>
<p>Ahora pintaremos nuestro dibujo en dicha superficie con ese contexto, y una vez que hayamos terminado, almacenaremos el objeto superficie de alguna manera que nos permita acceder a él desde nuestro callback (una variable global es la solución más rápida y menos elegante). Allí sólo tenemos que usar los métodos set_source_surface y paint para transferir la superficie a nuestro widget GtkDrawingArea, ahorrando mucho tiempo de proceso.</p>
<pre>    def repintado(dwidget,evento):
        cr=dwidget.window.cairo_create()
        # sf es la que pintamos en el paso anterior
        cr.set_source_surface(sf)

        # transferimos la superficie con el dibujo al Widget
        cr.paint()</pre>
<p>Este truco nos permite también pegar cualquier imagen en la superficie en la que estamos trabajando, e incluso redimensionarla en el proceso. Un ejemplo: queremos crear una imagen de tamaño X,Y, usando como fondo un PNG de tamaño cualquiera, pero de manera que éste se expanda o encoja para ocupar exactamente la superficie de nuestra imagen final. Sólo tendríamos que hacer:</p>
<pre>    sf_base= cairo.ImageSurface.create_from_png("imagen.png")
    sf_final=cairo.ImageSurface(cairo.FORMAT_ARGB32,X,Y)

    # creamos un contexto para pintar
    cr_final=cairo.Context(sf_final)

    # cogemos el ancho y alto de la imagen.
    # Importante: en coma flotante
    xbase=float(sf_base.get_width())
    ybase=float(sf_base.get_height())

    # aplicamos el escalado para que la imagen de fondo
    # pase a tener el mismo tamaño que la imagen final
    cr_final.scale(float(X)/xbase,float(Y)/ybase)

    # y estampamos la imagen de origen en la superficie final
    cr_final.set_source_surface(sf_base)
    cr_final.paint()

    # podemos usar el método identity para
    #restaurar la escala a 1:1
    cr.identity_matrix()</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2007/06/20/trabajando-con-pycairo-y-gtk/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Trabajando con GtkTreeView en Python</title>
		<link>http://blog.rastersoft.com/index.php/2007/01/27/trabajando-con-gtktreeview-en-python/</link>
		<comments>http://blog.rastersoft.com/index.php/2007/01/27/trabajando-con-gtktreeview-en-python/#comments</comments>
		<pubDate>Sat, 27 Jan 2007 10:20:41 +0000</pubDate>
		<dc:creator>raster</dc:creator>
				<category><![CDATA[Programación]]></category>

		<guid isPermaLink="false">http://blog.rastersoft.com/index.php/2007/01/27/trabajando-con-gtktreeview-en-python/</guid>
		<description><![CDATA[Esta semana tuve que trabajar en serio con el widget GtkTreeView en Python y se me hizo completamente cuesta arriba hasta que, después de buscar en múltiples tutoriales y pelearme con el código, me vino una epifanía y entendí su complejo pero potente esquema de funcionamiento; así que, en base a la experiencia adquirida, voy [...]]]></description>
			<content:encoded><![CDATA[<p>Esta semana tuve que trabajar en serio con el widget GtkTreeView en Python y se me hizo completamente cuesta arriba hasta que, después de buscar en múltiples tutoriales y pelearme con el código, me vino una epifanía y entendí su complejo pero potente esquema de funcionamiento; así que, en base a la experiencia adquirida, voy a escribir un pequeño tutorial, acompañado de algo de código para ejemplificar.</p>
<p>En GtkTreeView tenemos una clara división entre los datos, la presentación y el motor. Esto es lo que hace que sea un poco lioso al principio, pero también le da una potencia y versatilidad sorprendente.</p>
<p>Lo primero que necesitamos es añadir los módulos gtk, pygtk, gtk.glade y gobject. El último es necesario para los tipos de datos que vamos a manejar.</p>
<p>A continuación tenemos el widget <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeview.html">GtkTreeView</a>. Este elemento es el que mostrará nuestra lista de datos. Obtenemos un puntero a dicho objeto mediante GLADE:</p>
<pre>    treeview = arbol_xml.get_widget("nuestro_gtktreeview")</pre>
<p>Ahora vamos a definir los tipos de datos que contendrá cada fila. A ésto se le denomina <strong>modelo</strong>. Es importante entender que no todos los campos del modelo tienen por qué ser visibles. En este ejemplo vamos a reservar un entero para saber, cuando el usuario marque una opción, cual es la que está escogiendo. También habrá tres cadenas, que contendrán el texto que queremos mostrar en el TreeView y el color de una de ellas.<em></em></p>
<pre>    modelo = gtk.ListStore (gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)</pre>
<p>Existen dos tipos de modelos: <a href="http://www.pygtk.org/docs/pygtk/class-gtkliststore.html">ListStore</a> y <a href="http://www.pygtk.org/docs/pygtk/class-gtktreestore.html">TreeStore</a>. Ambos se manejan prácticamente igual, salvo que el segundo permite tener elementos anidados en forma de arbol. La diferencia principal se da a la hora de añadir los datos en sí, como veremos luego.</p>
<p>El siguiente paso es asociar el modelo al TreeView, de manera que éste sepa qué datos almacenar en cada fila:</p>
<pre>    treeview.set_model(modelo)</pre>
<p>Hasta aquí hemos definido los tipos de datos (mediante el modelo), y tenemos el motor (el TreeView), por lo que sólo nos falta la presentación. Para ello tenemos que usar dos clases más para definir cada una de las columnas que mostraremos:</p>
<pre>    render=gtk.CellRendererText() # renderer para la primera columna
    columna = gtk.TreeViewColumn ("titulo", render, text=1, background=2) # primera columna de datos
    treeview.append_column (columna)

    render2=gtk.CellRendererText()
    columna2 = gtk.TreeViewColumn ("otro titulo", render2, text=3) # segunda columna de datos
    treeview.append_column(columna2)</pre>
<p><a href="http://www.pygtk.org/docs/pygtk/class-gtkcellrenderertext.html">CellRendererText</a> es una clase encargada de mostrar un texto en una columna de un TreeView. En el ejemplo vemos que creamos dos de ellos, uno para cada columna que vamos a mostrar. Luego, creamos una <a href="http://www.pygtk.org/docs/pygtk/class-gtktreeviewcolumn.html">TreeViewColumn</a> que tendrá varios parámetros: el primero es el título que se mostrará en la parte superior del TreeView para dicha columna (si estamos mostrando los nombres de personas, el título podría ser <strong>nombre</strong>, por ejemplo), y el segundo es la referencia al tipo de CellRenderer que vamos a utilizar para esa columna (en este caso, como es texto, un CellRendererText, pero hay <a href="http://www.pygtk.org/docs/pygtk/index.html">más tipos</a>). El resto de parámetros depende del CellRenderer que estemos utilizando. En el caso del CellRendererText tenemos, entre otros, el parámetro <strong>text</strong>, que contendrá el texto que queremos mostrar, y el parámetro <strong>background</strong>, que contiene el color del texto en formato de cadena de texto.</p>
<p>Vemos que en la primera columna le asociamos el valor 1 para el texto y el 2 para el color de fondo, y para la segunda columna usamos el valor 3 para el texto. Estos valores se refieren a la variable correspondiente del modelo TreeStore que generamos unos pasos antes: la variable 0 es el entero, la 1 y la 2 son los dos strings que contendrán, respectivamente, el texto y el color de la primera columna, y la 3 el texto de la segunda columna.</p>
<p>Tras crear cada una de las columnas la añadimos a nuestro TreeView mediante el método <strong>append_column</strong>, con lo que nuestro TreeView estará listo para ser rellenado con los datos que deseemos. Esto lo hacemos de la siguiente manera:</p>
<pre>    iterador = modelo.insert(posicion) # el iterador es el objeto que contiene todos los valores de una fila concreta
    modelo.set_value(iterador,0,0) # el segundo cero es el dato que metemos (un entero)
    modelo.set_value(iterador,1,"Un texto")
    modelo.set_value(iterador,2,"#00FFE0")
    modelo.set_value(iterador,3,"Otro texto")</pre>
<p>Estas llamadas se repetirán tantas veces como filas queramos insertar en el TreeView. La variable posicion contendrá el número de fila en la que se insertarán estos datos.</p>
<p>En el caso de utilizar un TreeStore en lugar de un ListStore, el método insert precisaría de un parámetro adicional, antes de la posición, para indicar qué elemento (referenciado mediante un TreeIter) es el padre. Si se pone None, se supondrá que cuelga del padre.</p>
<p>Con ésto ya tenemos nuestro TreeView listo, pero aún nos faltan dos cosas: borrarlo para meter nuevas filas, y saber qué fila es la que un usuario ha escogido. Lo primero es tan simple como llamar al método clear del modelo, con lo que podemos volver al último paso (con los set_values) para introducir las nuevas columnas. Para lo segundo sólo tenemos que hacer:</p>
<pre>    seleccion,iterador = treeview.get_selection().get_selected()</pre>
<p>Si iterador es None, significa que no hay ninguna fila seleccionada. En caso contrario podemos utilizar</p>
<pre>    seleccion.get_value(iterador,VARIABLE)</pre>
<p>para obtener el valor de la columna indicada por VARIABLE. Si usamos el valor 1 nos devolvería el texto de la primera columna; si usamos el valor 2, el color de fondo de la primera columna, y con el valor 3, el texto de la segunda columna. Con el valor 0 nos devolvería un entero, que hasta ahora no hemos utilizado para nada; pero si a dicho entero le asignamos valores consecutivos al crear las filas, nos dirá cual es el número de fila que escogió el usuario.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.rastersoft.com/index.php/2007/01/27/trabajando-con-gtktreeview-en-python/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
