{"id":24,"date":"2007-06-20T23:08:41","date_gmt":"2007-06-20T21:08:41","guid":{"rendered":"http:\/\/blog.rastersoft.com\/index.php\/2007\/06\/20\/trabajando-con-pycairo-y-gtk\/"},"modified":"2015-08-15T18:10:32","modified_gmt":"2015-08-15T18:10:32","slug":"trabajando-con-pycairo-y-gtk","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=24","title":{"rendered":"Trabajando con PyCairo y GTK"},"content":{"rendered":"<p>En la nueva versi\u00f3n de DeVeDe (que espero sacar en un par de d\u00edas) he tenido que trabajar con <a title=\"http:\/\/cairographics.org\/\" href=\"http:\/\/cairographics.org\/\" target=\"_blank\">Cairo<\/a> para poder generar men\u00fas para el disco. La informaci\u00f3n la saqu\u00e9 de <a href=\"http:\/\/www.tortall.net\/mu\/wiki\/CairoTutorial\">este tutorial sobre PyCairo<\/a> (en ingl\u00e9s), en donde viene mucha informaci\u00f3n interesante. Sin embargo, faltaba un detalle importante: como mostrar el dibujo hecho en cairo en una ventana GTK.<\/p>\n<p>En efecto, esto era fundamental porque el usuario tiene que poder ver el resultado final del men\u00fa antes de generar el disco (para, por ejemplo, asegurarse de que los t\u00edtulos no son demasiado largos), y para eso necesito pintarlo en una ventana y no s\u00f3lo volcarlo a un PNG en disco. Despu\u00e9s de mucho buscar y de hacer varias pruebas encontr\u00e9 por fin como hacerlo.<\/p>\n<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\u00e1 donde hagamos el pintado. Para ello, lo primero que tenemos que hacer es conseguir un contexto de Cairo para dicho widget. As\u00ed, si el widget se llama MiDibujo, y arbol es el objeto Glade con nuestras ventanas, har\u00edamos:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\"># asignamos el callback a nuestra funcion de repintado\r\nw=arbol.get_widget(\"MiDibujo\")\r\nw.connect(\"expose-event\",repintado)\r\n\r\n# y definimos el callback. Tiene dos argumentos\r\ndef repintado(dwidget,evento):\r\n    cr=dwidget.window.cairo_create()\r\n    [funciones y m\u00e9todos de Cairo para pintar]<\/pre>\n<\/div>\n<p>Y ya podremos usar ese contexto para pintar y trazar todo lo que queramos.<\/p>\n<p>Sin embargo, si el dibujo es est\u00e1tico 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\u00f3lo tenemos que crear primero una superficie de Cairo y obtener un contexto para ella:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">sf=cairo.ImageSurface(cairo.FORMAT_ARGB32,ResX,Resy)\r\ncr1=cairo.Context(sf)<\/pre>\n<\/div>\n<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 \u00e9l desde nuestro callback (una variable global es la soluci\u00f3n m\u00e1s r\u00e1pida y menos elegante). All\u00ed s\u00f3lo tenemos que usar los m\u00e9todos set_source_surface y paint para transferir la superficie a nuestro widget GtkDrawingArea, ahorrando mucho tiempo de proceso.<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">def repintado(dwidget,evento):\r\n    cr=dwidget.window.cairo_create()\r\n \u00a0\u00a0 # sf es la que pintamos en el paso anterior\r\n\u00a0\u00a0\u00a0 cr.set_source_surface(sf)\r\n\r\n    # transferimos la superficie con el dibujo al Widget\r\n    cr.paint()<\/pre>\n<\/div>\n<p>Este truco nos permite tambi\u00e9n 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\u00f1o X,Y, usando como fondo un PNG de tama\u00f1o cualquiera, pero de manera que \u00e9ste se expanda o encoja para ocupar exactamente la superficie de nuestra imagen final. S\u00f3lo tendr\u00edamos que hacer:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">sf_base= cairo.ImageSurface.create_from_png(\"imagen.png\")\r\nsf_final=cairo.ImageSurface(cairo.FORMAT_ARGB32,X,Y)\r\n\r\n# creamos un contexto para pintar\r\ncr_final=cairo.Context(sf_final)\r\n\r\n# cogemos el ancho y alto de la imagen.\r\n# Importante: en coma flotante\r\nxbase=float(sf_base.get_width())\r\nybase=float(sf_base.get_height())\r\n\r\n# aplicamos el escalado para que la imagen de fondo\r\n# pase a tener el mismo tama\u00f1o que la imagen final\r\ncr_final.scale(float(X)\/xbase,float(Y)\/ybase)\r\n\r\n# y estampamos la imagen de origen en la superficie final\r\ncr_final.set_source_surface(sf_base)\r\ncr_final.paint()\r\n\r\n# podemos usar el m\u00e9todo identity para\r\n#restaurar la escala a 1:1\r\ncr.identity_matrix()<\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>En la nueva versi\u00f3n de DeVeDe (que espero sacar en un par de d\u00edas) he tenido que trabajar con Cairo para poder generar men\u00fas para el disco. La informaci\u00f3n la saqu\u00e9 de este tutorial sobre PyCairo (en ingl\u00e9s), en donde viene mucha informaci\u00f3n interesante. Sin embargo, faltaba un detalle importante: como mostrar el dibujo hecho &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=24\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">Trabajando con PyCairo y GTK<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,7],"tags":[],"class_list":["post-24","post","type-post","status-publish","format-standard","hentry","category-programacion","category-tutoriales"],"_links":{"self":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/24","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=24"}],"version-history":[{"count":2,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/24\/revisions"}],"predecessor-version":[{"id":1905,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/24\/revisions\/1905"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=24"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=24"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=24"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}