Aurélien's room

I mess with user interfaces, write code (mostly Qt & KDE stuff) and occasionally rant, but I (usually) don't bite

What’s new in Gwenview from KDE 4.8

Now that KDE 4.8 has been released, it’s time to recap all changes you will find in Gwenview.

The main change is the addition of animations when viewing images: crossfading between images and nicer-to-use comparisons. You can learn more from this previous blog article.

This change was not nice for users of some graphic cards whose OpenGL drivers do not support what Gwenview tries to do. I decided to play it safe for now: animations in Gwenview now use software rendering by default. For better performance, you can enable OpenGL rendering in the configuration dialog.

This new version of Gwenview also comes with a lot of smaller changes, some of them caused by the limitations which were introduced by the new animation system.

Scrolling and Zooming

  • No more scrollbars: A bird-eye view lets you scroll the image.
  • Nicer zoom cursor. I realized Qt now supports truecolor cursors, so I drew a nicer magnifying glass cursor instead of the black+white+1bit-alpha-channel version. Holding down Ctrl to zoom won’t bring you back to the 90s anymore!
  • Pressing ‘F’ toggles zoom-to-fit on and off.
  • More consistent behavior: SVG images can now be scrolled using the same shortcuts as scrolling raster images.

Global User interface changes

  • The “sidebar collapser”, the little arrow on the left of the view which let you hide the sidebar is gone. It has been replaced by a button in the statusbar.
  • Labels for some of the toolbar buttons have been removed, reducing its width. It should now be more usable on small netbook-like screens.

Tools

  • The red-eye reduction and crop tools no longer show floating widgets over the image, a thin bar slides from the bottom of the window instead.

Behavior

  • Compared images follow thumbnail view order: previously when one selected two or more images to compare them, they would not necessarily appear in the same order as in the thumbnail bar.
  • Arrow-key navigation in zoom-to-fit mode. This one has been requested by quite a few people. When an image is in zoom-to-fit mode, you can go to the previous and next image with the arrow keys. When you zoom in, arrow keys are used to scroll the image. This is very similar to the behavior provided by phones or digital cameras.

Video support

  • The on-screen-display is now transparent.
  • One can use the left and right arrow keys to seek
  • A late addition: an undocumented shortcut (P) to toggle playback
  • Note that video support is a bit fishy right now: it seems Phonon does not always play well with its video widget being embedded in a QGraphicsView, known symptoms are wrong colors or wrong aspect ratio. Hopefully this will improve in the next releases.

4.8.1 should bring you its usual series of bug fixes, among them is generating thumbnails for all images of the current folder, not only the currently visible ones. This fix is a bit bigger than your usual .1 fixes, so if you are willing to test it, you are welcome. The code is available on Gwenview git repository, in the “gen-all-thumbnails” branch.

I hope you enjoy this new Gwenview!


Flattr this

Swimming against the stream or preparing for next stream change? PyQt+WebKit experiments

Introduction

It is an interesting time to be a Qt developer: new ways to develop applications are emerging, one can either ignore them or experiment… I like experimentations.

In one corner we have traditional, QWidget-based Qt development.

In another corner we have QML, a declarative language coupled with JavaScript, which brings a whole new way to think about application development. Qt developers are betting a lot on QML, planning for it to be the preferred way to develop new applications with Qt 5.

In yet another corner is the Web, a perfect demonstration of technology hijacking: what was once designed to present structured documents has become one of the most powerful way to create applications.

Of those three technologies, I believe QML is the most innovative and powerful one, and I had high hopes it could secure a significant market share when Nokia and Intel were still planning to deliver it on their upcoming smart-phones and tablets. Now that Nokia switched to WP7 and Intel switched to EFL, I am a lot less confident QML will succeed. Experience shows that when two technologies are in competition, the best one does not automatically wins, parameters like market share and learning curve are as important as, if not more important than, technical excellence.

Not willing to ignore the behemoth that is the Web, I started an experiment: how would it feel to develop an application which would mix Qt and HTML rendering? Thanks to QtWebKit, this kind of integration is easy to achieve. My experimentation subject is based on Yokadi, a command-line based TODO list I work on. I decided to create a graphical frontend for it. The result is this:


This application, named QYok (yes, I suck at naming applications, suggestions for better names are welcome!), is an interesting mix of technologies. It uses:

  • PyQt, for the main window and most of the widgets
  • QtWebKit, to display the task lists
  • jQuery, to make it easier to manipulate the HTML and provide nice animations
  • Jinja2, a Python-based template system, using a syntax similar to Django (and thus Grantlee)
  • and of course, Yokadi itself, to provide access to the TODO database

In this series of articles, I am going to describe how one can mix PyQt and WebKit together, based on my learnings from the QYok project. In particular I want to show ways to generate native-looking HTML code, to ensure your application does not look alien on your desktop.

Getting a Qt application to show HTML code

This is the first, very easy, step. Here is a complete example:

import sys

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *

class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        view = QWebView(self)
        layout = QVBoxLayout(self)
        layout.addWidget(view)

        html = """
            <html><body>
            Hello World!
            </body></html>
            """
        view.setHtml(html)

def main():
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    app.exec_()

if __name__ == "__main__":
    main()

(Source code)

What all this does is create a window, create a webview in it and set some HTML. Nothing fancy.

HTML is nicer with images

One of the main point of using HTML is that it makes it reasonably easy to create complex documents which include images. So let’s add a folder named “static” to our code folder, with an image in it. Since we are feeding QWebView with HTML, it has no way to know where to look for our images. Luckily, setHtml() accepts a second parameter: the base url of the document. Any relative url contained in our HTML will be resolved using this url as a base.

Here is the modified call to setHtml():

pyDir = os.path.abspath(os.path.dirname(__file__))
baseUrl = QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
html = """
    <html><body>
    <div>Hello World!</div>
    <img src="test.png"/>
    </body></html>
    """
view.setHtml(html, baseUrl)

(Source code)

Be careful: always ensure baseUrl ends up with a trailing slash! If there is no trailing slash, QWebView will resolve “test.png” as “/path/to/tut1/statictest.png” instead of “/path/to/tut1/static/test.png”. I learnt that the hard way…

Calling Qt code from JavaScript

QWebView.setHtml() makes it easy for Qt code to provide HTML content to WebKit, but it is a one-way only setup: it doesn’t provide a way for JavaScript code to call Qt code. The way to do this is by exposing objects created on the Qt side to QWebView (or more precisely, to the main frame of the QWebPage inside QWebView).

QtWebKit takes advantage of Qt introspection features to access object methods and properties. To be exposed to JavaScript, an object must thus inherit from QObject and expose itself through Qt properties, Qt signals and Qt slots. Here is an example. First lets define a class named Foo which implements a compute() slot, performing complicated computations and a quit() slot:

class Foo(QObject):
    @pyqtSlot(int, result=int)
    def compute(self, value):
        return value * 2

    @pyqtSlot()
    def quit(self):
        QApplication.quit()

The important part here are the “@pyqtSlot()” decorators, which take care of turning our methods into slots. As you may have guessed, the decorator expects you to describe the type of the parameters accepted by your method, as well as the type of the returned value, if any.

Now that this done, lets create a Window class which will instantiate our Foo class and pass it to JavaScript:

class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        view = QWebView(self)
        layout = QVBoxLayout(self)
        layout.addWidget(view)

        self.foo = Foo(self)
        view.page().mainFrame().addToJavaScriptWindowObject("foo", self.foo)

        html = """
            <html>
            <head>
            <script>
            function updateEntry() {
                var element = document.getElementById("entry");
                var result = foo.compute(element.value);
                element.value = result;
            }
            </script>
            </head>

            <body>
            <div>
                <input type="text" id="entry" value="1"/>
                <input type="button" value="Compute" onclick="updateEntry()"/>
            </div>
            <div>
                <input type="button" value="Quit" onclick="foo.quit()"/>
            </div>
            </body>
            </html>
            """
        view.setHtml(html)

(Source code)

Clicking the “Compute” button doubles the value in the input widget, clicking the “Quit” button, quits the application.

In this example, we create self.foo, an instance of the Foo class, then expose it to our QWebView with the line:

view.page().mainFrame().addToJavaScriptWindowObject("foo", self.foo)

We then feed our QWebView with some HTML code with view.setHtml(). Note how JavaScript code can now refer to our foo object as if it was a native JavaScript object.

Conclusion

That’s it for now. Stay tuned for the next article.

PS: All examples are available from github: git clone git://github.com/agateau/pyqt-webkit-tutorial.git to get it.


Flattr this

Oups, my burst shots are all shuffled!

At the end of December my wife and I were invited with other parents to my daughter last dance lesson of the year.

Of course, I brought my camera with me. I shot a little bit more than 160 pictures from the one hour lesson (if that sounds like a crazy number, then you probably don’t have kids…).

Some of them were shot in burst mode, where the camera continuously takes a few pictures per second, increasing the chances the wannabe photographer gets at least one decent picture (and partially explaining the embarrassing large number of shots…)

Importing, aka, the mistake

Back home, I imported the pictures with Gwenview importer and started to comb through them to get rid of the cruft.

Burst mode is fun, but Gwenview importer does not play nice with it: by default it renames imported pictures using the shot date found in the image EXIF information. I like this feature because I find it more expressive to have a picture named “2011-12-17_12-47-47.jpg” than “pict0234.jpg”. It has one big drawback though: the precision of time information stored in EXIF is one second. When one shoots in burst mode, more than one picture per second is produced… In such a situation, Gwenview importer inserts a “_n” suffix just before the extension dot, where “n” starts at “1″ and is increased until the importer can create a file name which does not exist. It gets even a bit nastier when you realize Gwenview importer does not necessarily import pictures in file order, meaning the “n” values do not necessarily match the order in which the pictures were taken… not good.

Workarounds

I want to address this issue correctly in KDE SC 4.9, but meanwhile here are some workarounds.

If you haven’t yet imported your burst-mode pictures, you can either disable image renaming altogether or change the “Rename Format” from “{date}_{time}.{ext.lower}” to “{date}_{time}_{name.lower}.{ext.lower}”. This way imported images will still carry the original image name and will be properly ordered. File names will be a bit ugly, but at least you will be safe.

Configuring Gwenview importer to append the original file name at the end of imported pictures
Configuring Gwenview importer to append the original file name at the end of imported pictures

If you have already imported the pictures it gets a bit more tricky. I looked at the EXIF information my camera recorded and noticed pictures have a “Exif.Panasonic.SequenceNumber” tag (yes, that sounds vendor-specific :/). This tag is set to 0 for normal pictures, but starts at 1 and goes up for burst-mode pictures. I thus put together “pict-exif-rename”, a quick Python script using pyexiv2 to rename all images, appending a suffix based on the sequence number if it is greater than 0.

I pushed “pict-exif-rename” as a github gist, maybe you will find it useful.

It would be interesting to hear from different camera owners about the presence of the “Exif.Panasonic.SequenceNumber” tag (or an equivalent). You can quickly check its presence by running “exiv2 -Pkv a_picture.jpg | grep SequenceNumber”. All pictures taken with my camera (Panasonic DMC-FS16) have this tag, it is set to 0 for non burst-mode pictures.


Flattr this

Announcing Gwenview Forum

Gwenview has had a mailing-list for a very long time now, but time changes. Nowadays I believe forums are more appropriate for end-users discussions than mailing-lists for a few reasons:

  • Forum do not require setting up individual passwords for every topic you are interested in.
  • It is easier to join an existing conversation on a forum you just discovered than on a mailing list you have not subscribed yet.
  • It is easier for non-technical users to read and join a forum.

Therefore I asked for a Gwenview forum to be created on forum.kde.org. You can find it forum.kde.org/viewforum.php?f=213 (too bad forums do not get nicer urls :/). Come and ask your questions here!

I plan to phase out the mailing-list. I haven’t yet decided of a day when it will be turned off but it’s probably going to happen in a month or two. After that the only remaining bit of SourceForge infrastructure used for Gwenview will be the (outdated) website, maybe I’ll get around to create a site for Gwenview under kde.org domain…


Flattr this

Akademy FR at Capitole du Libre


Last weekend I went back to the beautiful city of Toulouse for Capitole du Libre, a free software event made of three co-hosted events: Akademy FR, Ubuntu Party and Drupal Camp. It was great to meet the usual gang of Toulibre KDE people and put faces on people I only knew from #kde-fr, the friendly French-speaking Freenode IRC channel. We even had Aleix Pol, of KDevelop and KAlgebra fame, coming from Barcelona to help us.

Saturday

Saturday, was the busiest day: we had a KDE booth and ran two conference tracks: users and contributors.

Being co-hosted with the Ubuntu Party brought in a different population from previous KDE Release Parties. We had nice conversations on the KDE booth with Ubuntu users who heard about KDE but did not really know what it looked like or even what it really was. We did our best to sell our beautiful products. The Plasma Active powered Wetab tablet made a very good impression. It is interesting how people had a much easier time understanding the concept of activities on the tablet than on traditional desktops.

Unfortunately I didn’t take any picture of the booth except for this one, our DIY KDE logo. Yes, it could have used an extra row at the bottom, but we were running out of “K”!

The conference tracks were quite packed as well. I gave a presentation on QML development, which went OK I think. My slides should be online soon. I wanted to attend the MyPaint presentation but it was unfortunately scheduled at the same time as mine. Will look at the video.

Sunday

Sunday was dedicated to workshops. It was a lot less crowded: the workshops mostly attracted people who were already familiar with free software. I attended Zanshin workshop. One of my many side-projects is a todo list system so I have to keep an eye on the competition :) Since I am a nice guy, I put together Ubuntu 11.10 packages for Zanshin 2.0 in my PPA.

After a nice pizza-powered lunch, I spent some time chatting with various people before leaving to Toulouse airport. The event was great, I hope there will be another one next year!


Flattr this

One year after

It has been a bit more than one year since I announced I was reducing my work hours to 4 days per week so that I would get one day to work on what I want to work on. One year after, it is time to look back how it went.

Donations

I was curious to find out how much money could be generated by asking for donations. To this end I wrote monthly wrap-ups, with the idea that people would be more motivated to donate if they knew what would be done. It started out reasonably well, but quickly slowed down. At some point I decided writing those wrap-ups was not worth the pain: They did not generate much, were getting tedious to write and made me look like I was begging for money when there are charities which are much more in need of money than me. I also experimented with Flattr. I like the idea behind Flattr and intend to continue using it.

A book, or not

Another project I worked on for a few months was a Qt book. Writing a book is a challenge I have been willing to take for a long time. A French editor was looking for an author to write a book on Qt, so I contacted them and we started to work together.
It did not turn out that well unfortunately and I decided to abort the project for two reasons: the book was supposed to target Qt 5, but the lack of a firm release date made it impossible for me to sign myself for a date when I could hand out a final script. More importantly, the editor wanted the book to be written following a specific organization which did not fit with what I had in mind: after two dozen pages, writing no longer fun, and a book which is not pleasant to write cannot be pleasant to read, in my opinion.

tropmignon.com

In my last year post, I also mentioned a web-based project. The idea is to provide a place for parents to share amusing quotes from their children… I assume if you don’t have kids it sounds a bit silly :) On the other hand, if you do have kids (and speak French) you should check out tropmignon.com! I didn’t get as much as I expected done on it, because I decided to put tropmignon.com on hold when I started working on the Qt book. Now that the book project has been canceled I resumed working on the site. It still lacks important features like Facebook login or comments, but they will come. This project is also a nice way for me to brush up my dusty web development skills.

KDE

Finally I have been busy working on KDE this year and intend to continue doing so. Until 4.8 is out, I am concentrating on Gwenview: the QGraphicsView port was quite involved and there are quite a few rough areas I want to get polished before it can be considered good enough.

That’s it for this year, we’ll see how it feels in November 2012!


Flattr this

Crossfade!

I have been a bit quiet lately, but haven’t been slacking :) Among other things, I started working on implementing one of the oldest most-wanted feature of Gwenview: crossfade transitions between images. I have pushed the code to Gwenview git repository. It still lives in topic branches for now though.

I first worked with an evolution of the QWidget-based code. This is quite stable but it is too slow at HD (1920×1080) resolution. You can find this work in the “agateau/xfade” branch.

I thus decided to bite the bullet and start a massive refactor of the code, rebasing it on QGraphicsView with an OpenGL viewport. This is the much-faster-but-currently-full-of-regressions “agateau/xfade-qgv” branch.

This change makes Gwenview much smoother to use when going through images but it also helps quite a bit in comparison mode: images move around when selected and unselected, making it easier to track where they go.

I recorded a short video to demonstrate the current state of the xfade-qgv branch. It demonstrates both going through images and image comparison. In this video Gwenview runs on a HD monitor. Note that I had to record it with a digital camera, desktop recorders did not produce a smooth animation, so the video quality is not exactly optimal.


(Watch on blip.tv)

I hope I can bring the xfade-qgv branch back to feature-parity with master so that it can be merged in before Soft Feature Freeze (October 27th!).


Flattr this

Make Skype a bit less shy

I already wrote some time ago about sni-qt, a Qt plugin which turns old-school systemtray icons from Qt applications into shiny StatusNotifierItem | AppIndicators.

Last week I was busy debugging a problem with Skype and sni-qt. To do so I added a runtime config option to enable debugging in sni-qt. It is quite efficient at flooding stderr with debug messages for all applications I have tried it on, except for Skype:

Disappointing! Come on Skype, talk to me!

I don’t give up that easily, so I decided to fiddle around with gdb.

I first tried to start Skype from within gdb with the “start” function. This function causes gdb to set a temporary breakpoint on main() and start the program. This is quite handy because when your program reachs the breakpoint the application has not started yet, but most libraries (except dlopen-ed ones) are already loaded, making it easier to set breakpoints. No such luck with Skype: for some reason gdb didn’t find the main() function and did not stop Skype at startup. Bummer.

Then I remembered I recently had a discussion about “qt_startup_hook()”, a function which is called at the end of the QCoreApplication constructor. This is close enough from startup to me. I set up a breakpoint on “qt_startup_hook” and said gdb to run Skype. That worked! Cool.

Now, I am stopped inside Skype, what to do next? A not so well-known feature of Qt logging system lets you install your own message handler for logging functions with the qInstallMsgHandler(yourHandler) function. Your message handler then receives all the output from qDebug(), qWarn() and friends. To go back to the default message handler, just call “qInstallMsgHandler(0)”. Not really confident in it, I tried to call qInstallMsgHandler(0) from within gdb. It turns out that did the trick: after calling resetting the message handler and asking gdb to continue, I got my error messages (plus Skype own debug messages, probably not a major achievement in reverse-engineering, but that was fun :) )

Oh and by the way, if you run {,K}Ubuntu Oneiric on a 64bit machine and want to get your Skype icon turned into a StatusNotifierItem, be sure to install the i386 version of sni-qt (you can do so with “sudo apt-get install sni-qt:i386″). Skype still does not provide 64bit binaries :(


Flattr this

Of pastry and Blogilo…

My wife enjoys cooking and recently got into creating decorated cakes. I suggested she starts a blog to show her creations instead of only sharing them on Facebook.

She had some prior experience with WordPress, but found the web-based editor painful to use: our Internet connection is not very fast so uploading images with the web-based editor is a pain. It would have been especially annoying to populate the blog with her existing pictures. So I looked around and gave Blogilo a try.
Blogilo is quite nice, but I found out it felt a bit cramped on her 1024×600 netbook, so I decided to look into it, see if I could make it more usable on a small screen. After a few commits, I find it a bit nicer. Mandatory before/after picture:
Not mentioned on this picture are the following changes:
  • The first row of tabs is hidden when only one entry is edited. This saves a bit of vertical space.
  • Icons and tooltips of some buttons in the toolbox have been adjusted to be more consistent with the rest of KDE.
  • Toolbox visibility is now correctly remembered at startup. Hiding the toolbox of course saves a lot of horizontal space.
  • Size of editor toolbar icons is no longer hard-coded to 22×22.
With those changes implemented we were able to populate her blog quickly. There is still room for improvements though, in particular I would like to have support for thumbnails. Being forced to use full-sized images is a pain.
Oh… and if you are into pastry, be sure to check lesdelicesdegwen.com!


Flattr this

Desktop Summit 2011

I am back from Desktop Summit, and off for holidays (yeah!). As usual, it was energizing and exhausting at the same time.

Presentations

We were treated with interesting presentations (and a few not so interesting ones :/). I particularly enjoyed these presentations:

Compositing after X – KWin on the Road to Wayland, by Martin Gräßlin

Martin presented his plans for Wayland, explaining how he plans to progressively rebase KWin on top of Wayland without “breaking the desktop”.

Building Bridges: Making KDE accessible, by Frederik Gladhorn

Frederik explained quite clearly the challenges with making Qt and KDE programs accessible, how to expose your user interface in an accessible way and why it was critical to make sure applications have good keyboard navigation.

Krita: the making of an artist’s tool, by Boudewijn Rempt

It was enlightening to see how much work has gone into Krita. Their strongest achievement in my opinion reaching out to real artists and managing to get them involved in the project, to the point where the developers created a script so that the artists could easily run the Git version of Krita. The slideshow running in the background of Boudjewin presentation was a nice testament of what the artists were able to create with Krita.

Software with the Quality that has no name, by Federico Mena Quintero

I am personally not sure we achieve a lot in term of cross-desktop work by merging Akademy and Guadec, but if there ever was one good reason to justify the merge, it would be Federico session, which I would never had the opportunity to attend if Guadec has been held as a separate event. His talk was about the design patterns you find in building architecture but which can also be found in user interface or in software design. It is very hard to summarize so I highly recommend you to check the video if there is one (hopefully it has been recorded). Federico also made his slides and notes available on his blog.

KDE Platform 5 Roadmap, by Aaron Seigo, David Faure, Kevin Ottens and Steven Kelly

In this talk, Aaron, David, Kevin and Steven presented the plan they put together during the KDE Platform 11 sprint. The core idea is to split our current KDE libs (kdecore, kdeui, kio, kfile,…) into smaller libraries, with a much more precisely defined set of dependencies, making it possible for “Qt-only” applications to reuse parts of KDE technologies without dragging in too many dependencies.

If you want to learn more, I recommend checking the KDE Frameworks Matrix describing the categories they defined and KDE Frameworks Dependencies Plan which shows how the existing libraries are planned be refactored and split to fit into these categories.

Introducing the Board, by Lucas Rocha

Lucas presented The Board, a project he started when he became a father. It is a fresh approach to the problem of keeping various bits of information together, those bits of information could be pictures, voice memo, notes, videos, urls… The project looks intriguing and the presentation was really nice.

We’re a family! — How five years of University Collaboration changed our town landscape, by Kevin Ottens

For the last five years, Kevin has been teaching software engineering at Toulouse University. Instead of having its students working on useless student projects, he got them to contribute to KDE projects. This talk was really invigorating, featuring Kevin as well as several students from all the generations. It even included a video from Alexis Ménard, one of the students from the first generation who became a Plasma developer and was able to land a job at Qt Software. Alexis explained he could not attend this year because he is busy training the future generation of KDE hackers, he is now the proud father of a beautiful little girl :)

Hacking

Desktop Summit is also a time for hacking. My most important challenge this year was holding a KDE UI Clinic session, where KDE developers could come and get some usability and design advices to improve their applications.

I was afraid by two things: being overwhelmed by the number of requests and spending too much time on each application, not being able to look at all requests. Luckily Björn Balazs of OpenSource Usability Labs came in to help, Thanks Björn! Having more practitioners was really nice because even if the number of requests were not as large as I expected, we spent a very long time on each of them. For example I only looked at two applications: KGet and Video Catcher, and it ended up taking me 3 hours… Luckily there was no other BoF I absolutely wanted to attend after the KDE UI Clinic slot.

We may also have recruited a new practitioner, more on this later…

Apart from the KDE UI Clinic session, I also worked on other projects:

  • Spent some time with Kåre Särs to rework libksane API so that it does not enforce a single widget layout. This should help to continue the work I started on Skanlite.
  • Revived a side-project of mine: Annot8. Who knows, maybe I can get it to release quality? Right now it is reasonably useful, but the UI is horrible and there are a few shortcuts I took that I would be too ashamed to release.
  • Reviewed Simon UI with Frederic Gladhorn: the project is impressive, but the UI really needs to be simplified.
  • Finally, worked a bit with Aleix Pol to improve the first page of the “new project” wizard in KDevelop.

This post is already long enough, I am heading back to my vacations for now. See you in September!


Flattr this

Follow

Get every new post delivered to your Inbox.