<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7970464356958190643</id><updated>2012-01-26T10:59:32.854+01:00</updated><category term='Python'/><category term='QuickCast'/><category term='KDE'/><category term='Fedora'/><category term='N9'/><category term='gphoto'/><category term='Nokia'/><category term='C'/><category term='plsparser'/><category term='QtQuick'/><category term='QML'/><category term='Unittesting'/><category term='digikam'/><category term='Linux video'/><category term='WebKit'/><category term='gmonitor'/><category term='pyshoutcast'/><category term='C++'/><category term='ctypes'/><category term='Quick'/><category term='Kubuntu'/><category term='Arduino'/><category term='ElementTree'/><category term='tvmatchen'/><category term='FUSE'/><category term='Linux'/><category term='Conference'/><category term='QtCreator'/><category term='Qt'/><category term='Ubuntu'/><category term='feedparser'/><category term='JavaScript'/><category term='gmail'/><category term='profiling'/><category term='QtCast'/><title type='text'>PySnippet</title><subtitle type='html'>Python snippets and Linux related stuff</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>51</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6720007096158727042</id><published>2011-10-12T10:49:00.000+02:00</published><updated>2011-10-12T10:50:40.099+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QtQuick'/><category scheme='http://www.blogger.com/atom/ns#' term='QML'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><title type='text'>Open links from Text-elements in QtQuick</title><content type='html'>I just finished yet another blog post on my companies blog site. It's about opening external application when clicking on links in QML Text-element.&lt;br /&gt;&lt;br /&gt;Read about it &lt;a href="http://blog.jayway.com/2011/10/12/open-links-from-text-elements-in-qtquick/"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6720007096158727042?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/6720007096158727042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/10/open-links-from-text-elements-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6720007096158727042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6720007096158727042'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/10/open-links-from-text-elements-in.html' title='Open links from Text-elements in QtQuick'/><author><name>Mario</name><uri>http://www.blogger.com/profile/06895806907930156083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-53830285519472803</id><published>2011-09-22T15:34:00.000+02:00</published><updated>2011-09-22T15:35:30.347+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C'/><category scheme='http://www.blogger.com/atom/ns#' term='QtCreator'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Arduino'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Using QtCreator for Arduino development</title><content type='html'>I just finished a blog post on my company's blog site about using QtCreator as development environment for the Arduino board.&lt;br /&gt;&lt;br /&gt;Please head over &lt;a href="http://blog.jayway.com/2011/09/22/using-qtcreator-for-arduino-development/"&gt;here&lt;/a&gt; to read all about it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-53830285519472803?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/53830285519472803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/09/using-qtcreator-for-arduino-development.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/53830285519472803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/53830285519472803'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/09/using-qtcreator-for-arduino-development.html' title='Using QtCreator for Arduino development'/><author><name>Mario</name><uri>http://www.blogger.com/profile/06895806907930156083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-3897541595493390454</id><published>2011-09-03T10:39:00.000+02:00</published><updated>2011-09-03T10:39:51.166+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubuntu'/><title type='text'>System 76: Linux Devices and Better Business Values</title><content type='html'>&lt;style type="text/css"&gt;	&lt;!--		@page { margin: 2cm }		P { margin-bottom: 0.21cm }		A:link { color: #0000ff }	--&gt;	&lt;/style&gt;&lt;br /&gt;&lt;div style="line-height: 100%; margin-bottom: 0cm;"&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;i&gt;Guest post by Nadia Jones. Look at the end of the post for more info about Nadia.&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt; &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;Tothe average computer user there are two types of operating systems:Apple and Microsoft. While for the mass market this basically standstrue, computer programmers, developers, and techies also recognizeLinux as one of the great computer operating systems. Though Linux isnot as widespread or popular as Apple or Microsoft, it is just aswell-respected (if not more so). There are several reasons that Linuxjust hasn't become a mass success like other operating systems have.While there are very few companies that produce computers with theLinux OS already installed, &lt;/span&gt;&lt;/span&gt;&lt;span style="color: blue;"&gt;&lt;u&gt;&lt;a href="http://www.system76.com/"&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;System76does&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0cm;"&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;Notonly is System76 one of the few companies that sells laptops withLinux preinstalled, they are one of the &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;i&gt;only&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;companies that sells them at reasonably competitive prices. No doubt,these computers will likely cost more than your average Dell becauseSystem76 is such a small outfit, but the prices remain extremelyreasonable. Selling laptops, desktops, and servers all outfitted withUbuntu, System76 is a firm believer in Linux as an operating system.As System76 explains, Linux offers stability, security, and the"freedom to use the software you like, the way you like to useit". One of the other big perks about using Linux and all of itsmachines is that they are completely free. System 76 will provide youwith preinstalled applications such as open office suite, Totem andXine movie players, GIMP image manipulation, and several others. &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0cm;"&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;Moreover,System76 is unique in that they actually do something with yourdevice before they send it to you. Rather than simply installingUbuntu on the laptop you order and then sending it to you, theSystem76 people give you their own drivers and special configurationsto make your machine run as smoothly and flawlessly as possible.Along with special attention paid to each individual product theyship out, System76 also pays special attention to each of theircustomers. Because they are such a small company, they are able tooffer exceptional customer service. If there is any sort of softwareor hardware issue, you can email the company and you will receive apersonal email back from someone involved with building yourcomputer. &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0cm;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0cm;"&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;Whilenot everything about these computers and this company is going towork impeccably, their focus on customer service and better businessis noteworthy. System76 offers a unique system with customizableapproaches. Furthermore, System76 is dedicated to eco-friendlybusiness values, making them not only reliable, but also current andforward thinking. For Linux enthusiasts and newbies alike, System76is certainly worth a look-see. &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0.49cm; margin-top: 0.49cm;"&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;u&gt;&lt;b&gt;AuthorBio:&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="line-height: 100%; margin-bottom: 0.49cm; margin-top: 0.49cm;"&gt;&lt;span style="font-family: Times New Roman,serif;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;Thisis a guest post by &lt;/span&gt;&lt;span style="color: black;"&gt;&lt;b&gt;Nadia Jones&lt;/b&gt;&lt;/span&gt;&lt;span style="color: black;"&gt;who blogs at &lt;/span&gt;&lt;span style="color: blue;"&gt;&lt;u&gt;&lt;a href="http://www.onlinecollege.org/"&gt;onlinecollege&lt;/a&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style="color: black;"&gt; about education,college, student, teacher, money saving, movie related topics. Youcan reach her at nadia.jones5 @ gmail.com.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-3897541595493390454?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/3897541595493390454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/09/system-76-linux-devices-and-better.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3897541595493390454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3897541595493390454'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/09/system-76-linux-devices-and-better.html' title='System 76: Linux Devices and Better Business Values'/><author><name>Mario</name><uri>http://www.blogger.com/profile/06895806907930156083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4424421555938541988</id><published>2011-08-13T09:26:00.010+02:00</published><updated>2011-08-13T09:48:06.700+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QtQuick'/><category scheme='http://www.blogger.com/atom/ns#' term='Nokia'/><category scheme='http://www.blogger.com/atom/ns#' term='tvmatchen'/><category scheme='http://www.blogger.com/atom/ns#' term='QML'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='N9'/><title type='text'>tvmatchen for Nokia N9</title><content type='html'>I just released a video of my port of tvmatchen for Nokia devices (Symbian^3 and now N9). Tvmatchen is a tv-guide application for live sports and is only available in Swedish.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://nokiagadgets.com/"&gt;NokiaGadgets&lt;/a&gt; actually wrote a couple of lines about the application, even though the app is in Swedish. The &lt;span style="font-style: italic;"&gt;review&lt;/span&gt; was very positive and the author wants to see more apps looking like this one. It does feel good in a coders heart when someone writes something positive about your app :)&lt;br /&gt;&lt;br /&gt;Link to the &lt;a href="http://nokiagadgets.com/2011/08/12/how-these-new-qt-apps-should-look-like-tvmatchen-for-n9n950swe/"&gt;article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And of course the video&lt;br /&gt;&lt;iframe src="http://www.youtube.com/embed/AN3Y2n2h9Xk" allowfullscreen="" frameborder="0" height="510" width="640"&gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4424421555938541988?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4424421555938541988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/08/tvmatchen-for-nokia-n9.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4424421555938541988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4424421555938541988'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/08/tvmatchen-for-nokia-n9.html' title='tvmatchen for Nokia N9'/><author><name>Mario</name><uri>http://www.blogger.com/profile/06895806907930156083</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/AN3Y2n2h9Xk/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-5036044057877498175</id><published>2011-06-22T09:13:00.002+02:00</published><updated>2011-06-22T09:13:43.448+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Nokia'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='N9'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Nokia N9 and Python</title><content type='html'>Yet another great day to be a python coder! The new Harmattan/MeeGo device, Nokia N9, will be packed with support for Python!&lt;br /&gt;&lt;br /&gt;For more info, look &lt;a href="http://wiki.meego.com/Harmattan_Python"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Yes, it seems that you can publish your python apps on ovi... Nokia Store too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-5036044057877498175?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/5036044057877498175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/06/nokia-n9-and-python.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/5036044057877498175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/5036044057877498175'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/06/nokia-n9-and-python.html' title='Nokia N9 and Python'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-2385328614801753947</id><published>2011-06-19T14:44:00.000+02:00</published><updated>2011-06-19T14:44:43.570+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QtQuick'/><category scheme='http://www.blogger.com/atom/ns#' term='QML'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>QtQuick - From Bling Bling To Blink Blink</title><content type='html'>&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;Probably most of you already know that QtQuick is used to create amazing blingalicous UIs without too much effort. The declarative QML language makes it very easy to create object instances and setup property bindings, animation, states etc. Lately I’ve been tinkering with my &lt;a href="http://www.blogger.com/goog_7803471"&gt;Velleman &lt;/a&gt;&lt;/span&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;&lt;a href="http://www.velleman.eu/distributor/products/view/?country=be&amp;amp;lang=en&amp;amp;id=351346"&gt;K8055&lt;/a&gt; USB experiment board and wanted to create a nice UI to control the board. &lt;/span&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;It didn’t take too long until I was thinking a little bit out side of the box and started experimenting with controlling the board with QtQuick but without a UI. I created a couple of QML components on the C++ side to control the input/outputs of the board and quickly realized the possibilities QML provides even for non-UI applications.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;Basically what I did was to create a declarative engine and not a declarative view. By doing this you can have a QML file interpreted and access to the object instances created by the engine from within your C++ code. I see a lot of potential and possibilities, one thing that comes to my mind straight away is to utilize QML as a &lt;i&gt;&lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt;&lt;/i&gt;&lt;/span&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt; &lt;/span&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;container :)&lt;/span&gt;&lt;br /&gt;&lt;pre class="cpp" name="code"&gt;QDeclarativeEngine *engine = new QDeclarativeEngine;&lt;br /&gt;QDeclarativeComponent component(engine, QUrl("qrc:/example1.qml"));&lt;br /&gt;Board *board = qobject_cast&amp;lt;board *&amp;gt;(component.create());&lt;/pre&gt;&lt;div style="text-align: center;"&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;&lt;i&gt;&lt;span style="font-size: x-small;"&gt;Example code to interpret a QML file&lt;/span&gt;&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;/div&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;The source code is available at &lt;a href="http://projects.developer.nokia.com/k8055tinker"&gt;k8055tinker&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;Check out the video below which demonstrates my code:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/wYD-he0ywew/0.jpg" height="266" width="320"&gt;&lt;param name="movie" value="http://www.youtube.com/v/wYD-he0ywew?f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="320" height="266"  src="http://www.youtube.com/v/wYD-he0ywew?f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;span id="internal-source-marker_0.6282135008404465" style="background-color: transparent; color: black; font-family: Arial; font-size: 11pt; font-style: normal; font-weight: normal; text-decoration: none; vertical-align: baseline;"&gt;&lt;i&gt;I've also tagged this post with &lt;/i&gt;&lt;i&gt;Python because it can be of interest for the planet python readers since this can also be accomplished with &lt;a href="http://www.pyside.org/"&gt;PySide&lt;/a&gt;. &lt;/i&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-2385328614801753947?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/2385328614801753947/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/06/qtquick-from-bling-bling-to-blink-blink.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/2385328614801753947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/2385328614801753947'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/06/qtquick-from-bling-bling-to-blink-blink.html' title='QtQuick - From Bling Bling To Blink Blink'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-1828537221416249640</id><published>2011-05-21T12:42:00.000+02:00</published><updated>2011-05-21T12:42:04.992+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Nokia'/><category scheme='http://www.blogger.com/atom/ns#' term='QML'/><category scheme='http://www.blogger.com/atom/ns#' term='Quick'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>I wish that I had Jessie's girl...</title><content type='html'>&lt;a href="http://www.youtube.com/watch?v=qYkbTyHXwbs"&gt;Rick Springfield&lt;/a&gt; had a great hit with a song called &lt;i&gt;Jessie's Girl&lt;/i&gt; and the same track is now used for the &lt;a href="http://mynokiablog.com/2011/05/17/video-holy-crap-its-the-nokia-n9-or-n950-meego-phone-teaser-advert/"&gt;Nokia N9 teaser ad&lt;/a&gt; which popped up a couple of days ago.&lt;br /&gt; &lt;br /&gt;I really hope this is a MeeGo device, or to be honest, I just hope it's a Linux + Qt/QtQuick enabled device with some MeeGo compliance.&lt;br /&gt;&lt;br /&gt;I've had quite a lot of fun with QtQuick lately (both for desktop and my N8) and I really enjoy using it. It just open so much possibilities and lets you be creative.&lt;br /&gt;&lt;br /&gt;Another thing that would be awesome is if Nokia actually puts &lt;a href="http://www.pyside.org/"&gt;PySide&lt;/a&gt;, the Nokia sponsored Qt-bindings for Python, on the handset. Having Python as a rapid back-end language combined with QtQuick for the view is just too sweet :)&lt;br /&gt;&lt;br /&gt;Grr... I hate not knowing, but I just have to wait and see.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-1828537221416249640?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/1828537221416249640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/05/i-wish-that-i-had-jessies-girl.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1828537221416249640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1828537221416249640'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/05/i-wish-that-i-had-jessies-girl.html' title='I wish that I had Jessie&apos;s girl...'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-1715740599612114</id><published>2011-04-20T15:58:00.000+02:00</published><updated>2011-04-20T15:58:52.868+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Nokia'/><category scheme='http://www.blogger.com/atom/ns#' term='QML'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Quick'/><title type='text'>I won the "Travelling salesman" competition!</title><content type='html'>Nokia held a mobile app competition at its Nordic blog which I won!&lt;br /&gt;&lt;br /&gt;For more info, look &lt;a href="http://blogs.nokia.com/nordicblog/news/mario-boikov-%E2%80%93-winner-of-the-traveling-salesman-competition/#date=2011-04-20,mode=month"&gt;here&lt;/a&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-1715740599612114?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/1715740599612114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/04/i-won-travelling-salesman-competition.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1715740599612114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1715740599612114'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/04/i-won-travelling-salesman-competition.html' title='I won the &quot;Travelling salesman&quot; competition!'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6881597626599772678</id><published>2011-04-13T13:08:00.000+02:00</published><updated>2011-04-13T13:08:03.105+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='KDE'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux video'/><title type='text'>Tips! Kdenlive - A great video editor</title><content type='html'>I've just started to take a look on how to editing videos and was searching the net to find some tools for Linux.&lt;br /&gt;&lt;br /&gt;After trying a couple I found out about the &lt;a href="http://www.kde.org/"&gt;KDE&lt;/a&gt; application &lt;a href="http://kdenlive.org/"&gt;Kdenlive&lt;/a&gt; from some blog post and I must say that this tools looks very promising. It did crash once when I tried it out but I can live with that since I'm not a heavy user of video editing. I just need something for creating youtube videos occasionally.&lt;br /&gt;&lt;br /&gt;In addition to the great app there's also a bunch of video tutorials &lt;a href="http://www.kdenlive.org/tutorial"&gt;here&lt;/a&gt; to help you getting started.&lt;br /&gt;&lt;br /&gt;So if you're in the same position as me I think you should take a look at Kdenlive&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6881597626599772678?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/6881597626599772678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/04/tips-kdenlive-great-video-editor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6881597626599772678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6881597626599772678'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/04/tips-kdenlive-great-video-editor.html' title='Tips! Kdenlive - A great video editor'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-8126162275600574399</id><published>2011-03-29T09:38:00.000+02:00</published><updated>2011-03-29T09:38:02.072+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QML'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Quick'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>PySide and QML</title><content type='html'>It has been out there for a while but I haven't had time to try it out, PySide 1.0 with QML support!&lt;br /&gt;&lt;br /&gt;This really gives you a über rapid app development environment. The power of Python combined with the awesomeness of the declarative language QML from the Qt framework.&lt;br /&gt;&lt;br /&gt;Take a look and feel free to play with the example below:&lt;pre name="code" class="python"&gt;&lt;br /&gt;from PySide import QtCore&lt;br /&gt;from PySide import QtGui&lt;br /&gt;from PySide import QtDeclarative&lt;br /&gt;&lt;br /&gt;class Message(QtDeclarative.QDeclarativeItem):&lt;br /&gt;&lt;br /&gt;    messageChanged = QtCore.Signal()&lt;br /&gt;&lt;br /&gt;    def __init__(self, parent = None):&lt;br /&gt;        QtDeclarative.QDeclarativeItem.__init__(self, parent)&lt;br /&gt;        self._msg = u''&lt;br /&gt;&lt;br /&gt;    def getMessage(self):&lt;br /&gt;        return self._msg&lt;br /&gt;&lt;br /&gt;    def setMessage(self, value):&lt;br /&gt;        if self._msg != value:&lt;br /&gt;            print "Setting message property to", value&lt;br /&gt;            self._msg = value&lt;br /&gt;            self.messageChanged.emit()&lt;br /&gt;        else:&lt;br /&gt;            print "Message property already set to", value&lt;br /&gt;&lt;br /&gt;    message = QtCore.Property(unicode, getMessage, setMessage, notify=messageChanged)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    app = QtGui.QApplication([])&lt;br /&gt;&lt;br /&gt;    QtDeclarative.qmlRegisterType(Message, "utils", 1, 0, "Message")&lt;br /&gt;&lt;br /&gt;    win = QtDeclarative.QDeclarativeView()&lt;br /&gt;    win.setSource("main.qml")&lt;br /&gt;    win.setWindowTitle("Hello World")&lt;br /&gt;    win.setResizeMode(QtDeclarative.QDeclarativeView.SizeRootObjectToView)&lt;br /&gt;&lt;br /&gt;    win.show()&lt;br /&gt;    app.exec_()&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;&lt;pre name="code" class="javascript"&gt;&lt;br /&gt;import Qt 4.7 // or QtQuick 1.0 if applicable&lt;br /&gt;&lt;br /&gt;import utils 1.0&lt;br /&gt;&lt;br /&gt;Rectangle {&lt;br /&gt;    id: main&lt;br /&gt;&lt;br /&gt;    signal clicked&lt;br /&gt;&lt;br /&gt;    color: "black"&lt;br /&gt;    width: 360; height: 360&lt;br /&gt;&lt;br /&gt;    Message {&lt;br /&gt;        id: msg&lt;br /&gt;&lt;br /&gt;        message: "Click Me!"&lt;br /&gt;        onMessageChanged: label.font.pixelSize = 40&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    Text {&lt;br /&gt;        id: label&lt;br /&gt;&lt;br /&gt;        anchors.centerIn: parent&lt;br /&gt;&lt;br /&gt;        text: msg.message&lt;br /&gt;        color: "white"&lt;br /&gt;        font.pixelSize: 25&lt;br /&gt;&lt;br /&gt; Behavior on font.pixelSize {&lt;br /&gt;            NumberAnimation { duration: 800; easing.type: Easing.OutBounce }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        MouseArea {&lt;br /&gt;            anchors.fill: parent&lt;br /&gt;            onClicked: msg.message = "Hello World"&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;For more information point your browser to &lt;a href="http://www.pyside.org/"&gt;http://www.pyside.org&lt;/a&gt; and download the package for your OS.&lt;br /&gt;&lt;br /&gt;You'll also find useful information at &lt;a href="http://developer.qt.nokia.com/wiki/Category:LanguageBindings::PySide"&gt;http://developer.qt.nokia.com/wiki/Category:LanguageBindings::PySide&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Happy hacking!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-8126162275600574399?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/8126162275600574399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/03/pyside-and-qml.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8126162275600574399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8126162275600574399'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/03/pyside-and-qml.html' title='PySide and QML'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-5108547008651555036</id><published>2011-02-01T20:24:00.000+01:00</published><updated>2011-02-01T20:24:32.079+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Nokia'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><title type='text'>Guest blog post at Nokia Nordic Blog</title><content type='html'>I've been invited to write a guest blog post at Nokia Nordic blog. Please check it out if you're interested in Qt-development.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blogs.nokia.com/nordicblog/news/meet-mario-the-man-behind-the-qt-app-tvmatchen/#date=2011-02-01,mode=month"&gt;http://blogs.nokia.com/nordicblog/news/meet-mario-the-man-behind-the-qt-app-tvmatchen/#date=2011-02-01,mode=month&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-5108547008651555036?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/5108547008651555036/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2011/02/guest-blog-post-at-nokia-nordic-blog.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/5108547008651555036'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/5108547008651555036'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2011/02/guest-blog-post-at-nokia-nordic-blog.html' title='Guest blog post at Nokia Nordic Blog'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4207433361950286664</id><published>2010-11-29T20:37:00.000+01:00</published><updated>2010-11-29T20:37:53.314+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><title type='text'>Nokia Certified Qt Developer</title><content type='html'>Now I finally received my Qt certification diploma by mail. Actually, I passed the exam when I attended the Qt DevDays 2010 (october) in Munich. See if I'll go for the &lt;a href="http://qt.nokia.com/developer/learning/qt-curriculum/advanced-exam-curricula/"&gt;advanced exam&lt;/a&gt; some day.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_ilsnPCKxGBM/TPQALgSxz6I/AAAAAAAAAC0/NQ3dzZlmdLY/s1600/cert.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="209" src="http://3.bp.blogspot.com/_ilsnPCKxGBM/TPQALgSxz6I/AAAAAAAAAC0/NQ3dzZlmdLY/s320/cert.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4207433361950286664?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4207433361950286664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/11/nokia-certified-qt-developer.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4207433361950286664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4207433361950286664'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/11/nokia-certified-qt-developer.html' title='Nokia Certified Qt Developer'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_ilsnPCKxGBM/TPQALgSxz6I/AAAAAAAAAC0/NQ3dzZlmdLY/s72-c/cert.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-5355502242413859071</id><published>2010-11-12T11:52:00.000+01:00</published><updated>2010-11-12T11:52:04.564+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Nokia'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Conference'/><title type='text'>Nokia Rules!</title><content type='html'>There's a conference in the southern part of Sweden (Malmoe) called &lt;a href="http://www.oredev.org/"&gt;Oredev&lt;/a&gt; which I have been attending this week. I was interested in a talk about MeeGo, meet some Nokia people and to attend a two days Qt course.&lt;br /&gt;&lt;br /&gt;Nokia had a booth and I was passing by to check out if they had some Qt stickers which I needed for a demonstration (a simple QML app) I was going to show at my employers booth. To my disappointment they had no stickers but instead I had a great chat with one of guys.&lt;br /&gt;&lt;br /&gt;We had an interesting and giving chat about Qt, apps, OviStore, Nokia and MeeGo. Suddenly the guy grabs a package and hands it to me, to my surprise I just realized that he gave me a Nokia N8! He smiled at me and told me to make my app available on OviStore.&lt;br /&gt;&lt;br /&gt;Now finally Nokia has given me the opportunity to develop apps for mobile devices since I can use Qt (I already made an app w/o owning a device).&lt;br /&gt;&lt;br /&gt;I can't wait for MeeGo devices to show up. &lt;br /&gt;&lt;br /&gt;Thanks Nokia!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-5355502242413859071?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/5355502242413859071/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/11/nokia-rules.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/5355502242413859071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/5355502242413859071'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/11/nokia-rules.html' title='Nokia Rules!'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4086185834288703263</id><published>2010-11-06T17:08:00.000+01:00</published><updated>2010-11-06T17:08:59.223+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Getting Interactive With PyQt</title><content type='html'>One of the cooler features with PyQt (IMHO) is the possibility to write Qt code interactively. It isn't actually something new and I'm sure it's been there for a while but some of you might not be aware of it. I find it very useful when I want to learn new things in Qt, so without further ado, here's a short introduction on using it.&lt;br /&gt;&lt;br /&gt;Start an interactive python session by executing python in a shell:&lt;br /&gt;&lt;pre class="python" name="code"&gt;$&gt; python&lt;br /&gt;Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56) &lt;br /&gt;[GCC 4.4.5] on linux2&lt;br /&gt;Type "help", "copyright", "credits" or "license" for more information.&lt;br /&gt;&gt;&gt;&gt;&lt;br /&gt;&lt;/pre&gt;Let's create a simle top-level window (I wont add the '&amp;gt;&amp;gt;&amp;gt;' in case you would like to copy/paste the code):&lt;br /&gt;&lt;pre class="python" name="code"&gt;from PyQt4.QtCore import *&lt;br /&gt;from PyQt4.QtGui import *&lt;br /&gt;app = QApplication([])&lt;br /&gt;window = QWidget()&lt;br /&gt;window.resize(400, 300)&lt;br /&gt;window.show()&lt;br /&gt;&lt;/pre&gt;Now you should have a window similar to the screen shot below:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV0F-KvpiI/AAAAAAAAACU/TAG8AyiWDeo/s1600/window.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="259" src="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV0F-KvpiI/AAAAAAAAACU/TAG8AyiWDeo/s320/window.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Ok, let's create a push button:&lt;br /&gt;&lt;pre class="python" name="code"&gt;button = QPushButton("Click Me!", window)&lt;br /&gt;&lt;/pre&gt;Hmm, no button shows up... Why? Well, that's because we need to make it visible after we have added it to the window:&lt;br /&gt;&lt;pre class="python" name="code"&gt;button.show()&lt;br /&gt;&lt;/pre&gt;Ahh, sweet! We have added the button to the window interactively:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV0bcFHqOI/AAAAAAAAACY/3st5zlKCvLA/s1600/button.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="259" src="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV0bcFHqOI/AAAAAAAAACY/3st5zlKCvLA/s320/button.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Let's put the button at the center of the window by using a layout manager:&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;layout = QHBoxLayout(window)&lt;br /&gt;layout.addWidget(button)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV5Qa6XbfI/AAAAAAAAACc/XU8Xu33BbHc/s1600/center.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV5Qa6XbfI/AAAAAAAAACc/XU8Xu33BbHc/s320/center.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;And finally, let's see how 'layout.addStretch()' affects the layout:&lt;br /&gt;&lt;pre class="python" name="code"&gt;layout.addStretch()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV5U6VjBmI/AAAAAAAAACg/Hbz2JbvtuCQ/s1600/stretch.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="259" src="http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV5U6VjBmI/AAAAAAAAACg/Hbz2JbvtuCQ/s320/stretch.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;You can easily add a slot to the button's clicked signal:&lt;br /&gt;&lt;pre class="python" name="code"&gt;def mySlot():&lt;br /&gt;    print "Button Clicked"&lt;br /&gt;button.clicked.connect(mySlot)&lt;br /&gt;&lt;/pre&gt;As you can see, it's an easy way to try out thing in Qt.&lt;br /&gt;&lt;br /&gt;Thanks for reading!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4086185834288703263?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4086185834288703263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/11/getting-interactive-with-pyqt.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4086185834288703263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4086185834288703263'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/11/getting-interactive-with-pyqt.html' title='Getting Interactive With PyQt'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_ilsnPCKxGBM/TNV0F-KvpiI/AAAAAAAAACU/TAG8AyiWDeo/s72-c/window.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7517201890707351749</id><published>2010-09-27T20:10:00.000+02:00</published><updated>2010-09-27T20:10:39.384+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Conference'/><category scheme='http://www.blogger.com/atom/ns#' term='Quick'/><title type='text'>Qt DevDays 2010</title><content type='html'>I'm attending &lt;a href="http://qt.nokia.com/qtdevdays2010/"&gt;Qt DevDays 2010&lt;/a&gt; and really looking forward to it.&lt;br /&gt; &lt;br /&gt; I haven't decided yet which tracks I'll attend, there are so many interesting but I definitely go for some Quick-talks. I believe I'll have a great time.&lt;br /&gt;&lt;br /&gt;And that's not all, in November I'll also attend a two day &lt;a href="http://oredev.org/2010/courses#qt"&gt;Mobile Qt&lt;/a&gt; course at &lt;a href="http://oredev.org/2010"&gt;Öredev&lt;/a&gt; with topics such as Qt Mobility API, Gesture framework, Hybrid JavaScript apps and last but not least Quick. Looks very promising.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7517201890707351749?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7517201890707351749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/09/qt-devdays-2010.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7517201890707351749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7517201890707351749'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/09/qt-devdays-2010.html' title='Qt DevDays 2010'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-3411100633783850240</id><published>2010-09-11T09:23:00.000+02:00</published><updated>2010-09-11T09:23:11.328+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>PyQt and Signal overloads</title><content type='html'>I almost spent half an hour trying to figure out why my code didn't work.&lt;br /&gt;&lt;br /&gt;I used a QSignalMapper to connect a couple of clickable widgets to emit just one signal, &lt;i&gt;widgetClicked(int index)&lt;/i&gt;, instead of having multiple signals, one for each widget. I connected the &lt;i&gt;widgetClicked&lt;/i&gt; signal to the &lt;i&gt;mapped&lt;/i&gt; signal of the QSignalMapper class and did the required set up for the signal mapper, something like the following snippet:&lt;br /&gt;&lt;pre class="python" name="code"&gt;self._signalMapper = QSignalMapper(self)&lt;br /&gt;self._signalMapper.mapped.connect(self.widgetClicked)&lt;br /&gt;&lt;br /&gt;for index, widget in enumerate(buttons):&lt;br /&gt;    widget.clicked.connect(self._signalMapper.map)&lt;br /&gt;    self._signalMapper.setMapping(widget, index)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Everything worked fine until I changed the signature of &lt;i&gt;widgetClicked&lt;/i&gt; to pass a string instead of an int, it didn't work anymore. I only got the following print out: &lt;b&gt;TypeError: connect() failed between 'mapped' and 'widgetClicked'&lt;/b&gt; and had no clue why because the &lt;i&gt;mapped&lt;/i&gt; signal has a couple of overloads:&lt;br /&gt;&lt;pre class="c++" name="code"&gt;void mapped(int i)&lt;br /&gt;void mapped(const QString &amp;amp; text)&lt;br /&gt;void mapped(QWidget * widget)&lt;br /&gt;void mapped(QObject * object)&lt;br /&gt;&lt;/pre&gt;I did also try changing &lt;i&gt;widgetClicked&lt;/i&gt; to not receive any arguments and that worked, which was expected because the receiver is not required to match all arguments of a signal. Of course, it also worked if I changed back to int...&lt;br /&gt;&lt;br /&gt;What the #@! is going on? Naturally, I had no option of searching for answers since I was sitting on the train back home from work and had no Internet connection.&lt;br /&gt;&lt;br /&gt;Then it come to me, Python is not aware of the C++ signature overload. Python doesn't support overloading. Python/PyQt can't set up the right connection between &lt;i&gt;widgetClicked&lt;/i&gt; and &lt;i&gt;mapped&lt;/i&gt;. But how do you specify which of the overloads you want to connect to? By indexing:&lt;br /&gt;&lt;pre class="python" name="code"&gt;# This connects your slot/signal to the string-overload&lt;br /&gt;signalMapper.mapped[str].connect(...)&lt;br /&gt;&lt;/pre&gt;The int overload will be used default by PyQt, that's why my code worked when I connected &lt;i&gt;widgetClicked&lt;/i&gt; to &lt;i&gt;mapped&lt;/i&gt; w/o specifying which overload I wanted.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-3411100633783850240?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/3411100633783850240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/09/pyqt-and-signal-overloads.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3411100633783850240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3411100633783850240'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/09/pyqt-and-signal-overloads.html' title='PyQt and Signal overloads'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7675298162266809189</id><published>2010-09-05T15:03:00.000+02:00</published><updated>2010-09-05T15:03:58.694+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QuickCast'/><category scheme='http://www.blogger.com/atom/ns#' term='QtCast'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Quick'/><title type='text'>IcecastRadio - Qt widget/Qt Quick example Icecast player</title><content type='html'>I've been toying with modeling a Qt application which supports both a traditional widget-based view and a quick-based (qml) view.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;About IcecastRadio&lt;/b&gt;&lt;br /&gt;The intention of this project/application is to demonstrate how important&lt;br /&gt;it can be to design a model for your application. If the model is correctly&lt;br /&gt;designed, it can be used from both traditional widget based UIs and&lt;br /&gt;modern UIs built with Qt Quick without too much effort.&lt;br /&gt;&lt;br /&gt;The advantage is of course that you can provide both a desktop application which integrates with the native look and feel of the OS and at the same time have a totally different experience UI-wise, for example in an embedded device by only changing the view-layer.&lt;br /&gt;&lt;br /&gt;IcecastRadio is actually two applications, QtCast and QuickCast. QtCast implements the widget-based view and QuickCast the quick-based view. Both application depends on a third entity, the model. You'll see this division in the source code in form of three projects, qtcast, quickcast and model.&lt;br /&gt;&lt;br /&gt;The application was successfully compiled against Qt 4.7-beta 1 and 2. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Source code&lt;/b&gt;&lt;br /&gt;You'll find the source code at &lt;a href="http://gitorious.org/icecastradio"&gt;http://gitorious.org/icecastradio&lt;/a&gt;. The player code is released under the MIT license, clone and have fun. Please read the README file found in the repository for more information.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Screenshots&lt;/b&gt;&lt;br /&gt;Must have...&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_ilsnPCKxGBM/TIOPAjSVJ3I/AAAAAAAAAB8/jdoPpQ_1gsk/s1600/qtcast.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="312" src="http://1.bp.blogspot.com/_ilsnPCKxGBM/TIOPAjSVJ3I/AAAAAAAAAB8/jdoPpQ_1gsk/s400/qtcast.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_ilsnPCKxGBM/TIOPJ-huZ4I/AAAAAAAAACE/24PHo5GJjhc/s1600/quickcast.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/_ilsnPCKxGBM/TIOPJ-huZ4I/AAAAAAAAACE/24PHo5GJjhc/s400/quickcast.png" width="400" /&gt;&lt;/a&gt;&lt;a class="cssButton ubtn-disabled" href="javascript:void(0)" id="draftButton" onclick="if (this.className.indexOf(&amp;quot;ubtn-disabled&amp;quot;) == -1) {var e = document['postingForm'].saveDraft;(e.length) ? e[0].click() : e.click(); if (window.event) window.event.cancelBubble = true; return false;}" target=""&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Remark&lt;/b&gt;&lt;br /&gt;The application lacks a lot of features that could be expected from a radio player, for example there is no way of sorting the list of stations nor any filtering can be applied. The intention was not to create a full blown Icecast player but to demonstrate how to create a model that is usable from both a widget-based application and a quick-based application.&lt;br /&gt;&lt;br /&gt;Patches are welcome :) &lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7675298162266809189?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7675298162266809189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/09/icecastradio-qt-widgetqt-quick-example.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7675298162266809189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7675298162266809189'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/09/icecastradio-qt-widgetqt-quick-example.html' title='IcecastRadio - Qt widget/Qt Quick example Icecast player'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_ilsnPCKxGBM/TIOPAjSVJ3I/AAAAAAAAAB8/jdoPpQ_1gsk/s72-c/qtcast.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-8714195959889462272</id><published>2010-09-01T21:48:00.000+02:00</published><updated>2010-09-01T21:48:26.884+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unittesting'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Python Koans - A Great Way to Learn Python!</title><content type='html'>I just found out about &lt;a href="http://bitbucket.org/gregmalcolm/python_koans/wiki/Home"&gt;Python Koans&lt;/a&gt; by Greg Malcolm (thanks dude) after listening to the &lt;i&gt;&lt;a href="http://frompythonimportpodcast.com/"&gt;from python import podcast&lt;/a&gt;&lt;/i&gt; podcast (which I find amusing, thanks guys).&lt;br /&gt;&lt;br /&gt;It's an awesome way to learn Python. Instead of just reading tutorials and/or books you learn Python by coding.&lt;br /&gt; &lt;br /&gt;The interactive &lt;i&gt;tutorial&lt;/i&gt; is built around unit-tests and you advance and gain new skills by passing tests and it's really funny. You do learn a lot about the Python language when doing the Koans so I recommend it even if you've been using Python for a while.&lt;br /&gt;&lt;br /&gt;Another cool thing is that you learn how to do unit testing in Python, if you're not already familiar with it.&lt;br /&gt; &lt;br /&gt; &lt;br /&gt; &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-8714195959889462272?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/8714195959889462272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/09/python-koans-great-way-to-learn-python.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8714195959889462272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8714195959889462272'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/09/python-koans-great-way-to-learn-python.html' title='Python Koans - A Great Way to Learn Python!'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-3042484001602798453</id><published>2010-08-03T23:53:00.000+02:00</published><updated>2010-08-03T23:53:06.397+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>My python fix</title><content type='html'>It has been a while since I did something in Python. I've been coding C++, Qt and Qt Quick lately and I do enjoy it, especially Quick. I really like the declarative way of describing UIs.&lt;br/&gt;&lt;br/&gt;But I still need my shot of Python now and then. Often I end up just doing small non useful snippets like list comprehensions stuff. I just love them, they are so beautiful and they make me happy :)&lt;pre class="python" name="code"&gt;&lt;br /&gt;&gt;&gt;&gt; # Find the longest words&lt;br /&gt;&gt;&gt;&gt; words = "Some random words in a text string"&lt;br /&gt;&gt;&gt;&gt; max([(len(w), w) for w in words.split()], key=lambda x:x[0])[1]&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-3042484001602798453?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/3042484001602798453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/08/my-python-fix.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3042484001602798453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3042484001602798453'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/08/my-python-fix.html' title='My python fix'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7038503134687455232</id><published>2010-06-10T20:06:00.000+02:00</published><updated>2010-06-10T20:06:02.599+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>QSignalMapper - at your service</title><content type='html'>The &lt;a href="http://doc.qt.nokia.com/4.6/qsignalmapper.html"&gt;QSignalMapper&lt;/a&gt; class is very useful, for example, in situations were you have a couple of QPushButtons and you want to connect the &lt;i&gt;clicked&lt;/i&gt;-signal of each button to a single &lt;i&gt;slot&lt;/i&gt; and still be able to identify the sender&lt;i&gt;.&lt;/i&gt; It's possible to find out which object emitted a signal by calling &lt;a href="http://doc.qt.nokia.com/4.6/qobject.html#sender"&gt;&lt;i&gt;sender()&lt;/i&gt;&lt;/a&gt; inside the &lt;i&gt;slot&lt;/i&gt;, but I don't think that's good practice because you introduce a strong coupling between the signaling object and the slot.&lt;br /&gt;&lt;br /&gt;A better solution is to use the QSignalMapper class which re-emits the signal with an additional configurable parameter. The parameter can be one of the following types: int, QString, QObject or QWidget.&lt;br /&gt;&lt;br /&gt;So without further ado, here's a self describing example:&lt;pre name="code" class="python"&gt;&lt;br /&gt;#&lt;br /&gt;# Example showing how QSignalMapper can be used to manage an arbitrary&lt;br /&gt;# numbers of parameterless signals and re-emit them with an argument&lt;br /&gt;# identifying the sender.&lt;br /&gt;#&lt;br /&gt;# Each signal is associated in the QSignalMapper with either an int,&lt;br /&gt;# QString, QObject or a QWidget which is passed as argument to the slot&lt;br /&gt;# connected to the QSignalMapper.&lt;br /&gt;#&lt;br /&gt;from PyQt4.QtCore import QSignalMapper, pyqtSignal&lt;br /&gt;from PyQt4.QtGui import QApplication, QFrame, QGridLayout, QPushButton&lt;br /&gt;&lt;br /&gt;class Grid(QFrame):&lt;br /&gt;    """ Lay out widgets in a grid. """&lt;br /&gt;&lt;br /&gt;    clicked =  pyqtSignal(int)&lt;br /&gt;    """&lt;br /&gt;    This signal will be emitted when on one of the grid items is clicked.&lt;br /&gt;    The grid item's index will be passed as argument.&lt;br /&gt;    """&lt;br /&gt;&lt;br /&gt;    def __init__(self, items, colCount, parent=None):&lt;br /&gt;        """&lt;br /&gt;        Create Grid and setup QSignalMapper.&lt;br /&gt;&lt;br /&gt;        Connect each item's 'clicked' signal and use the sequence index&lt;br /&gt;        as map value which will be passed as argument when the Grid's&lt;br /&gt;        clicked signal is emitted.&lt;br /&gt;&lt;br /&gt;        items: sequence with widgets having a 'void clicked()' signal&lt;br /&gt;        colCount: column count for each row&lt;br /&gt;        parent: parent widget, default None&lt;br /&gt;        """&lt;br /&gt;        super(Grid, self).__init__(parent)&lt;br /&gt;&lt;br /&gt;        # Create a grid layout.&lt;br /&gt;        layout = QGridLayout()&lt;br /&gt;        self.setLayout(layout)&lt;br /&gt;&lt;br /&gt;        # Create the signal mapper.&lt;br /&gt;        signalMapper = QSignalMapper(self)&lt;br /&gt;&lt;br /&gt;        for cnt, item in enumerate(items):&lt;br /&gt;            # Setup mapping for the item. In this case, the&lt;br /&gt;            # mapping is the sequence index of the item.&lt;br /&gt;            signalMapper.setMapping(item, cnt)&lt;br /&gt;&lt;br /&gt;            # Connect the item's 'clicked' signal to the signal&lt;br /&gt;            # mapper's 'map' slot.&lt;br /&gt;            # The 'map' slot will emit the 'mapped' signal&lt;br /&gt;            # when invoked and the mapping set previously will be&lt;br /&gt;            # passed as an argument to the slot/signal that is&lt;br /&gt;            # connected to the signal mapper's 'mapped' signal.&lt;br /&gt;            item.clicked.connect(signalMapper.map)&lt;br /&gt;&lt;br /&gt;            # Add the widget to the grid layout&lt;br /&gt;            layout.addWidget(item, cnt / colCount, cnt % colCount)&lt;br /&gt;&lt;br /&gt;        # Forward the signal mapper's 'mapped' signal via the Grid's&lt;br /&gt;        # 'clicked' signal. This will handle all widgets' 'clicked'&lt;br /&gt;        # ssignals.&lt;br /&gt;        signalMapper.mapped.connect(self.clicked)&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    import sys&lt;br /&gt;    app = QApplication(sys.argv)&lt;br /&gt;&lt;br /&gt;    # Create grid items&lt;br /&gt;    items = (QPushButton('Open'), QPushButton('Close'),&lt;br /&gt;             QPushButton('Read'), QPushButton('Write'),&lt;br /&gt;             QPushButton('Delete'))&lt;br /&gt;&lt;br /&gt;    win = Grid(items, 2)&lt;br /&gt;&lt;br /&gt;    # Handle grid clicks here&lt;br /&gt;    @win.clicked.connect&lt;br /&gt;    def clicked(index):&lt;br /&gt;        print "Button at:", index, " - text:", items[index].text()&lt;br /&gt;&lt;br /&gt;    win.show()&lt;br /&gt;    sys.exit(app.exec_())&lt;br /&gt;&lt;/pre&gt;Hope this was to any help.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7038503134687455232?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7038503134687455232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/06/qsignalmapper-at-your-service.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7038503134687455232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7038503134687455232'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/06/qsignalmapper-at-your-service.html' title='QSignalMapper - at your service'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-9093109319899551571</id><published>2010-05-26T20:59:00.001+02:00</published><updated>2010-05-26T21:01:56.340+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>ABCs - Abstract Base Classes</title><content type='html'>&lt;b&gt;What's ABCs?&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;/b&gt;The &lt;a href="http://docs.python.org/library/abc.html"&gt;abc&lt;/a&gt;&amp;nbsp;(abstract base class) module was introduced in version 2.6 of Python. The module provides the infrastructure for defining an abstract base class and can typically be used in situations where frameworks need to specify a bunch of interfaces to be implemented to ensure the functionality. An example can be a database application where the framework requires you to provide a &lt;a href="http://en.wikipedia.org/wiki/Data_access_object"&gt;&lt;i&gt;Data Access Object&lt;/i&gt;&lt;/a&gt; with basic &lt;a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;&lt;i&gt;CRUD&lt;/i&gt;&lt;/a&gt; operations and therefore defines an interface, call it UserDao, with the following methods:&lt;br /&gt;&lt;br /&gt;UserDao&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;getById - Return a user entity with the specified id&lt;/li&gt;&lt;li&gt;store - store a user entity&lt;/li&gt;&lt;li&gt;remove - remove a user entity from the persistent storage&lt;/li&gt;&lt;/ul&gt;To ensure that developers follows the protocol an abc is introduced.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The UserDao abc&lt;/b&gt;&lt;div&gt;&lt;b&gt;&lt;/b&gt;&lt;i&gt;The following example will show you basic usage of the abc module. You should&amp;nbsp;read the module documentation for more details.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Let's create the UserDao abc:&lt;/div&gt;&lt;pre name="code" class="python"&gt;&lt;br /&gt;from abc import ABCMeta, abstractmethod&lt;br /&gt;&lt;br /&gt;class UserDao:&lt;br /&gt;    """&lt;br /&gt;    This interface should be implemented to provide the framework with&lt;br /&gt;    basic user CRUD operations.&lt;br /&gt;    """&lt;br /&gt;    __metaclass__ = ABCMeta&lt;br /&gt;&lt;br /&gt;    @abstractmethod&lt;br /&gt;    def getById(self, userId):&lt;br /&gt;        """ Return the user with the user id. """&lt;br /&gt;&lt;br /&gt;    @abstractmethod&lt;br /&gt;    def store(self, user):&lt;br /&gt;        """ Persist the user. """&lt;br /&gt;&lt;br /&gt;    @abstractmethod&lt;br /&gt;    def remove(self, user):&lt;br /&gt;        """ Remove the user from the persistent storage. """&lt;br /&gt;&lt;/pre&gt;If we try to create an instance of this class we get the following error:&lt;pre name="code" class="python"&gt;&lt;br /&gt;&gt;&gt;&gt; dao = UserDao()&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;  File "&lt;stdin&gt;", line 1, in &lt;module&gt;&lt;br /&gt;TypeError: Can't instantiate abstract class UserDao with abstract methods getById, remove, store&lt;br /&gt;&lt;/pre&gt;In other words, we have a well defined interface to support CRUD operations for User entities and we also have an easy to read documentation of what's expected from a UserDao. If we would rely on &lt;a href="http://en.wikipedia.org/wiki/Duck_typing"&gt;&lt;i&gt;duck typing&lt;/i&gt;&lt;/a&gt; instead of defining the abc, we would need to document the expected behavior for the UserDao elsewhere. I think that the biggest advantage of using ABCs is that you have the interface definition and documentation in one place.&lt;br /&gt;&lt;br /&gt;Just to prevent flaming :) I enjoy duck typing and use it a lot but I think that ABCs are useful is some situations.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;b&gt;Implementing the abc&lt;/b&gt;&lt;div&gt;&lt;b&gt;&lt;/b&gt;Implementing the UserDao can be done by sub-classing the UserDao.&lt;/div&gt;&lt;pre name="code" class="python"&gt;&lt;br /&gt;class User(object):&lt;br /&gt;    def __init__(self):&lt;br /&gt;        self.id = None&lt;br /&gt;        self.name = None&lt;br /&gt;&lt;br /&gt;class UserDaoImpl(UserDao):&lt;br /&gt;&lt;br /&gt;    def getById(self, userId):&lt;br /&gt;        user = User()&lt;br /&gt;        user.id = userId&lt;br /&gt;        user.name = 'A User'&lt;br /&gt;        return user&lt;br /&gt;&lt;br /&gt;    def store(self, user):&lt;br /&gt;        print "Storing: ", user.id, "-", user.name&lt;br /&gt;&lt;br /&gt;    def remove(self, user):&lt;br /&gt;        print "Removing: ", user.id&lt;br /&gt;&lt;/pre&gt;And to complete the example:&lt;pre name="code" class="python"&gt;&lt;br /&gt;userDao = UserDaoImpl()&lt;br /&gt;user = userDao.getById(10)&lt;br /&gt;user.name = "New Name"&lt;br /&gt;userDao.store(user)&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;Wrap up&lt;/b&gt;&lt;br /&gt;I haven't investigated if ABCs introduces any performance overhead but I can't really see why they should. One advantage of using ABCs in frameworks is that you can have the documentation and interface details as a simple Python class. Another advantage is that Python will issue an error if a sub-class doesn't implement all abstract methods/properties of the abc, which is probably the author's intention.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-9093109319899551571?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/9093109319899551571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/05/abcs-abstract-base-classes.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/9093109319899551571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/9093109319899551571'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/05/abcs-abstract-base-classes.html' title='ABCs - Abstract Base Classes'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-3011247372158752575</id><published>2010-05-04T21:34:00.001+02:00</published><updated>2010-05-04T21:37:18.034+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Kubuntu'/><title type='text'>Network manager problems on Kubuntu 10.04</title><content type='html'>I installed 10.04 as soon as it was released and it worked right out of the box. But suddenly today the network manager refused to start.&lt;br /&gt;&lt;br /&gt;Well, I could see the icon in the systray but it didn't show any network interfaces (I have eth0 and wlan0).&lt;br /&gt;&lt;br /&gt;To get me an IP I had to run dhclient manually from the terminal which was successful. Finally I could google for help.&lt;br /&gt;&lt;br /&gt;Google pointed me to this &lt;a href="http://cyberponcho.blogspot.com/2010/05/network-manager-issue-on-kubuntu-1004.html"&gt;blog post&lt;/a&gt; where the author had run into the same problem.&lt;br /&gt;&lt;br /&gt;Apparently there is a bug filed for this issue, I'm just too lazy to search in the bug database :)&lt;br /&gt;&lt;br /&gt;Just wanted to share this with you if you bump into it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-3011247372158752575?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/3011247372158752575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/05/network-manager-problems-on-kubuntu.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3011247372158752575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3011247372158752575'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/05/network-manager-problems-on-kubuntu.html' title='Network manager problems on Kubuntu 10.04'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4377986386221259415</id><published>2010-04-16T21:01:00.000+02:00</published><updated>2010-04-16T21:01:49.643+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Unittesting'/><title type='text'>Unit testing with Qt</title><content type='html'>It's very easy to do unit testing with Qt. If you have any previous experience withJUnit you shouldn't have any difficulties using &lt;a href="http://qt.nokia.com/doc/4.6/qtestlib-manual.html"&gt;QTestLib&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;All that is required to create a unit test, besides adding 'QT += testlib' to your .pro file, is that the test class is a sub-class of QObject and each test function is declared as a private slot. To run all your test functions in a class simply call &lt;a href="http://qt.nokia.com/doc/4.6/qtest.html#qExec"&gt;QTest::qExec()&lt;/a&gt; from the main function.&lt;br /&gt;&lt;br /&gt;The following snippet shows what a unit test looks like:&lt;br /&gt;&lt;pre class="c++" name="code"&gt;#include &amp;lt;QtTest/QTest&amp;gt;&lt;br /&gt;&lt;br /&gt;class MyTest : public QObject&lt;br /&gt;{&lt;br /&gt;Q_OBJECT&lt;br /&gt;public:&lt;br /&gt;    explicit MyTest(QObject *parent = 0) : QObject(parent) {}&lt;br /&gt;&lt;br /&gt;private slots:&lt;br /&gt;    /** Test function 1 */&lt;br /&gt;    void test1()&lt;br /&gt;    {&lt;br /&gt;        QCOMPARE('a', 'a');&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /** Test function 2 */&lt;br /&gt;    void test2()&lt;br /&gt;    {&lt;br /&gt;        QVERIFY(10 != 11);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;};&lt;br /&gt;int main(int argc, char *argv[])&lt;br /&gt;{&lt;br /&gt;    // Simple test cases doesn't require a&lt;br /&gt;    // Q[Core]Application instance.&lt;br /&gt;    MyTest t;&lt;br /&gt;    QTest::qExec(&amp;amp;t);&lt;br /&gt;}&lt;br /&gt;// The next line is only required if we have the&lt;br /&gt;// definition and implementation in the same file&lt;br /&gt;// as in this case.&lt;br /&gt;#include "qunittest.moc"&lt;br /&gt;&lt;/pre&gt;The test above produces the following output when executed:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;Config: Using QTest library 4.6.2, Qt 4.6.2&lt;br /&gt;PASS   : MyTest::initTestCase()&lt;br /&gt;PASS   : MyTest::test1()&lt;br /&gt;PASS   : MyTest::test2()&lt;br /&gt;PASS   : MyTest::cleanupTestCase()&lt;br /&gt;Totals: 4 passed, 0 failed, 0 skipped&lt;br /&gt;&lt;/pre&gt;&lt;a href="http://qt.nokia.com/doc/4.6/qtest.html#QVERIFY"&gt;QVERIFY&lt;/a&gt; evaluates the expression and continues the execution if the expression evaluates to true.If the expression evaluates to false a message is appended to the test log and the test functionis stopped. &lt;a href="http://qt.nokia.com/doc/4.6/qtest.html#QCOMPARE"&gt;QCOMPARE&lt;/a&gt; is similar to QVERIFY and gives a more verbose output.In addition to the test functions the framework will look for the following four functions (must alsobe declared as private slots):&lt;br /&gt;&lt;ul&gt;&lt;li&gt;initTestCase() - Executed before any test function&lt;/li&gt;&lt;li&gt;cleanupTestCase() - Executed after the last test function&lt;/li&gt;&lt;li&gt;init() - Executed before each test function&lt;/li&gt;&lt;li&gt;cleanup() - Executed after each test function&lt;/li&gt;&lt;/ul&gt;Ok, now we know the basics, lets go on and check how to do data driven tests.&lt;br /&gt;&lt;br /&gt;Data driven tests are used when you want to exercise some piece of code withdifferent values and is supported by the Qt testlib. To feed test data to a test functionadd a function with the same name as the test function appended with a '_data' suffix. So ifthe test function is named &lt;i&gt;testFeature&lt;/i&gt;, the function that serves the test with datashould be named &lt;i&gt;testFeature_data&lt;/i&gt;. The test data is setup in a table. Two functions areused to create this table, &lt;a href="http://qt.nokia.com/doc/4.6/qtest.html#addColumn"&gt;addColumn&lt;/a&gt; and &lt;a href="http://qt.nokia.com/doc/4.6/qtest.html#newRow"&gt;newRow&lt;/a&gt;. &lt;i&gt;addColumn&lt;/i&gt; is used to define elementsin the table and &lt;i&gt;newRow&lt;/i&gt; adds data. &lt;a href="http://qt.nokia.com/doc/4.6/qtest.html#QFETCH"&gt;QFETCH&lt;/a&gt; is used to fetch data from the table.&lt;br /&gt;&lt;br /&gt;Lets create a simple 'capitalize' function that takes a string, capitalizes the first character and returns the result as a new string. We want to test the function with different data such as an empty string, first character is lower case and first character is upper case.&lt;br /&gt;&lt;pre class="c++" name="code"&gt;#include &amp;lt;QtTest/QTest&amp;gt;&lt;br /&gt;&lt;br /&gt;class MyTest : public QObject&lt;br /&gt;{&lt;br /&gt;Q_OBJECT&lt;br /&gt;public:&lt;br /&gt;    explicit MyTest(QObject *parent = 0) : QObject(parent) {}&lt;br /&gt;&lt;br /&gt;    QString capitalize(const QString &amp;amp;text)&lt;br /&gt;    {&lt;br /&gt;        QString result = text;&lt;br /&gt;        result[0] = text[0].toUpper();&lt;br /&gt;        return result;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;private slots:&lt;br /&gt;    void testCapitalize_data()&lt;br /&gt;    {&lt;br /&gt;        QTest::addColumn&lt;qstring&gt;("string");&lt;br /&gt;        QTest::addColumn&lt;qstring&gt;("expected");&lt;br /&gt;&lt;br /&gt;        QTest::newRow("empty string") &amp;lt;&amp;lt; "" &amp;lt;&amp;lt; "";&lt;br /&gt;        QTest::newRow("lower case") &amp;lt;&amp;lt; "lower case" &amp;lt;&amp;lt; "Lower case";&lt;br /&gt;        QTest::newRow("upper case") &amp;lt;&amp;lt; "Upper case" &amp;lt;&amp;lt; "Upper case";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void testCapitalize()&lt;br /&gt;    {&lt;br /&gt;        QFETCH(QString, string);&lt;br /&gt;        QFETCH(QString, expected);&lt;br /&gt;        QCOMPARE(capitalize(string), expected);&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;// Macro that implements main&lt;br /&gt;QTEST_APPLESS_MAIN(MyTest);&lt;br /&gt;#include "qunittest.moc"&lt;br /&gt;&lt;/qstring&gt;&lt;/qstring&gt;&lt;/pre&gt;Running the test gives the following output:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;********* Start testing of MyTest *********&lt;br /&gt;Config: Using QTest library 4.6.2, Qt 4.6.2&lt;br /&gt;PASS   : MyTest::initTestCase()&lt;br /&gt;QFATAL : MyTest::testCapitalize(empty string) ASSERT: "i &amp;gt;= 0 &amp;amp;&amp;amp; i &amp;lt; size()" in file ../../../../opt/qtsdk-2010.02/qt/include/QtCore/qstring.h, line 690&lt;br /&gt;FAIL!  : MyTest::testCapitalize(empty string) Received a fatal error.&lt;br /&gt;   Loc: [Unknown file(0)]&lt;br /&gt;Totals: 1 passed, 1 failed, 0 skipped&lt;br /&gt;********* Finished testing of MyTest *********&lt;br /&gt;&lt;/pre&gt;Oops, forgot to check if the string is empty. We fix that by adding the following check at the beginning of the capitalize function:&lt;br /&gt;&lt;pre class="c++" name="code"&gt;if (text.isEmpty()) {&lt;br /&gt;    return text;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Running the test again:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;********* Start testing of MyTest *********&lt;br /&gt;Config: Using QTest library 4.6.2, Qt 4.6.2&lt;br /&gt;PASS   : MyTest::initTestCase()&lt;br /&gt;PASS   : MyTest::testCapitalize()&lt;br /&gt;PASS   : MyTest::cleanupTestCase()&lt;br /&gt;Totals: 3 passed, 0 failed, 0 skipped&lt;br /&gt;********* Finished testing of MyTest *********&lt;br /&gt;&lt;/pre&gt;Ok, this was a quick introduction to get you started. To find out more about the test lib read the &lt;a href="http://qt.nokia.com/doc/4.6/qtestlib-manual.html"&gt;manual&lt;/a&gt;, &lt;a href="http://qt.nokia.com/doc/4.6/qtest.html"&gt;api docs&lt;/a&gt;, and the &lt;a href="http://qt.nokia.com/doc/4.6/qtestlib-tutorial.html"&gt;tutorial.&lt;/a&gt; You'll find examples on how to test your GUI by simulating mouse clicks, key presses and how to do simple benchmarking.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4377986386221259415?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4377986386221259415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/04/unit-testing-with-qt.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4377986386221259415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4377986386221259415'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/04/unit-testing-with-qt.html' title='Unit testing with Qt'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6681930085280436544</id><published>2010-03-19T07:55:00.001+01:00</published><updated>2010-03-19T07:56:14.442+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Ternary operation</title><content type='html'>I'm a big fan of using ternary operations when possible but Python didn't introduced them until the 2.5 release, see &lt;a href="http://en.wikipedia.org/wiki/Ternary_operation"&gt;Wikipedia page&lt;/a&gt;. Before the 2.5 release there existed options like using the &lt;i&gt;and&lt;/i&gt; and &lt;i&gt;or&lt;/i&gt; operator &lt;pre name="code" class="python"&gt;x and y or z&lt;/pre&gt; or a &lt;i&gt;lambda&lt;/i&gt; construction, but none if them are considered foolproof (see the wiki page for explanation).So how do you write the ternary operation in python?&lt;pre name="code" class="python"&gt;&lt;br /&gt;a, b = 1, 2&lt;br /&gt;&lt;br /&gt;# non ternary operation&lt;br /&gt;if a &gt; b:&lt;br /&gt;    z = a&lt;br /&gt;else:&lt;br /&gt;    z = b&lt;br /&gt;&lt;br /&gt;# rewritten to&lt;br /&gt;&lt;br /&gt;z = a if a &gt; b else b&lt;br /&gt;&lt;/pre&gt;Clean, readable and simple!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6681930085280436544?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/6681930085280436544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/03/ternary-operation.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6681930085280436544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6681930085280436544'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/03/ternary-operation.html' title='Ternary operation'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-3008648353788429803</id><published>2010-02-20T08:49:00.000+01:00</published><updated>2010-02-20T08:49:50.287+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>QTabWidget - Hiding the bar</title><content type='html'>It has been a while since my last post. I've been sick, my son has been sick and so has my girlfriend. Now I'm back and have been coding on a private project for a couple of days.&lt;br /&gt;&lt;br /&gt;While I was toying and trying some ideas I found out how to create tab bars with Qt that are only visible if you have more than one tab. This is quite popular in browser for example. So without further ado, here's the code:&lt;br /&gt;&lt;pre class="python" name="code"&gt;from PyQt4 import QtGui&lt;br /&gt;&lt;br /&gt;class TabWidget(QtGui.QTabWidget):&lt;br /&gt;    def __init__(self, parent=None):&lt;br /&gt;        super (TabWidget, self).__init__(parent)&lt;br /&gt;        self.setTabsClosable(True)&lt;br /&gt;        self.tabCloseRequested.connect(self.removeTab)&lt;br /&gt;&lt;br /&gt;    def tabInserted(self, index):&lt;br /&gt;        self.tabBar().setVisible(self.count() &amp;gt; 1)&lt;br /&gt;&lt;br /&gt;    def tabRemoved(self, index):&lt;br /&gt;        self.tabBar().setVisible(self.count() &amp;gt; 1)&lt;br /&gt;&lt;/pre&gt;As you can see, it isn't rocket science. I did some googling but couldn't find anything about it, that's why I want to share my findings. The magic is to only hide the &lt;i&gt;TabBar&lt;/i&gt; associated with the TabWidget, not the TabWidget itself.&lt;br /&gt;&lt;br /&gt;You can use the following snippet to see the &lt;i&gt;TabWidget&lt;/i&gt; in action:&lt;br /&gt;&lt;pre class="python" name="code"&gt;import sys&lt;br /&gt;from PyQt4 import QtGui&lt;br /&gt;&lt;br /&gt;class TabWidget(QtGui.QTabWidget):&lt;br /&gt;    def __init__(self, parent=None):&lt;br /&gt;        super (TabWidget, self).__init__(parent)&lt;br /&gt;        self.setTabsClosable(True)&lt;br /&gt;        self.tabCloseRequested.connect(self.removeTab)&lt;br /&gt;&lt;br /&gt;    def tabInserted(self, index):&lt;br /&gt;        self.tabBar().setVisible(self.count() &amp;gt; 1)&lt;br /&gt;&lt;br /&gt;    def tabRemoved(self, index):&lt;br /&gt;        self.tabBar().setVisible(self.count() &amp;gt; 1)&lt;br /&gt;&lt;br /&gt;qApp = QtGui.QApplication(sys.argv)&lt;br /&gt;&lt;br /&gt;tab = TabWidget()&lt;br /&gt;&lt;br /&gt;button = QtGui.QPushButton('Hello')&lt;br /&gt;@button.clicked.connect&lt;br /&gt;def clicked():&lt;br /&gt;    tab.addTab(QtGui.QLabel('Hello'), 'Hello')&lt;br /&gt;&lt;br /&gt;tab.addTab(button, 'Button')&lt;br /&gt;&lt;br /&gt;layout = QtGui.QHBoxLayout()&lt;br /&gt;layout.addWidget(tab)&lt;br /&gt;&lt;br /&gt;window = QtGui.QWidget()&lt;br /&gt;window.setLayout(layout)&lt;br /&gt;window.resize(600, 400)&lt;br /&gt;window.show()&lt;br /&gt;&lt;br /&gt;qApp.exec_()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-3008648353788429803?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/3008648353788429803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/02/qtabwidget-hiding-bar.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3008648353788429803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3008648353788429803'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/02/qtabwidget-hiding-bar.html' title='QTabWidget - Hiding the bar'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-3402078798489989207</id><published>2010-01-30T13:51:00.000+01:00</published><updated>2010-01-30T13:51:35.098+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Named tuple</title><content type='html'>I recently read a post on planet python and the author mentioned something about &lt;i&gt;named tuple&lt;/i&gt; which made my curious.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;So what's a named tuple?&lt;/b&gt;&lt;br /&gt;The&lt;b&gt; &lt;/b&gt;&lt;a href="http://docs.python.org/library/collections.html#namedtuple-factory-function-for-tuples-with-named-fields"&gt;namedtuple&lt;/a&gt; was introduced in Python 2.6. A named tuple is created using a factory function from the collections module and it extends the basic tuple by assigning a name to each position in a tuple but can still be used as a regular tuple. This makes it possible to access fields by name instead of an index. The named tuple should not require more memory, according to the documentation, than regular tuples since they don't have a per instance dictionary.&lt;br /&gt;&lt;br /&gt;The factory function signature is:&lt;br /&gt;&lt;pre class="python" name="code"&gt;collections.namedtuple(typename, field_names[, verbose])&lt;br /&gt;&lt;/pre&gt;The first argument specifies the name of the new type, the second argument is a string (space or comma separated) containing the field names and finally if&lt;i&gt; verbose&lt;/i&gt; is true the factory function will also print the class generated.&lt;br /&gt;&lt;br /&gt;Enough theory, I'll show you an example.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Example&lt;/b&gt;&lt;br /&gt;Say you have a tuple containing username and password. To access the username you get the item at position zero and the password is accessed at position one:&lt;br /&gt;&lt;pre class="python" name="code"&gt;credential = ('mario', 'secret')&lt;br /&gt;print 'Username:', credential[0]&lt;br /&gt;print 'Password:', credential[1]&lt;br /&gt;&lt;/pre&gt;There's nothing wrong with this code but the tuple isn't self-documented. You have to find and read the documentation about the positioning of the fields in the tuple. This is where named tuple can enter the scene. We can rewrite the previous example as following:&lt;br /&gt;&lt;pre class="python" name="code"&gt;import collections&lt;br /&gt;# Create a new sub-tuple named Credential&lt;br /&gt;Credential = collections.namedtuple('Credential', 'username, password')&lt;br /&gt;&lt;br /&gt;credential = Credential(username='mario', password='secret')&lt;br /&gt;&lt;br /&gt;print 'Username:', credential.username&lt;br /&gt;print 'Password:', credential.password&lt;br /&gt;&lt;/pre&gt;Nice, don't you agree?&lt;br /&gt;&lt;br /&gt;If you are interested of what the code looks like for the newly created &lt;i&gt;Credential&lt;/i&gt;-type you can add &lt;i&gt;verbose=True&lt;/i&gt; to the argument list when creating the type, in this particular case we get the following output:&lt;pre class="python" name="code"&gt;import collections&lt;br /&gt;Credential = collections.namedtuple('Credential', 'username, password', verbose=True)&lt;br /&gt;&lt;br /&gt;class Credential(tuple):                                     &lt;br /&gt;        'Credential(username, password)'                     &lt;br /&gt;&lt;br /&gt;        __slots__ = () &lt;br /&gt;&lt;br /&gt;        _fields = ('username', 'password') &lt;br /&gt;&lt;br /&gt;        def __new__(_cls, username, password):&lt;br /&gt;            return _tuple.__new__(_cls, (username, password)) &lt;br /&gt;&lt;br /&gt;        @classmethod&lt;br /&gt;        def _make(cls, iterable, new=tuple.__new__, len=len):&lt;br /&gt;            'Make a new Credential object from a sequence or iterable'&lt;br /&gt;            result = new(cls, iterable)                               &lt;br /&gt;            if len(result) != 2:                                      &lt;br /&gt;                raise TypeError('Expected 2 arguments, got %d' % len(result))&lt;br /&gt;            return result&lt;br /&gt;&lt;br /&gt;        def __repr__(self):&lt;br /&gt;            return 'Credential(username=%r, password=%r)' % self&lt;br /&gt;&lt;br /&gt;        def _asdict(t):&lt;br /&gt;            'Return a new dict which maps field names to their values'&lt;br /&gt;            return {'username': t[0], 'password': t[1]}&lt;br /&gt;&lt;br /&gt;        def _replace(_self, **kwds):&lt;br /&gt;            'Return a new Credential object replacing specified fields with new values'&lt;br /&gt;            result = _self._make(map(kwds.pop, ('username', 'password'), _self))&lt;br /&gt;            if kwds:&lt;br /&gt;                raise ValueError('Got unexpected field names: %r' % kwds.keys())&lt;br /&gt;            return result&lt;br /&gt;&lt;br /&gt;        def __getnewargs__(self):&lt;br /&gt;            return tuple(self)&lt;br /&gt;&lt;br /&gt;        username = _property(_itemgetter(0))&lt;br /&gt;        password = _property(_itemgetter(1))&lt;br /&gt;&lt;/pre&gt;The named tuple doesn't only provide access to fields by name but also contains helper functions such as the &lt;i&gt;_make()&lt;/i&gt; function which helps creating an &lt;i&gt;Credential&lt;/i&gt; instance from a sequence or iterable. For example:&lt;br /&gt;&lt;pre class="python" name="code"&gt;cred_tuple = ('mario', 'secret')&lt;br /&gt;credential = Credential._make(cred_tuple)&lt;br /&gt;&lt;/pre&gt;There are more interesting use-cases and examples in the documentation, so I suggest that you take a peek.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Comments&lt;/b&gt;&lt;br /&gt;I think the named tuple is useful. They remove the error-prone indexing in tuples by providingaccess to fields by name without adding any memory overhead. They are also regular Python classes which means you can do anything you can do with classes.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-3402078798489989207?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/3402078798489989207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/01/named-tuple.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3402078798489989207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3402078798489989207'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/01/named-tuple.html' title='Named tuple'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-74856748239045839</id><published>2010-01-20T21:31:00.000+01:00</published><updated>2010-01-20T21:31:46.026+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='gmonitor'/><category scheme='http://www.blogger.com/atom/ns#' term='WebKit'/><category scheme='http://www.blogger.com/atom/ns#' term='gmail'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><title type='text'>More fun with QWebKit</title><content type='html'>In the previous &lt;a href="http://pysnippet.blogspot.com/2010/01/calling-python-from-javascript-in-pyqts.html"&gt;post&lt;/a&gt; I wrote about calling python methods and accessingproperties from JavaScript executed in QWebKit. In this post I'll showyou how to do the other way around, calling a JavaScript function fromPython.&lt;br /&gt;&lt;br /&gt;I would like to thank Rich Moore for &lt;a href="http://pysnippet.blogspot.com/2010/01/calling-python-from-javascript-in-pyqts.html?showComment=1263736779561#c2241955066500325004"&gt;commenting&lt;/a&gt; on the previous post and pointing out that you should re-add your python object&amp;nbsp; every time the  &lt;a href="http://qt.nokia.com/doc/4.5/qwebframe.html#javaScriptWindowObjectCleared"&gt;javaScriptWindowObjectCleared()&lt;/a&gt; signal is emitted.&lt;br /&gt;&lt;br /&gt;Actually, I think this is almost to simple to do a post about so I'll try doing something fun/useful with it.&lt;br /&gt;&lt;br /&gt;QWebFrame contains a public slot:&lt;br /&gt;&lt;pre class="c++" name="code"&gt;QVariant evaluateJavaScript(const QString &amp;amp; scriptSource)&lt;br /&gt;&lt;/pre&gt;You simply pass the JavaScript-snippet to the function. The snippet will be evaluated using the frame as context and returns the result of the last executed statement. This makes it possible to 'click' submit buttons, fill form fields and other interesting stuff in the currently loaded frame. I'll give you an example on how you can create an auto-login GMail widget. The widget will automatically login the user by populating the login form with the user's credentials and 'click' the login button.&lt;br /&gt;&lt;pre class="python" name="code"&gt;#!/usr/bin/env python&lt;br /&gt;from PyQt4 import QtCore&lt;br /&gt;from PyQt4 import QtGui&lt;br /&gt;from PyQt4 import QtWebKit&lt;br /&gt;&lt;br /&gt;javaScriptLogin = """&lt;br /&gt;document.getElementsByName('Email').item(0).value='{email}';&lt;br /&gt;document.getElementsByName('Passwd').item(0).value='{password}';&lt;br /&gt;document.getElementsByName('signIn').item(0).click(); void(0);&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;class GmailWebView(QtWebKit.QWebView):&lt;br /&gt;    def __init__(self, parent=None):&lt;br /&gt;        super(GmailWebView, self).__init__(parent)&lt;br /&gt;        self.loggedIn = False&lt;br /&gt;&lt;br /&gt;    def login(self, url, email, password):&lt;br /&gt;        """Login to gmail."""&lt;br /&gt;        self.url = QtCore.QUrl(url)&lt;br /&gt;        self.email = email&lt;br /&gt;        self.password = password  &lt;br /&gt;        self.loadFinished.connect(self._loadFinished)&lt;br /&gt;        self.load(self.url)&lt;br /&gt;&lt;br /&gt;    def createWindow(self, windowType):&lt;br /&gt;        """Load links in the same web-view."""&lt;br /&gt;        return self&lt;br /&gt;&lt;br /&gt;    def _loadFinished(self):&lt;br /&gt;        if self.loggedIn:&lt;br /&gt;            self.loadFinished.disconnect(self._loadFinished)&lt;br /&gt;&lt;br /&gt;        self.loggedIn = True&lt;br /&gt;        jscript = javaScriptLogin.format(email=self.email, password=self.password)&lt;br /&gt;        self.page().mainFrame().evaluateJavaScript(jscript)&lt;br /&gt;&lt;br /&gt;    def contextMenuEvent(self, event):&lt;br /&gt;        """Add a 'Back to GMail' entry."""&lt;br /&gt;        menu = self.page().createStandardContextMenu()&lt;br /&gt;        menu.addSeparator()&lt;br /&gt;        action = menu.addAction('Back to GMail')&lt;br /&gt;        @action.triggered.connect&lt;br /&gt;        def backToGMail():&lt;br /&gt;            self.load(self.url)&lt;br /&gt;        menu.exec_(QtGui.QCursor.pos())&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    import sys&lt;br /&gt;    qApp = QtGui.QApplication(sys.argv)&lt;br /&gt;&lt;br /&gt;    # Prevents me from posting my password on the blog :)&lt;br /&gt;    password, ok = QtGui.QInputDialog.getText(None, "Password request", "Enter password", QtGui.QLineEdit.Password)&lt;br /&gt;    if not ok:&lt;br /&gt;        return&lt;br /&gt;&lt;br /&gt;    gmailWebView = GmailWebView()&lt;br /&gt;    gmailWebView.login('https://mail.google.com/mail',&lt;br /&gt;                       'mario.boikov',&lt;br /&gt;                       password)&lt;br /&gt;    gmailWebView.show()&lt;br /&gt;&lt;br /&gt;    sys.exit(qApp.exec_())&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;This is just a quick hack, it lacks a bunch of checkings...&lt;/i&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Maybe it's time to wipe the dust off my &lt;a href="http://pysnippet.blogspot.com/2009/11/gmonitor.html"&gt;GMonitor&lt;/a&gt;-plasmoid and add support for opening my account in a new window based on the GMail widget above.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-74856748239045839?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/74856748239045839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/01/more-fun-with-qwebkit.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/74856748239045839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/74856748239045839'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/01/more-fun-with-qwebkit.html' title='More fun with QWebKit'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7553552088546864765</id><published>2010-01-16T08:31:00.000+01:00</published><updated>2010-01-16T08:31:37.555+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='WebKit'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Calling Python from JavaScript in PyQt's QWebkit</title><content type='html'>QtWebKit makes it very easy to expose methods and properties implemented in Python to JavaScript. Qt will automatically expose Qt-slots and Qt-properties to a JavaScript when a QObject is made available in the frame's JavaScript context.&lt;br /&gt;&lt;br /&gt;I think the code speaks for itself&lt;br /&gt;&lt;pre class="python" name="code"&gt;import sys&lt;br /&gt;from PyQt4 import QtCore, QtGui, QtWebKit&lt;br /&gt;&lt;br /&gt;"""Html snippet."""&lt;br /&gt;html = """&lt;br /&gt;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&lt;br /&gt;  &amp;lt;center&amp;gt;&lt;br /&gt;  &amp;lt;script language="JavaScript"&amp;gt;&lt;br /&gt;    document.write('&amp;lt;p&amp;gt;Python ' + pyObj.pyVersion + '&amp;lt;/p&amp;gt;')&lt;br /&gt;  &amp;lt;/script&amp;gt;&lt;br /&gt;  &amp;lt;button onClick="pyObj.showMessage('Hello from WebKit')"&amp;gt;Press me&amp;lt;/button&amp;gt;&lt;br /&gt;  &amp;lt;/center&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;br /&gt;"""&lt;br /&gt;&lt;br /&gt;class StupidClass(QtCore.QObject):&lt;br /&gt;    """Simple class with one slot and one read-only property."""&lt;br /&gt;&lt;br /&gt;    @QtCore.pyqtSlot(str)&lt;br /&gt;    def showMessage(self, msg):&lt;br /&gt;        """Open a message box and display the specified message."""&lt;br /&gt;        QtGui.QMessageBox.information(None, "Info", msg)&lt;br /&gt;&lt;br /&gt;    def _pyVersion(self):&lt;br /&gt;        """Return the Python version."""&lt;br /&gt;        return sys.version&lt;br /&gt;&lt;br /&gt;    """Python interpreter version property."""&lt;br /&gt;    pyVersion = QtCore.pyqtProperty(str, fget=_pyVersion)&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    app = QtGui.QApplication(sys.argv)&lt;br /&gt;&lt;br /&gt;    myObj = StupidClass()&lt;br /&gt;&lt;br /&gt;    webView = QtWebKit.QWebView()&lt;br /&gt;    # Make myObj exposed as JavaScript object named 'pyObj'&lt;br /&gt;    webView.page().mainFrame().addToJavaScriptWindowObject("pyObj", myObj)&lt;br /&gt;    webView.setHtml(html)&lt;br /&gt;&lt;br /&gt;    window = QtGui.QMainWindow()&lt;br /&gt;    window.setCentralWidget(webView)&lt;br /&gt;    window.show()&lt;br /&gt;&lt;br /&gt;    sys.exit(app.exec_())&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;Some references:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://qt.nokia.com/doc/4.5/qtwebkit.html"&gt;QtWebKit&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://qt.nokia.com/doc/4.5/qwebframe.html#addToJavaScriptWindowObject"&gt;void QWebFrame::addToJavaScriptWindowObject ()&lt;/a&gt;&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html#integrating-python-and-javascript-in-qtwebkit"&gt;Integrating Python and JavaScript in QtWebKit&lt;/a&gt;&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7553552088546864765?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7553552088546864765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/01/calling-python-from-javascript-in-pyqts.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7553552088546864765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7553552088546864765'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/01/calling-python-from-javascript-in-pyqts.html' title='Calling Python from JavaScript in PyQt&apos;s QWebkit'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7549922284587958145</id><published>2010-01-14T20:22:00.001+01:00</published><updated>2011-01-25T20:16:44.741+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>New-style PyQt Signals and Slots</title><content type='html'>I was to lazy to take a look at the &lt;i&gt;new-style&lt;/i&gt; signal and slot support which was introduced in PyQt 4.5 until yesterday. I did know that there were something called &lt;i&gt;new-style&lt;/i&gt; signals and slots but that was the end of the story. Now I have taken the time and I think it's a cleaner solution than the &lt;i&gt;old-style&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;I'll just give you a short intro to whet your appetite, find all details &lt;a href="http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/pyqt4ref.html#new-style-signal-and-slot-support"&gt;here&lt;/a&gt; yourself.&lt;br /&gt;&lt;pre class="python" name="code"&gt;import sys&lt;br /&gt;from PyQt4 import QtCore&lt;br /&gt;from PyQt4 import QtGui&lt;br /&gt;&lt;br /&gt;def clicked():&lt;br /&gt;    print "Button Clicked"&lt;br /&gt;&lt;br /&gt;qApp = QtGui.QApplication(sys.argv)&lt;br /&gt;&lt;br /&gt;button = QtGui.QPushButton("Click Me")&lt;br /&gt;QtCore.QObject.connect(button, QtCore.SIGNAL('clicked()'), clicked)&lt;br /&gt;button.show()&lt;br /&gt;&lt;br /&gt;sys.exit(qApp.exec_())&lt;br /&gt;&lt;/pre&gt;This is the old way of connecting a signal to a slot. To use the &lt;i&gt;new-style&lt;/i&gt; support just replace line 11 with following code&lt;br /&gt;&lt;pre class="python" name="code"&gt;button.clicked.connect(clicked)&lt;br /&gt;&lt;/pre&gt;The &lt;i&gt;new-style&lt;/i&gt; support introduces an attribute with the same name as the signal, in this case &lt;i&gt;clicked&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;If you need to define your own signal you'll do something like this (off the top of my head):&lt;br /&gt;&lt;pre class="python" name="code"&gt;class X(QtCore.QObject):&lt;br /&gt;    mySignal = QtCore.pyqtSignal(int)&lt;br /&gt;&lt;br /&gt;    def emitMySignal(self):&lt;br /&gt;        self.mySignal.emit(100)&lt;br /&gt;&lt;/pre&gt;And the old way:&lt;br /&gt;&lt;pre class="python" name="code"&gt;class X(QtCore.QObject):&lt;br /&gt;    def emitMySignal(self):&lt;br /&gt;        self.emit(QtCore.SIGNAL('mySignal'), 100)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;IMHO the &lt;i&gt;new-style&lt;/i&gt; support is more pythonic and you don't have to specify your signals as strings when connecting. If you use pydev (eclipse) you'll also have completion support for signals.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7549922284587958145?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7549922284587958145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/01/new-style-pyqt-signals-and-slots.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7549922284587958145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7549922284587958145'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/01/new-style-pyqt-signals-and-slots.html' title='New-style PyQt Signals and Slots'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-1909408191673918763</id><published>2010-01-08T12:47:00.002+01:00</published><updated>2010-01-08T12:51:58.801+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='KDE'/><category scheme='http://www.blogger.com/atom/ns#' term='gphoto'/><category scheme='http://www.blogger.com/atom/ns#' term='digikam'/><title type='text'>Digikam  - Light table</title><content type='html'>This post is somewhat related to the post about &lt;a href="http://pysnippet.blogspot.com/2009/12/when-ctypes-comes-to-rescue.html"&gt;ctypes and gphoto&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Background&lt;/b&gt; &lt;br /&gt;I consider myself a pre-amateur photographer and that means I'm taking a lot of shots just to learn how the camera works. I own a Canon EOS 1000D and have recently moved from the &lt;i&gt;fully automatic exposure modes&lt;/i&gt; to the &lt;i&gt;advanced exposure modes&lt;/i&gt;, and to be more specific, the &lt;i&gt;programmed auto exposure&lt;/i&gt; mode.&lt;br /&gt;&lt;br /&gt;So, I needed an application that could load a couple of pictures, showing two pictures side-by-side for visual comparison and at the same time presenting the more important meta-data attributes such as &lt;i&gt;focal length&lt;/i&gt;, &lt;i&gt;exposure time&lt;/i&gt;, &lt;i&gt;ISO&lt;/i&gt; and &lt;i&gt;aperture.&lt;/i&gt; I considered doing such application myself and that's why I came across and started to explore gphoto. But luckily, I managed to stop myself and took yet another look at what &lt;a href="http://www.digikam.org/"&gt;Digikam&lt;/a&gt; offers. I'm a die-hard KDE-fan and have been using digikam, which is a KDE photo manager application, for quite some time to organize my photos but never actually used any other features except removing red-eyes.&lt;br /&gt;&lt;br /&gt;To my surprise digikam included exactly the feature I was looking for! It's called the &lt;i&gt;Light Table&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Light Table&lt;/b&gt; &lt;br /&gt;Digikam allows you to select a couple of pictures which can be placed onto the light table, as shown in the screenshot below.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_ilsnPCKxGBM/S0cHdlP7WXI/AAAAAAAAABk/kRYBeUS5Xzg/s1600-h/place_onto.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_ilsnPCKxGBM/S0cHdlP7WXI/AAAAAAAAABk/kRYBeUS5Xzg/s400/place_onto.jpg" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;By choosing &lt;i&gt;Place onto Light Table&lt;/i&gt; a new window is opened with, in this case, the three pictures loaded and two of them shown side-by-side.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_ilsnPCKxGBM/S0cIpd_3loI/AAAAAAAAABs/BHNdgncMyPw/s1600-h/light_table.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_ilsnPCKxGBM/S0cIpd_3loI/AAAAAAAAABs/BHNdgncMyPw/s400/light_table.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The light table also displays the meta-data for the respective picture (sorry about that some of the attributes are in Swedish) that are currently shown. You can synchronize operations like zooming and panning. When you zoom, both pictures will be zoomed to the same level and when panning, both pictures will be panned to the same area, really nice.&lt;br /&gt;&lt;br /&gt;This was exactly what I was looking for. Now I can take a couple of shots with different settings, for example, changing the &lt;i&gt;white balance&lt;/i&gt;, &lt;i&gt;ISO&lt;/i&gt; and/or other parameters and compare the result rather easily. I get both a visual and a meta-data diff in the same view.&lt;br /&gt;&lt;br /&gt;I just wanted to share my findings and hope this post was to some use for others that are looking for something similar.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-1909408191673918763?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/1909408191673918763/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/01/digikam-light-table.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1909408191673918763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1909408191673918763'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/01/digikam-light-table.html' title='Digikam  - Light table'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_ilsnPCKxGBM/S0cHdlP7WXI/AAAAAAAAABk/kRYBeUS5Xzg/s72-c/place_onto.jpg' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4422436960843509648</id><published>2010-01-05T14:52:00.000+01:00</published><updated>2010-01-05T14:52:55.273+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='profiling'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Let's timeit!</title><content type='html'>Every now and then you might want to time snippets just to make sure that you choose the more efficient solution. In those cases you can use the &lt;a href="http://docs.python.org/library/timeit.html"&gt;&lt;i&gt;timeit&lt;/i&gt;&lt;/a&gt; module to measure execution time for snippets.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Introduction&lt;/b&gt;&lt;br /&gt;It's very easy to setup and measure the execution time for a snippet with &lt;i&gt;timeit&lt;/i&gt;. The module contains a class, &lt;i&gt;Timer&lt;/i&gt;, which is used to perform the measurement. The class has one constructor and three methods:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Timer([stmt='pass'[, setup='pass'[, timer=&amp;lt;timer function&amp;gt;]]]) - &lt;i&gt;stmt&lt;/i&gt; is the statement to be timed and &lt;i&gt;setup&lt;/i&gt; is called once before executing the main statement. A timer function can be specified and default is &lt;i&gt;time.time()&lt;/i&gt; for all platforms but windows which is set to &lt;i&gt;time.clock()&lt;/i&gt; instead (according to my &lt;i&gt;timeit.py&lt;/i&gt;)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;timeit([number=1000000]) - Executes the main statement passed to the constructor &lt;i&gt;number&lt;/i&gt; of times and returns the result in seconds as a float. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;repeat([repeat=3[, number=1000000]]) -&amp;nbsp;Convenience function that calls &lt;i&gt;timeit(number)&lt;/i&gt; &lt;i&gt;repeat&lt;/i&gt; times. Returns a list with the results.&lt;/li&gt;&lt;li&gt;print_exc([file=None]) - Helper to print a traceback from the timed snippet.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Starting with Python 2.6 the &lt;i&gt;timeit&lt;/i&gt; module also defines two convenience functions, &lt;i&gt;timeit.timeit() and timeit.repeat(). &lt;/i&gt;They are basically wrappers around the &lt;i&gt;Timer&lt;/i&gt; class.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Example&lt;/b&gt;&lt;br /&gt;Suppose that I would like to create a list containing 100 'c':s like this ['c', 'c', ...]. There are at least two ways of doing this:&lt;br /&gt;&lt;pre class="python" name="code"&gt;lst = ['c'] * 100&lt;br /&gt;# or&lt;br /&gt;lst = ['c' for i in xrange(100)]&lt;br /&gt;&lt;/pre&gt;Which one should I choose? Well, let's execute both statements with &lt;i&gt;timeit &lt;/i&gt;and measure the execution time.&lt;br /&gt;&lt;pre class="python" name="code"&gt;&amp;gt;&amp;gt;&amp;gt; import timeit&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; t = timeit.Timer(stmt="lst = ['c'] * 100")&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; print t.timeit()&lt;br /&gt;1.10580182076&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; t = timeit.Timer(stmt="lst = ['c' for x in xrange(100)]")&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; print t.timeit()&lt;br /&gt;7.66900897026&lt;br /&gt;&lt;/pre&gt;Ok, I &lt;i&gt;think&lt;/i&gt; I'll stick with the first snippet :)&lt;br /&gt;&lt;br /&gt;The result returned is the total execution time in seconds. In this particular case when we are executing the snippet 1000000 times the result is also the execution time in microseconds for one single pass (1000000*exe_time/1000000 == exe_time).&lt;br /&gt; &lt;br /&gt;Normally, the &lt;i&gt;timeit&lt;/i&gt; module doesn't have access to things that you have defined in your module. If you would like to measure a function that you have defined in your module you can specify the import statement in the &lt;i&gt;setup&lt;/i&gt; parameter:&lt;br /&gt;&lt;pre class="python" name="code"&gt;&amp;gt;&amp;gt;&amp;gt; def create_lst(size):&lt;br /&gt;...    return ['c'] * size&lt;br /&gt;...&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; t = timeit.Timer(stmt="create_lst(100)", setup="from __main__ import create_lst")&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; print t.timeit()&lt;br /&gt;1.21339488029&lt;br /&gt;&lt;/pre&gt;This will introduce a little overhead since the &lt;i&gt;create_lst()&lt;/i&gt; function is called in the measurement loop instead of just executing an inlined snippet.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;Note: &lt;i&gt;Timer.timeit()&lt;/i&gt; will by default disable garbage collection during timing. To enable GC you can pass '&lt;i&gt;gc.enable()&lt;/i&gt;' as a &lt;i&gt;setup&lt;/i&gt;&lt;/span&gt; statement.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;I find the &lt;i&gt;timeit&lt;/i&gt; module as a simple and convenient way to measure execution time for small snippets.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4422436960843509648?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4422436960843509648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2010/01/lets-timeit.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4422436960843509648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4422436960843509648'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2010/01/lets-timeit.html' title='Let&apos;s timeit!'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4809216108680629752</id><published>2009-12-29T21:13:00.000+01:00</published><updated>2009-12-29T21:13:51.991+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ctypes'/><category scheme='http://www.blogger.com/atom/ns#' term='gphoto'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='FUSE'/><title type='text'>When ctypes comes to the rescue</title><content type='html'>I recently purchased a DSLR (Canon 1000D) and almost at the same time I read an article about remote controlling your camera using &lt;a href="http://www.gphoto.org/"&gt;gphoto&lt;/a&gt;. I tried it out and thought it was cool. During the holidays I had some time over to spend on hacking and wanted to try controlling my camera from Python. To my disappointment there were no Python bindings included with Ubuntu for gphoto. I did some googling but couldn't find any pre-compiled bindings, what to do?&lt;br /&gt;&lt;br /&gt;Well, I could always try doing it with &lt;a href="http://docs.python.org/library/ctypes.html"&gt;ctypes&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;From the docs:&lt;br /&gt;&lt;blockquote&gt;&lt;tt class="docutils literal"&gt;ctypes&lt;/tt&gt; is a foreign function library for Python.  It provides C compatibledata types, and allows calling functions in DLLs or shared libraries.  It can beused to wrap these libraries in pure Python.&lt;br /&gt;&lt;/blockquote&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;i&gt;Note: I haven't done any serious stuff with ctypes nor gphoto before so if you find any errors etc please post a comment. &lt;/i&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;It amazed my how easy it is to use ctypes. Here's a snippet that will take a picture (from the first camera found), download the image to local storage and then delete it from the camera's storage.&lt;br /&gt;&lt;pre class="python" name="code"&gt;import ctypes&lt;br /&gt;import os&lt;br /&gt;&lt;br /&gt;# gphoto structures&lt;br /&gt;""" From 'gphoto2-camera.h'&lt;br /&gt;typedef struct {&lt;br /&gt;        char name [128];&lt;br /&gt;        char folder [1024];&lt;br /&gt;} CameraFilePath;&lt;br /&gt;"""&lt;br /&gt;class CameraFilePath(ctypes.Structure):&lt;br /&gt;    _fields_ = [('name', (ctypes.c_char * 128)),&lt;br /&gt;                ('folder', (ctypes.c_char * 1024))]&lt;br /&gt;&lt;br /&gt;# gphoto constants&lt;br /&gt;# Defined in 'gphoto2-port-result.h'&lt;br /&gt;GP_OK = 0&lt;br /&gt;# CameraCaptureType enum in 'gphoto2-camera.h'&lt;br /&gt;GP_CAPTURE_IMAGE = 0&lt;br /&gt;# CameraFileType enum in 'gphoto2-file.h'&lt;br /&gt;GP_FILE_TYPE_NORMAL = 1&lt;br /&gt;&lt;br /&gt;# Load library&lt;br /&gt;gp = ctypes.CDLL('libgphoto2.so.2')&lt;br /&gt;&lt;br /&gt;# Init camera&lt;br /&gt;context = gp.gp_context_new()&lt;br /&gt;camera = ctypes.c_void_p()&lt;br /&gt;gp.gp_camera_new(ctypes.pointer(camera))&lt;br /&gt;gp.gp_camera_init(camera, context)&lt;br /&gt;&lt;br /&gt;# Capture image&lt;br /&gt;cam_path = CameraFilePath()&lt;br /&gt;gp.gp_camera_capture(camera,&lt;br /&gt;                     GP_CAPTURE_IMAGE,&lt;br /&gt;                     ctypes.pointer(cam_path),&lt;br /&gt;                     context)&lt;br /&gt;&lt;br /&gt;# Download and delete&lt;br /&gt;cam_file = ctypes.c_void_p()&lt;br /&gt;fd = os.open('image.jpg', os.O_CREAT | os.O_WRONLY)&lt;br /&gt;gp.gp_file_new_from_fd(ctypes.pointer(cam_file), fd)&lt;br /&gt;gp.gp_camera_file_get(camera,&lt;br /&gt;                      cam_path.folder,&lt;br /&gt;                      cam_path.name,&lt;br /&gt;                      GP_FILE_TYPE_NORMAL,&lt;br /&gt;                      cam_file,&lt;br /&gt;                      context)&lt;br /&gt;gp.gp_camera_file_delete(camera,&lt;br /&gt;                         cam_path.folder,&lt;br /&gt;                         cam_path.name,&lt;br /&gt;                         context)&lt;br /&gt;gp.gp_file_unref(cam_file)&lt;br /&gt;&lt;br /&gt;# Release the camera&lt;br /&gt;gp.gp_camera_exit(camera, context)&lt;br /&gt;gp.gp_camera_unref(camera)&lt;br /&gt;&lt;/pre&gt;Ok, remember that I haven't done any wrapper or anything, it almost looks like 'C' code. I have also skipped all error checking for brevity.&lt;br /&gt;&lt;br /&gt;You can always use a &lt;i&gt;c_void_p&lt;/i&gt; if you don't need access to the data in Python (if you only need to pass a pointer between foreign functions). I'm using &lt;i&gt;c_void_p&lt;/i&gt; instead of defining ctypes structures for gphoto's data types such as Camera and CameraFile. I still had to define CameraFilePath since I needed access to the data in Python.&lt;br /&gt;&lt;br /&gt;I really like ctypes because I don't have to maintain code in C to access native functionality. Maybe you won't get the same performance as with traditional bindings but in this particular case it's not an issue.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4809216108680629752?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4809216108680629752/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/12/when-ctypes-comes-to-rescue.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4809216108680629752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4809216108680629752'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/12/when-ctypes-comes-to-rescue.html' title='When ctypes comes to the rescue'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6782164637252103417</id><published>2009-12-22T09:04:00.000+01:00</published><updated>2009-12-22T09:04:53.313+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Hello Planet Python!</title><content type='html'>I just got added to Planet Python and want to give the readers a quick introduction to myself.&lt;br /&gt;&lt;br /&gt;My name is Mario and I live in Sweden. I've been working with Java development since 1999, mainly with enterprise systems. In 2007 I switched to embedded development and primary focusing on embedded Linux systems.&lt;br /&gt;&lt;br /&gt;Early this year (2009) I got curious about developing KDE/QT applications since I've been using the KDE desktop for years now. I didn't want to use Java nor C++ for desktop development, I needed a language which was simple to use, feature rich and well supported by QT and KDE. There were two candidates for the job, Ruby and Python, guess which one I choosed.&lt;br /&gt;&lt;br /&gt;I really like Python and it feels fun coding again, it's my first choice of programming language now.&lt;br /&gt;&lt;br /&gt;On my blog you'll find posts about everything regarding Python, language features, APIs, frameworks, etc. I hope you'll enjoy reading.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6782164637252103417?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/6782164637252103417/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/12/hello-planet-python.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6782164637252103417'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6782164637252103417'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/12/hello-planet-python.html' title='Hello Planet Python!'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4482607412708661983</id><published>2009-12-15T08:03:00.002+01:00</published><updated>2009-12-15T08:09:26.340+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>try/for/while else... else what?</title><content type='html'>The Python language actually has support for &lt;i&gt;else&lt;/i&gt;-clauses in some &lt;a href="http://docs.python.org/reference/compound_stmts.html"&gt;compound statements&lt;/a&gt; such as &lt;i&gt;try&lt;/i&gt;, &lt;i&gt;for&lt;/i&gt; and &lt;i&gt;while&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;So how do you use them?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I'll illustrate the &lt;i&gt;for&lt;/i&gt; usage with an example:&lt;br /&gt;&lt;pre class="python" name="code"&gt;def has_only_alpha_string(lst):&lt;br /&gt;    for s in lst:&lt;br /&gt;        if not s.isalpha():&lt;br /&gt;            print '[{0}] contains non alphabetic character'.format(s)&lt;br /&gt;            break&lt;br /&gt;    else:&lt;br /&gt;        print 'All strings contains only alphabetic characters'&lt;br /&gt;&lt;br /&gt;lst = ['Hello', 'World!']&lt;br /&gt;&lt;br /&gt;only_alpha_string(lst)&lt;br /&gt;&lt;/pre&gt;If &lt;i&gt;lst&lt;/i&gt; is empty or exhausted the execution will continue in the &lt;i&gt;else&lt;/i&gt;-clause. If the &lt;i&gt;break&lt;/i&gt; is executed, the &lt;i&gt;else&lt;/i&gt;-clause will not be executed. The same applies to &lt;i&gt;while&lt;/i&gt; statements, the &lt;i&gt;else&lt;/i&gt; is only executed if no &lt;i&gt;break&lt;/i&gt; is executed within the &lt;i&gt;while&lt;/i&gt; statement.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: small;"&gt;Note: It's valid to have a &lt;i&gt;for/while-else&lt;/i&gt; without a &lt;i&gt;break&lt;/i&gt;. In that case the &lt;i&gt;else&lt;/i&gt;-clause will always be executed. &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The first time I tried it I got it all wrong (before actually reading the documentation). I expected that the &lt;i&gt;else&lt;/i&gt;-clause should be executed only if the list sequence was empty or exhausted, but it's the other way around.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;What about try-else?&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;For me, at least, the &lt;i&gt;try-else&lt;/i&gt; is probably a bit more intuitive.&lt;br /&gt;&lt;pre class="python" name="code"&gt;try:&lt;br /&gt;    f = None&lt;br /&gt;    f = open('a_file', 'r')&lt;br /&gt;except IOError as err:&lt;br /&gt;    print err&lt;br /&gt;else:&lt;br /&gt;    print f.read()&lt;br /&gt;finally:&lt;br /&gt;    if f:&lt;br /&gt;        print "Closing file..."&lt;br /&gt;        f.close()&lt;br /&gt;&lt;/pre&gt;If the &lt;i&gt;open&lt;/i&gt; call is successful the &lt;i&gt;else&lt;/i&gt;-clause is executed and of course if the &lt;i&gt;open&lt;/i&gt; call raises an &lt;i&gt;IOError&lt;/i&gt; exception the &lt;i&gt;else&lt;/i&gt;-clause isn't executed. Both &lt;i&gt;open&lt;/i&gt; and &lt;i&gt;read&lt;/i&gt; can raise &lt;i&gt;IOError&lt;/i&gt; exceptions, the &lt;i&gt;finally&lt;/i&gt;-clause will always be executed even if an exception occurs in the &lt;i&gt;else&lt;/i&gt;-clause. In this particular case I'm only interested in catching the exception raised by &lt;i&gt;open&lt;/i&gt;. If &lt;i&gt;read&lt;/i&gt; raises an exception, it should be forwarded to the caller.&lt;br /&gt;&lt;br /&gt;I could also write the code as following:&lt;br /&gt;&lt;pre class="python" name="code"&gt;try:&lt;br /&gt;    f = None&lt;br /&gt;    f = open('a_file', 'r')&lt;br /&gt;except IOError as err:&lt;br /&gt;    print err&lt;br /&gt;    # return or os.exit()&lt;br /&gt;&lt;br /&gt;print f.read()&lt;br /&gt;print "Closing file..."&lt;br /&gt;f.close()&lt;br /&gt;&lt;/pre&gt;The problem with this solution is that file wouldn't be closed if &lt;i&gt;read&lt;/i&gt; raises an exception.&lt;br /&gt;&lt;br /&gt;Hope this post made the &lt;i&gt;else&lt;/i&gt;-clause thing in combination with &lt;i&gt;try/for/while&lt;/i&gt; more clear.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4482607412708661983?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4482607412708661983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/12/tryforwhile-else-else-what.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4482607412708661983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4482607412708661983'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/12/tryforwhile-else-else-what.html' title='try/for/while else... else what?'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6038914494172451640</id><published>2009-12-08T21:28:00.002+01:00</published><updated>2009-12-08T21:40:28.864+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='profiling'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Profiling your Python code</title><content type='html'>Python provides support for &lt;a href="http://docs.python.org/library/profile.html#what-is-deterministic-profiling"&gt;deterministic profiling&lt;/a&gt; of your application. It's very easy to setup and use. To start profiling your code, two python modules are used:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&amp;nbsp;&lt;i&gt;cProfile&lt;/i&gt; - the application that collects profiling data&lt;/li&gt;&lt;li&gt;&lt;i&gt;pstats&lt;/i&gt; - the application that makes the profling data human readable &lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;i&gt;&lt;/i&gt;You can read more about Python profiling stuff at &lt;i&gt;&lt;a href="http://docs.python.org/library/profile.html"&gt;The Python Profilers&lt;/a&gt;&lt;/i&gt; page.&lt;br /&gt;&lt;br /&gt;I'll show an example of how you can use the profiler.&lt;br /&gt;&lt;br /&gt;Say I need to calculate the sum of all odd numbers from zero to an arbitrary positive value. My initial code might end up something like this:&lt;br /&gt;&lt;pre class="python" name="code"&gt;def odd_numbers(max):&lt;br /&gt;    """ Returns a list with all odd numbers between 0 to max (inclusive) """&lt;br /&gt;    l = list()&lt;br /&gt;    for i in xrange(max+1):&lt;br /&gt;        if (i &amp;amp; 1):&lt;br /&gt;            l.append(i)&lt;br /&gt;    return l&lt;br /&gt;&lt;br /&gt;def sum_odd_numbers(max):&lt;br /&gt;    """ Sum all odd numbers between 0 to max (inclusive) """&lt;br /&gt;    odd_nbrs = odd_numbers(max)&lt;br /&gt;&lt;br /&gt;    res = 0&lt;br /&gt;    for odd in odd_nbrs:&lt;br /&gt;        res += odd&lt;br /&gt;    return res&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    # Run this 100 times to make it measurable&lt;br /&gt;    for i in xrange(100):&lt;br /&gt;        print sum_odd_numbers(1024)&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;Now I want to find out where my code spend most if its time to help me optimize the code if possible. To profile this snippet I run:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ python -m cProfile sum_odd.py&lt;br /&gt;&lt;/pre&gt;This will output some statistics about the code (try it), but I'll show you a more handy way to browse and examine the profile dump.&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ python -m cProfile -o profile_dump sum_odd.py&lt;br /&gt;&lt;/pre&gt;This will output the profiling statistics to a file (in non-human readable format) which can be loaded and examined with &lt;i&gt;pstats&lt;/i&gt;.Start &lt;i&gt;pstats&lt;/i&gt; and browse the profile dump:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ python -m pstats&lt;br /&gt;Welcome to the profile statistics browser.                    &lt;br /&gt;% help                                                        &lt;br /&gt;&lt;br /&gt;Documented commands (type help topic):&lt;br /&gt;========================================&lt;br /&gt;EOF  add  callees  callers  quit  read  reverse  sort  stats  strip&lt;br /&gt;&lt;br /&gt;Undocumented commands:&lt;br /&gt;======================&lt;br /&gt;help&lt;br /&gt;&lt;br /&gt;% read profile_dump&lt;br /&gt;profile_dump% stats&lt;br /&gt;Tue Dec  8 20:55:41 2009    profile_dump&lt;br /&gt;&lt;br /&gt;         51405 function calls in 0.186 CPU seconds&lt;br /&gt;&lt;br /&gt;   Random listing order was used&lt;br /&gt;&lt;br /&gt;   ncalls  tottime  percall  cumtime  percall filename:lineno(function)&lt;br /&gt;    51200    0.082    0.000    0.082    0.000 {method 'append' of 'list' objects}&lt;br /&gt;      100    0.099    0.001    0.181    0.002 main.py:1(odd_numbers)&lt;br /&gt;        1    0.000    0.000    0.185    0.185 main.py:1(module)&lt;br /&gt;        1    0.000    0.000    0.186    0.186 {execfile}&lt;br /&gt;      100    0.004    0.000    0.184    0.002 main.py:9(sum_odd_numbers)&lt;br /&gt;        1    0.000    0.000    0.186    0.186 string:1(module)&lt;br /&gt;        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}&lt;br /&gt;        1    0.001    0.001    0.185    0.185 main.py:18(main)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;profile_dump%&lt;br /&gt;&lt;/pre&gt;This will of course give the same information as if you just executed &lt;i&gt;cProfile&lt;/i&gt; without specifying an output file. The advantage of using &lt;i&gt;pstats&lt;/i&gt; interactively is that you can view the data in different ways.&lt;br /&gt;&lt;br /&gt;Now I want to find out in which function we spend most time. This can be done by using the &lt;i&gt;sort&lt;/i&gt; command:&lt;br /&gt;&lt;br /&gt;&lt;pre class="bash" name="code"&gt;profile_dump% sort&lt;br /&gt;Valid sort keys (unique prefixes are accepted):&lt;br /&gt;stdname -- standard name&lt;br /&gt;nfl -- name/file/line&lt;br /&gt;pcalls -- call count&lt;br /&gt;file -- file name&lt;br /&gt;calls -- call count&lt;br /&gt;time -- internal time&lt;br /&gt;line -- line number&lt;br /&gt;cumulative -- cumulative time&lt;br /&gt;module -- file name&lt;br /&gt;name -- function name&lt;br /&gt;profile_dump% sort time&lt;br /&gt;profile_dump% stats&lt;br /&gt;Tue Dec  8 20:55:41 2009    profile_dump&lt;br /&gt;&lt;br /&gt;         51405 function calls in 0.186 CPU seconds&lt;br /&gt;&lt;br /&gt;   Ordered by: internal time&lt;br /&gt;&lt;br /&gt;   ncalls  tottime  percall  cumtime  percall filename:lineno(function)&lt;br /&gt;      100    0.099    0.001    0.181    0.002 main.py:1(odd_numbers)&lt;br /&gt;    51200    0.082    0.000    0.082    0.000 {method 'append' of 'list' objects}&lt;br /&gt;      100    0.004    0.000    0.184    0.002 main.py:9(sum_odd_numbers)&lt;br /&gt;        1    0.001    0.001    0.185    0.185 main.py:18(main)&lt;br /&gt;        1    0.000    0.000    0.186    0.186 {execfile}&lt;br /&gt;        1    0.000    0.000    0.185    0.185 main.py:1(module)&lt;br /&gt;        1    0.000    0.000    0.186    0.186 string:1(module)&lt;br /&gt;        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}&lt;br /&gt;&lt;/pre&gt;Nice! We can see that most time is spent in the &lt;i&gt;odd_numbers&lt;/i&gt; function. The &lt;i&gt;time&lt;/i&gt; key specifies that we would like to sort the data by the time spent in a function (exclusive calls to other functions).&lt;br /&gt;&lt;br /&gt;Time to optimize, change the &lt;i&gt;odd_numbers&lt;/i&gt; function to the following snippet:&lt;br /&gt;&lt;pre name="code" class="python"&gt;&lt;br /&gt;def odd_numbers(max):&lt;br /&gt;    """ Returns a list with all odd numbers between 0 to max (inclusive) """&lt;br /&gt;    return [i for i in xrange(max+1) if (i &amp; 1)]&lt;br /&gt;&lt;/pre&gt;Now profile the code and load the dump in &lt;i&gt;pstats&lt;/i&gt;:&lt;pre name="code" class="bash"&gt;&lt;br /&gt;profile_dump% stats&lt;br /&gt;Tue Dec  8 21:20:19 2009    profile_dump&lt;br /&gt;&lt;br /&gt;         205 function calls in 0.020 CPU seconds&lt;br /&gt;&lt;br /&gt;   Ordered by: internal time&lt;br /&gt;&lt;br /&gt;   ncalls  tottime  percall  cumtime  percall filename:lineno(function)&lt;br /&gt;      100    0.015    0.000    0.015    0.000 main.py:1(odd_numbers)&lt;br /&gt;      100    0.004    0.000    0.019    0.000 main.py:5(sum_odd_numbers)&lt;br /&gt;        1    0.001    0.001    0.020    0.020 main.py:14(main)&lt;br /&gt;        1    0.000    0.000    0.020    0.020 {execfile}&lt;br /&gt;        1    0.000    0.000    0.020    0.020 main.py:1(module)&lt;br /&gt;        1    0.000    0.000    0.020    0.020 string:1(module)&lt;br /&gt;        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}&lt;br /&gt;&lt;/pre&gt;Wow! Not that bad, we decreased the number of function calls from 51405 to 205. We also decreased the total time spent in the application from 0.186 to 0.020 CPU seconds by writting proper Python code :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6038914494172451640?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/6038914494172451640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/12/profiling-your-python-code.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6038914494172451640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6038914494172451640'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/12/profiling-your-python-code.html' title='Profiling your Python code'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-2217662127660893735</id><published>2009-12-01T21:09:00.002+01:00</published><updated>2009-12-01T21:13:33.271+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='pyshoutcast'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='FUSE'/><title type='text'>FUSE - Filesystem in Userspace part 3 (final)</title><content type='html'>Finally, as I promised, the last blog post on implementing file systems using FUSE.&lt;br /&gt;&lt;br /&gt;I've created a file system, &lt;i&gt;shoutcastfs,&lt;/i&gt; which enables you to mount the Shoutcast Radio directory as a file system. The genres are represented as directories and stations as files. Each file contains the station's playlist and the files are suffixed with &lt;i&gt;.pls&lt;/i&gt; which makes it possible to load the playlist in a media player such as &lt;a href="http://amarok.kde.org/"&gt;Amarok&lt;/a&gt; by double-clicking the file.&lt;br /&gt;&lt;br /&gt;Of course, I'm using &lt;a href="http://github.com/mariob/pyshoutcast/"&gt;pyshoutcast&lt;/a&gt; (Python shoutcast API) to access the shoutcast service.&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;#!/usr/bin/env python&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;import errno&lt;br /&gt;import fuse&lt;br /&gt;import stat&lt;br /&gt;import os&lt;br /&gt;import shoutcast&lt;br /&gt;&lt;br /&gt;fuse.fuse_python_api = (0, 2)&lt;br /&gt;&lt;br /&gt;_shoutcastApi = shoutcast.ShoutCast()&lt;br /&gt;&lt;br /&gt;class RootInfo(fuse.Stat):&lt;br /&gt;    def __init__(self):&lt;br /&gt;        fuse.Stat.__init__(self)&lt;br /&gt;        self.st_mode = stat.S_IFDIR | 0755&lt;br /&gt;        self.st_nlink = 2&lt;br /&gt;        self._genres = {}&lt;br /&gt;&lt;br /&gt;    @property&lt;br /&gt;    def genres(self):&lt;br /&gt;        if not self._genres:&lt;br /&gt;            for g in _shoutcastApi.genres():&lt;br /&gt;                self._genres[g] = GenreInfo(g)&lt;br /&gt;        return self._genres&lt;br /&gt;&lt;br /&gt;class GenreInfo(fuse.Stat):&lt;br /&gt;    def __init__(self, name):&lt;br /&gt;        fuse.Stat.__init__(self)&lt;br /&gt;        self.st_mode = stat.S_IFDIR | 0755&lt;br /&gt;        self.st_nlink = 2&lt;br /&gt;        self.name = name&lt;br /&gt;        self._stations = {}&lt;br /&gt;&lt;br /&gt;    @property&lt;br /&gt;    def stations(self):&lt;br /&gt;        if not self._stations:&lt;br /&gt;            for s in _shoutcastApi.stations(self.name):&lt;br /&gt;                name = '{0}.pls'.format(s[0])&lt;br /&gt;                name = name.replace('/', '|')&lt;br /&gt;                self._stations[name] = StationInfo(name, s[1])&lt;br /&gt;        return self._stations&lt;br /&gt;&lt;br /&gt;class StationInfo(fuse.Stat):&lt;br /&gt;    def __init__(self, name, station_id):&lt;br /&gt;        fuse.Stat.__init__(self)&lt;br /&gt;        self.st_mode = stat.S_IFREG | 0644&lt;br /&gt;        self.st_nlink = 1&lt;br /&gt;        # Hope no playlist exceeds this size&lt;br /&gt;        self.st_size = 4096&lt;br /&gt;        self.name = name&lt;br /&gt;        self.station_id = station_id&lt;br /&gt;        self._content = None&lt;br /&gt;&lt;br /&gt;    @property&lt;br /&gt;    def content(self):&lt;br /&gt;        if self._content is None:&lt;br /&gt;            self._content = _shoutcastApi.tune_in(self.station_id).read()&lt;br /&gt;        return self._content&lt;br /&gt;&lt;br /&gt;class ShoutcastFS(fuse.Fuse):&lt;br /&gt;    def __init__(self, *args, **kw):&lt;br /&gt;        fuse.Fuse.__init__(self, *args, **kw)&lt;br /&gt;        self.root = RootInfo()&lt;br /&gt;&lt;br /&gt;    def split_path(self, path):&lt;br /&gt;        """ Returns genre and station """&lt;br /&gt;        if path == '/':&lt;br /&gt;            return (None, None)&lt;br /&gt;&lt;br /&gt;        parts = path.split('/')[1:]&lt;br /&gt;        if len(parts) == 1:&lt;br /&gt;            return (parts[0], None)&lt;br /&gt;        else:&lt;br /&gt;            return parts&lt;br /&gt;&lt;br /&gt;    def getattr(self, path):&lt;br /&gt;        genre, station = self.split_path(path)&lt;br /&gt;&lt;br /&gt;        if genre is None:&lt;br /&gt;            stat = self.root&lt;br /&gt;        else:&lt;br /&gt;            stat = self.root.genres.get(genre)&lt;br /&gt;            if not stat:&lt;br /&gt;                return -errno.ENOENT&lt;br /&gt;&lt;br /&gt;            if station:&lt;br /&gt;                stat = stat.stations.get(station)&lt;br /&gt;                if not stat:&lt;br /&gt;                    return -errno.ENOENT&lt;br /&gt;        return stat&lt;br /&gt;&lt;br /&gt;    def readdir(self, path, offset):&lt;br /&gt;        yield fuse.Direntry('.')&lt;br /&gt;        yield fuse.Direntry('..')&lt;br /&gt;&lt;br /&gt;        if path == '/':&lt;br /&gt;            entries = self.root.genres.keys()&lt;br /&gt;        else:&lt;br /&gt;            entries = self.root.genres[path[1:]].stations.keys()&lt;br /&gt;&lt;br /&gt;        for e in entries:&lt;br /&gt;            yield fuse.Direntry(e)&lt;br /&gt;&lt;br /&gt;    def open(self, path, flags):&lt;br /&gt;        # Only support for 'READ ONLY' flag&lt;br /&gt;        access_flags = os.O_RDONLY | os.O_WRONLY | os.O_RDWR&lt;br /&gt;        if flags &amp;amp; access_flags != os.O_RDONLY:&lt;br /&gt;            return -errno.EACCES&lt;br /&gt;        else:&lt;br /&gt;            return 0&lt;br /&gt;&lt;br /&gt;    def read(self, path, size, offset):&lt;br /&gt;        genre, station = self.split_path(path)&lt;br /&gt;        info = self.root.genres[genre].stations[station]&lt;br /&gt;        if offset &amp;lt; info.st_size:&lt;br /&gt;            if offset + size &amp;gt; info.st_size:&lt;br /&gt;                size = info.st_size - offset&lt;br /&gt;            return info.content[offset:offset+size]&lt;br /&gt;        else:&lt;br /&gt;            return ''&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    fs = ShoutcastFS()&lt;br /&gt;    fs.parse(errex=1)&lt;br /&gt;    fs.multithreaded = False&lt;br /&gt;    fs.main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To try the file system, run:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ # Download shoutcast.py&lt;br /&gt;$ wget http://github.com/mariob/pyshoutcast/raw/master/src/shoutcast.py&lt;br /&gt;$ mkdir mnt&lt;br /&gt;$ ./shoutcastfs mnt&lt;br /&gt;$ cd mnt/&lt;br /&gt;$ ls&lt;br /&gt;...A list of genres...&lt;br /&gt;$ cd Samba&lt;br /&gt;$ ls&lt;br /&gt;...A list of 'Samba' stations...&lt;br /&gt;$ cat [station name]&lt;br /&gt;...Playlist data...&lt;br /&gt;&lt;/pre&gt;Have fun!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-2217662127660893735?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/2217662127660893735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/12/fuse-filesystem-in-userspace-part-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/2217662127660893735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/2217662127660893735'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/12/fuse-filesystem-in-userspace-part-3.html' title='FUSE - Filesystem in Userspace part 3 (final)'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7480108807145432278</id><published>2009-11-23T21:07:00.000+01:00</published><updated>2009-11-23T21:07:47.367+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Fedora'/><category scheme='http://www.blogger.com/atom/ns#' term='Kubuntu'/><title type='text'>Fedora 12</title><content type='html'>For the moment I'm evaluating Fedora 12. I was a bit disappointed on the Kubuntu 9.10 release, the distribution felt a little bit slow and tiered. The only thing I'm satisfied with is the boot time, it starts quite fast.&lt;br /&gt;&lt;br /&gt;The KDE4 desktop in Fedora feels kind of snappier than KDE in Kubuntu. I'm not sure if it's because Fedora is compiled for i686 and not i386 or if the team have done any optimizations. I didn't enabled desktop effects in neither distribution. I installed the nvidia proprietary driver in Kubuntu but use &lt;a href="http://nouveau.freedesktop.org/wiki/"&gt;Nouveau&lt;/a&gt; (open source driver) in Fedora. I might try the proprietary driver in Fedora to see if I gain any speed.&lt;br /&gt;&lt;br /&gt;There's one bad thing I found in Fedora in comparision with Kubuntu, &lt;i&gt;KPackageKit&lt;/i&gt;. It takes ages before starting compared to the Kubuntu version so I'm using &lt;i&gt;yum&lt;/i&gt; from the shell prompt instead. Well, to be honest, I'm not a heavy user of UI front-ends to package management systems. In Kubuntu I prefer using &lt;i&gt;aptitude&lt;/i&gt; instead of &lt;i&gt;KPackageKit&lt;/i&gt;. &lt;br /&gt;&lt;br /&gt;I think I'll give Fedora a chance and try it out for a while.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7480108807145432278?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7480108807145432278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/11/fedora-12.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7480108807145432278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7480108807145432278'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/11/fedora-12.html' title='Fedora 12'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-8494681059523692971</id><published>2009-11-19T07:48:00.004+01:00</published><updated>2009-11-19T09:02:38.819+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ElementTree'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='FUSE'/><title type='text'>FUSE - Filesystem in Userspace part 2</title><content type='html'>This is the second post on implementing file systems using FUSE. In this post I'll show you how to create a read-only file system that is backed by an XML file.Each tag in the XML file is either a file or a directory. If the tag is representing a directory, it should have a &lt;i&gt;is_dir="1"&lt;/i&gt; attribute added. When the tag represents a file it should have its content between the tags. The following XML snippet shows an example:&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;root is_dir="1"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;a is_dir="1"&amp;gt;&lt;br /&gt;    &amp;lt;file1.txt&amp;gt;File content for this file&amp;lt;/file1.txt&amp;gt;&lt;br /&gt;    &amp;lt;hello&amp;gt;Hello World&amp;lt;/hello&amp;gt;&lt;br /&gt;  &amp;lt;/a&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;empty_dir is_dir="1" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;br /&gt;&lt;/pre&gt;The example models the following directory tree:&lt;pre name="code" class="bash"&gt;&lt;br /&gt;/&lt;br /&gt;|-- a&lt;br /&gt;|   |-- file1.txt&lt;br /&gt;|   `-- hello&lt;br /&gt;`-- empty_dir&lt;br /&gt;&lt;/pre&gt;There are some limitations in having an XML file representing a file system. You can't for example have filenames starting with a dot because tags are not allowed to start with a dot in XML.&lt;br /&gt;&lt;br /&gt;On to the code, the following snippet implements the read-only file system which is backed by an XML file:&lt;pre name="code" class="python"&gt;&lt;br /&gt;#!/usr/bin/env python&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;import errno&lt;br /&gt;import fuse&lt;br /&gt;import stat&lt;br /&gt;import os&lt;br /&gt;import time&lt;br /&gt;import xml.etree.ElementTree as etree&lt;br /&gt;&lt;br /&gt;fuse.fuse_python_api = (0, 2)&lt;br /&gt;&lt;br /&gt;# Use same timestamp for all files&lt;br /&gt;_file_timestamp = int(time.time())&lt;br /&gt;&lt;br /&gt;class MyStat(fuse.Stat):&lt;br /&gt;    """&lt;br /&gt;    Convenient class for Stat objects.&lt;br /&gt;    Set up the stat object with appropriate&lt;br /&gt;    values depending on constructor args.&lt;br /&gt;    """&lt;br /&gt;    def __init__(self, is_dir, size):&lt;br /&gt;        fuse.Stat.__init__(self)&lt;br /&gt;        if is_dir:&lt;br /&gt;            self.st_mode = stat.S_IFDIR | 0555&lt;br /&gt;            self.st_nlink = 2&lt;br /&gt;        else:&lt;br /&gt;            self.st_mode = stat.S_IFREG | 0444&lt;br /&gt;            self.st_nlink = 1&lt;br /&gt;            self.st_size = size&lt;br /&gt;        self.st_atime = _file_timestamp&lt;br /&gt;        self.st_mtime = _file_timestamp&lt;br /&gt;        self.st_ctime = _file_timestamp&lt;br /&gt;&lt;br /&gt;class MyFS(fuse.Fuse):&lt;br /&gt;    def __init__(self, xml_tree, *args, **kw):&lt;br /&gt;        fuse.Fuse.__init__(self, *args, **kw)&lt;br /&gt;        self.tree = xml_tree&lt;br /&gt;&lt;br /&gt;    def getattr(self, path):&lt;br /&gt;        # We do not support 'dot' files&lt;br /&gt;        # since xml tags cannot start with a dot.&lt;br /&gt;        if path.find('/.') != -1:&lt;br /&gt;            return -errno.ENOENT&lt;br /&gt;&lt;br /&gt;        entry = self.tree.find(path)&lt;br /&gt;&lt;br /&gt;        if entry is None:&lt;br /&gt;            return -errno.ENOENT&lt;br /&gt;        else:&lt;br /&gt;            is_dir = entry.get('is_dir', False)&lt;br /&gt;            size = entry.text and len(entry.text.strip()) or 0&lt;br /&gt;            return MyStat(is_dir, size)&lt;br /&gt;&lt;br /&gt;    def readdir(self, path, offset):&lt;br /&gt;        yield fuse.Direntry('.')&lt;br /&gt;        yield fuse.Direntry('..')&lt;br /&gt;        for e in self.tree.find(path).getchildren():&lt;br /&gt;            yield fuse.Direntry(e.tag)&lt;br /&gt;&lt;br /&gt;    def open(self, path, flags):&lt;br /&gt;        # Only support for 'READ ONLY' flag&lt;br /&gt;        access_flags = os.O_RDONLY | os.O_WRONLY | os.O_RDWR&lt;br /&gt;        if flags &amp; access_flags != os.O_RDONLY:&lt;br /&gt;            return -errno.EACCES&lt;br /&gt;        else:&lt;br /&gt;            return 0&lt;br /&gt;&lt;br /&gt;    def read(self, path, size, offset):&lt;br /&gt;        entry = self.tree.find(path)&lt;br /&gt;        content = entry.text and entry.text.strip() or ''&lt;br /&gt;        file_size = len(content)&lt;br /&gt;        if offset &lt; file_size:&lt;br /&gt;            if offset + size &gt; file_size:&lt;br /&gt;                size = file_size - offset&lt;br /&gt;            return content[offset:offset+size]&lt;br /&gt;        else:&lt;br /&gt;            return ''&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    tree = etree.parse('tree.xml')&lt;br /&gt;&lt;br /&gt;    fs = MyFS(tree)&lt;br /&gt;    fs.parse(errex=1)&lt;br /&gt;    fs.main()&lt;br /&gt;&lt;/pre&gt;I'm using the &lt;a href="http://docs.python.org/library/xml.etree.elementtree.html"&gt;&lt;i&gt;ElementTree XML API&lt;/i&gt;&lt;/a&gt; to parse the XML file. The ElementTree API is very easy to use and supports finding elements by specifying a path which fits very well in this context. For example, the following snippet shows how to get the &lt;i&gt;file1.txt&lt;/i&gt; element from the example XML file above and extract the content between the tags:&lt;pre name="code" class="python"&gt;&lt;br /&gt;import xml.etree.ElementTree as etree&lt;br /&gt;tree = etree.parse('tree.xml')&lt;br /&gt;el = tree.find('/a/file1.txt')&lt;br /&gt;print el.text.strip() # Remove any white-spaces&lt;br /&gt;&lt;/pre&gt;The code hasn't change that much since the &lt;a href="http://pysnippet.blogspot.com/2009/11/fuse-filesystem-in-userspace-part-1.html"&gt;part 1 post&lt;/a&gt;. I've added two new methods which adds support for opening and reading files. The MyStat class is only used as an convenient class to help creating appropriate stat objects. You might notice that I don't do a lot of checking in the code, this is because FUSE do a lot of them for me, this &lt;a href="http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FuseInvariants"&gt;page&lt;/a&gt; list some of the assumptions you can make when implementing file system using FUSE.&lt;br /&gt;&lt;br /&gt;Did you believe it would be this easy to create a mountable file system that uses an XML file for the layout? I didn't.&lt;br /&gt;&lt;br /&gt;In the next (and final) post about FUSE I think I'll create some kind of &lt;i&gt;'use a service on the Internet'&lt;/i&gt; file system which can be useful and not just another &lt;i&gt;toy fs&lt;/i&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-8494681059523692971?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/8494681059523692971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/11/fuse-filesystem-in-userspace-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8494681059523692971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8494681059523692971'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/11/fuse-filesystem-in-userspace-part-2.html' title='FUSE - Filesystem in Userspace part 2'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-138020025185706347</id><published>2009-11-15T20:50:00.005+01:00</published><updated>2009-11-15T20:57:08.656+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='FUSE'/><title type='text'>FUSE - Filesystem in Userspace part 1</title><content type='html'>Here's the first part in a series of posts on implementing file systems using FUSE.&lt;br /&gt;&lt;br /&gt;Creating a file system can seem to be an intimidating task but with &lt;a href="http://fuse.sourceforge.net/"&gt;FUSE&lt;/a&gt; it's very easy and it gets even easier with python bindings. Any decent Linux distribution should include support for FUSE and be configured such that regular users (or a group) can mount file systems written with FUSE without having root access.&lt;br /&gt;&lt;br /&gt;If you are running Ubuntu you need to install the &lt;i&gt;python-fuse&lt;/i&gt; package which contains the Python bindings for FUSE.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;- Enough, show me the code&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;#!/usr/bin/env python&lt;br /&gt;# -*- coding: utf-8 -*-&lt;br /&gt;import errno&lt;br /&gt;import fuse&lt;br /&gt;import stat&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;fuse.fuse_python_api = (0, 2)&lt;br /&gt;&lt;br /&gt;class MyFS(fuse.Fuse):&lt;br /&gt;    def __init__(self, *args, **kw):&lt;br /&gt;        fuse.Fuse.__init__(self, *args, **kw)&lt;br /&gt;&lt;br /&gt;    def getattr(self, path):&lt;br /&gt;        st = fuse.Stat()&lt;br /&gt;        st.st_mode = stat.S_IFDIR | 0755&lt;br /&gt;        st.st_nlink = 2&lt;br /&gt;        st.st_atime = int(time.time())&lt;br /&gt;        st.st_mtime = st.st_atime&lt;br /&gt;        st.st_ctime = st.st_atime&lt;br /&gt;&lt;br /&gt;        if path == '/':&lt;br /&gt;            pass&lt;br /&gt;        else:&lt;br /&gt;            return - errno.ENOENT&lt;br /&gt;        return st&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    fs = MyFS()&lt;br /&gt;    fs.parse(errex=1)&lt;br /&gt;    fs.main()&lt;br /&gt;&lt;/pre&gt;The example above shows how easy it is to create a mountable file system, it does nothing but is still a valid file system. The &lt;i&gt;MyFS&lt;/i&gt; class implements a file system with no entries, it will always return &lt;i&gt;ENOENT&lt;/i&gt; (No such file or directory) except for the root path. You should take a look at the &lt;a href="http://sourceforge.net/apps/mediawiki/fuse/index.php?title=FUSE_Python_Reference"&gt;FUSE Python Reference&lt;/a&gt; page which contains details about &lt;i&gt;FusePython&lt;/i&gt; and, among other things, describes the stat object returned from the &lt;i&gt;getattr&lt;/i&gt; method.&lt;br /&gt;&lt;br /&gt;Paste the example to a file, &lt;i&gt;MyFS.py&lt;/i&gt;, and make the file executable:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ chmod 755 MyFS.py&lt;br /&gt;&lt;/pre&gt;Now create a directory which will be used as mount point and mount the file system:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ mkdir myfs&lt;br /&gt;$ ls -l&lt;br /&gt;totalt 4&lt;br /&gt;drwxr-xr-x 2 mario mario 4096 2009-11-15 15:59 myfs&lt;br /&gt;-rwxr-xr-x 1 mario mario  648 2009-11-15 15:59 MyFS.py&lt;br /&gt;$ # This will mount the file system, try ./MyFS.py --help&lt;br /&gt;$ ./MyFS.py myfs&lt;br /&gt;$ ls -l&lt;br /&gt;totalt 0&lt;br /&gt;drwxr-xr-x 2 root  root    0 2009-11-15 16:10 myfs&lt;br /&gt;-rwxr-xr-x 1 mario mario 648 2009-11-15 15:59 MyFS.py&lt;br /&gt;&lt;/pre&gt;Here we can see that the owner, size and time stamp changed on &lt;i&gt;myfs&lt;/i&gt; after we mounted the file system. You can &lt;i&gt;cd&lt;/i&gt; into &lt;i&gt;myfs&lt;/i&gt;, but if you try to list the directory (file system root) you'll get an error:&lt;pre class="bash" name="code"&gt;&lt;br /&gt;$ cd myfs&lt;br /&gt;$ ls&lt;br /&gt;ls: reading directory .: Function not implemented&lt;br /&gt;&lt;/pre&gt;You got this error because we haven't implemented the &lt;i&gt;readdir&lt;/i&gt; method which is invoked when listing a directory. To make this work we need to add a &lt;i&gt;readdir&lt;/i&gt; method which returns (at least) the &lt;i&gt;'.'&lt;/i&gt; and &lt;i&gt;'..'&lt;/i&gt; entries. Add the following snippet to the class:&lt;pre name="code" class="python"&gt;&lt;br /&gt;    def readdir(self, path, offset):&lt;br /&gt;        for e in '.', '..':&lt;br /&gt;            yield fuse.Direntry(e) &lt;br /&gt;&lt;/pre&gt;Before you continue you should unmount the file system:&lt;pre class="bash" name="code"&gt;&lt;br /&gt;$ fusermount -u myfs&lt;br /&gt;&lt;/pre&gt;This will unmount your FUSE file system as a regular user and doesn't require root access.&lt;br /&gt;&lt;br /&gt;Retry to mount and cd into the &lt;i&gt;myfs&lt;/i&gt; directory:&lt;pre name="code" class="bash"&gt;&lt;br /&gt;$ ./MyFS.py myfs&lt;br /&gt;$ cd myfs&lt;br /&gt;$ ls -a&lt;br /&gt;.  ..&lt;br /&gt;&lt;/pre&gt;Alright, I think I'll stop for today.Oh, just one more thing... You won't get any print outputs nor tracebacks from your code if you don't start the file system in foreground mode. This is done by adding '-f' as argument when starting the file system.&lt;pre name="code" class="bash"&gt;&lt;br /&gt;$ ./MyFS.py -f myfs&lt;br /&gt;&lt;/pre&gt;It can be hard to tell why something doesn't work if you can't see tracebacks and print outputs.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-138020025185706347?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/138020025185706347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/11/fuse-filesystem-in-userspace-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/138020025185706347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/138020025185706347'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/11/fuse-filesystem-in-userspace-part-1.html' title='FUSE - Filesystem in Userspace part 1'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-1805855733443281580</id><published>2009-11-05T22:47:00.101+01:00</published><updated>2009-11-06T22:15:24.536+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='KDE'/><category scheme='http://www.blogger.com/atom/ns#' term='gmonitor'/><category scheme='http://www.blogger.com/atom/ns#' term='gmail'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='feedparser'/><title type='text'>GMonitor</title><content type='html'>Ah, I've finally managed to make my new fresh Kubuntu 9.10 installation usable. Now it's time to do some KDE stuff and I'll show you how easy it's to create a plasmoid (applet/widget) for KDE4.&lt;br /&gt;&lt;br /&gt;I've created a simple applet which shows the number of unread mail in a Gmail inbox. We already knowhow to fetch the list with unread mail, which I have explained in a previous &lt;a href="http://pysnippet.blogspot.com/2009/10/diy-google-mail-notifier.html"&gt;blog post&lt;/a&gt;. Now I'll create a nice applet showing the count. I'm going to keep it assimple as possible, writing hard-coded values, skip error handling andso on to avoid making things more complicated than necessary.&lt;br /&gt;&lt;br /&gt;Thereare several good tutorials at &lt;a href="http://techbase.kde.org/"&gt;techbase.kde.org&lt;/a&gt; which explains plasmaprogramming. They cover the basics and explains things in detail soI'll only make references to the pages instead of repeating what'salready been written.&lt;br /&gt;&lt;br /&gt;The first tutorial you should read (youdon't have to read it right now, you can read it after you tried doingthe applet) is &lt;a href="http://techbase.kde.org/Development/Tutorials/Plasma/Python/GettingStarted"&gt;Python Plasma Getting Started&lt;/a&gt;. The tutorial coverssetting up a simple plasmoid, packaging, installing and running.&lt;br /&gt;&lt;br /&gt;I'll name the plasmoid &lt;i&gt;GMonitor&lt;/i&gt; because it only monitors the mailbox andshows the number of unread mail, it will not actually try to do somekind of notification (yet?!).&lt;br /&gt;&lt;br /&gt;I've put the code on &lt;a href="http://github.com/mariob/gmonitor"&gt;github&lt;/a&gt;, you can clone it with:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ git clone git://github.com/mariob/gmonitor.git&lt;br /&gt;&lt;/pre&gt;You'll find a &lt;i&gt;Makefile&lt;/i&gt; in the git which can be useful when doing plasmoids. The &lt;i&gt;Makefile&lt;/i&gt; supports installing, un-installing, viewing, packaging and updating the plasmoid.&lt;br /&gt;&lt;br /&gt;The directory tree looks like this:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;gmonitor/&lt;br /&gt;|-- Makefile&lt;br /&gt;|-- README&lt;br /&gt;|-- contents&lt;br /&gt;|   `-- code&lt;br /&gt;|       `-- main.py&lt;br /&gt;`-- metadata.desktop&lt;br /&gt;&lt;/pre&gt;The &lt;i&gt;gmonitor&lt;/i&gt; directory is the project home and &lt;i&gt;contents/code&lt;/i&gt; contains the python source. The &lt;i&gt;metadata.desktop&lt;/i&gt; file will be explained below.&lt;br /&gt;&lt;br /&gt;To be able to install and run your applet you need to provide a &lt;i&gt;metadata.desktop&lt;/i&gt; file to plasma. The &lt;i&gt;metadata.desktop&lt;/i&gt; file containsimportant information about the applet and you can read more about thefile in the Plasma Getting Started tutorial.&lt;br /&gt;&lt;br /&gt;Feel free to change the file. The &lt;i&gt;Name&lt;/i&gt; specifies the applet nameand the &lt;i&gt;Icon&lt;/i&gt; field gives the name to the icon to associated with thisapplet. These two fields are typically shown when listing applets inthe 'Add Widget' dialog. There are two important fields whichmust be present for plasma to run your applet, the &lt;i&gt;X-Plasma-API&lt;/i&gt; fieldspecifies which script-engine to be used and the&lt;i&gt;X-Plasma-MainScript&lt;/i&gt; field which script to be executed. It's also good to know that the &lt;i&gt;X-KDE-PluginInfo-Name&lt;/i&gt; fields is used as a plasmoid identification, so it should be unique.&lt;br /&gt;&lt;br /&gt;Some notes about the implementation. There are two classes, &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;GMonitor&lt;/span&gt; and &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;MailFrame&lt;/span&gt; (I know, poor name). The &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;MaiFrame&lt;/span&gt; class has an icon and a label. When the mail count is set to zero (by calling &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;setCount(cnt)&lt;/span&gt;), the icon is disabled, grayed and the label is set to 'No new mail'. When the mail count is set to a value greater than zero, the icon is enabled, colored and the label is set to 'Count: x', where &lt;i&gt;x&lt;/i&gt; is the number of mail in the inbox. See the images below.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_ilsnPCKxGBM/SvNN5XSWPMI/AAAAAAAAABU/YzojfyTLf6Q/s1600-h/no_new_mail.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_ilsnPCKxGBM/SvNN5XSWPMI/AAAAAAAAABU/YzojfyTLf6Q/s320/no_new_mail.png" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/_ilsnPCKxGBM/SvNN7UiO5rI/AAAAAAAAABc/6pjz8jXWFnQ/s1600-h/one_new_mail.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_ilsnPCKxGBM/SvNN7UiO5rI/AAAAAAAAABc/6pjz8jXWFnQ/s320/one_new_mail.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;i&gt;Sample of what the applet looks like&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;When the icon is enabled and clicked, a 'clicked()' signal is emitted. The &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;GMonitor&lt;/span&gt; class connects the &lt;i&gt;openBrowser()&lt;/i&gt; method to the 'clicked()' signal. The &lt;i&gt;openBrowser()&lt;/i&gt; method opens a default browser in KDE to load the google mail url. The &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;GMonitor&lt;/span&gt; also sets up a timer to fetch the feed in 60 second interval. The &lt;i&gt;fetchFeed()&lt;/i&gt; method uses the &lt;a href="http://api.kde.org/pykde-4.3-api/kio/KIO.html"&gt;KIO&lt;/a&gt; framework in KDE to download the content from a URL and &lt;i&gt;parseFeed() &lt;/i&gt;parses the downloaded feed and emits a 'mailcount' signal.&lt;br /&gt;&lt;br /&gt;The implementation depends on &lt;a href="http://www.feedparser.org/"&gt;feedparser&lt;/a&gt; which can be installed on Ubuntu by running:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ sudo aptitude install python-feedparser&lt;br /&gt;&lt;/pre&gt;You can also download and put the feedparser.py file inside the code directory.&lt;br /&gt;&lt;br /&gt;Not that bad. It's actually possible to create a very simple 'good looking' Gmail monitor in less than 100 lines of code (excluding comments) with Python and KDE.&lt;br /&gt;&lt;br /&gt;To view this plasmoid without installing it run (while standing in the gmonitor folder):&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ make view&lt;/pre&gt;This might only work on KDE4.3, not sure if the plasmoidviewer in 4.2 supports running plasmoids without first installing them. If it fails you should install the plasmoid and run plasmoidviewer yourself (or modify the makefile):&lt;br /&gt;&lt;pre class="bash" name="code"&gt;$ make install&lt;br /&gt;$ plasmoidviewer pysnippet-gmonitor&lt;br /&gt;&lt;/pre&gt;If you never heard of Qt signals and slots you should read this &lt;a href="http://techbase.kde.org/Development/Tutorials/Python_introduction_to_signals_and_slots"&gt;introduction&lt;/a&gt; on the topic. For example, a button can 'emit' a signal when it's clicked. A slot (function/method) can be connected to a signal and each time the signal is emitted the function/method will be called.&lt;br /&gt;&lt;br /&gt;You can find the Python KDE4.3 API docs &lt;a href="http://api.kde.org/pykde-4.3-api/modules.html"&gt;here&lt;/a&gt;. Unfortunately, I don't find them as good as the Qt docs.&lt;br /&gt;&lt;br /&gt;When running the applet, KDE will ask for the username/password when it tries to connect to Google. You can check the 'remember the password' checkbox which will make KDE cache the password as long as the desktop session is alive. If you choose not to, you'll have to enter your username/password each time the code tries to fetch the feed. A better solution is to use &lt;a href="http://api.kde.org/pykde-4.3-api/kdeui/KWallet.Wallet.html"&gt;KWallet&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Ok, hope you enjoyed it and start doing cool plasmoids!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-1805855733443281580?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/1805855733443281580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/11/gmonitor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1805855733443281580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/1805855733443281580'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/11/gmonitor.html' title='GMonitor'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_ilsnPCKxGBM/SvNN5XSWPMI/AAAAAAAAABU/YzojfyTLf6Q/s72-c/no_new_mail.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-2889997600379340750</id><published>2009-11-02T21:52:00.000+01:00</published><updated>2009-11-02T21:52:17.387+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='KDE'/><category scheme='http://www.blogger.com/atom/ns#' term='gmail'/><title type='text'>Kubuntu 9.10</title><content type='html'>I've installed Kubuntu 9.10 and I'm busy configuring it to a usable state. For the moment I don't have too much spare time but I'm almost done.&lt;br /&gt;&lt;br /&gt;I hope I'll have time to do a post about creating plasmoids (KDE4 desktop widgets) in the next couple of days. I think I'll do a &lt;i&gt;gmonitor&lt;/i&gt; applet which will show the number of unread mail in your Gmail inbox.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-2889997600379340750?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/2889997600379340750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/11/kubuntu-910.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/2889997600379340750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/2889997600379340750'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/11/kubuntu-910.html' title='Kubuntu 9.10'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6539984547446048375</id><published>2009-10-28T21:50:00.004+01:00</published><updated>2009-10-28T21:54:27.245+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gmail'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='feedparser'/><title type='text'>DIY: Google Mail Notifier</title><content type='html'>It's quite easy to create your own GMail notifier. Your inbox, gmail or domain,&amp;nbsp; can be monitored by fetching and parsing an atom feed.&lt;br /&gt;&lt;br /&gt;Following code snippet prints new (unread) mail in your inbox: &lt;br /&gt;&lt;pre class="python" name="code"&gt;# -*- coding: utf-8 -*-&lt;br /&gt;import feedparser&lt;br /&gt;import urllib2&lt;br /&gt;&lt;br /&gt;# Create http basic auth handler&lt;br /&gt;auth_handler = urllib2.HTTPBasicAuthHandler()&lt;br /&gt;auth_handler.add_password('New mail feed', 'https://mail.google.com/',&lt;br /&gt;                          'USERNAME@DOMAIN', 'PASSWORD')&lt;br /&gt;&lt;br /&gt;# Open url using the auth handler&lt;br /&gt;opener = urllib2.build_opener(auth_handler)&lt;br /&gt;feed_file = opener.open('https://mail.google.com/mail/feed/atom/')&lt;br /&gt;&lt;br /&gt;# Parse feed using feedparser&lt;br /&gt;d = feedparser.parse(feed_file)&lt;br /&gt;&lt;br /&gt;# Print mail count and mails&lt;br /&gt;print 'Mail count:', d.feed.fullcount&lt;br /&gt;&lt;br /&gt;for entry in d.entries:&lt;br /&gt;        print '----------------------------------------------'&lt;br /&gt; print 'Author: ', entry.author&lt;br /&gt; print 'Subject:', entry.title&lt;br /&gt; print 'Summary:', entry.summary&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Just change &lt;i&gt;USERNAME&lt;/i&gt; and &lt;i&gt;PASSWORD&lt;/i&gt;. You also need to download &lt;a href="http://www.feedparser.org/"&gt;feedparser&lt;/a&gt; which is free to use, or write one yourself.&lt;br /&gt;&lt;br /&gt;Have fun!&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6539984547446048375?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/6539984547446048375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/diy-google-mail-notifier.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6539984547446048375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6539984547446048375'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/diy-google-mail-notifier.html' title='DIY: Google Mail Notifier'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7911895078215647030</id><published>2009-10-28T09:44:00.000+01:00</published><updated>2009-10-28T09:44:52.744+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Conference'/><title type='text'>ELCE 2009 presentations</title><content type='html'>The presentations from ELCE 2009 are now available from &lt;a href="http://tree.celinuxforum.org/CelfPubWiki/ELCEurope2009Presentations"&gt;here&lt;/a&gt;. Some of the presentations have not been posted yet.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7911895078215647030?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7911895078215647030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/elce-2009-presentations.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7911895078215647030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7911895078215647030'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/elce-2009-presentations.html' title='ELCE 2009 presentations'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6692399588344600651</id><published>2009-10-26T21:19:00.000+01:00</published><updated>2009-10-26T21:19:52.196+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pyshoutcast'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>pyshoutcast v0.7.1</title><content type='html'>&lt;a href="http://github.com/mariob/pyshoutcast/"&gt;pyshoutcast&lt;/a&gt; version 0.7.1 has just been released. It implements a Python shoutcast API which makes it possible to list genres, stations, do search, etc, using the shoutcast &lt;i&gt;yellow pages&lt;/i&gt; service.&lt;br /&gt;&lt;br /&gt;Unfortunately, I just found a &lt;a href="http://forum.kde.org/viewtopic.php?f=116&amp;amp;t=82419&amp;amp;p=130589&amp;amp;hilit=shoutcast#p130589"&gt;forum&lt;/a&gt; post stating that Amarok and VLC have removed support for shoutcast due to licensing issues. I'm not sure why because I can't find any info about it, I'll try to find out more details.&lt;br /&gt;&lt;br /&gt;Anyway, to try it out you can start a interactive python prompt and paste the following code:&lt;br /&gt;&lt;pre class="python" name="code"&gt;&amp;gt;&amp;gt;&amp;gt; import shoutcast&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; api = shoutcast.ShoutCast()&lt;br /&gt;&amp;gt;&amp;gt;&amp;gt; api.random()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6692399588344600651?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/6692399588344600651/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/pyshoutcast-v071.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6692399588344600651'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6692399588344600651'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/pyshoutcast-v071.html' title='pyshoutcast v0.7.1'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-357886988398831699</id><published>2009-10-21T12:56:00.007+02:00</published><updated>2009-10-21T13:15:58.069+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>'with' statement</title><content type='html'>Python have a great number of cool language features and one of them is the &lt;a href="http://docs.python.org/reference/compound_stmts.html#the-with-statement"&gt;&lt;i&gt;with&lt;/i&gt;&lt;/a&gt; statement. The &lt;i&gt;with&lt;/i&gt; statement can be used in numerous ways but I think that the most common usage is to track and release some kind of resource and at the same time encapsulate the code block with a try/finally clause.&lt;br /&gt;&lt;br /&gt;For example:&lt;br /&gt;&lt;pre class="python" name="code"&gt;with open('my_file.txt') as f:&lt;br /&gt;    for line in f:&lt;br /&gt;        print line&lt;br /&gt;&lt;/pre&gt;which could be translated to something like this:&lt;br /&gt;&lt;pre class="python" name="code"&gt;f = open('my_file.txt')&lt;br /&gt;try:&lt;br /&gt;    for line in f:&lt;br /&gt;        print line&lt;br /&gt;finally:&lt;br /&gt;    f.close()&lt;br /&gt;&lt;/pre&gt;and in a general form:&lt;br /&gt;&lt;pre class="python" name="code"&gt;with &amp;lt;EXPRESSION&amp;gt; [as &amp;lt;VAR&amp;gt;]:&lt;br /&gt;    &amp;lt;BLOCK&amp;gt;&lt;br /&gt;&lt;/pre&gt;The &lt;i&gt;with&lt;/i&gt; statement guarantees that the file will be closed no matter what happens, for example an exception is thrown or a &lt;i&gt;return&lt;/i&gt; is executed inside the &lt;i&gt;for&lt;/i&gt; statement etc. The result from the expression that is evaluated after the &lt;i&gt;with&lt;/i&gt; keyword will be bound to the variable specified after the &lt;i&gt;as&lt;/i&gt; keyword. The &lt;i&gt;as&lt;/i&gt; part is optional and I'll show you a use case later.&lt;br /&gt;&lt;br /&gt;But wait a minute, how does the &lt;i&gt;with&lt;/i&gt; keyword know how to close the file? Well, the magic is called &lt;i&gt;'&lt;a href="http://docs.python.org/library/stdtypes.html#typecontextmanager" title="Context Manager"&gt;Context Manager&lt;/a&gt;'&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;A context manager is a class which defines two methods, &lt;i&gt;&lt;a href="http://docs.python.org/library/stdtypes.html#contextmanager.__enter__"&gt;__enter__()&lt;/a&gt;&lt;/i&gt; and &lt;i&gt;&lt;a href="http://docs.python.org/library/stdtypes.html#contextmanager.__exit__"&gt;__exit__()&lt;/a&gt;&lt;/i&gt;. The expression in the &lt;i&gt;with&lt;/i&gt; statement must evaluate to a context manager else you'll get an &lt;i&gt;AttributeError&lt;/i&gt;. The &lt;i&gt;__enter__&lt;/i&gt; method is called when the &lt;i&gt;with&lt;/i&gt; statement is evaluated and should return the value which should be assigned to the variable specified after the &lt;i&gt;as&lt;/i&gt; keyword. After the block has finished executing the &lt;i&gt;__exit__&lt;/i&gt; method is called. The &lt;i&gt;__exit__&lt;/i&gt; method will be passed any exception occurred while executing the block and can decide if the exception should be muted by returning &lt;i&gt;True&lt;/i&gt; or be propagated to the caller by returning &lt;i&gt;False&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Let's look at the following example (not a good design, just an example):&lt;br /&gt;&lt;pre class="python" name="code"&gt;class TransactionContextManager(object):&lt;br /&gt;    def __init__(self, obj):&lt;br /&gt;        self.obj = obj&lt;br /&gt;&lt;br /&gt;    def __enter__(self):&lt;br /&gt;        self.obj.begin()&lt;br /&gt;        return self.obj&lt;br /&gt;&lt;br /&gt;    def __exit__(self, exc_type, exc_val, exc_tb):&lt;br /&gt;        if exc_type:&lt;br /&gt;            self.obj.rollback()&lt;br /&gt;        else:&lt;br /&gt;            self.obj.commit()&lt;br /&gt;        return False&lt;br /&gt;&lt;br /&gt;class Transaction(object):&lt;br /&gt;    def begin(self):&lt;br /&gt;        print "Start transaction"&lt;br /&gt;&lt;br /&gt;    def query(self, sql):&lt;br /&gt;        print "SQL:", sql&lt;br /&gt;&lt;br /&gt;    def rollback(self):&lt;br /&gt;        print "Rollback transaction"&lt;br /&gt;&lt;br /&gt;    def commit(self):&lt;br /&gt;        print "Commit transaction"&lt;br /&gt;&lt;br /&gt;print "Before 'with'"&lt;br /&gt;&lt;br /&gt;with TransactionContextManager(Transaction()) as tx:&lt;br /&gt;    tx.query("SELECT * FROM users")&lt;br /&gt;&lt;br /&gt;print "After 'with'"&lt;br /&gt;&lt;/pre&gt;The code produces the following output:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;Before 'with'&lt;br /&gt;Start transaction&lt;br /&gt;SQL: SELECT * FROM users&lt;br /&gt;Commit transaction&lt;br /&gt;After 'with'&lt;br /&gt;&lt;/pre&gt;It's easy to trace the execution but I'll explain it anyway.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The code starts by printing "Before 'with'".&lt;/li&gt;&lt;li&gt;The &lt;i&gt;with&lt;/i&gt; statement is executed and instances of &lt;i&gt;TransactionContextManager&lt;/i&gt; and &lt;i&gt;Transaction&lt;/i&gt; are created. The context manager's &lt;i&gt;__enter__&lt;/i&gt; method is invoked which will invoke the &lt;i&gt;Transaction&lt;/i&gt; instance's &lt;i&gt;begin&lt;/i&gt; method. The &lt;i&gt;begin&lt;/i&gt; method prints "Start transaction" and finally the &lt;i&gt;__enter__&lt;/i&gt; method returns the &lt;i&gt;Transaction&lt;/i&gt; instance.&lt;/li&gt;&lt;li&gt;The return value from the context manager's &lt;i&gt;__enter__&lt;/i&gt; method is bound to the variable &lt;i&gt;tx&lt;/i&gt;.&lt;/li&gt;&lt;li&gt;The block is executed and the &lt;i&gt;query&lt;/i&gt; method is called.&lt;/li&gt;&lt;li&gt;The &lt;i&gt;query&lt;/i&gt; method prints "SQL: SELECT * FROM users".&lt;/li&gt;&lt;li&gt;The block is done executing and the context manager's &lt;i&gt;__exit__&lt;/i&gt; method is invoked. Since no exception occurred the &lt;i&gt;commit&lt;/i&gt; method is invoked on the &lt;i&gt;Transaction&lt;/i&gt; instance. The &lt;i&gt;__exit__&lt;/i&gt; method returns &lt;i&gt;False&lt;/i&gt; which means that any exceptions should be propagated to the caller, none in this case.&lt;/li&gt;&lt;li&gt;The code ends executing by printing "After 'with'". &lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Let's change the &lt;i&gt;Transaction&lt;/i&gt; class so that it throws an exception from the &lt;i&gt;query&lt;/i&gt; method. This should execute the &lt;i&gt;rollback&lt;/i&gt; method instead of the &lt;i&gt;commit&lt;/i&gt; method. The exception should also be propagated to the caller since we return &lt;i&gt;False&lt;/i&gt; from the &lt;i&gt;__exit__&lt;/i&gt; method.&lt;br /&gt;&lt;pre class="python" name="code"&gt;def query(self, sql):&lt;br /&gt;    raise Exception("SQL Error occurred")&lt;br /&gt;&lt;/pre&gt;And the output looks like following:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;Before 'with'&lt;br /&gt;Start transaction&lt;br /&gt;Rollback transaction&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;  File "transaction.py", line 32, in &lt;module&gt;&lt;br /&gt;    tx.query("SELECT * FROM users")&lt;br /&gt;  File "transaction.py", line 21, in query&lt;br /&gt;    raise Exception("SQL Error occurred")&lt;br /&gt;Exception: SQL Error occurred&lt;/pre&gt;Here we see that the &lt;i&gt;rollback&lt;/i&gt; method is called instead of &lt;i&gt;commit&lt;/i&gt;. The &lt;i&gt;rollback&lt;/i&gt; method is called because the code inside the &lt;i&gt;with&lt;/i&gt; block raised an exception and a valid &lt;i&gt;exc_type&lt;/i&gt; argument was passed to the &lt;i&gt;__exit__&lt;/i&gt; method. We can also see that the exception is propagated to the caller because we return &lt;i&gt;False&lt;/i&gt; from the &lt;i&gt;__exit__&lt;/i&gt; method.&lt;br /&gt;&lt;br /&gt;If we change the &lt;i&gt;__exit__&lt;/i&gt; method to return &lt;i&gt;True&lt;/i&gt; instead of &lt;i&gt;False&lt;/i&gt; we get the following output:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;Before 'with'&lt;br /&gt;Start transaction&lt;br /&gt;Rollback transaction&lt;br /&gt;After 'with'&lt;br /&gt;&lt;/pre&gt;As expected, we don't see the exception anymore. Note that the &lt;i&gt;__exit__&lt;/i&gt; method is not called if an exception occurs before the &lt;i&gt;with&lt;/i&gt; statement starts executing the block.&lt;br /&gt;&lt;br /&gt;Alright, I hope you agree with me that the &lt;i&gt;with&lt;/i&gt; statement is very useful. Now I'll show you a final example where we don't need to bind the return value from the context manager to a variable. I've re-factored the previous example and hopefully made a better design.&lt;br /&gt;&lt;pre class="python" name="code"&gt;class Connection(object):&lt;br /&gt;    def __init__(self):&lt;br /&gt;        # Default behaviour is to do auto commits&lt;br /&gt;        self._auto_commit = True&lt;br /&gt;&lt;br /&gt;    def connect(self):&lt;br /&gt;        pass&lt;br /&gt;&lt;br /&gt;    def auto_commit(self):&lt;br /&gt;        return self._auto_commit&lt;br /&gt;&lt;br /&gt;    def set_auto_commit(self, mode):&lt;br /&gt;        print "Auto commit:", mode&lt;br /&gt;        self._auto_commit = mode&lt;br /&gt;&lt;br /&gt;    def rollback(self):&lt;br /&gt;        print "Rollback transaction"&lt;br /&gt;&lt;br /&gt;    def commit(self):&lt;br /&gt;        print "Commit transaction"&lt;br /&gt;&lt;br /&gt;    def executeQuery(self, sql):&lt;br /&gt;        # Handle auto commit here if it's enabled&lt;br /&gt;        print "SQL:", sql&lt;br /&gt;&lt;br /&gt;class Transaction(object):&lt;br /&gt;    def __init__(self, conn):&lt;br /&gt;        self.conn = conn&lt;br /&gt;        self.auto_commit = False&lt;br /&gt;&lt;br /&gt;    def __enter__(self):&lt;br /&gt;        self.auto_commit = self.conn.auto_commit()&lt;br /&gt;        self.conn.set_auto_commit(False)&lt;br /&gt;        return self.conn&lt;br /&gt;&lt;br /&gt;    def __exit__(self, exc_type, exc_val, exc_tb):&lt;br /&gt;        if exc_type:&lt;br /&gt;            self.conn.rollback()&lt;br /&gt;        else:&lt;br /&gt;            self.conn.commit()&lt;br /&gt;&lt;br /&gt;        self.conn.set_auto_commit(self.auto_commit)&lt;br /&gt;        return False&lt;br /&gt;&lt;br /&gt;conn = Connection()&lt;br /&gt;conn.connect()&lt;br /&gt;&lt;br /&gt;with Transaction(conn):&lt;br /&gt;    conn.executeQuery("SELECT a user FROM users")&lt;br /&gt;    conn.executeQuery("INSERT some data INTO users")&lt;br /&gt;&lt;/pre&gt;The output:&lt;br /&gt;&lt;pre class="bash" name="code"&gt;Auto commit: False&lt;br /&gt;SQL: SELECT a user FROM users&lt;br /&gt;SQL: INSERT some data INTO users&lt;br /&gt;Commit transaction&lt;br /&gt;Auto commit: True&lt;br /&gt;&lt;/pre&gt;As the example shows, we don't need to use the return value from the context manager to still have use of them. We only want to be sure that the queries inside the &lt;i&gt;with&lt;/i&gt; block is executed in the same transaction and rollbacked if an exception occurs or commited if successful. Without the &lt;i&gt;with&lt;/i&gt; statement the code would be implemented something like the following snippet:&lt;br /&gt;&lt;pre class="python" name="code"&gt;conn = Connection()&lt;br /&gt;conn.connect()&lt;br /&gt;auto_comm = conn.auto_commit()&lt;br /&gt;&lt;br /&gt;try:&lt;br /&gt;    conn.set_auto_commit(False)&lt;br /&gt;    conn.executeQuery("SELECT a user FROM users")&lt;br /&gt;    conn.executeQuery("INSERT some data INTO users")&lt;br /&gt;    conn.commit()&lt;br /&gt;except:&lt;br /&gt;    conn.rollback()&lt;br /&gt;    raise&lt;br /&gt;finally:&lt;br /&gt;    conn.set_auto_commit(auto_comm)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Well, for me the &lt;i&gt;with&lt;/i&gt; statement seems more clean and convenient to use than the last example.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-357886988398831699?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/357886988398831699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/python-have-great-number-of-cool.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/357886988398831699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/357886988398831699'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/python-have-great-number-of-cool.html' title='&apos;with&apos; statement'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-7132342537151881242</id><published>2009-10-18T19:19:00.000+02:00</published><updated>2009-10-18T19:19:42.486+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Conference'/><title type='text'>Back home from ELCE 2009</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_ilsnPCKxGBM/StrJbpNJMPI/AAAAAAAAAAU/gNdnJuFJI2I/s1600-h/IMG_1218.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_ilsnPCKxGBM/StrJbpNJMPI/AAAAAAAAAAU/gNdnJuFJI2I/s320/IMG_1218.jpeg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;It has been a great visit to Grenoble. I'm pleased with the conference because&amp;nbsp;most&amp;nbsp;of the sessions were held by developers, which I prefer, and not marketing people. The major focus seemed to be on build systems and reducing boot time. I learned a lot about tools which can give you hints on performance issues etc. Some celebrities also showed up like &lt;a href="http://jonmasters.org/"&gt;Jon Masters&lt;/a&gt; and &lt;a href="http://ar.linux.it/"&gt;Alessandro Rubini&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I assume that the slides from the sessions will be available on&amp;nbsp;the home page so you can take a look at them yourself.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.adeneo-embedded.com/"&gt;Adeneo&lt;/a&gt; had a cool demo of booting up a Linux system with a Qt-app doing 3D (software rendering) in less than 5 seconds. Unfortunately I can't give you a clip since the new blogger editor doesn't support movie clips, or am I wrong?. A picture will do.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_ilsnPCKxGBM/StrOeew84lI/AAAAAAAAAAc/_Rzb02YYl1A/s1600-h/IMG_1222.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_ilsnPCKxGBM/StrOeew84lI/AAAAAAAAAAc/_Rzb02YYl1A/s320/IMG_1222.jpeg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The conference had only a little exhibiton hall (which was ok for me) and here's a picture of &lt;a href="http://free-electrons.com/"&gt;Free Electrons'&lt;/a&gt; booth.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_ilsnPCKxGBM/StrPZKpAMRI/AAAAAAAAAAk/_DRZ2eIEyKU/s1600-h/IMG_1225.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_ilsnPCKxGBM/StrPZKpAMRI/AAAAAAAAAAk/_DRZ2eIEyKU/s320/IMG_1225.jpeg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The food was a success! A lot of different finger food and of course wine. Here's an example of what the dessert looked like the second day.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_ilsnPCKxGBM/StrQ07yaMPI/AAAAAAAAABE/0vuxVd8pHLI/s1600-h/dessert.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_ilsnPCKxGBM/StrQ07yaMPI/AAAAAAAAABE/0vuxVd8pHLI/s320/dessert.jpeg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The restroom has a really cool designed washbasin.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_ilsnPCKxGBM/StrUK0iexTI/AAAAAAAAABM/VyOpQ4o6S4o/s1600-h/wash-stand.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_ilsnPCKxGBM/StrUK0iexTI/AAAAAAAAABM/VyOpQ4o6S4o/s320/wash-stand.jpeg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;As I mentioned before I'm pleased with the conference and I believe I'll attend again next year.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-7132342537151881242?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/7132342537151881242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/back-home-from-elce-2009.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7132342537151881242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/7132342537151881242'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/back-home-from-elce-2009.html' title='Back home from ELCE 2009'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_ilsnPCKxGBM/StrJbpNJMPI/AAAAAAAAAAU/gNdnJuFJI2I/s72-c/IMG_1218.jpeg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-352129355035316780</id><published>2009-10-13T20:27:00.002+02:00</published><updated>2009-10-13T20:29:07.620+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Conference'/><title type='text'>I'm going to ELCE 2009</title><content type='html'>I'll be visiting the &lt;a href="http://www.embeddedlinuxconference.com/elc_europe09/index.html"&gt;Embedded LInux Conference Europe 2009&lt;/a&gt; which is held in Grenoble, France. It's a two day conference, 15-16 October. I'm looking forward to attend the sessions about build systems and other cool low-level kernel stuff.&lt;br /&gt;&lt;br /&gt;Hope I'll meet some interesting people.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-352129355035316780?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/352129355035316780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/im-going-to-elce-2009.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/352129355035316780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/352129355035316780'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/im-going-to-elce-2009.html' title='I&apos;m going to ELCE 2009'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-3009540120479181598</id><published>2009-10-10T23:05:00.002+02:00</published><updated>2009-10-12T15:52:08.310+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='KDE'/><category scheme='http://www.blogger.com/atom/ns#' term='Qt'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>KDE Hello World</title><content type='html'>I have plans to do a web radio player (I wonder if we'll ever see this app). My intention is to learn Qt/KDE programming in Python. I've played around with Qt a bit and I'm starting to get comfy. I've decided to try doing the player as a KDE app, just to learn more about KDE. Qt is great for doing cross-platform stuff but I want to take a look at what KDE offers on top of Qt. You can find docs, tutorials and more on &lt;a href="http://techbase.kde.org/"&gt;KDE TechBase&lt;/a&gt;.The following snippet shows the minimum amount of code needed to create a typical KDE app in Python.&lt;br /&gt;&lt;pre class="python" name="code"&gt;from PyKDE4 import kdecore&lt;br /&gt;from PyKDE4 import kdeui&lt;br /&gt;import sys&lt;br /&gt;&lt;br /&gt;def createAboutData():&lt;br /&gt;    """&lt;br /&gt;    Create a KAboutData with information about this application.&lt;br /&gt;    """&lt;br /&gt;    return kdecore.KAboutData(&lt;br /&gt;            # Program name used internally&lt;br /&gt;            "hello",&lt;br /&gt;            &lt;br /&gt;            # Catalog name&lt;br /&gt;            "",                       &lt;br /&gt;            &lt;br /&gt;            # Displayable program name&lt;br /&gt;            kdecore.ki18n("Hello World app"),&lt;br /&gt;            &lt;br /&gt;            # Program version&lt;br /&gt;            "0.1.0",&lt;br /&gt;            &lt;br /&gt;            # Short description about the program&lt;br /&gt;            kdecore.ki18n("A simple KDE Hello World app"),&lt;br /&gt;            &lt;br /&gt;            # Program license&lt;br /&gt;            kdecore.KAboutData.License_BSD,&lt;br /&gt;            &lt;br /&gt;            # Copyright statement&lt;br /&gt;            kdecore.ki18n ("(c) 2009 Mario Boikov"),&lt;br /&gt;            &lt;br /&gt;            # Free form text&lt;br /&gt;            kdecore.ki18n("Free form text\nsupporting newlines"),&lt;br /&gt;            &lt;br /&gt;            # Home page address for this program&lt;br /&gt;            "http://www.pysnippet.com",&lt;br /&gt;            &lt;br /&gt;            # Bug report email address&lt;br /&gt;            "mario@beblue.org",&lt;br /&gt;            )&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;    about = createAboutData()&lt;br /&gt;    kdecore.KCmdLineArgs.init(sys.argv, about)&lt;br /&gt;&lt;br /&gt;    app = kdeui.KApplication()&lt;br /&gt;&lt;br /&gt;    # INSERT APP CODE HERE&lt;br /&gt;&lt;br /&gt;    # Start event loop&lt;br /&gt;    sys.exit(app.exec_())&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;Nothing special will happen if you execute this application, we need some kind of window/widget to display something. To accomplish this we can create a &lt;a href="http://api.kde.org/pykde-4.2-api/kdeui/KMainWindow.html"&gt;KMainWindow&lt;/a&gt; with a &lt;a href="http://api.kde.org/pykde-4.2-api/kdeui/KPushButton.html"&gt;KPushButton&lt;/a&gt;. We'll also add code to print &lt;i&gt;Hello World&lt;/i&gt; when the button is clicked. The following snippet will create a window with a push button:&lt;br /&gt;&lt;pre class="python" name="code"&gt;from PyKDE4 import kdeui&lt;br /&gt;from PyKDE4 import kdecore&lt;br /&gt;from PyQt4 import QtCore&lt;br /&gt;&lt;br /&gt;class MainWindow(kdeui.KMainWindow):&lt;br /&gt;    def __init__(self):&lt;br /&gt;        super(MainWindow, self).__init__()&lt;br /&gt;&lt;br /&gt;        # Create widget with vertical box layout manager&lt;br /&gt;        layout = kdeui.KVBox()&lt;br /&gt;&lt;br /&gt;        # Create push button and add to layout&lt;br /&gt;        button = kdeui.KPushButton(kdecore.i18n("Push Me!"), layout)&lt;br /&gt;&lt;br /&gt;        # Connect to button's 'clicked' signal &lt;br /&gt;        QtCore.QObject.connect(button, QtCore.SIGNAL("clicked()"),&lt;br /&gt;                               self.buttonClicked)&lt;br /&gt;&lt;br /&gt;        self.setCentralWidget(layout)&lt;br /&gt;&lt;br /&gt;    def buttonClicked(self):&lt;br /&gt;        print "Hello World!"&lt;br /&gt;&lt;/pre&gt;You might wonder why I use &lt;i&gt;ki18n()&lt;/i&gt; when creating the &lt;a href="http://api.kde.org/pykde-4.2-api/kdecore/KAboutData.html"&gt;KAboutData&lt;/a&gt; and &lt;i&gt;i18n()&lt;/i&gt; for the MainWindow. Both functions do translations, &lt;i&gt;i18n()&lt;/i&gt; requires an instance of &lt;a href="http://api.kde.org/pykde-4.2-api/kdeui/KApplication.html"&gt;KApplication&lt;/a&gt; before use but &lt;i&gt;ki18n()&lt;/i&gt; don't.&lt;br /&gt;&lt;br /&gt;To actually show the window we have to instantiate it and make it visible, the "INSERT APP CODE HERE" comment should be replaced with the following lines:&lt;br /&gt;&lt;pre class="python" name="code"&gt;    mainWindow = MainWindow()&lt;br /&gt;    mainWindow.show()&lt;br /&gt;&lt;/pre&gt;As you can see, it doesn't take too much effort to create a simple app in KDE. There's almost more code to create the &lt;a href="http://api.kde.org/pykde-4.2-api/kdecore/KAboutData.html"&gt;KAboutData&lt;/a&gt; than doing the window with a push button.Here's a screen shot of what the window looks like on my Kubuntu installation:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_ilsnPCKxGBM/StDvwhUEaoI/AAAAAAAAAAM/jFBQmWEz20w/s1600-h/kde_hello_world.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_ilsnPCKxGBM/StDvwhUEaoI/AAAAAAAAAAM/jFBQmWEz20w/s320/kde_hello_world.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;If we compare Qt and KDE there aren't too much differences, at least with this example. The code below implements the same Hello World app made with Qt:&lt;br /&gt;&lt;br /&gt;&lt;pre class="python" name="code"&gt;import sys&lt;br /&gt;from PyQt4 import QtCore, QtGui&lt;br /&gt;&lt;br /&gt;class MainWindow(QtGui.QMainWindow):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def __init__(self, title):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(MainWindow, self).__init__()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.setWindowTitle(title)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; centralWidget = QtGui.QWidget()&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; button = QtGui.QPushButton("Push Me!")&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; layout = QtGui.QVBoxLayout()&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; layout.addWidget(button)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; centralWidget.setLayout(layout)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; QtCore.QObject.connect(button, QtCore.SIGNAL("clicked()"),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.buttonClicked)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; self.setCentralWidget(centralWidget)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; def buttonClicked(self):&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; print "Hello World!"&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; app = QtGui.QApplication(sys.argv)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; mainWindow = MainWindow("Hello World App")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; mainWindow.show()&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; sys.exit(app.exec_())&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; main()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;The biggest difference is how the layout is handled and the absence of &lt;i&gt;About Data&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-3009540120479181598?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/3009540120479181598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/kde-hello-world.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3009540120479181598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/3009540120479181598'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/kde-hello-world.html' title='KDE Hello World'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_ilsnPCKxGBM/StDvwhUEaoI/AAAAAAAAAAM/jFBQmWEz20w/s72-c/kde_hello_world.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-8904464234432948846</id><published>2009-10-07T21:20:00.021+02:00</published><updated>2009-10-07T21:50:23.149+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unittesting'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Python and unit testing</title><content type='html'>Writing unit tests in python is really easy. It's the dynamic properties of the language which makes it very simple to mock class methods, instance methods and functions. This makes the task of writing isolated unit tests easy when you practice &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;test-driven development&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Say you have the following class:&lt;br /&gt;&lt;pre class="python" name="code"&gt;&lt;br /&gt;import urllib2&lt;br /&gt;&lt;br /&gt;class UrlDownloader(object):&lt;br /&gt;    def download(self, url):&lt;br /&gt;        """&lt;br /&gt;        Read the content from the url.&lt;br /&gt;        The content is returned as a string object.&lt;br /&gt;        """&lt;br /&gt;        return urllib2.urlopen(url).read()&lt;br /&gt;&lt;/pre&gt;You actually don't want to download something from the net when you do a unit test for this class. You would like to have a fixed string returned from the read() method. This can be hard to achieve in some languages (for example Java) but that's not true for Python. Python supports redefining class methods, functions and other things in run-time. The following snippet shows how easy it's to mock the &lt;a href="http://docs.python.org/library/urllib2.html#urllib2.urlopen"&gt;urlopen()&lt;/a&gt; function in the &lt;a href="http://docs.python.org/library/urllib2.html"&gt;urllib2&lt;/a&gt; module to return a mocked file-like object:&lt;br /&gt;&lt;pre class="python" name="code"&gt;import urllib2&lt;br /&gt;import unittest&lt;br /&gt;&lt;br /&gt;class MockFileObject(object):&lt;br /&gt;    def read(self):&lt;br /&gt;        return "DATA"&lt;br /&gt;&lt;br /&gt;def mock_open(url):&lt;br /&gt;    return MockFileObject()&lt;br /&gt;&lt;br /&gt;class UrlDownloaderTest(unittest.TestCase):&lt;br /&gt;    def testDownload(self):&lt;br /&gt;        urllib2.urlopen = mock_open&lt;br /&gt;        dl = UrlDownloader()&lt;br /&gt;        self.assertEqual(dl.download(""), "DATA")&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    unittest.main()&lt;br /&gt;&lt;/pre&gt;In this unit test we redefine the urlopen-function (remember, this is not a member of an instance, it's a plain function) with our own which returns a mock file-like object. It's not that hard to imagen all the possibilities this gives when writing tests.There are of course frameworks that will help you with creating mocks etc, you could try &lt;a href="http://python-mock.sourceforge.net/"&gt;The Python Mock Module&lt;/a&gt; which should cover most of the use cases. &lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-8904464234432948846?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/8904464234432948846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/python-and-unit-testing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8904464234432948846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/8904464234432948846'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/python-and-unit-testing.html' title='Python and unit testing'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-4507799069120904268</id><published>2009-10-02T13:45:00.068+02:00</published><updated>2009-10-05T08:24:48.134+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unittesting'/><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><category scheme='http://www.blogger.com/atom/ns#' term='plsparser'/><title type='text'>File-like objects</title><content type='html'>A Python &lt;i&gt;file-like&lt;/i&gt; object can be seen as an abstraction to input/output streams. They are actually similar to Java's java.io.Reader/java.io.Writer abstraction (I'm coming from Java). The typical usage of file-like objects can be found in frameworks and APIs. The file-like object provides a generic way of reading/writing data and would fit an HTML-parser for example. The parser is not interested of where the data come from (files, sockets, etc), only the content, so naturally the implementation should not depend on how to open resources etc.&lt;br /&gt;&lt;br /&gt;Basic methods that need to be implemented by a file-like object are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;read([size]) - read at most &lt;i&gt;size&lt;/i&gt; bytes from the file.&lt;/li&gt;&lt;li&gt;readLine([size]) - read one entire line from the file.&lt;/li&gt;&lt;li&gt;write(str) - write a string to the file.&lt;/li&gt;&lt;li&gt;close() - close the file. &lt;/li&gt;&lt;/ul&gt;You should read the documentation of the function/method that uses the file-like object, often they specify which methods that are expected to be implemented by the file-like object. If the file-like object is only used for reading, no write-functions need to be supported. More information about file-like objects can be found &lt;a href="http://docs.python.org/library/stdtypes.html#bltin-file-objects"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Ok, so why should you use file-like objects in your code?&lt;br /&gt;&lt;br /&gt;Let's take a look at the &lt;a href="http://docs.python.org/library/configparser.html"&gt;ConfigParser&lt;/a&gt; class which can be used to read/write configuration data. You can expect to find methods for reading and writing configuration data using files but the class does also include support for file-like objects, which is great. For example it's very easy to write &lt;a href="http://docs.python.org/library/unittest.html"&gt;unittests&lt;/a&gt; for this class, thanks to the support of file-like objects,&amp;nbsp; without having to create real files with test data. To create a file-like object from a string we can use the &lt;a href="http://docs.python.org/library/stringio.html"&gt;StringIO&lt;/a&gt; module. The module have both read and write support.&lt;br /&gt;&lt;br /&gt;Here's some code:&lt;br /&gt;&lt;pre class="python" name="code"&gt;import unittest&lt;br /&gt;import StringIO&lt;br /&gt;import ConfigParser&lt;br /&gt;&lt;br /&gt;class ConfigParserTest(unittest.TestCase):&lt;br /&gt;&lt;br /&gt;    def testHasSection(self):&lt;br /&gt;        """&lt;br /&gt;        has_section() should return True if the section exists&lt;br /&gt;        and False if is doesn't.&lt;br /&gt;        """&lt;br /&gt;        data = StringIO.StringIO("[My section]\nSetting=1\n")&lt;br /&gt;        config = ConfigParser.ConfigParser()&lt;br /&gt;        config.readfp(data)&lt;br /&gt;        self.assertTrue(config.has_section("My section"))&lt;br /&gt;        self.assertFalse(config.has_section("Not a section"))&lt;br /&gt;&lt;br /&gt;    def testMissingHeader(self):&lt;br /&gt;        """&lt;br /&gt;        readfp() should raise MissingSectionHeaderError&lt;br /&gt;        when parsing a file without section headers.&lt;br /&gt;        """&lt;br /&gt;        data = StringIO.StringIO("\nSetting=1\n")&lt;br /&gt;&lt;br /&gt;        config = ConfigParser.ConfigParser()&lt;br /&gt;        self.assertRaises(ConfigParser.MissingSectionHeaderError,&lt;br /&gt;                          config.readfp, data)&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    unittest.main()&lt;br /&gt;&lt;/pre&gt;And a simple &lt;i&gt;write configuration&lt;/i&gt; test:&lt;br /&gt;&lt;pre class="python" name="code"&gt;import unittest&lt;br /&gt;import StringIO&lt;br /&gt;import ConfigParser&lt;br /&gt;&lt;br /&gt;class WriteConfigTest(unittest.TestCase):&lt;br /&gt;&lt;br /&gt;    def testWriteConfig(self):&lt;br /&gt;        """&lt;br /&gt;        Test write a simple config&lt;br /&gt;        """&lt;br /&gt;        data = StringIO.StringIO()&lt;br /&gt;        config = ConfigParser.ConfigParser()&lt;br /&gt;        config.add_section("My Section")&lt;br /&gt;        config.set("My Section", "my_option", "my_value")&lt;br /&gt;        config.write(data)&lt;br /&gt;&lt;br /&gt;        expected = "[My Section]\nmy_option = my_value\n\n"&lt;br /&gt;        self.assertEquals(expected, data.getvalue())&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    unittest.main()&amp;nbsp;&lt;/pre&gt;&lt;i&gt;These unittests are of course only examples and do not fully test the ConfigParser class.&lt;/i&gt;&lt;br /&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;&lt;br /&gt;It's easy to see that file-like objects are very handy and fits well in frameworks and APIs.There are of course many advantages of using an abstraction when reading/writing data, some of them are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Implementations do not depend on real files, socket, etc, only on a file-like object.&lt;/li&gt;&lt;li&gt;Easier to fake data in unittests.&lt;/li&gt;&lt;li&gt;Caller responsible of opening/closing the resource.&lt;/li&gt;&lt;li&gt;Less error-handling in framework code, delegate exceptions to the caller.&lt;/li&gt;&lt;/ul&gt;The downside with the current file-like object abstraction is that it's tailored for text I/O. In Python 2.6 and 3.x a new module have been added named &lt;a href="http://docs.python.org/library/io.html"&gt;io&lt;/a&gt;. The module originates from the New I/O &lt;a href="http://www.python.org/dev/peps/pep-3116/"&gt;PEP&lt;/a&gt;. The module enriches Python with an three-layer solution, the raw I/O layer, the buffered I/O layer and finally the text I/O layer. The raw and buffered layers deals with units of bytes and the text layer deals with units of characters. There are some things you should be aware of if you use the new I/O library in Python 2.6, read more about it &lt;a href="http://www.wingware.com/psupport/python-manual/3.0/whatsnew/2.6.html#pep-3116-new-i-o-library"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I've implemented a shoutcast playlist parser, &lt;a href="http://github.com/mariob/plsparser"&gt;plsparser&lt;/a&gt;, using the ConfigParser class. The parser is implemented as a Python generator function. Actually, I might do a post about generator functions someday.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-4507799069120904268?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pysnippet.blogspot.com/feeds/4507799069120904268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pysnippet.blogspot.com/2009/10/file-like-objects.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4507799069120904268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/4507799069120904268'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/file-like-objects.html' title='File-like objects'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7970464356958190643.post-6527507645744978359</id><published>2009-10-01T12:46:00.005+02:00</published><updated>2009-10-01T20:40:13.824+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Python'/><title type='text'>Hello World</title><content type='html'>What would be more appropriate than a simple &lt;i&gt;Hello World&lt;/i&gt; as the first code snippet!&lt;br /&gt;&lt;pre class="python" name="code"&gt;def main():&lt;br /&gt;    print "Hello World"&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    main()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7970464356958190643-6527507645744978359?l=pysnippet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6527507645744978359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7970464356958190643/posts/default/6527507645744978359'/><link rel='alternate' type='text/html' href='http://pysnippet.blogspot.com/2009/10/hello-world.html' title='Hello World'/><author><name>Mario</name><uri>http://www.blogger.com/profile/02460214494179780692</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>
