<?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>handyfloss &#187; gui</title>
	<atom:link href="http://handyfloss.net/tag/gui/feed/" rel="self" type="application/rss+xml" />
	<link>http://handyfloss.net</link>
	<description>Because FLOSS is handy, isn&#039;t it?</description>
	<lastBuildDate>Mon, 21 May 2012 06:44:32 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Speed up PyGTK and Cairo by reusing images</title>
		<link>http://handyfloss.net/2010.03/speed-up-pygtk-and-cairo-by-reusing-images/</link>
		<comments>http://handyfloss.net/2010.03/speed-up-pygtk-and-cairo-by-reusing-images/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 12:15:24 +0000</pubDate>
		<dc:creator>isilanes</dc:creator>
				<category><![CDATA[Free software and related beasts]]></category>
		<category><![CDATA[about me]]></category>
		<category><![CDATA[cairo]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[en]]></category>
		<category><![CDATA[FLOSS]]></category>
		<category><![CDATA[graphics]]></category>
		<category><![CDATA[gtk]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[neo freerunner]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://handyfloss.net/?p=1022</guid>
		<description><![CDATA[As you might have read in this blog, I own a Neo FreeRunner since one year ago. I have used it far less than I should have, mostly because it&#8217;s a wonderful toy, but a lousy phone. The hardware is fine, although externally quite a bit less sexy than other smartphones such as the iPhone. [...]]]></description>
			<content:encoded><![CDATA[<p>As you might have read in this blog, <a href="http://handyfloss.net/2009.01/first-impressions-on-a-neo-freerunner/">I own a Neo FreeRunner since one year ago</a>. I have used it far less than I should have, mostly because it&#8217;s a wonderful toy, but a lousy phone. The hardware is fine, although externally quite a bit less sexy than other smartphones such as the iPhone. The software, however, is not very mature. Being as open as it is, different Linux-centric distros have been developed for it, but I haven&#8217;t been able to find one that converts the Neo into an everyday use phone.</p>
<p>But let&#8217;s cut the rant, and stick to the issue: that the Neo is a nice playground for a computer geek. Following my desire to play, I installed <a href="http://wiki.openmoko.org/wiki/Debian">Debian</a> on it. Next, I decided to make some GUI programs for it, such a screen locker. I found <a href="http://wiki.openmoko.org/wiki/Zedlock">Zedlock</a>, a program written in Python, using GTK+ and Cairo. Basically, Zedlock paints a lock on the screen, and refuses to disappear until you paint a big &#8220;Z&#8221; on the screen with your finger. Well, that&#8217;s what it&#8217;s <i>supposed</i> to do, because the 0.1 version available at the Openmoko wiki is not functional. However, with Zedlock I found just what I wanted: a piece of software capable of doing really cool graphical things on the screen of my Neo, while being simple enough for me to understand.</p>
<p>Using Zedlock as a base, I am starting to have real fun programming GUIs, but a problem has quickly arisen: their response is slow. My programs, as all GUIs, draw an image on the screen, and react to tapping in certain places (that is, buttons) by doing things that require that the image on the screen be modified and repainted. This repainting, done as in Zedlock, is too slow. To speed things up, I googled the issue, and found <a href="http://stackoverflow.com/questions/2172525/why-is-my-simple-python-gtkcairo-program-running-so-slowly-stutteringly">a StackOverflow question</a> that suggested the obvious route: to cache the images. Let&#8217;s see how I did it, and how it turned out.</p>
<h2>Material</h2>
<p>You can download the three Python scripts, plus two sample PNGs, from: <a href="http://isilanes.org/pub/blog/pygtk/">http://isilanes.org/pub/blog/pygtk/</a>.</p>
<h2>Version 0</h2>
<p>You can download this program <a href="http://isilanes.org/pub/blog/pygtk/p0.py">here</a>. Its main loop follows:</p>
<pre class="brush:python">
C = Canvas()

# Main window:
C.win = gtk.Window()
C.win.set_default_size(C.width, C.height)

# Drawing area:
C.canvas = gtk.DrawingArea()
C.win.add(C.canvas)
C.canvas.connect('expose_event', C.expose_win)

C.regenerate_base()

# Repeat drawing of bg:
try:
  C.times = int(sys.argv[1])
except:
  C.times = 1

gobject.idle_add(C.regenerate_base)
C.win.show_all()

# Main loop:
gtk.main()
</pre>
<p>As you can see, it generates a GTK+ window (line 04), with a DrawingArea inside (line 08), and then executes the <code>regenerate_base()</code> function every time the main loop is idle (line 20). <code>Canvas()</code> is a class whose structure is not relevant for the discussion here. It basically holds all variables and relevant functions. The <code>regenerate_base()</code> function follows:</p>
<pre class="brush:python">
def regenerate_base(self):

    # Base Cairo Destination surface:
    self.DestSurf = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
    self.target   = cairo.Context(self.DestSurf)

    # Background:
    if self.bg == 'bg1.png':
      self.bg = 'bg2.png'
    else:
      self.bg = 'bg1.png'

    self.i += 1

    image       = cairo.ImageSurface.create_from_png(self.bg)
    buffer_surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
    buffer      = cairo.Context(buffer_surf)
    buffer.set_source_surface(image, 0,0)
    buffer.paint()

    self.target.set_source_surface(buffer_surf, 0, 0)
    self.target.paint()

    # Redraw interface:
    self.win.queue_draw()

    if self.i > self.times:
      sys.exit()

    return True
</pre>
<p>As you can see, it paints the whole window with a PNG file (lines 15-25), choosing alternately <code>bg1.png</code> and <code>bg2.png</code> each time it is called (lines 07-11). Since the re-painting is done every time the main event loop is idle, it just means that images are painted to screen as fast as possible. After a given amount of re-paintings, the script exits.</p>
<p>You can run the code above by placing two suitable PNGs (480&#215;640 pixels) in the same directory as the above code. If an integer argument is given to the script, it re-paints the window that many times, then exits (default, just once). You can time this script by executing, e.g.:</p>
<div class="codeblock">
% /usr/bin/time -f %e ./p0.py 1000
</div>
<h2>Version 1</h2>
<p>You can download this version <a href="http://isilanes.org/pub/blog/pygtk/p1.py">here</a>. </p>
<p>The first difference with <code>p1.py</code> is that the <code>regenerate_base()</code> function has been separated into the first part (<code>generate_base()</code>), which is executed only once at program startup (see below), and all the rest, which is executed every time the background is changed.</p>
<pre class="brush:python">
def generate_base(self):

    # Base Cairo Destination surface:
    self.DestSurf = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
    self.target   = cairo.Context(self.DestSurf)
</pre>
<p>The main difference, though, is that two new functions are introduced:</p>
<pre class="brush:python">
  def mk_iface(self):

    if not self.bg in self.buffers:
      self.buffers[self.bg] = self.generate_buffer(self.bg)

    self.target.set_source_surface(self.buffers[self.bg], 0, 0)
    self.target.paint()

  def generate_buffer(self, fn):

    image       = cairo.ImageSurface.create_from_png(fn)
    buffer_surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
    buffer      = cairo.Context(buffer_surf)
    buffer.set_source_surface(image, 0,0)
    buffer.paint()

    # Return buffer surface:
    return buffer_surf
</pre>
<p>The function <code>mk_iface()</code> is called within <code>regenerate_base()</code>, and draws the background. However, the actual generation of the background image (the Cairo surface) is done in the second function, <code>generate_buffer()</code>, and only happens once per each background (i.e., twice in total), because <code>mk_iface()</code> reuses previously generated (and cached) surfaces.</p>
<h2>Version 2</h2>
<p>You can download this version <a href="http://isilanes.org/pub/blog/pygtk/p2.py">here</a>.</p>
<p>The difference with Revision 1 is that I eliminated some apparently redundant procedures for creating surfaces upon surfaces. As a result, the <code>generate_base()</code> function disappears again. I get rid of the <code>DestSurf</code> and <code>C.target</code> variables, so the <code>mk_iface()</code> and <code>expose_win()</code> functions end up as follows:</p>
<pre class="brush:python">
  def mk_iface(self):

    if not self.bg in self.buffers:
      self.buffers[self.bg] = self.generate_buffer(self.bg)

    buffer = self.canvas.window.cairo_create()
    buffer.set_source_surface(self.buffers[self.bg],0,0)
    buffer.paint()

  def expose_win(self, drawing_area, event):

    nm = 'bg1.png'

    if not nm in self.buffers:
      self.buffers[nm] = self.generate_buffer(nm)

    ctx = drawing_area.window.cairo_create()
    ctx.set_source_surface(self.buffers[nm], 0, 0)
    ctx.paint()
</pre>
<p>A side effect is that I can get also rid of the forced redraws of <code>self.win.queue_draw()</code>.</p>
<h2>Results</h2>
<p>I have run the three versions above, varying the <code>C.times</code> variable, i.e., making a varying number of reprints. The command used (actually inside a script) would be something like the one mentioned above:</p>
<div class="codeblock">
% /usr/bin/time -f %e ./p0.py 1000
</div>
<p>The following table sumarizes the results for Flanders and Maude (see <a href="http://handyfloss.net/my-computers/">my computers</a>), a desktop P4 and my Neo FreeRunner, respectively. All times in seconds.</p>
<div align="center">
<table cellspacing="12">
<tr>
<td><i>Flanders</i></td>
</tr>
<tr>
<td><b>Repaints</b></td>
<td><b>Version 0</b></td>
<td><b>Version 1</b></td>
<td><b>Version 2</b></td>
</tr>
<tr>
<td>1</td>
<td>0.26</td>
<td>0.43</td>
<td>0.33</td>
</tr>
<tr>
<td>4</td>
<td>0.48</td>
<td>0.40</td>
<td>0.42</td>
</tr>
<tr>
<td>16</td>
<td>0.99</td>
<td>0.43</td>
<td>0.40</td>
</tr>
<tr>
<td>64</td>
<td>2.77</td>
<td>0.76</td>
<td>0.56</td>
</tr>
<tr>
<td>256</td>
<td>9.09</td>
<td>1.75</td>
<td>1.15</td>
</tr>
<tr>
<td>1024</td>
<td>37.03</td>
<td>6.26</td>
<td>3.44</td>
</tr>
</table>
</div>
<div align="center">
<table cellspacing="12">
<tr>
<td><i>Maude</i></td>
</tr>
<tr>
<td><b>Repaints</b></td>
<td><b>Version 0</b></td>
<td><b>Version 1</b></td>
<td><b>Version 2</b></td>
</tr>
<tr>
<td>1</td>
<td>4.17</td>
<td>4.70</td>
<td>5.22</td>
</tr>
<tr>
<td>4</td>
<td>8.16</td>
<td>6.35</td>
<td>6.41</td>
</tr>
<tr>
<td>16</td>
<td>21.58</td>
<td>14.17</td>
<td>12.28</td>
</tr>
<tr>
<td>64</td>
<td>75.14</td>
<td>44.43</td>
<td>35.76</td>
</tr>
<tr>
<td>256</td>
<td>288.11</td>
<td>165.58</td>
<td>129.56</td>
</tr>
<tr>
<td>512</td>
<td>561.78</td>
<td>336.58</td>
<td>254.73</td>
</tr>
</table>
</div>
<p>Data in the tables above has been fitted to a linear equation, of the form <i>t = A + B n</i>, where <i>n</i> is the number of repaints. In that equation, parameter <i>A</i> would represent a startup time, whereas <i>B</i> represents the time taken by each repaint. The linear fits are quite good, and the values for the parameters are given in the following tables (units are milliseconds, and milliseconds/repaint):</p>
<div align="center">
<table cellspacing="12">
<tr>
<td><i>Flanders</i></td>
</tr>
<tr>
<td><b>Parameter</b></td>
<td><b>Version 0</b></td>
<td><b>Version 1</b></td>
<td><b>Version 2</b></td>
</tr>
<tr>
<td><i>A</i></td>
<td>291</td>
<td>366</td>
<td>366</td>
</tr>
<tr>
<td><i>B</i></td>
<td>36</td>
<td>6</td>
<td>3</td>
</tr>
</table>
</div>
<div align="center">
<table cellspacing="12">
<tr>
<td><i>Maude</i></td>
</tr>
<tr>
<td><b>Parameter</b></td>
<td><b>Version 0</b></td>
<td><b>Version 1</b></td>
<td><b>Version 2</b></td>
</tr>
<tr>
<td><i>A</i></td>
<td>453</td>
<td>3218</td>
<td>4530</td>
</tr>
<tr>
<td><i>B</i></td>
<td>1092</td>
<td>648</td>
<td>487</td>
</tr>
</table>
</div>
<p>Darn it! I have mixed feelings for the results. In the desktop computer (Flanders), the gains are huge, but hardly noticeable. Cacheing the images (Version 1) makes for a 6x speedup, whereas Version 2 gives another twofold increase in speed (a total of 12x speedup!). However, from a user&#8217;s point of view, a 36 ms refresh is just as immediate as a 6 ms refresh.</p>
<p>On the other hand, on the Neo, the gains are less spectacular: the total gain in speed for Version 2 is a mere 2x. Anyway, half-a-second repaints instead of one-second ones <i>are</i> noticeable, so there&#8217;s that.</p>
<p>And at least I had fun and learned in the process! :^)</p>
]]></content:encoded>
			<wfw:commentRss>http://handyfloss.net/2010.03/speed-up-pygtk-and-cairo-by-reusing-images/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Amarok WTF</title>
		<link>http://handyfloss.net/2010.01/amarok-wtf/</link>
		<comments>http://handyfloss.net/2010.01/amarok-wtf/#comments</comments>
		<pubDate>Mon, 18 Jan 2010 09:34:38 +0000</pubDate>
		<dc:creator>isilanes</dc:creator>
				<category><![CDATA[Free software and related beasts]]></category>
		<category><![CDATA[about me]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[en]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[interface]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[wtf]]></category>

		<guid isPermaLink="false">http://handyfloss.net/?p=983</guid>
		<description><![CDATA[Warning: the following rant could be caused by my idiocy, more than by Amarok&#8217;s fault. See comments. I have been using Amarok as music player even since I had first contact with it. I was really delighted with its capabilities, and everything was intuitive and useful in its UI. That was until version 1.4.x. Version [...]]]></description>
			<content:encoded><![CDATA[<p><i>Warning: the following rant could be caused by my idiocy, more than by Amarok&#8217;s fault. See comments.</i></p>
<p>I have been using <a href="http://en.wikipedia.org/wiki/Amarok (software)">Amarok</a> as music player even since I had first contact with it. I was really delighted with its capabilities, and everything was intuitive and useful in its <a href="http://en.wikipedia.org/wiki/User Interface">UI</a>. That was until version 1.4.x.</p>
<p>Version 2.0 was an almost complete rewrite of the code, and as such many things changed. The UI suffered a large redesign, in my opinion for worse&#8230; but that&#8217;s just an opinion. There are, however, other issues that are facts, not opinion. Amarok 2.0 lacked many of the features of Amarok 1.x, as the developers themselves admitted (not much room to deny). Fine, I have no problem with that. It is understandable: until version 2.x things will not settle down. The only problem is that Linux distros (at least Ubuntu) adopted Amarok 2.0 almost immediately, leaving us users with a broken toy. Not nice.</p>
<p>My latest gripe with Amarok? I run Ubuntu 9.10 at work (Amarok 2.2.0), and latest Arch at home. In the latter, I just updated Amarok 2.2.1 to 2.2.2 in the weekend (Arch is much more up to date than Ubuntu, since it&#8217;s based in almost bleeding-edge rolling releases). Well, unlike Amarok 2.2.1 before (or Amarok 2.2.0 at work), the new Amarok 2.2.2 does not have an option for <b>random play</b>. Yes, you read correctly. There is no way I know of to avoid playing all the songs in the playlist in the exact order (in principle, alphabetical) they are laid on. In older versions, you could play songs <i>or</i> albums randomly. With 2.2.2, they lost this capability. Amazing feature regression, if you ask me.</p>
]]></content:encoded>
			<wfw:commentRss>http://handyfloss.net/2010.01/amarok-wtf/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Changing font style in PyGTK ComboBox</title>
		<link>http://handyfloss.net/2009.06/changing-font-style-in-pygtk-combobox/</link>
		<comments>http://handyfloss.net/2009.06/changing-font-style-in-pygtk-combobox/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 10:34:16 +0000</pubDate>
		<dc:creator>isilanes</dc:creator>
				<category><![CDATA[Free software and related beasts]]></category>
		<category><![CDATA[about me]]></category>
		<category><![CDATA[en]]></category>
		<category><![CDATA[FLOSS]]></category>
		<category><![CDATA[glade]]></category>
		<category><![CDATA[gnome]]></category>
		<category><![CDATA[gtk]]></category>
		<category><![CDATA[gui]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[interface]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[software]]></category>

		<guid isPermaLink="false">http://handyfloss.net/?p=762</guid>
		<description><![CDATA[I am using the Glade Interface Designer to produce (very) small (and simple) graphical apps for my Neo FreeRunner. I produce the graphical layout in the form of an XML file (using Glade), then load this XML from a PyGTK program. The thing is some defaults are not really usable for a device such as [...]]]></description>
			<content:encoded><![CDATA[<p>I am using the <a href="http://en.wikipedia.org/wiki/Glade Interface Designer">Glade Interface Designer</a> to produce (very) small (and simple) graphical apps for my <a href="http://en.wikipedia.org/wiki/Neo FreeRunner">Neo FreeRunner</a>. I produce the graphical layout in the form of an <a href="http://en.wikipedia.org/wiki/XML">XML</a> file (using Glade), then load this XML from a <a href="http://en.wikipedia.org/wiki/PyGTK">PyGTK</a> program.</p>
<p>The thing is some defaults are not really usable for a device such as the NFR. For example, default fonts are in general too small for the tiny screen of the Neo, which favors apps with only a few, big and shinny buttons. In the case of Label widgets, you can use <a href="http://library.gnome.org/devel/pango/unstable/PangoMarkupFormat.html">Pango markup format</a> with the <tt>set_markup</tt> method, as follows:</p>
<div class="codeblock">
<pre>
mylabel  = self.glade.get_widget('label1')
txt  = '&lt;span font_size="80000" color="red"&gt;%s&lt;/span&gt;' % (text_string)
mylabel.set_markup(txt)
</pre>
</div>
<p>However, for other widgets it is not so evident. For example, in ComboBoxes (buttons with a drop-down list), you can&#8217;t put in the item list anything other than strings, which are displayed literally (markup is not interpreted). Moreover, CBs do not have a &#8220;<tt>set_font_style</tt>&#8221; method, or anything similar.</p>
<p>Searching the web did not provide immediate results, but I managed to find <a href="http://eccentric.cx/misc/pygtk/pygtkfaq.html#4.1">this FAQ item</a> at eccentric.cx. I quote:</p>
<blockquote><p>
<b>4.1.581 How do I change font properties on gtk.Labels and other widgets?</b><br />
Easy:</p>
<pre>
 label = gtk.Label("MyLabel")
 label.modify_font(pango.FontDescription("sans 48"))
</pre>
<p>This method applies to all widgets that use text, so you can change the text of gtk.Entry and other widgets in the same manner.</p>
<p>Note that, some widgets are only containers for others, like gtk.Button. For those you&#8217;d have to get the child widget. For a gtk.Button do this:</p>
<pre>
  if button.get_use_stock():
     label = button.child.get_children()[1]
  elif isinstance(button.child, gtk.Label):
     label = button.child
  else:
     raise ValueError("button does not have a label")
</pre>
<p>Last changed on Thu Sep 1 14:46:30 2005 by Johan Dahlin (johan-at-gnome-org)
</p></blockquote>
<p>In the case of a CB, we have to pick its child (which is the list itself), and modify it thusly:</p>
<div class="codeblock">
<pre>
cbox = self.glade.get_widget("CBlist")
cblist  = cbox.child
cblist.modify_font(pango.FontDescription("sans 32"))
</pre>
</div>
<p>In my examples above, a class has been created in the script beforehand, and it binds to the Glade XML:</p>
<div class="codeblock">
<pre>
class whatever:

  def __init__(self):

    #Set the Glade file
    self.glade    = gtk.glade.XML(<i>gladefile</i>)
    self.glade.signal_autoconnect(self)
</pre>
</div>
<p>Of course, the <tt>CBlist</tt> and <tt>MyLabel</tt> mentioned in my code are the appropriate widget names defined in that XML.</p>
]]></content:encoded>
			<wfw:commentRss>http://handyfloss.net/2009.06/changing-font-style-in-pygtk-combobox/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

